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 6 7from autotest_lib.client.cros import constants 8from autotest_lib.client.bin import utils 9from telemetry.core import cros_interface, exceptions, util 10from telemetry.internal.browser import browser_finder, browser_options 11from telemetry.internal.browser import extension_to_load 12 13Error = exceptions.Error 14 15 16class Chrome(object): 17 """Wrapper for creating a telemetry browser instance with extensions.""" 18 19 20 BROWSER_TYPE_LOGIN = 'system' 21 BROWSER_TYPE_GUEST = 'system-guest' 22 23 24 def __init__(self, logged_in=True, extension_paths=[], autotest_ext=False, 25 is_component=True, num_tries=3, extra_browser_args=None, 26 clear_enterprise_policy=True, dont_override_profile=False, 27 disable_gaia_services=True, disable_default_apps = True, 28 auto_login=True, gaia_login=False, 29 username=None, password=None, gaia_id=None): 30 """ 31 Constructor of telemetry wrapper. 32 33 @param logged_in: Regular user (True) or guest user (False). 34 @param extension_paths: path of unpacked extension to install. 35 @param autotest_ext: Load a component extension with privileges to 36 invoke chrome.autotestPrivate. 37 @param is_component: Whether extensions should be loaded as component 38 extensions. 39 @param num_tries: Number of attempts to log in. 40 @param extra_browser_args: Additional argument(s) to pass to the 41 browser. It can be a string or a list. 42 @param clear_enterprise_policy: Clear enterprise policy before 43 logging in. 44 @param dont_override_profile: Don't delete cryptohome before login. 45 Telemetry will output a warning with this 46 option. 47 @param disable_gaia_services: For enterprise autotests, this option may 48 be used to enable policy fetch. 49 @param disable_default_apps: For tests that exercise default apps. 50 @param auto_login: Does not login automatically if this is False. 51 Useful if you need to examine oobe. 52 @param gaia_login: Logs in to real gaia. 53 @param username: Log in using this username instead of the default. 54 @param password: Log in using this password instead of the default. 55 @param gaia_id: Log in using this gaia_id instead of the default. 56 """ 57 self._autotest_ext_path = None 58 if autotest_ext: 59 self._autotest_ext_path = os.path.join(os.path.dirname(__file__), 60 'autotest_private_ext') 61 extension_paths.append(self._autotest_ext_path) 62 63 finder_options = browser_options.BrowserFinderOptions() 64 self._browser_type = (self.BROWSER_TYPE_LOGIN 65 if logged_in else self.BROWSER_TYPE_GUEST) 66 finder_options.browser_type = self.browser_type 67 if extra_browser_args: 68 finder_options.browser_options.AppendExtraBrowserArgs( 69 extra_browser_args) 70 71 if logged_in: 72 extensions_to_load = finder_options.extensions_to_load 73 for path in extension_paths: 74 extension = extension_to_load.ExtensionToLoad( 75 path, self.browser_type, is_component=is_component) 76 extensions_to_load.append(extension) 77 self._extensions_to_load = extensions_to_load 78 79 # finder options must be set before parse_args(), browser options must 80 # be set before Create(). 81 # TODO(crbug.com/360890) Below MUST be '2' so that it doesn't inhibit 82 # autotest debug logs 83 finder_options.verbosity = 2 84 finder_options.CreateParser().parse_args(args=[]) 85 b_options = finder_options.browser_options 86 b_options.disable_component_extensions_with_background_pages = False 87 b_options.create_browser_with_oobe = True 88 b_options.clear_enterprise_policy = clear_enterprise_policy 89 b_options.dont_override_profile = dont_override_profile 90 b_options.disable_gaia_services = disable_gaia_services 91 b_options.disable_default_apps = disable_default_apps 92 b_options.disable_component_extensions_with_background_pages = disable_default_apps 93 94 b_options.auto_login = auto_login 95 b_options.gaia_login = gaia_login 96 self.username = b_options.username if username is None else username 97 self.password = b_options.password if password is None else password 98 b_options.username = self.username 99 b_options.password = self.password 100 # gaia_id will be added to telemetry code in chromium repository later 101 try: 102 self.gaia_id = b_options.gaia_id if gaia_id is None else gaia_id 103 b_options.gaia_id = self.gaia_id 104 except AttributeError: 105 pass 106 107 # Turn on collection of Chrome coredumps via creation of a magic file. 108 # (Without this, Chrome coredumps are trashed.) 109 open(constants.CHROME_CORE_MAGIC_FILE, 'w').close() 110 111 for i in range(num_tries): 112 try: 113 browser_to_create = browser_finder.FindBrowser(finder_options) 114 self._browser = browser_to_create.Create(finder_options) 115 break 116 except (exceptions.LoginException) as e: 117 logging.error('Timed out logging in, tries=%d, error=%s', 118 i, repr(e)) 119 if i == num_tries-1: 120 raise 121 122 123 def __enter__(self): 124 return self 125 126 127 def __exit__(self, *args): 128 self.close() 129 130 131 @property 132 def browser(self): 133 """Returns a telemetry browser instance.""" 134 return self._browser 135 136 137 def get_extension(self, extension_path): 138 """Fetches a telemetry extension instance given the extension path.""" 139 for ext in self._extensions_to_load: 140 if extension_path == ext.path: 141 return self.browser.extensions[ext] 142 return None 143 144 145 @property 146 def autotest_ext(self): 147 """Returns the autotest extension.""" 148 return self.get_extension(self._autotest_ext_path) 149 150 151 @property 152 def login_status(self): 153 """Returns login status.""" 154 ext = self.autotest_ext 155 if not ext: 156 return None 157 158 ext.ExecuteJavaScript(''' 159 window.__login_status = null; 160 chrome.autotestPrivate.loginStatus(function(s) { 161 window.__login_status = s; 162 }); 163 ''') 164 return ext.EvaluateJavaScript('window.__login_status') 165 166 167 @property 168 def browser_type(self): 169 """Returns the browser_type.""" 170 return self._browser_type 171 172 173 @staticmethod 174 def did_browser_crash(func): 175 """Runs func, returns True if the browser crashed, False otherwise. 176 177 @param func: function to run. 178 179 """ 180 try: 181 func() 182 except (Error): 183 return True 184 return False 185 186 187 @staticmethod 188 def wait_for_browser_restart(func): 189 """Runs func, and waits for a browser restart. 190 191 @param func: function to run. 192 193 """ 194 _cri = cros_interface.CrOSInterface() 195 pid = _cri.GetChromePid() 196 Chrome.did_browser_crash(func) 197 utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60) 198 199 200 def wait_for_browser_to_come_up(self): 201 """Waits for the browser to come up. This should only be called after a 202 browser crash. 203 """ 204 def _BrowserReady(cr): 205 tabs = [] # Wrapper for pass by reference. 206 if self.did_browser_crash( 207 lambda: tabs.append(cr.browser.tabs.New())): 208 return False 209 try: 210 tabs[0].Close() 211 except: 212 # crbug.com/350941 213 logging.error('Timed out closing tab') 214 return True 215 util.WaitFor(lambda: _BrowserReady(self), timeout=10) 216 217 218 def close(self): 219 """Closes the browser.""" 220 self._browser.Close() 221