Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / backends / chrome / chrome_browser_backend.py
1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import contextlib
6 import httplib
7 import json
8 import logging
9 import pprint
10 import re
11 import socket
12 import sys
13 import urllib2
14
15 from telemetry import decorators
16 from telemetry.core import exceptions
17 from telemetry.core import forwarders
18 from telemetry.core import user_agent
19 from telemetry.core import util
20 from telemetry.core import web_contents
21 from telemetry.core import wpr_modes
22 from telemetry.core import wpr_server
23 from telemetry.core.backends import browser_backend
24 from telemetry.core.backends.chrome import extension_backend
25 from telemetry.core.backends.chrome import system_info_backend
26 from telemetry.core.backends.chrome import tab_list_backend
27 from telemetry.core.backends.chrome import tracing_backend
28 from telemetry.timeline import tracing_timeline_data
29 from telemetry.unittest import options_for_unittests
30
31
32 class ChromeBrowserBackend(browser_backend.BrowserBackend):
33   """An abstract class for chrome browser backends. Provides basic functionality
34   once a remote-debugger port has been established."""
35   # It is OK to have abstract methods. pylint: disable=W0223
36
37   def __init__(self, supports_tab_control, supports_extensions, browser_options,
38                output_profile_path, extensions_to_load):
39     super(ChromeBrowserBackend, self).__init__(
40         supports_extensions=supports_extensions,
41         browser_options=browser_options,
42         tab_list_backend=tab_list_backend.TabListBackend)
43     self._port = None
44
45     self._supports_tab_control = supports_tab_control
46     self._tracing_backend = None
47     self._system_info_backend = None
48
49     self._output_profile_path = output_profile_path
50     self._extensions_to_load = extensions_to_load
51
52     if browser_options.netsim:
53       self.wpr_port_pairs = forwarders.PortPairs(
54           http=forwarders.PortPair(80, 80),
55           https=forwarders.PortPair(443, 443),
56           dns=forwarders.PortPair(53, 53))
57     else:
58       self.wpr_port_pairs = forwarders.PortPairs(
59           http=forwarders.PortPair(0, 0),
60           https=forwarders.PortPair(0, 0),
61           dns=None)
62
63     if (self.browser_options.dont_override_profile and
64         not options_for_unittests.AreSet()):
65       sys.stderr.write('Warning: Not overriding profile. This can cause '
66                        'unexpected effects due to profile-specific settings, '
67                        'such as about:flags settings, cookies, and '
68                        'extensions.\n')
69
70   def AddReplayServerOptions(self, extra_wpr_args):
71     if self.browser_options.netsim:
72       extra_wpr_args.append('--net=%s' % self.browser_options.netsim)
73     else:
74       extra_wpr_args.append('--no-dns_forwarding')
75
76   @property
77   @decorators.Cache
78   def extension_backend(self):
79     if not self.supports_extensions:
80       return None
81     return extension_backend.ExtensionBackendDict(self)
82
83   def GetBrowserStartupArgs(self):
84     args = []
85     args.extend(self.browser_options.extra_browser_args)
86     args.append('--disable-background-networking')
87     args.append('--enable-net-benchmarking')
88     args.append('--metrics-recording-only')
89     args.append('--no-default-browser-check')
90     args.append('--no-first-run')
91
92     # Turn on GPU benchmarking extension for all runs. The only side effect of
93     # the extension being on is that render stats are tracked. This is believed
94     # to be effectively free. And, by doing so here, it avoids us having to
95     # programmatically inspect a pageset's actions in order to determine if it
96     # might eventually scroll.
97     args.append('--enable-gpu-benchmarking')
98
99     # Set --no-proxy-server to work around some XP issues unless
100     # some other flag indicates a proxy is needed.
101     if not '--enable-spdy-proxy-auth' in args:
102       args.append('--no-proxy-server')
103
104     if self.browser_options.netsim:
105       args.append('--ignore-certificate-errors')
106     elif self.browser_options.wpr_mode != wpr_modes.WPR_OFF:
107       args.extend(wpr_server.GetChromeFlags(self.forwarder_factory.host_ip,
108                                             self.wpr_port_pairs))
109     args.extend(user_agent.GetChromeUserAgentArgumentFromType(
110         self.browser_options.browser_user_agent_type))
111
112     extensions = [extension.local_path
113                   for extension in self._extensions_to_load
114                   if not extension.is_component]
115     extension_str = ','.join(extensions)
116     if len(extensions) > 0:
117       args.append('--load-extension=%s' % extension_str)
118
119     component_extensions = [extension.local_path
120                             for extension in self._extensions_to_load
121                             if extension.is_component]
122     component_extension_str = ','.join(component_extensions)
123     if len(component_extensions) > 0:
124       args.append('--load-component-extension=%s' % component_extension_str)
125
126     if self.browser_options.no_proxy_server:
127       args.append('--no-proxy-server')
128
129     if self.browser_options.disable_component_extensions_with_background_pages:
130       args.append('--disable-component-extensions-with-background-pages')
131
132     return args
133
134   def HasBrowserFinishedLaunching(self):
135     try:
136       self.Request('')
137     except (exceptions.BrowserGoneException,
138             exceptions.BrowserConnectionGoneException):
139       return False
140     else:
141       return True
142
143   def _WaitForBrowserToComeUp(self, wait_for_extensions=True):
144     try:
145       util.WaitFor(self.HasBrowserFinishedLaunching, timeout=30)
146     except (util.TimeoutException, exceptions.ProcessGoneException) as e:
147       if not self.IsBrowserRunning():
148         raise exceptions.BrowserGoneException(self.browser, e)
149       raise exceptions.BrowserConnectionGoneException(self.browser, e)
150
151     def AllExtensionsLoaded():
152       # Extension pages are loaded from an about:blank page,
153       # so we need to check that the document URL is the extension
154       # page in addition to the ready state.
155       extension_ready_js = """
156           document.URL.lastIndexOf('chrome-extension://%s/', 0) == 0 &&
157           (document.readyState == 'complete' ||
158            document.readyState == 'interactive')
159       """
160       for e in self._extensions_to_load:
161         if not e.extension_id in self.extension_backend:
162           return False
163         for extension_object in self.extension_backend[e.extension_id]:
164           try:
165             res = extension_object.EvaluateJavaScript(
166                 extension_ready_js % e.extension_id)
167           except exceptions.EvaluateException:
168             # If the inspected page is not ready, we will get an error
169             # when we evaluate a JS expression, but we can just keep polling
170             # until the page is ready (crbug.com/251913).
171             res = None
172
173           # TODO(tengs): We don't have full support for getting the Chrome
174           # version before launch, so for now we use a generic workaround to
175           # check for an extension binding bug in old versions of Chrome.
176           # See crbug.com/263162 for details.
177           if res and extension_object.EvaluateJavaScript(
178               'chrome.runtime == null'):
179             extension_object.Reload()
180           if not res:
181             return False
182       return True
183
184     if wait_for_extensions and self._supports_extensions:
185       try:
186         util.WaitFor(AllExtensionsLoaded, timeout=60)
187       except util.TimeoutException:
188         logging.error('ExtensionsToLoad: ' +
189             repr([e.extension_id for e in self._extensions_to_load]))
190         logging.error('Extension list: ' +
191             pprint.pformat(self.extension_backend, indent=4))
192         raise
193
194   def ListInspectableContexts(self):
195     return json.loads(self.Request(''))
196
197   def Request(self, path, timeout=5, throw_network_exception=False):
198     url = 'http://127.0.0.1:%i/json' % self._port
199     if path:
200       url += '/' + path
201     try:
202       proxy_handler = urllib2.ProxyHandler({})  # Bypass any system proxy.
203       opener = urllib2.build_opener(proxy_handler)
204       with contextlib.closing(opener.open(url, timeout=timeout)) as req:
205         return req.read()
206     except (socket.error, httplib.BadStatusLine, urllib2.URLError) as e:
207       if throw_network_exception:
208         raise e
209       if not self.IsBrowserRunning():
210         raise exceptions.BrowserGoneException(self.browser, e)
211       raise exceptions.BrowserConnectionGoneException(self.browser, e)
212
213   @property
214   def browser_directory(self):
215     raise NotImplementedError()
216
217   @property
218   def profile_directory(self):
219     raise NotImplementedError()
220
221   @property
222   @decorators.Cache
223   def chrome_branch_number(self):
224     # Detect version information.
225     data = self.Request('version')
226     resp = json.loads(data)
227     if 'Protocol-Version' in resp:
228       if 'Browser' in resp:
229         branch_number_match = re.search('Chrome/\d+\.\d+\.(\d+)\.\d+',
230                                         resp['Browser'])
231       else:
232         branch_number_match = re.search(
233             'Chrome/\d+\.\d+\.(\d+)\.\d+ (Mobile )?Safari',
234             resp['User-Agent'])
235
236       if branch_number_match:
237         branch_number = int(branch_number_match.group(1))
238         if branch_number:
239           return branch_number
240
241     # Branch number can't be determined, so fail any branch number checks.
242     return 0
243
244   @property
245   def supports_tab_control(self):
246     return self._supports_tab_control
247
248   @property
249   def supports_tracing(self):
250     return True
251
252   def StartTracing(self, trace_options, custom_categories=None,
253                    timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
254     """
255     Args:
256         trace_options: An tracing_options.TracingOptions instance.
257         custom_categories: An optional string containing a list of
258                          comma separated categories that will be traced
259                          instead of the default category set.  Example: use
260                          "webkit,cc,disabled-by-default-cc.debug" to trace only
261                          those three event categories.
262     """
263     assert trace_options and trace_options.enable_chrome_trace
264     if self._tracing_backend is None:
265       self._tracing_backend = tracing_backend.TracingBackend(self._port, self)
266     return self._tracing_backend.StartTracing(
267         trace_options, custom_categories, timeout)
268
269   @property
270   def is_tracing_running(self):
271     if not self._tracing_backend:
272       return None
273     return self._tracing_backend.is_tracing_running
274
275   def StopTracing(self):
276     """ Stops tracing and returns the result as TimelineData object. """
277     tab_ids_list = []
278     for (i, _) in enumerate(self._browser.tabs):
279       tab = self.tab_list_backend.Get(i, None)
280       if tab:
281         success = tab.EvaluateJavaScript(
282             "console.time('" + tab.id + "');" +
283             "console.timeEnd('" + tab.id + "');" +
284             "console.time.toString().indexOf('[native code]') != -1;")
285         if not success:
286           raise Exception('Page stomped on console.time')
287         tab_ids_list.append(tab.id)
288     trace_events = self._tracing_backend.StopTracing()
289     # Augment tab_ids data to trace events.
290     event_data = {'traceEvents' : trace_events, 'tabIds': tab_ids_list}
291     return tracing_timeline_data.TracingTimelineData(event_data)
292
293   def GetProcessName(self, cmd_line):
294     """Returns a user-friendly name for the process of the given |cmd_line|."""
295     if not cmd_line:
296       # TODO(tonyg): Eventually we should make all of these known and add an
297       # assertion.
298       return 'unknown'
299     if 'nacl_helper_bootstrap' in cmd_line:
300       return 'nacl_helper_bootstrap'
301     if ':sandboxed_process' in cmd_line:
302       return 'renderer'
303     m = re.match(r'.* --type=([^\s]*) .*', cmd_line)
304     if not m:
305       return 'browser'
306     return m.group(1)
307
308   def Close(self):
309     if self._tracing_backend:
310       self._tracing_backend.Close()
311       self._tracing_backend = None
312
313   @property
314   def supports_system_info(self):
315     return self.GetSystemInfo() != None
316
317   def GetSystemInfo(self):
318     if self._system_info_backend is None:
319       self._system_info_backend = system_info_backend.SystemInfoBackend(
320           self._port)
321     return self._system_info_backend.GetSystemInfo()