blob: 053e883de5398ef892968172a41dc41626d75733 [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 Bhandarkar4a46bb82016-10-10 12:23:28 -070077 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.
Achuith Bhandarkarb7ef5e52014-03-20 14:18:45 -070090 @param num_tries: Number of attempts to log in.
Dean Liaob12e2ee2013-11-19 16:49:12 +080091 @param extra_browser_args: Additional argument(s) to pass to the
Achuith Bhandarkar3875e0c2014-03-20 14:17:31 -070092 browser. It can be a string or a list.
Achuith Bhandarkar5d6c26c2014-04-25 11:19:38 -070093 @param clear_enterprise_policy: Clear enterprise policy before
94 logging in.
Achuith Bhandarkar882a8ab2014-08-26 15:51:20 -070095 @param dont_override_profile: Don't delete cryptohome before login.
96 Telemetry will output a warning with this
97 option.
Achuith Bhandarkar3c654f32014-09-29 06:33:35 -070098 @param disable_gaia_services: For enterprise autotests, this option may
99 be used to enable policy fetch.
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -0800100 @param disable_default_apps: For tests that exercise default apps.
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800101 @param auto_login: Does not login automatically if this is False.
Achuith Bhandarkar3875e0c2014-03-20 14:17:31 -0700102 Useful if you need to examine oobe.
103 @param gaia_login: Logs in to real gaia.
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800104 @param username: Log in using this username instead of the default.
Alexander Alekseevd9682862015-10-01 22:42:49 -0700105 @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.
Victor Hsieh43651ac2016-04-20 12:52:21 -0700107 @param arc_mode: How ARC instance should be started. Default is to not
108 start.
F#md6d56ba2016-06-10 14:40:52 -0700109 @param disable_arc_opt_in: For opt in flow autotest. This option is used
110 to disable the arc opt in flow.
Dean Liaob12e2ee2013-11-19 16:49:12 +0800111 """
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700112 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
Achuith Bhandarkar7fb57f72013-08-29 15:29:06 -0700118 finder_options = browser_options.BrowserFinderOptions()
Nicolas Norvez56f9b232016-06-23 17:58:59 -0700119 if is_arc_available() and arc_util.should_start_arc(arc_mode):
F#md6d56ba2016-06-10 14:40:52 -0700120 if disable_arc_opt_in:
121 finder_options.browser_options.AppendExtraBrowserArgs(
122 arc_util.get_extra_chrome_flags())
Victor Hsieh43651ac2016-04-20 12:52:21 -0700123 logged_in = True
124
Achuith Bhandarkared498932013-07-16 17:01:40 -0700125 self._browser_type = (self.BROWSER_TYPE_LOGIN
126 if logged_in else self.BROWSER_TYPE_GUEST)
Achuith Bhandarkarf130c402013-09-11 14:39:22 -0700127 finder_options.browser_type = self.browser_type
Dean Liaob12e2ee2013-11-19 16:49:12 +0800128 if extra_browser_args:
129 finder_options.browser_options.AppendExtraBrowserArgs(
130 extra_browser_args)
Achuith Bhandarkarf130c402013-09-11 14:39:22 -0700131
Achuith Bhandarkar3e5051d2013-10-15 15:20:12 -0700132 # finder options must be set before parse_args(), browser options must
133 # be set before Create().
Todd Brocheb6f4812014-04-29 16:04:28 -0700134 # TODO(crbug.com/360890) Below MUST be '2' so that it doesn't inhibit
135 # autotest debug logs
136 finder_options.verbosity = 2
Achuith Bhandarkardafc9a52013-09-24 15:26:33 +0200137 finder_options.CreateParser().parse_args(args=[])
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700138 b_options = finder_options.browser_options
139 b_options.disable_component_extensions_with_background_pages = False
140 b_options.create_browser_with_oobe = True
Achuith Bhandarkar5d6c26c2014-04-25 11:19:38 -0700141 b_options.clear_enterprise_policy = clear_enterprise_policy
Achuith Bhandarkar882a8ab2014-08-26 15:51:20 -0700142 b_options.dont_override_profile = dont_override_profile
Achuith Bhandarkar3c654f32014-09-29 06:33:35 -0700143 b_options.disable_gaia_services = disable_gaia_services
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -0800144 b_options.disable_default_apps = disable_default_apps
145 b_options.disable_component_extensions_with_background_pages = disable_default_apps
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800146
147 b_options.auto_login = auto_login
Achuith Bhandarkar3875e0c2014-03-20 14:17:31 -0700148 b_options.gaia_login = gaia_login
F#md6d56ba2016-06-10 14:40:52 -0700149
Nicolas Norvez56f9b232016-06-23 17:58:59 -0700150 if is_arc_available() and not disable_arc_opt_in:
F#md6d56ba2016-06-10 14:40:52 -0700151 arc_util.set_browser_options_for_opt_in(b_options)
152
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800153 self.username = b_options.username if username is None else username
154 self.password = b_options.password if password is None else password
Achuith Bhandarkarf3469ec2016-06-08 16:58:57 -0700155 self.username = NormalizeEmail(self.username)
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800156 b_options.username = self.username
157 b_options.password = self.password
Achuith Bhandarkar6f4accc2016-08-23 14:03:44 -0700158 self.gaia_id = b_options.gaia_id if gaia_id is None else gaia_id
159 b_options.gaia_id = self.gaia_id
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700160
Shuhei Takahashi8462b882016-05-31 17:27:32 +0900161 self.arc_mode = arc_mode
162
Achuith Bhandarkar531a62a2016-04-13 11:09:43 -0700163 if logged_in:
Achuith Bhandarkar6f4accc2016-08-23 14:03:44 -0700164 extensions_to_load = b_options.extensions_to_load
165 for path in extension_paths:
166 extension = extension_to_load.ExtensionToLoad(
Achuith Bhandarkar4a46bb82016-10-10 12:23:28 -0700167 path, self.browser_type)
Achuith Bhandarkar6f4accc2016-08-23 14:03:44 -0700168 extensions_to_load.append(extension)
169 self._extensions_to_load = extensions_to_load
Achuith Bhandarkar531a62a2016-04-13 11:09:43 -0700170
David James54a830e2015-05-06 17:21:47 -0700171 # Turn on collection of Chrome coredumps via creation of a magic file.
172 # (Without this, Chrome coredumps are trashed.)
Don Garrett11b13972015-09-11 17:37:53 +0000173 open(constants.CHROME_CORE_MAGIC_FILE, 'w').close()
David James54a830e2015-05-06 17:21:47 -0700174
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800175 for i in range(num_tries):
176 try:
177 browser_to_create = browser_finder.FindBrowser(finder_options)
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -0800178 self._browser = browser_to_create.Create(finder_options)
Nicolas Norvez56f9b232016-06-23 17:58:59 -0700179 if is_arc_available():
khmele0434292016-11-10 13:27:57 -0800180 if disable_arc_opt_in:
181 if arc_util.should_start_arc(arc_mode):
182 arc_util.enable_arc_setting(self.browser)
183 else:
F#md6d56ba2016-06-10 14:40:52 -0700184 arc_util.opt_in(self.browser)
Ricky Zhoucf350ee2016-06-13 15:47:33 -0700185 arc_util.post_processing_after_browser(self)
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800186 break
khmela7cf9dd2016-09-20 10:50:23 -0700187 except exceptions.LoginException as e:
Achuith Bhandarkarf800edf2013-12-06 13:37:52 -0800188 logging.error('Timed out logging in, tries=%d, error=%s',
189 i, repr(e))
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800190 if i == num_tries-1:
191 raise
John Budorick0038e102016-11-08 10:39:15 -0800192 self._browser.platform.network_controller.InitializeIfNeeded()
Achuith Bhandarkared498932013-07-16 17:01:40 -0700193
194 def __enter__(self):
195 return self
196
197
198 def __exit__(self, *args):
Derek Basehore41acbf82014-07-11 18:34:30 -0700199 self.close()
Achuith Bhandarkared498932013-07-16 17:01:40 -0700200
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
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700217 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
Victor Hsiehfc3417e2016-03-25 16:49:59 -0700238 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
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700259 @property
Achuith Bhandarkared498932013-07-16 17:01:40 -0700260 def browser_type(self):
261 """Returns the browser_type."""
262 return self._browser_type
Achuith Bhandarkar49c72d92013-07-25 11:10:10 -0700263
264
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800265 @staticmethod
266 def did_browser_crash(func):
Achuith Bhandarkar7fb57f72013-08-29 15:29:06 -0700267 """Runs func, returns True if the browser crashed, False otherwise.
268
269 @param func: function to run.
270
271 """
Achuith Bhandarkar5abf60b2013-08-01 12:35:53 -0700272 try:
273 func()
khmela7cf9dd2016-09-20 10:50:23 -0700274 except Error:
Achuith Bhandarkar5abf60b2013-08-01 12:35:53 -0700275 return True
276 return False
Derek Basehore41acbf82014-07-11 18:34:30 -0700277
278
Achuith Bhandarkar88a2bab2015-07-09 17:36:25 -0700279 @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
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800292 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()
Achuith Bhandarkarfab82d12015-02-26 11:05:35 -0800303 except:
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800304 # crbug.com/350941
305 logging.error('Timed out closing tab')
306 return True
307 util.WaitFor(lambda: _BrowserReady(self), timeout=10)
308
309
Derek Basehore41acbf82014-07-11 18:34:30 -0700310 def close(self):
Achuith Bhandarkar1cc41572016-09-30 09:47:46 -0700311 """Closes the browser.
312 """
khmela7cf9dd2016-09-20 10:50:23 -0700313 try:
Nicolas Norvez56f9b232016-06-23 17:58:59 -0700314 if is_arc_available():
Shuhei Takahashi8462b882016-05-31 17:27:32 +0900315 arc_util.pre_processing_before_close(self)
Hidehiko Abe202ed322016-04-14 15:08:43 +0900316 finally:
Ricky Liang2bfa5642016-11-09 10:18:37 +0800317 # 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()
Hidehiko Abe202ed322016-04-14 15:08:43 +0900323 self._browser.Close()
John Budorick0038e102016-11-08 10:39:15 -0800324 self._browser.platform.network_controller.Close()