1 # Copyright 2013 The Chromium 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.
8 from telemetry import decorators
10 from telemetry.core import exceptions
11 from telemetry.core import forwarders
12 from telemetry.core import util
13 from telemetry.core.backends.chrome import chrome_browser_backend
14 from telemetry.core.backends.chrome import misc_web_contents_backend
15 from telemetry.core.forwarders import cros_forwarder
18 class CrOSBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
19 def __init__(self, browser_type, browser_options, cri, is_guest,
21 super(CrOSBrowserBackend, self).__init__(
22 is_content_shell=False, supports_extensions=not is_guest,
23 browser_options=browser_options,
24 output_profile_path=None, extensions_to_load=extensions_to_load)
26 # Initialize fields so that an explosion during init doesn't break in Close.
27 self._browser_type = browser_type
29 self._is_guest = is_guest
30 self._forwarder = None
32 from telemetry.core.backends.chrome import chrome_browser_options
33 assert isinstance(browser_options,
34 chrome_browser_options.CrosBrowserOptions)
36 self.wpr_port_pairs = forwarders.PortPairs(
37 http=forwarders.PortPair(self.wpr_port_pairs.http.local_port,
38 self._cri.GetRemotePort()),
39 https=forwarders.PortPair(self.wpr_port_pairs.https.local_port,
40 self._cri.GetRemotePort()),
42 self._remote_debugging_port = self._cri.GetRemotePort()
43 self._port = self._remote_debugging_port
45 # Copy extensions to temp directories on the device.
46 # Note that we also perform this copy locally to ensure that
47 # the owner of the extensions is set to chronos.
48 for e in extensions_to_load:
49 output = cri.RunCmdOnDevice(['mktemp', '-d', '/tmp/extension_XXXXX'])
50 extension_dir = output[0].rstrip()
51 cri.PushFile(e.path, extension_dir)
52 cri.Chown(extension_dir)
53 e.local_path = os.path.join(extension_dir, os.path.basename(e.path))
55 self._cri.RestartUI(self.browser_options.clear_enterprise_policy)
56 util.WaitFor(self.IsBrowserRunning, 20)
58 # Delete test user's cryptohome vault (user data directory).
59 if not self.browser_options.dont_override_profile:
60 self._cri.RunCmdOnDevice(['cryptohome', '--action=remove', '--force',
61 '--user=%s' % self._username])
62 if self.browser_options.profile_dir:
63 cri.RmRF(self.profile_directory)
64 cri.PushFile(self.browser_options.profile_dir + '/Default',
65 self.profile_directory)
66 cri.Chown(self.profile_directory)
68 self._SetBranchNumber(self._GetChromeVersion())
70 def GetBrowserStartupArgs(self):
71 args = super(CrOSBrowserBackend, self).GetBrowserStartupArgs()
73 '--enable-smooth-scrolling',
74 '--enable-threaded-compositing',
75 '--enable-per-tile-painting',
76 '--force-compositing-mode',
77 # Disables the start page, as well as other external apps that can
78 # steal focus or make measurements inconsistent.
79 '--disable-default-apps',
80 # Skip user image selection screen, and post login screens.
81 '--oobe-skip-postlogin',
82 # Allow devtools to connect to chrome.
83 '--remote-debugging-port=%i' % self._remote_debugging_port,
84 # Open a maximized window.
87 '--vmodule=*/chromeos/net/*=2,*/chromeos/login/*=2'])
91 def _GetChromeVersion(self):
92 result = util.WaitFor(self._cri.GetChromeProcess, timeout=30)
93 assert result and result['path']
94 (version, _) = self._cri.RunCmdOnDevice([result['path'], '--version'])
100 return self._cri.GetChromePid()
103 def browser_directory(self):
104 result = self._cri.GetChromeProcess()
105 if result and 'path' in result:
106 return os.path.dirname(result['path'])
110 def profile_directory(self):
111 return '/home/chronos/Default'
113 def GetRemotePort(self, port):
116 return self._cri.GetRemotePort()
122 # Escape all commas in the startup arguments we pass to Chrome
123 # because dbus-send delimits array elements by commas
124 startup_args = [a.replace(',', '\\,') for a in self.GetBrowserStartupArgs()]
126 # Restart Chrome with the login extension and remote debugging.
127 logging.info('Restarting Chrome with flags and login')
128 args = ['dbus-send', '--system', '--type=method_call',
129 '--dest=org.chromium.SessionManager',
130 '/org/chromium/SessionManager',
131 'org.chromium.SessionManagerInterface.EnableChromeTesting',
133 'array:string:"%s"' % ','.join(startup_args)]
134 self._cri.RunCmdOnDevice(args)
136 if not self._cri.local:
137 self._port = util.GetUnreservedAvailableLocalPort()
138 self._forwarder = self.forwarder_factory.Create(
139 forwarders.PortPairs(
140 http=forwarders.PortPair(self._port, self._remote_debugging_port),
142 dns=None), forwarding_flag='L')
145 self._WaitForBrowserToComeUp(wait_for_extensions=False)
146 self._PostBrowserStartupInitialization()
149 traceback.print_exc()
153 util.WaitFor(lambda: self.oobe_exists, 10)
155 if self.browser_options.auto_login:
158 self.oobe.NavigateGuestLogin()
159 # Guest browsing shuts down the current browser and launches an
160 # incognito browser in a separate process, which we need to wait for.
161 util.WaitFor(lambda: pid != self.pid, 10)
162 elif self.browser_options.gaia_login:
164 self.oobe.NavigateGaiaLogin(self._username, self._password)
165 except util.TimeoutException:
166 self._cri.TakeScreenShot('gaia-login')
169 self.oobe.NavigateFakeLogin(self._username, self._password)
172 logging.info('Browser is up!')
175 super(CrOSBrowserBackend, self).Close()
178 self._cri.RestartUI(False) # Logs out.
180 util.WaitFor(lambda: not self._IsCryptohomeMounted(), 30)
183 self._forwarder.Close()
184 self._forwarder = None
187 for e in self._extensions_to_load:
188 self._cri.RmRF(os.path.dirname(e.local_path))
194 def forwarder_factory(self):
195 return cros_forwarder.CrOsForwarderFactory(self._cri)
197 def IsBrowserRunning(self):
198 return bool(self.pid)
200 def GetStandardOutput(self):
201 return 'Cannot get standard output on CrOS'
203 def GetStackTrace(self):
204 return 'Cannot get stack trace on CrOS'
208 def misc_web_contents_backend(self):
209 """Access to chrome://oobe/login page."""
210 return misc_web_contents_backend.MiscWebContentsBackend(self)
214 return self.misc_web_contents_backend.GetOobe()
217 def oobe_exists(self):
218 return self.misc_web_contents_backend.oobe_exists
222 return self.browser_options.username
226 return self.browser_options.password
228 def _IsCryptohomeMounted(self):
229 username = '$guest' if self._is_guest else self._username
230 return self._cri.IsCryptohomeMounted(username, self._is_guest)
232 def _IsLoggedIn(self):
233 """Returns True if cryptohome has mounted, the browser is
234 responsive to devtools requests, and the oobe has been dismissed."""
235 return (self._IsCryptohomeMounted() and
236 self.HasBrowserFinishedLaunching() and
237 not self.oobe_exists)
239 def _WaitForLogin(self):
241 self._WaitForBrowserToComeUp()
242 util.WaitFor(self._IsCryptohomeMounted, 30)
246 util.WaitFor(self._IsLoggedIn, 60)
247 except util.TimeoutException:
248 self._cri.TakeScreenShot('login-screen')
249 raise exceptions.LoginException('Timed out going through login screen')
251 # Wait for extensions to load.
253 self._WaitForBrowserToComeUp()
254 except util.TimeoutException:
255 logging.error('Chrome args: %s' % self._cri.GetChromeProcess()['args'])
256 self._cri.TakeScreenShot('extension-timeout')
259 # Workaround for crbug.com/329271, crbug.com/334726.
263 # Open a new window/tab.
264 tab = self.tab_list_backend.New(timeout=30)
265 tab.Navigate('about:blank', timeout=10)
267 except (exceptions.TabCrashException, util.TimeoutException,
270 logging.warn('TabCrashException/TimeoutException in '
271 'new tab creation/navigation, '
272 'remaining retries %d' % retries)