blob: 221e80c2e2b9d793c7ffc884e27d0c3b8a3744e2 [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():
F#md6d56ba2016-06-10 14:40:52 -0700182 if not disable_arc_opt_in:
183 arc_util.opt_in(self.browser)
Ricky Zhoucf350ee2016-06-13 15:47:33 -0700184 arc_util.post_processing_after_browser(self)
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800185 break
khmela7cf9dd2016-09-20 10:50:23 -0700186 except exceptions.LoginException as e:
Achuith Bhandarkarf800edf2013-12-06 13:37:52 -0800187 logging.error('Timed out logging in, tries=%d, error=%s',
188 i, repr(e))
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800189 if i == num_tries-1:
190 raise
John Budorick0038e102016-11-08 10:39:15 -0800191 self._browser.platform.network_controller.InitializeIfNeeded()
Achuith Bhandarkared498932013-07-16 17:01:40 -0700192
193 def __enter__(self):
194 return self
195
196
197 def __exit__(self, *args):
Derek Basehore41acbf82014-07-11 18:34:30 -0700198 self.close()
Achuith Bhandarkared498932013-07-16 17:01:40 -0700199
200
201 @property
202 def browser(self):
203 """Returns a telemetry browser instance."""
204 return self._browser
205
206
207 def get_extension(self, extension_path):
208 """Fetches a telemetry extension instance given the extension path."""
209 for ext in self._extensions_to_load:
210 if extension_path == ext.path:
211 return self.browser.extensions[ext]
212 return None
213
214
215 @property
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700216 def autotest_ext(self):
217 """Returns the autotest extension."""
218 return self.get_extension(self._autotest_ext_path)
219
220
221 @property
222 def login_status(self):
223 """Returns login status."""
224 ext = self.autotest_ext
225 if not ext:
226 return None
227
228 ext.ExecuteJavaScript('''
229 window.__login_status = null;
230 chrome.autotestPrivate.loginStatus(function(s) {
231 window.__login_status = s;
232 });
233 ''')
234 return ext.EvaluateJavaScript('window.__login_status')
235
236
Victor Hsiehfc3417e2016-03-25 16:49:59 -0700237 def get_visible_notifications(self):
238 """Returns an array of visible notifications of Chrome.
239
240 For specific type of each notification, please refer to Chromium's
241 chrome/common/extensions/api/autotest_private.idl.
242 """
243 ext = self.autotest_ext
244 if not ext:
245 return None
246
247 ext.ExecuteJavaScript('''
248 window.__items = null;
249 chrome.autotestPrivate.getVisibleNotifications(function(items) {
250 window.__items = items;
251 });
252 ''')
253 if ext.EvaluateJavaScript('window.__items') is None:
254 return None
255 return ext.EvaluateJavaScript('window.__items')
256
257
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700258 @property
Achuith Bhandarkared498932013-07-16 17:01:40 -0700259 def browser_type(self):
260 """Returns the browser_type."""
261 return self._browser_type
Achuith Bhandarkar49c72d92013-07-25 11:10:10 -0700262
263
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800264 @staticmethod
265 def did_browser_crash(func):
Achuith Bhandarkar7fb57f72013-08-29 15:29:06 -0700266 """Runs func, returns True if the browser crashed, False otherwise.
267
268 @param func: function to run.
269
270 """
Achuith Bhandarkar5abf60b2013-08-01 12:35:53 -0700271 try:
272 func()
khmela7cf9dd2016-09-20 10:50:23 -0700273 except Error:
Achuith Bhandarkar5abf60b2013-08-01 12:35:53 -0700274 return True
275 return False
Derek Basehore41acbf82014-07-11 18:34:30 -0700276
277
Achuith Bhandarkar88a2bab2015-07-09 17:36:25 -0700278 @staticmethod
279 def wait_for_browser_restart(func):
280 """Runs func, and waits for a browser restart.
281
282 @param func: function to run.
283
284 """
285 _cri = cros_interface.CrOSInterface()
286 pid = _cri.GetChromePid()
287 Chrome.did_browser_crash(func)
288 utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60)
289
290
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800291 def wait_for_browser_to_come_up(self):
292 """Waits for the browser to come up. This should only be called after a
293 browser crash.
294 """
295 def _BrowserReady(cr):
296 tabs = [] # Wrapper for pass by reference.
297 if self.did_browser_crash(
298 lambda: tabs.append(cr.browser.tabs.New())):
299 return False
300 try:
301 tabs[0].Close()
Achuith Bhandarkarfab82d12015-02-26 11:05:35 -0800302 except:
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800303 # crbug.com/350941
304 logging.error('Timed out closing tab')
305 return True
306 util.WaitFor(lambda: _BrowserReady(self), timeout=10)
307
308
Derek Basehore41acbf82014-07-11 18:34:30 -0700309 def close(self):
Achuith Bhandarkar1cc41572016-09-30 09:47:46 -0700310 """Closes the browser.
311 """
khmela7cf9dd2016-09-20 10:50:23 -0700312 try:
Nicolas Norvez56f9b232016-06-23 17:58:59 -0700313 if is_arc_available():
Shuhei Takahashi8462b882016-05-31 17:27:32 +0900314 arc_util.pre_processing_before_close(self)
Hidehiko Abe202ed322016-04-14 15:08:43 +0900315 finally:
Ricky Liang2bfa5642016-11-09 10:18:37 +0800316 # Calling platform.StopAllLocalServers() to tear down the telemetry
317 # server processes such as the one started by
318 # platform.SetHTTPServerDirectories(). Not calling this function
319 # will leak the process and may affect test results.
320 # (crbug.com/663387)
321 self._browser.platform.StopAllLocalServers()
Hidehiko Abe202ed322016-04-14 15:08:43 +0900322 self._browser.Close()
John Budorick0038e102016-11-08 10:39:15 -0800323 self._browser.platform.network_controller.Close()