blob: 2b3bb72beaef7eb941988966ad3523e9563d9e44 [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
Achuith Bhandarkarf3469ec2016-06-08 16:58:57 -070017def NormalizeEmail(username):
18 """Remove dots from username. Add @gmail.com if necessary.
19
20 TODO(achuith): Get rid of this when crbug.com/358427 is fixed.
21
22 @param username: username/email to be scrubbed.
23 """
24 parts = re.split('@', username)
25 parts[0] = re.sub('\.', '', parts[0])
26
27 if len(parts) == 1:
28 parts.append('gmail.com')
29 return '@'.join(parts)
30
31
Achuith Bhandarkared498932013-07-16 17:01:40 -070032class Chrome(object):
Ricky Liang2bfa5642016-11-09 10:18:37 +080033 """Wrapper for creating a telemetry browser instance with extensions.
34
35 The recommended way to use this class is to create the instance using the
36 with statement:
37
38 >>> with chrome.Chrome(...) as cr:
39 >>> # Do whatever you need with cr.
40 >>> pass
41
42 This will make sure all the clean-up functions are called. If you really
43 need to use this class without the with statement, make sure to call the
44 close() method once you're done with the Chrome instance.
45 """
Achuith Bhandarkar86c46812013-07-15 17:13:39 -070046
47
Achuith Bhandarkared498932013-07-16 17:01:40 -070048 BROWSER_TYPE_LOGIN = 'system'
49 BROWSER_TYPE_GUEST = 'system-guest'
Achuith Bhandarkar86c46812013-07-15 17:13:39 -070050
51
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -080052 def __init__(self, logged_in=True, extension_paths=[], autotest_ext=False,
Achuith Bhandarkar4a46bb82016-10-10 12:23:28 -070053 num_tries=3, extra_browser_args=None,
Achuith Bhandarkar882a8ab2014-08-26 15:51:20 -070054 clear_enterprise_policy=True, dont_override_profile=False,
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -080055 disable_gaia_services=True, disable_default_apps = True,
56 auto_login=True, gaia_login=False,
Victor Hsieh000240b2016-04-11 16:04:31 -070057 username=None, password=None, gaia_id=None,
Achuith Bhandarkar08f9a592017-01-20 17:14:02 -080058 arc_mode=None, disable_arc_opt_in=True,
59 init_network_controller=True):
Dean Liaob12e2ee2013-11-19 16:49:12 +080060 """
Achuith Bhandarkar944405a2013-11-21 14:47:48 -080061 Constructor of telemetry wrapper.
62
63 @param logged_in: Regular user (True) or guest user (False).
64 @param extension_paths: path of unpacked extension to install.
65 @param autotest_ext: Load a component extension with privileges to
66 invoke chrome.autotestPrivate.
Achuith Bhandarkarb7ef5e52014-03-20 14:18:45 -070067 @param num_tries: Number of attempts to log in.
Dean Liaob12e2ee2013-11-19 16:49:12 +080068 @param extra_browser_args: Additional argument(s) to pass to the
Achuith Bhandarkar3875e0c2014-03-20 14:17:31 -070069 browser. It can be a string or a list.
Achuith Bhandarkar5d6c26c2014-04-25 11:19:38 -070070 @param clear_enterprise_policy: Clear enterprise policy before
71 logging in.
Achuith Bhandarkar882a8ab2014-08-26 15:51:20 -070072 @param dont_override_profile: Don't delete cryptohome before login.
73 Telemetry will output a warning with this
74 option.
Achuith Bhandarkar3c654f32014-09-29 06:33:35 -070075 @param disable_gaia_services: For enterprise autotests, this option may
76 be used to enable policy fetch.
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -080077 @param disable_default_apps: For tests that exercise default apps.
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -080078 @param auto_login: Does not login automatically if this is False.
Achuith Bhandarkar3875e0c2014-03-20 14:17:31 -070079 Useful if you need to examine oobe.
80 @param gaia_login: Logs in to real gaia.
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -080081 @param username: Log in using this username instead of the default.
Alexander Alekseevd9682862015-10-01 22:42:49 -070082 @param password: Log in using this password instead of the default.
83 @param gaia_id: Log in using this gaia_id instead of the default.
Victor Hsieh43651ac2016-04-20 12:52:21 -070084 @param arc_mode: How ARC instance should be started. Default is to not
85 start.
F#md6d56ba2016-06-10 14:40:52 -070086 @param disable_arc_opt_in: For opt in flow autotest. This option is used
87 to disable the arc opt in flow.
Dean Liaob12e2ee2013-11-19 16:49:12 +080088 """
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -070089 self._autotest_ext_path = None
90 if autotest_ext:
91 self._autotest_ext_path = os.path.join(os.path.dirname(__file__),
92 'autotest_private_ext')
93 extension_paths.append(self._autotest_ext_path)
94
Achuith Bhandarkar7fb57f72013-08-29 15:29:06 -070095 finder_options = browser_options.BrowserFinderOptions()
Achuith Bhandarkar89c442d2017-02-15 11:19:54 +010096 if utils.is_arc_available() and arc_util.should_start_arc(arc_mode):
F#md6d56ba2016-06-10 14:40:52 -070097 if disable_arc_opt_in:
98 finder_options.browser_options.AppendExtraBrowserArgs(
99 arc_util.get_extra_chrome_flags())
Victor Hsieh43651ac2016-04-20 12:52:21 -0700100 logged_in = True
101
Achuith Bhandarkared498932013-07-16 17:01:40 -0700102 self._browser_type = (self.BROWSER_TYPE_LOGIN
103 if logged_in else self.BROWSER_TYPE_GUEST)
Achuith Bhandarkarf130c402013-09-11 14:39:22 -0700104 finder_options.browser_type = self.browser_type
Dean Liaob12e2ee2013-11-19 16:49:12 +0800105 if extra_browser_args:
106 finder_options.browser_options.AppendExtraBrowserArgs(
107 extra_browser_args)
Achuith Bhandarkarf130c402013-09-11 14:39:22 -0700108
Achuith Bhandarkar3e5051d2013-10-15 15:20:12 -0700109 # finder options must be set before parse_args(), browser options must
110 # be set before Create().
Todd Brocheb6f4812014-04-29 16:04:28 -0700111 # TODO(crbug.com/360890) Below MUST be '2' so that it doesn't inhibit
112 # autotest debug logs
113 finder_options.verbosity = 2
Achuith Bhandarkardafc9a52013-09-24 15:26:33 +0200114 finder_options.CreateParser().parse_args(args=[])
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700115 b_options = finder_options.browser_options
116 b_options.disable_component_extensions_with_background_pages = False
117 b_options.create_browser_with_oobe = True
Achuith Bhandarkar5d6c26c2014-04-25 11:19:38 -0700118 b_options.clear_enterprise_policy = clear_enterprise_policy
Achuith Bhandarkar882a8ab2014-08-26 15:51:20 -0700119 b_options.dont_override_profile = dont_override_profile
Achuith Bhandarkar3c654f32014-09-29 06:33:35 -0700120 b_options.disable_gaia_services = disable_gaia_services
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -0800121 b_options.disable_default_apps = disable_default_apps
122 b_options.disable_component_extensions_with_background_pages = disable_default_apps
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800123
124 b_options.auto_login = auto_login
Achuith Bhandarkar3875e0c2014-03-20 14:17:31 -0700125 b_options.gaia_login = gaia_login
F#md6d56ba2016-06-10 14:40:52 -0700126
Achuith Bhandarkar89c442d2017-02-15 11:19:54 +0100127 if utils.is_arc_available() and not disable_arc_opt_in:
F#md6d56ba2016-06-10 14:40:52 -0700128 arc_util.set_browser_options_for_opt_in(b_options)
129
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800130 self.username = b_options.username if username is None else username
131 self.password = b_options.password if password is None else password
Achuith Bhandarkarf3469ec2016-06-08 16:58:57 -0700132 self.username = NormalizeEmail(self.username)
Achuith Bhandarkar1cce9dc2014-02-05 14:23:34 -0800133 b_options.username = self.username
134 b_options.password = self.password
Achuith Bhandarkar6f4accc2016-08-23 14:03:44 -0700135 self.gaia_id = b_options.gaia_id if gaia_id is None else gaia_id
136 b_options.gaia_id = self.gaia_id
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700137
Shuhei Takahashi8462b882016-05-31 17:27:32 +0900138 self.arc_mode = arc_mode
139
Achuith Bhandarkar531a62a2016-04-13 11:09:43 -0700140 if logged_in:
Achuith Bhandarkar6f4accc2016-08-23 14:03:44 -0700141 extensions_to_load = b_options.extensions_to_load
142 for path in extension_paths:
143 extension = extension_to_load.ExtensionToLoad(
Achuith Bhandarkar4a46bb82016-10-10 12:23:28 -0700144 path, self.browser_type)
Achuith Bhandarkar6f4accc2016-08-23 14:03:44 -0700145 extensions_to_load.append(extension)
146 self._extensions_to_load = extensions_to_load
Achuith Bhandarkar531a62a2016-04-13 11:09:43 -0700147
David James54a830e2015-05-06 17:21:47 -0700148 # Turn on collection of Chrome coredumps via creation of a magic file.
149 # (Without this, Chrome coredumps are trashed.)
Don Garrett11b13972015-09-11 17:37:53 +0000150 open(constants.CHROME_CORE_MAGIC_FILE, 'w').close()
David James54a830e2015-05-06 17:21:47 -0700151
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800152 for i in range(num_tries):
153 try:
154 browser_to_create = browser_finder.FindBrowser(finder_options)
Achuith Bhandarkara2bceaf2014-11-14 12:10:16 -0800155 self._browser = browser_to_create.Create(finder_options)
Achuith Bhandarkar89c442d2017-02-15 11:19:54 +0100156 if utils.is_arc_available():
khmele0434292016-11-10 13:27:57 -0800157 if disable_arc_opt_in:
158 if arc_util.should_start_arc(arc_mode):
159 arc_util.enable_arc_setting(self.browser)
160 else:
F#md6d56ba2016-06-10 14:40:52 -0700161 arc_util.opt_in(self.browser)
Ricky Zhoucf350ee2016-06-13 15:47:33 -0700162 arc_util.post_processing_after_browser(self)
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800163 break
khmela7cf9dd2016-09-20 10:50:23 -0700164 except exceptions.LoginException as e:
Achuith Bhandarkarf800edf2013-12-06 13:37:52 -0800165 logging.error('Timed out logging in, tries=%d, error=%s',
166 i, repr(e))
Achuith Bhandarkarb0e99072013-11-18 16:15:03 -0800167 if i == num_tries-1:
168 raise
Achuith Bhandarkar08f9a592017-01-20 17:14:02 -0800169 if init_network_controller:
170 self._browser.platform.network_controller.InitializeIfNeeded()
Achuith Bhandarkared498932013-07-16 17:01:40 -0700171
172 def __enter__(self):
173 return self
174
175
176 def __exit__(self, *args):
Derek Basehore41acbf82014-07-11 18:34:30 -0700177 self.close()
Achuith Bhandarkared498932013-07-16 17:01:40 -0700178
179
180 @property
181 def browser(self):
182 """Returns a telemetry browser instance."""
183 return self._browser
184
185
186 def get_extension(self, extension_path):
187 """Fetches a telemetry extension instance given the extension path."""
188 for ext in self._extensions_to_load:
189 if extension_path == ext.path:
190 return self.browser.extensions[ext]
191 return None
192
193
194 @property
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700195 def autotest_ext(self):
196 """Returns the autotest extension."""
197 return self.get_extension(self._autotest_ext_path)
198
199
200 @property
201 def login_status(self):
202 """Returns login status."""
203 ext = self.autotest_ext
204 if not ext:
205 return None
206
207 ext.ExecuteJavaScript('''
208 window.__login_status = null;
209 chrome.autotestPrivate.loginStatus(function(s) {
210 window.__login_status = s;
211 });
212 ''')
213 return ext.EvaluateJavaScript('window.__login_status')
214
215
Victor Hsiehfc3417e2016-03-25 16:49:59 -0700216 def get_visible_notifications(self):
217 """Returns an array of visible notifications of Chrome.
218
219 For specific type of each notification, please refer to Chromium's
220 chrome/common/extensions/api/autotest_private.idl.
221 """
222 ext = self.autotest_ext
223 if not ext:
224 return None
225
226 ext.ExecuteJavaScript('''
227 window.__items = null;
228 chrome.autotestPrivate.getVisibleNotifications(function(items) {
229 window.__items = items;
230 });
231 ''')
232 if ext.EvaluateJavaScript('window.__items') is None:
233 return None
234 return ext.EvaluateJavaScript('window.__items')
235
236
Achuith Bhandarkara2df47b2013-10-09 21:23:15 -0700237 @property
Achuith Bhandarkared498932013-07-16 17:01:40 -0700238 def browser_type(self):
239 """Returns the browser_type."""
240 return self._browser_type
Achuith Bhandarkar49c72d92013-07-25 11:10:10 -0700241
242
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800243 @staticmethod
244 def did_browser_crash(func):
Achuith Bhandarkar7fb57f72013-08-29 15:29:06 -0700245 """Runs func, returns True if the browser crashed, False otherwise.
246
247 @param func: function to run.
248
249 """
Achuith Bhandarkar5abf60b2013-08-01 12:35:53 -0700250 try:
251 func()
khmela7cf9dd2016-09-20 10:50:23 -0700252 except Error:
Achuith Bhandarkar5abf60b2013-08-01 12:35:53 -0700253 return True
254 return False
Derek Basehore41acbf82014-07-11 18:34:30 -0700255
256
Achuith Bhandarkar88a2bab2015-07-09 17:36:25 -0700257 @staticmethod
258 def wait_for_browser_restart(func):
259 """Runs func, and waits for a browser restart.
260
261 @param func: function to run.
262
263 """
264 _cri = cros_interface.CrOSInterface()
265 pid = _cri.GetChromePid()
266 Chrome.did_browser_crash(func)
267 utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60)
268
269
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800270 def wait_for_browser_to_come_up(self):
271 """Waits for the browser to come up. This should only be called after a
272 browser crash.
273 """
274 def _BrowserReady(cr):
275 tabs = [] # Wrapper for pass by reference.
276 if self.did_browser_crash(
277 lambda: tabs.append(cr.browser.tabs.New())):
278 return False
279 try:
280 tabs[0].Close()
Achuith Bhandarkarfab82d12015-02-26 11:05:35 -0800281 except:
Achuith Bhandarkar16ee9772015-01-23 17:18:18 -0800282 # crbug.com/350941
283 logging.error('Timed out closing tab')
284 return True
285 util.WaitFor(lambda: _BrowserReady(self), timeout=10)
286
287
Derek Basehore41acbf82014-07-11 18:34:30 -0700288 def close(self):
Achuith Bhandarkar1cc41572016-09-30 09:47:46 -0700289 """Closes the browser.
290 """
khmela7cf9dd2016-09-20 10:50:23 -0700291 try:
Achuith Bhandarkar89c442d2017-02-15 11:19:54 +0100292 if utils.is_arc_available():
Shuhei Takahashi8462b882016-05-31 17:27:32 +0900293 arc_util.pre_processing_before_close(self)
Hidehiko Abe202ed322016-04-14 15:08:43 +0900294 finally:
Ricky Liang2bfa5642016-11-09 10:18:37 +0800295 # Calling platform.StopAllLocalServers() to tear down the telemetry
296 # server processes such as the one started by
297 # platform.SetHTTPServerDirectories(). Not calling this function
298 # will leak the process and may affect test results.
299 # (crbug.com/663387)
300 self._browser.platform.StopAllLocalServers()
Hidehiko Abe202ed322016-04-14 15:08:43 +0900301 self._browser.Close()
John Budorick0038e102016-11-08 10:39:15 -0800302 self._browser.platform.network_controller.Close()