chrome.py revision 4a46bb8d138f121b64fee8f7e84b5e8016e10d3c
1# Copyright (c) 2013 The Chromium OS 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 5import logging, os, re 6 7from autotest_lib.client.common_lib.cros import arc_util 8from autotest_lib.client.cros import constants 9from autotest_lib.client.bin import utils 10from telemetry.core import cros_interface, exceptions, util 11from telemetry.internal.browser import browser_finder, browser_options 12from telemetry.internal.browser import extension_to_load 13 14Error = exceptions.Error 15 16 17# Cached result of whether ARC is available on current device. 18_arc_available = None 19 20 21def is_arc_available(): 22 """Returns true if ARC is available on current device.""" 23 global _arc_available 24 if _arc_available is not None: 25 return _arc_available 26 27 def _check_lsb_release(): 28 lsb_release = '/etc/lsb-release' 29 if not os.path.exists(lsb_release): 30 return False 31 with open(lsb_release) as f: 32 for line in f: 33 if line.startswith('CHROMEOS_ARC_VERSION='): 34 return True 35 return False 36 37 _arc_available = _check_lsb_release() 38 return _arc_available 39 40 41def NormalizeEmail(username): 42 """Remove dots from username. Add @gmail.com if necessary. 43 44 TODO(achuith): Get rid of this when crbug.com/358427 is fixed. 45 46 @param username: username/email to be scrubbed. 47 """ 48 parts = re.split('@', username) 49 parts[0] = re.sub('\.', '', parts[0]) 50 51 if len(parts) == 1: 52 parts.append('gmail.com') 53 return '@'.join(parts) 54 55 56class Chrome(object): 57 """Wrapper for creating a telemetry browser instance with extensions. 58 59 The recommended way to use this class is to create the instance using the 60 with statement: 61 62 >>> with chrome.Chrome(...) as cr: 63 >>> # Do whatever you need with cr. 64 >>> pass 65 66 This will make sure all the clean-up functions are called. If you really 67 need to use this class without the with statement, make sure to call the 68 close() method once you're done with the Chrome instance. 69 """ 70 71 72 BROWSER_TYPE_LOGIN = 'system' 73 BROWSER_TYPE_GUEST = 'system-guest' 74 75 76 def __init__(self, logged_in=True, extension_paths=[], autotest_ext=False, 77 num_tries=3, extra_browser_args=None, 78 clear_enterprise_policy=True, dont_override_profile=False, 79 disable_gaia_services=True, disable_default_apps = True, 80 auto_login=True, gaia_login=False, 81 username=None, password=None, gaia_id=None, 82 arc_mode=None, disable_arc_opt_in=True): 83 """ 84 Constructor of telemetry wrapper. 85 86 @param logged_in: Regular user (True) or guest user (False). 87 @param extension_paths: path of unpacked extension to install. 88 @param autotest_ext: Load a component extension with privileges to 89 invoke chrome.autotestPrivate. 90 @param num_tries: Number of attempts to log in. 91 @param extra_browser_args: Additional argument(s) to pass to the 92 browser. It can be a string or a list. 93 @param clear_enterprise_policy: Clear enterprise policy before 94 logging in. 95 @param dont_override_profile: Don't delete cryptohome before login. 96 Telemetry will output a warning with this 97 option. 98 @param disable_gaia_services: For enterprise autotests, this option may 99 be used to enable policy fetch. 100 @param disable_default_apps: For tests that exercise default apps. 101 @param auto_login: Does not login automatically if this is False. 102 Useful if you need to examine oobe. 103 @param gaia_login: Logs in to real gaia. 104 @param username: Log in using this username instead of the default. 105 @param password: Log in using this password instead of the default. 106 @param gaia_id: Log in using this gaia_id instead of the default. 107 @param arc_mode: How ARC instance should be started. Default is to not 108 start. 109 @param disable_arc_opt_in: For opt in flow autotest. This option is used 110 to disable the arc opt in flow. 111 """ 112 self._autotest_ext_path = None 113 if autotest_ext: 114 self._autotest_ext_path = os.path.join(os.path.dirname(__file__), 115 'autotest_private_ext') 116 extension_paths.append(self._autotest_ext_path) 117 118 finder_options = browser_options.BrowserFinderOptions() 119 if is_arc_available() and arc_util.should_start_arc(arc_mode): 120 if disable_arc_opt_in: 121 finder_options.browser_options.AppendExtraBrowserArgs( 122 arc_util.get_extra_chrome_flags()) 123 logged_in = True 124 125 self._browser_type = (self.BROWSER_TYPE_LOGIN 126 if logged_in else self.BROWSER_TYPE_GUEST) 127 finder_options.browser_type = self.browser_type 128 if extra_browser_args: 129 finder_options.browser_options.AppendExtraBrowserArgs( 130 extra_browser_args) 131 132 # finder options must be set before parse_args(), browser options must 133 # be set before Create(). 134 # TODO(crbug.com/360890) Below MUST be '2' so that it doesn't inhibit 135 # autotest debug logs 136 finder_options.verbosity = 2 137 finder_options.CreateParser().parse_args(args=[]) 138 b_options = finder_options.browser_options 139 b_options.disable_component_extensions_with_background_pages = False 140 b_options.create_browser_with_oobe = True 141 b_options.clear_enterprise_policy = clear_enterprise_policy 142 b_options.dont_override_profile = dont_override_profile 143 b_options.disable_gaia_services = disable_gaia_services 144 b_options.disable_default_apps = disable_default_apps 145 b_options.disable_component_extensions_with_background_pages = disable_default_apps 146 147 b_options.auto_login = auto_login 148 b_options.gaia_login = gaia_login 149 150 if is_arc_available() and not disable_arc_opt_in: 151 arc_util.set_browser_options_for_opt_in(b_options) 152 153 self.username = b_options.username if username is None else username 154 self.password = b_options.password if password is None else password 155 self.username = NormalizeEmail(self.username) 156 b_options.username = self.username 157 b_options.password = self.password 158 self.gaia_id = b_options.gaia_id if gaia_id is None else gaia_id 159 b_options.gaia_id = self.gaia_id 160 161 self.arc_mode = arc_mode 162 163 if logged_in: 164 extensions_to_load = b_options.extensions_to_load 165 for path in extension_paths: 166 extension = extension_to_load.ExtensionToLoad( 167 path, self.browser_type) 168 extensions_to_load.append(extension) 169 self._extensions_to_load = extensions_to_load 170 171 # Turn on collection of Chrome coredumps via creation of a magic file. 172 # (Without this, Chrome coredumps are trashed.) 173 open(constants.CHROME_CORE_MAGIC_FILE, 'w').close() 174 175 for i in range(num_tries): 176 try: 177 browser_to_create = browser_finder.FindBrowser(finder_options) 178 self._browser = browser_to_create.Create(finder_options) 179 if is_arc_available(): 180 if disable_arc_opt_in: 181 if arc_util.should_start_arc(arc_mode): 182 arc_util.enable_arc_setting(self.browser) 183 else: 184 arc_util.opt_in(self.browser) 185 arc_util.post_processing_after_browser(self) 186 break 187 except exceptions.LoginException as e: 188 logging.error('Timed out logging in, tries=%d, error=%s', 189 i, repr(e)) 190 if i == num_tries-1: 191 raise 192 self._browser.platform.network_controller.InitializeIfNeeded() 193 194 def __enter__(self): 195 return self 196 197 198 def __exit__(self, *args): 199 self.close() 200 201 202 @property 203 def browser(self): 204 """Returns a telemetry browser instance.""" 205 return self._browser 206 207 208 def get_extension(self, extension_path): 209 """Fetches a telemetry extension instance given the extension path.""" 210 for ext in self._extensions_to_load: 211 if extension_path == ext.path: 212 return self.browser.extensions[ext] 213 return None 214 215 216 @property 217 def autotest_ext(self): 218 """Returns the autotest extension.""" 219 return self.get_extension(self._autotest_ext_path) 220 221 222 @property 223 def login_status(self): 224 """Returns login status.""" 225 ext = self.autotest_ext 226 if not ext: 227 return None 228 229 ext.ExecuteJavaScript(''' 230 window.__login_status = null; 231 chrome.autotestPrivate.loginStatus(function(s) { 232 window.__login_status = s; 233 }); 234 ''') 235 return ext.EvaluateJavaScript('window.__login_status') 236 237 238 def get_visible_notifications(self): 239 """Returns an array of visible notifications of Chrome. 240 241 For specific type of each notification, please refer to Chromium's 242 chrome/common/extensions/api/autotest_private.idl. 243 """ 244 ext = self.autotest_ext 245 if not ext: 246 return None 247 248 ext.ExecuteJavaScript(''' 249 window.__items = null; 250 chrome.autotestPrivate.getVisibleNotifications(function(items) { 251 window.__items = items; 252 }); 253 ''') 254 if ext.EvaluateJavaScript('window.__items') is None: 255 return None 256 return ext.EvaluateJavaScript('window.__items') 257 258 259 @property 260 def browser_type(self): 261 """Returns the browser_type.""" 262 return self._browser_type 263 264 265 @staticmethod 266 def did_browser_crash(func): 267 """Runs func, returns True if the browser crashed, False otherwise. 268 269 @param func: function to run. 270 271 """ 272 try: 273 func() 274 except Error: 275 return True 276 return False 277 278 279 @staticmethod 280 def wait_for_browser_restart(func): 281 """Runs func, and waits for a browser restart. 282 283 @param func: function to run. 284 285 """ 286 _cri = cros_interface.CrOSInterface() 287 pid = _cri.GetChromePid() 288 Chrome.did_browser_crash(func) 289 utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60) 290 291 292 def wait_for_browser_to_come_up(self): 293 """Waits for the browser to come up. This should only be called after a 294 browser crash. 295 """ 296 def _BrowserReady(cr): 297 tabs = [] # Wrapper for pass by reference. 298 if self.did_browser_crash( 299 lambda: tabs.append(cr.browser.tabs.New())): 300 return False 301 try: 302 tabs[0].Close() 303 except: 304 # crbug.com/350941 305 logging.error('Timed out closing tab') 306 return True 307 util.WaitFor(lambda: _BrowserReady(self), timeout=10) 308 309 310 def close(self): 311 """Closes the browser. 312 """ 313 try: 314 if is_arc_available(): 315 arc_util.pre_processing_before_close(self) 316 finally: 317 # Calling platform.StopAllLocalServers() to tear down the telemetry 318 # server processes such as the one started by 319 # platform.SetHTTPServerDirectories(). Not calling this function 320 # will leak the process and may affect test results. 321 # (crbug.com/663387) 322 self._browser.platform.StopAllLocalServers() 323 self._browser.Close() 324 self._browser.platform.network_controller.Close() 325