blob: d8d48e4c38954c897de5ca7937a8691b879a6056 [file] [log] [blame]
Dennis Jeffreydffc0bd2013-05-03 13:24:31 -07001# 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
Achuith Bhandarkarf3469ec2016-06-08 16:58:57 -07005import logging, os, re
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -07006
Nicolas Boichat451e4662016-07-16 07:51:01 +08007from autotest_lib.client.common_lib.cros import arc_util
Don Garrett11b13972015-09-11 17:37:53 +00008from autotest_lib.client.cros import constants
Achuith Bhandarkar88a2bab2015-07-09 17:36:25 -07009from autotest_lib.client.bin import utils
Prathmesh Prabhuc3961df2015-07-21 10:44:54 -070010from telemetry.core import cros_interface, exceptions, util
Achuith Bhandarkarb058a8f2015-07-02 01:29:05 -070011from telemetry.internal.browser import browser_finder, browser_options
12from telemetry.internal.browser import extension_to_load
Achuith Bhandarkar704134d2015-03-05 17:31:57 -080013
14Error = exceptions.Error
Dennis Jeffreydffc0bd2013-05-03 13:24:31 -070015
16
Victor Hsieh43651ac2016-04-20 12:52:21 -070017# Cached result of whether ARC is available on current device.
18_arc_available = None
19
20
Nicolas Norvez56f9b232016-06-23 17:58:59 -070021def is_arc_available():
Victor Hsieh000240b2016-04-11 16:04:31 -070022 """Returns true if ARC is available on current device."""
Victor Hsieh43651ac2016-04-20 12:52:21 -070023 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
Achuith Bhandarkarf3469ec2016-06-08 16:58:57 -070041def 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
Achuith Bhandarkared498932013-07-16 17:01:40 -070056class Chrome(object):
Ricky Liang2bfa5642016-11-09 10:18:37 +080057 """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 """
Achuith Bhandarkar86c46812013-07-15 17:13:39 -070070
71
Achuith Bhandarkared498932013-07-16 17:01:40 -070072 BROWSER_TYPE_LOGIN = 'system'
73 BROWSER_TYPE_GUEST = 'system-guest'
Achuith Bhandarkar86c46812013-07-15 17:13:39 -070074
75
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -080076 def __init__(self, logged_in=True, extension_paths=[], autotest_ext=False,
Achuith Bhandarkarb7ef5e52014-03-20 14:18:45 -070077 is_component=True, num_tries=3, extra_browser_args=None,
Achuith Bhandarkar882a8ab2014-08-26 15:51:20 -070078 clear_enterprise_policy=True, dont_override_profile=False,
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -080079 disable_gaia_services=True, disable_default_apps = True,
80 auto_login=True, gaia_login=False,
Victor Hsieh000240b2016-04-11 16:04:31 -070081 username=None, password=None, gaia_id=None,
F#md6d56ba2016-06-10 14:40:52 -070082 arc_mode=None, disable_arc_opt_in=True):
Dean Liaob12e2ee2013-11-19 16:49:12 +080083 """
Achuith Bhandarkar944405a2013-11-21 14:47:48 -080084 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 is_component: Whether extensions should be loaded as component
91 extensions.
Achuith Bhandarkarb7ef5e52014-03-20 14:18:45 -070092 @param num_tries: Number of attempts to log in.
Dean Liaob12e2ee2013-11-19 16:49:12 +080093 @param extra_browser_args: Additional argument(s) to pass to the
Achuith Bhandarkar3875e0c2014-03-20 14:17:31 -070094 browser. It can be a string or a list.
Achuith Bhandarkar5d6c26c2014-04-25 11:19:38 -070095 @param clear_enterprise_policy: Clear enterprise policy before
96 logging in.
Achuith Bhandarkar882a8ab2014-08-26 15:51:20 -070097 @param dont_override_profile: Don't delete cryptohome before login.
98 Telemetry will output a warning with this
99 option.
Achuith Bhandarkar3c654f32014-09-29 06:33:35 -0700100 @param disable_gaia_services: For enterprise autotests, this option may
101 be used to enable policy fetch.
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -0800102 @param disable_default_apps: For tests that exercise default apps.
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800103 @param auto_login: Does not login automatically if this is False.
Achuith Bhandarkar3875e0c2014-03-20 14:17:31 -0700104 Useful if you need to examine oobe.
105 @param gaia_login: Logs in to real gaia.
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800106 @param username: Log in using this username instead of the default.
Alexander Alekseevd9682862015-10-01 22:42:49 -0700107 @param password: Log in using this password instead of the default.
108 @param gaia_id: Log in using this gaia_id instead of the default.
Victor Hsieh43651ac2016-04-20 12:52:21 -0700109 @param arc_mode: How ARC instance should be started. Default is to not
110 start.
F#md6d56ba2016-06-10 14:40:52 -0700111 @param disable_arc_opt_in: For opt in flow autotest. This option is used
112 to disable the arc opt in flow.
Dean Liaob12e2ee2013-11-19 16:49:12 +0800113 """
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700114 self._autotest_ext_path = None
115 if autotest_ext:
116 self._autotest_ext_path = os.path.join(os.path.dirname(__file__),
117 'autotest_private_ext')
118 extension_paths.append(self._autotest_ext_path)
119
Achuith Bhandarkar7fb57f72013-08-29 15:29:06 -0700120 finder_options = browser_options.BrowserFinderOptions()
Nicolas Norvez56f9b232016-06-23 17:58:59 -0700121 if is_arc_available() and arc_util.should_start_arc(arc_mode):
F#md6d56ba2016-06-10 14:40:52 -0700122 if disable_arc_opt_in:
123 finder_options.browser_options.AppendExtraBrowserArgs(
124 arc_util.get_extra_chrome_flags())
Victor Hsieh43651ac2016-04-20 12:52:21 -0700125 logged_in = True
126
Achuith Bhandarkared498932013-07-16 17:01:40 -0700127 self._browser_type = (self.BROWSER_TYPE_LOGIN
128 if logged_in else self.BROWSER_TYPE_GUEST)
Achuith Bhandarkarf130c402013-09-11 14:39:22 -0700129 finder_options.browser_type = self.browser_type
Dean Liaob12e2ee2013-11-19 16:49:12 +0800130 if extra_browser_args:
131 finder_options.browser_options.AppendExtraBrowserArgs(
132 extra_browser_args)
Achuith Bhandarkarf130c402013-09-11 14:39:22 -0700133
Achuith Bhandarkar3e5051d2013-10-15 15:20:12 -0700134 # finder options must be set before parse_args(), browser options must
135 # be set before Create().
Todd Brocheb6f4812014-04-29 16:04:28 -0700136 # TODO(crbug.com/360890) Below MUST be '2' so that it doesn't inhibit
137 # autotest debug logs
138 finder_options.verbosity = 2
Achuith Bhandarkardafc9a52013-09-24 15:26:33 +0200139 finder_options.CreateParser().parse_args(args=[])
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700140 b_options = finder_options.browser_options
141 b_options.disable_component_extensions_with_background_pages = False
142 b_options.create_browser_with_oobe = True
Achuith Bhandarkar5d6c26c2014-04-25 11:19:38 -0700143 b_options.clear_enterprise_policy = clear_enterprise_policy
Achuith Bhandarkar882a8ab2014-08-26 15:51:20 -0700144 b_options.dont_override_profile = dont_override_profile
Achuith Bhandarkar3c654f32014-09-29 06:33:35 -0700145 b_options.disable_gaia_services = disable_gaia_services
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -0800146 b_options.disable_default_apps = disable_default_apps
147 b_options.disable_component_extensions_with_background_pages = disable_default_apps
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800148
149 b_options.auto_login = auto_login
Achuith Bhandarkar3875e0c2014-03-20 14:17:31 -0700150 b_options.gaia_login = gaia_login
F#md6d56ba2016-06-10 14:40:52 -0700151
Nicolas Norvez56f9b232016-06-23 17:58:59 -0700152 if is_arc_available() and not disable_arc_opt_in:
F#md6d56ba2016-06-10 14:40:52 -0700153 arc_util.set_browser_options_for_opt_in(b_options)
154
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800155 self.username = b_options.username if username is None else username
156 self.password = b_options.password if password is None else password
Achuith Bhandarkarf3469ec2016-06-08 16:58:57 -0700157 self.username = NormalizeEmail(self.username)
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800158 b_options.username = self.username
159 b_options.password = self.password
Achuith Bhandarkar6f4accc2016-08-23 14:03:44 -0700160 self.gaia_id = b_options.gaia_id if gaia_id is None else gaia_id
161 b_options.gaia_id = self.gaia_id
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700162
Shuhei Takahashi8462b882016-05-31 17:27:32 +0900163 self.arc_mode = arc_mode
164
Achuith Bhandarkar531a62a2016-04-13 11:09:43 -0700165 if logged_in:
Achuith Bhandarkar6f4accc2016-08-23 14:03:44 -0700166 extensions_to_load = b_options.extensions_to_load
167 for path in extension_paths:
168 extension = extension_to_load.ExtensionToLoad(
169 path, self.browser_type, is_component=is_component)
170 extensions_to_load.append(extension)
171 self._extensions_to_load = extensions_to_load
Achuith Bhandarkar531a62a2016-04-13 11:09:43 -0700172
David James54a830e2015-05-06 17:21:47 -0700173 # Turn on collection of Chrome coredumps via creation of a magic file.
174 # (Without this, Chrome coredumps are trashed.)
Don Garrett11b13972015-09-11 17:37:53 +0000175 open(constants.CHROME_CORE_MAGIC_FILE, 'w').close()
David James54a830e2015-05-06 17:21:47 -0700176
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800177 for i in range(num_tries):
178 try:
179 browser_to_create = browser_finder.FindBrowser(finder_options)
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -0800180 self._browser = browser_to_create.Create(finder_options)
Nicolas Norvez56f9b232016-06-23 17:58:59 -0700181 if is_arc_available():
khmele0434292016-11-10 13:27:57 -0800182 if disable_arc_opt_in:
183 if arc_util.should_start_arc(arc_mode):
184 arc_util.enable_arc_setting(self.browser)
185 else:
F#md6d56ba2016-06-10 14:40:52 -0700186 arc_util.opt_in(self.browser)
Ricky Zhoucf350ee2016-06-13 15:47:33 -0700187 arc_util.post_processing_after_browser(self)
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800188 break
khmela7cf9dd2016-09-20 10:50:23 -0700189 except exceptions.LoginException as e:
Achuith Bhandarkarf800edf2013-12-06 13:37:52 -0800190 logging.error('Timed out logging in, tries=%d, error=%s',
191 i, repr(e))
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800192 if i == num_tries-1:
193 raise
John Budorick0038e102016-11-08 10:39:15 -0800194 self._browser.platform.network_controller.InitializeIfNeeded()
Achuith Bhandarkared498932013-07-16 17:01:40 -0700195
196 def __enter__(self):
197 return self
198
199
200 def __exit__(self, *args):
Derek Basehore41acbf82014-07-11 18:34:30 -0700201 self.close()
Achuith Bhandarkared498932013-07-16 17:01:40 -0700202
203
204 @property
205 def browser(self):
206 """Returns a telemetry browser instance."""
207 return self._browser
208
209
210 def get_extension(self, extension_path):
211 """Fetches a telemetry extension instance given the extension path."""
212 for ext in self._extensions_to_load:
213 if extension_path == ext.path:
214 return self.browser.extensions[ext]
215 return None
216
217
218 @property
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700219 def autotest_ext(self):
220 """Returns the autotest extension."""
221 return self.get_extension(self._autotest_ext_path)
222
223
224 @property
225 def login_status(self):
226 """Returns login status."""
227 ext = self.autotest_ext
228 if not ext:
229 return None
230
231 ext.ExecuteJavaScript('''
232 window.__login_status = null;
233 chrome.autotestPrivate.loginStatus(function(s) {
234 window.__login_status = s;
235 });
236 ''')
237 return ext.EvaluateJavaScript('window.__login_status')
238
239
Victor Hsiehfc3417e2016-03-25 16:49:59 -0700240 def get_visible_notifications(self):
241 """Returns an array of visible notifications of Chrome.
242
243 For specific type of each notification, please refer to Chromium's
244 chrome/common/extensions/api/autotest_private.idl.
245 """
246 ext = self.autotest_ext
247 if not ext:
248 return None
249
250 ext.ExecuteJavaScript('''
251 window.__items = null;
252 chrome.autotestPrivate.getVisibleNotifications(function(items) {
253 window.__items = items;
254 });
255 ''')
256 if ext.EvaluateJavaScript('window.__items') is None:
257 return None
258 return ext.EvaluateJavaScript('window.__items')
259
260
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700261 @property
Achuith Bhandarkared498932013-07-16 17:01:40 -0700262 def browser_type(self):
263 """Returns the browser_type."""
264 return self._browser_type
Achuith Bhandarkar49c72d92013-07-25 11:10:10 -0700265
266
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800267 @staticmethod
268 def did_browser_crash(func):
Achuith Bhandarkar7fb57f72013-08-29 15:29:06 -0700269 """Runs func, returns True if the browser crashed, False otherwise.
270
271 @param func: function to run.
272
273 """
Achuith Bhandarkar5abf60b2013-08-01 12:35:53 -0700274 try:
275 func()
khmela7cf9dd2016-09-20 10:50:23 -0700276 except Error:
Achuith Bhandarkar5abf60b2013-08-01 12:35:53 -0700277 return True
278 return False
Derek Basehore41acbf82014-07-11 18:34:30 -0700279
280
Achuith Bhandarkar88a2bab2015-07-09 17:36:25 -0700281 @staticmethod
282 def wait_for_browser_restart(func):
283 """Runs func, and waits for a browser restart.
284
285 @param func: function to run.
286
287 """
288 _cri = cros_interface.CrOSInterface()
289 pid = _cri.GetChromePid()
290 Chrome.did_browser_crash(func)
291 utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60)
292
293
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800294 def wait_for_browser_to_come_up(self):
295 """Waits for the browser to come up. This should only be called after a
296 browser crash.
297 """
298 def _BrowserReady(cr):
299 tabs = [] # Wrapper for pass by reference.
300 if self.did_browser_crash(
301 lambda: tabs.append(cr.browser.tabs.New())):
302 return False
303 try:
304 tabs[0].Close()
Achuith Bhandarkarfab82d12015-02-26 11:05:35 -0800305 except:
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800306 # crbug.com/350941
307 logging.error('Timed out closing tab')
308 return True
309 util.WaitFor(lambda: _BrowserReady(self), timeout=10)
310
311
Derek Basehore41acbf82014-07-11 18:34:30 -0700312 def close(self):
Achuith Bhandarkar1cc41572016-09-30 09:47:46 -0700313 """Closes the browser.
314 """
khmela7cf9dd2016-09-20 10:50:23 -0700315 try:
Nicolas Norvez56f9b232016-06-23 17:58:59 -0700316 if is_arc_available():
Shuhei Takahashi8462b882016-05-31 17:27:32 +0900317 arc_util.pre_processing_before_close(self)
Hidehiko Abe202ed322016-04-14 15:08:43 +0900318 finally:
Ricky Liang2bfa5642016-11-09 10:18:37 +0800319 # Calling platform.StopAllLocalServers() to tear down the telemetry
320 # server processes such as the one started by
321 # platform.SetHTTPServerDirectories(). Not calling this function
322 # will leak the process and may affect test results.
323 # (crbug.com/663387)
324 self._browser.platform.StopAllLocalServers()
Hidehiko Abe202ed322016-04-14 15:08:43 +0900325 self._browser.Close()
John Budorick0038e102016-11-08 10:39:15 -0800326 self._browser.platform.network_controller.Close()