Upstream version 5.34.104.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 httplib
6 import json
7 import logging
8 import pprint
9 import re
10 import socket
11 import sys
12 import urllib2
13
14 from telemetry.core import exceptions
15 from telemetry.core import forwarders
16 from telemetry.core import user_agent
17 from telemetry.core import util
18 from telemetry.core import web_contents
19 from telemetry.core import wpr_modes
20 from telemetry.core import wpr_server
21 from telemetry.core.backends import browser_backend
22 from telemetry.core.backends.chrome import extension_dict_backend
23 from telemetry.core.backends.chrome import misc_web_contents_backend
24 from telemetry.core.backends.chrome import system_info_backend
25 from telemetry.core.backends.chrome import tab_list_backend
26 from telemetry.core.backends.chrome import tracing_backend
27 from telemetry.unittest import options_for_unittests
28
29
30 class ChromeBrowserBackend(browser_backend.BrowserBackend):
31   """An abstract class for chrome browser backends. Provides basic functionality
32   once a remote-debugger port has been established."""
33   # It is OK to have abstract methods. pylint: disable=W0223
34
35   def __init__(self, is_content_shell, supports_extensions, browser_options,
36                output_profile_path, extensions_to_load):
37     super(ChromeBrowserBackend, self).__init__(
38         is_content_shell=is_content_shell,
39         supports_extensions=supports_extensions,
40         browser_options=browser_options,
41         tab_list_backend=tab_list_backend.TabListBackend)
42     self._port = None
43
44     self._inspector_protocol_version = 0
45     self._chrome_branch_number = None
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     self._misc_web_contents_backend = (
70         misc_web_contents_backend.MiscWebContentsBackend(self))
71     self._extension_dict_backend = None
72     if supports_extensions:
73       self._extension_dict_backend = (
74           extension_dict_backend.ExtensionDictBackend(self))
75
76   def AddReplayServerOptions(self, extra_wpr_args):
77     if self.browser_options.netsim:
78       extra_wpr_args.append('--net=%s' % self.browser_options.netsim)
79     else:
80       extra_wpr_args.append('--no-dns_forwarding')
81
82   @property
83   def misc_web_contents_backend(self):
84     """Access to chrome://oobe/login page which is neither a tab nor an
85     extension."""
86     return self._misc_web_contents_backend
87
88   @property
89   def extension_dict_backend(self):
90     return self._extension_dict_backend
91
92   def GetBrowserStartupArgs(self):
93     args = []
94     args.extend(self.browser_options.extra_browser_args)
95     args.append('--disable-background-networking')
96     args.append('--enable-net-benchmarking')
97     args.append('--metrics-recording-only')
98     args.append('--no-default-browser-check')
99     args.append('--no-first-run')
100     args.append('--no-proxy-server')
101     if self.browser_options.netsim:
102       args.append('--ignore-certificate-errors')
103     elif self.browser_options.wpr_mode != wpr_modes.WPR_OFF:
104       args.extend(wpr_server.GetChromeFlags(self.forwarder_factory.host_ip,
105                                             self.wpr_port_pairs))
106     args.extend(user_agent.GetChromeUserAgentArgumentFromType(
107         self.browser_options.browser_user_agent_type))
108
109     extensions = [extension.local_path
110                   for extension in self._extensions_to_load
111                   if not extension.is_component]
112     extension_str = ','.join(extensions)
113     if len(extensions) > 0:
114       args.append('--load-extension=%s' % extension_str)
115
116     component_extensions = [extension.local_path
117                             for extension in self._extensions_to_load
118                             if extension.is_component]
119     component_extension_str = ','.join(component_extensions)
120     if len(component_extensions) > 0:
121       args.append('--load-component-extension=%s' % component_extension_str)
122
123     if self.browser_options.no_proxy_server:
124       args.append('--no-proxy-server')
125
126     if self.browser_options.disable_component_extensions_with_background_pages:
127       args.append('--disable-component-extensions-with-background-pages')
128
129     return args
130
131   def HasBrowserFinishedLaunching(self):
132     try:
133       self.Request('')
134     except (exceptions.BrowserGoneException,
135             exceptions.BrowserConnectionGoneException):
136       return False
137     else:
138       return True
139
140   def _WaitForBrowserToComeUp(self, wait_for_extensions=True):
141     try:
142       util.WaitFor(self.HasBrowserFinishedLaunching, timeout=30)
143     except (util.TimeoutException, exceptions.ProcessGoneException) as e:
144       raise exceptions.BrowserGoneException(self.GetStackTrace())
145
146     def AllExtensionsLoaded():
147       # Extension pages are loaded from an about:blank page,
148       # so we need to check that the document URL is the extension
149       # page in addition to the ready state.
150       extension_ready_js = """
151           document.URL.lastIndexOf('chrome-extension://%s/', 0) == 0 &&
152           (document.readyState == 'complete' ||
153            document.readyState == 'interactive')
154       """
155       for e in self._extensions_to_load:
156         if not e.extension_id in self._extension_dict_backend:
157           return False
158         extension_object = self._extension_dict_backend[e.extension_id]
159         try:
160           res = extension_object.EvaluateJavaScript(
161               extension_ready_js % e.extension_id)
162         except exceptions.EvaluateException:
163           # If the inspected page is not ready, we will get an error
164           # when we evaluate a JS expression, but we can just keep polling
165           # until the page is ready (crbug.com/251913).
166           res = None
167
168         # TODO(tengs): We don't have full support for getting the Chrome
169         # version before launch, so for now we use a generic workaround to
170         # check for an extension binding bug in old versions of Chrome.
171         # See crbug.com/263162 for details.
172         if res and extension_object.EvaluateJavaScript(
173             'chrome.runtime == null'):
174           extension_object.Reload()
175         if not res:
176           return False
177       return True
178     if wait_for_extensions and self._supports_extensions:
179       try:
180         util.WaitFor(AllExtensionsLoaded, timeout=60)
181       except util.TimeoutException:
182         logging.error('ExtensionsToLoad: ' +
183             repr([e.extension_id for e in self._extensions_to_load]))
184         logging.error('Extension list: ' +
185             pprint.pformat(self._extension_dict_backend.GetExtensionInfoList(),
186                            indent=4))
187         raise
188
189
190   def _PostBrowserStartupInitialization(self):
191     # Detect version information.
192     data = self.Request('version')
193     resp = json.loads(data)
194     if 'Protocol-Version' in resp:
195       self._inspector_protocol_version = resp['Protocol-Version']
196
197       if self._chrome_branch_number:
198         return
199
200       if 'Browser' in resp:
201         branch_number_match = re.search('Chrome/\d+\.\d+\.(\d+)\.\d+',
202                                         resp['Browser'])
203       else:
204         branch_number_match = re.search(
205             'Chrome/\d+\.\d+\.(\d+)\.\d+ (Mobile )?Safari',
206             resp['User-Agent'])
207
208       if branch_number_match:
209         self._chrome_branch_number = int(branch_number_match.group(1))
210
211       if not self._chrome_branch_number:
212         # Content Shell returns '' for Browser, WebViewShell returns '0'.
213         # For now we have to fall-back and assume branch 1025.
214         self._chrome_branch_number = 1025
215       return
216
217     # Detection has failed: assume 18.0.1025.168 ~= Chrome Android.
218     self._inspector_protocol_version = 1.0
219     self._chrome_branch_number = 1025
220
221   def Request(self, path, timeout=None, throw_network_exception=False):
222     url = 'http://127.0.0.1:%i/json' % self._port
223     if path:
224       url += '/' + path
225     try:
226       proxy_handler = urllib2.ProxyHandler({})  # Bypass any system proxy.
227       opener = urllib2.build_opener(proxy_handler)
228       req = opener.open(url, timeout=timeout)
229       return req.read()
230     except (socket.error, httplib.BadStatusLine, urllib2.URLError) as e:
231       if throw_network_exception:
232         raise e
233       if not self.IsBrowserRunning():
234         raise exceptions.BrowserGoneException(e)
235       raise exceptions.BrowserConnectionGoneException(e)
236
237   @property
238   def browser_directory(self):
239     raise NotImplementedError()
240
241   @property
242   def profile_directory(self):
243     raise NotImplementedError()
244
245   @property
246   def chrome_branch_number(self):
247     assert self._chrome_branch_number
248     return self._chrome_branch_number
249
250   @property
251   def supports_tab_control(self):
252     return self.chrome_branch_number >= 1303
253
254   @property
255   def supports_tracing(self):
256     return self.is_content_shell or self.chrome_branch_number >= 1385
257
258   def StartTracing(self, custom_categories=None,
259                    timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT):
260     """ custom_categories is an optional string containing a list of
261     comma separated categories that will be traced instead of the
262     default category set.  Example: use
263     "webkit,cc,disabled-by-default-cc.debug" to trace only those three
264     event categories.
265     """
266     if self._tracing_backend is None:
267       self._tracing_backend = tracing_backend.TracingBackend(self._port)
268     return self._tracing_backend.StartTracing(custom_categories, timeout)
269
270   def StopTracing(self):
271     """ Stops tracing and returns the result as TraceResult object. """
272     for (i, debugger_url) in enumerate(self._browser.tabs):
273       tab = self.tab_list_backend.Get(i, None)
274       if tab:
275         success = tab.EvaluateJavaScript(
276             "console.time('" + debugger_url + "');" +
277             "console.timeEnd('" + debugger_url + "');" +
278             "console.time.toString().indexOf('[native code]') != -1;")
279         if not success:
280           raise Exception('Page stomped on console.time')
281         self._tracing_backend.AddTabToMarkerMapping(tab, debugger_url)
282     return self._tracing_backend.StopTracing()
283
284   def GetProcessName(self, cmd_line):
285     """Returns a user-friendly name for the process of the given |cmd_line|."""
286     if not cmd_line:
287       # TODO(tonyg): Eventually we should make all of these known and add an
288       # assertion.
289       return 'unknown'
290     if 'nacl_helper_bootstrap' in cmd_line:
291       return 'nacl_helper_bootstrap'
292     if ':sandboxed_process' in cmd_line:
293       return 'renderer'
294     m = re.match(r'.* --type=([^\s]*) .*', cmd_line)
295     if not m:
296       return 'browser'
297     return m.group(1)
298
299   def Close(self):
300     if self._tracing_backend:
301       self._tracing_backend.Close()
302       self._tracing_backend = None
303
304   @property
305   def supports_system_info(self):
306     return self.GetSystemInfo() != None
307
308   def GetSystemInfo(self):
309     if self._system_info_backend is None:
310       self._system_info_backend = system_info_backend.SystemInfoBackend(
311           self._port)
312     return self._system_info_backend.GetSystemInfo()
313
314   def _SetBranchNumber(self, version):
315     assert version
316     self._chrome_branch_number = re.search(r'\d+\.\d+\.(\d+)\.\d+',
317                                            version).group(1)