OLD | NEW |
| (Empty) |
1 # Copyright (c) 2012 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. | |
4 import logging | |
5 import os | |
6 import subprocess | |
7 | |
8 from telemetry import browser_backend | |
9 from telemetry import util | |
10 | |
11 class CrOSBrowserBackend(browser_backend.BrowserBackend): | |
12 def __init__(self, browser_type, options, cri): | |
13 super(CrOSBrowserBackend, self).__init__(is_content_shell=False, | |
14 supports_extensions=True, options=options) | |
15 # Initialize fields so that an explosion during init doesn't break in Close. | |
16 self._options = options | |
17 self._cri = cri | |
18 self._browser_type = browser_type | |
19 | |
20 self._remote_debugging_port = self._cri.GetRemotePort() | |
21 self._login_ext_dir = '/tmp/chromeos_login_ext' | |
22 | |
23 # Ensure the UI is running and logged out. | |
24 self._RestartUI() | |
25 | |
26 # Delete test@test.test's cryptohome vault (user data directory). | |
27 if not options.dont_override_profile: | |
28 logging.info('Deleting user\'s cryptohome vault (the user data dir)') | |
29 self._cri.GetCmdOutput( | |
30 ['cryptohome', '--action=remove', '--force', '--user=test@test.test']) | |
31 | |
32 # Push a dummy login extension to the device. | |
33 # This extension automatically logs in as test@test.test | |
34 logging.info('Copying dummy login extension to the device') | |
35 cri.PushFile( | |
36 os.path.join(os.path.dirname(__file__), 'chromeos_login_ext'), '/tmp/') | |
37 cri.GetCmdOutput(['chown', '-R', 'chronos:chronos', self._login_ext_dir]) | |
38 | |
39 # Restart Chrome with the login extension and remote debugging. | |
40 logging.info('Restarting Chrome with flags and login') | |
41 args = ['dbus-send', '--system', '--type=method_call', | |
42 '--dest=org.chromium.SessionManager', | |
43 '/org/chromium/SessionManager', | |
44 'org.chromium.SessionManagerInterface.EnableChromeTesting', | |
45 'boolean:true', | |
46 'array:string:"%s"' % '","'.join(self.GetBrowserStartupArgs())] | |
47 cri.GetCmdOutput(args) | |
48 | |
49 # Find a free local port. | |
50 self._port = util.GetAvailableLocalPort() | |
51 | |
52 # Forward the remote debugging port. | |
53 logging.info('Forwarding remote debugging port') | |
54 self._forwarder = SSHForwarder( | |
55 cri, 'L', | |
56 util.PortPair(self._port, self._remote_debugging_port)) | |
57 | |
58 # Wait for the browser to come up. | |
59 logging.info('Waiting for browser to be ready') | |
60 try: | |
61 self._WaitForBrowserToComeUp() | |
62 self._PostBrowserStartupInitialization() | |
63 except: | |
64 import traceback | |
65 traceback.print_exc() | |
66 self.Close() | |
67 raise | |
68 | |
69 | |
70 logging.info('Browser is up!') | |
71 | |
72 def GetBrowserStartupArgs(self): | |
73 self.webpagereplay_remote_http_port = self._cri.GetRemotePort() | |
74 self.webpagereplay_remote_https_port = self._cri.GetRemotePort() | |
75 | |
76 args = super(CrOSBrowserBackend, self).GetBrowserStartupArgs() | |
77 | |
78 args.extend([ | |
79 '--allow-webui-compositing', | |
80 '--aura-host-window-use-fullscreen', | |
81 '--enable-smooth-scrolling', | |
82 '--enable-threaded-compositing', | |
83 '--enable-per-tile-painting', | |
84 '--enable-gpu-sandboxing', | |
85 '--force-compositing-mode', | |
86 '--remote-debugging-port=%i' % self._remote_debugging_port, | |
87 '--auth-ext-path=%s' % self._login_ext_dir, | |
88 '--start-maximized']) | |
89 | |
90 return args | |
91 | |
92 def GetRemotePort(self, _): | |
93 return self._cri.GetRemotePort() | |
94 | |
95 def SetBrowser(self, browser): | |
96 super(CrOSBrowserBackend, self).SetBrowser(browser) | |
97 | |
98 # TODO(hartmanng): crbug.com/166886 (Remove these temporary hacks when | |
99 # _ListTabs is fixed) | |
100 | |
101 # Wait for the oobe login screen to disappear. Unfortunately, once it does, | |
102 # our TabList needs to be refreshed to point at the new non-login tab. | |
103 tab_url = None | |
104 | |
105 # When tab_url is None, we have to create or refresh the TabList | |
106 # and wait for the oobe login screen to disappear. | |
107 while tab_url is None: | |
108 self._tab_list_backend.Reset() | |
109 | |
110 # Wait for the login screen to disappear. This can cause tab_url to be | |
111 # None or to not be 'chrome://oobe/login'. | |
112 def IsTabNoneOrOobeLogin(): | |
113 tab = self._tab_list_backend.Get(0, None) | |
114 if tab is not None: | |
115 tab_url = tab.url | |
116 else: | |
117 return False | |
118 return tab_url is None or tab_url != 'chrome://oobe/login' | |
119 | |
120 # TODO(hartmanng): find a better way to detect the getting started window | |
121 # (crbug.com/171520) | |
122 try: | |
123 util.WaitFor(lambda: IsTabNoneOrOobeLogin(), 20) # pylint: disable=W0108 | |
124 except util.TimeoutException: | |
125 break | |
126 | |
127 # Refresh our tab_url variable with the current tab[0].url. If it is None | |
128 # at this point, we need to continue the loop to refresh TabController. | |
129 tab = self._tab_list_backend.Get(0, None) | |
130 if tab is not None: | |
131 tab_url = tab.url | |
132 else: | |
133 tab_url = None | |
134 | |
135 # Once we're sure that the login screen is gone, we can close all open tabs | |
136 # to make sure the first-start window doesn't interfere. | |
137 while len(self._tab_list_backend) > 1: | |
138 tab = self._tab_list_backend.Get(0, None) | |
139 if tab is not None: | |
140 tab.Close() | |
141 | |
142 # Finally open one regular tab. Normally page_runner takes care of this, | |
143 # but page_runner isn't necesarily always used (for example, in some unit | |
144 # tests). | |
145 self._tab_list_backend.New(20) | |
146 | |
147 def __del__(self): | |
148 self.Close() | |
149 | |
150 def Close(self): | |
151 super(CrOSBrowserBackend, self).Close() | |
152 | |
153 self._RestartUI() # Logs out. | |
154 | |
155 if self._forwarder: | |
156 self._forwarder.Close() | |
157 self._forwarder = None | |
158 | |
159 if self._login_ext_dir: | |
160 self._cri.RmRF(self._login_ext_dir) | |
161 self._login_ext_dir = None | |
162 | |
163 self._cri = None | |
164 | |
165 def IsBrowserRunning(self): | |
166 # On ChromeOS, there should always be a browser running. | |
167 for _, process in self._cri.ListProcesses(): | |
168 if process.startswith('/opt/google/chrome/chrome'): | |
169 return True | |
170 return False | |
171 | |
172 def GetStandardOutput(self): | |
173 return 'Cannot get standard output on CrOS' | |
174 | |
175 def CreateForwarder(self, *port_pairs): | |
176 assert self._cri | |
177 return SSHForwarder(self._cri, 'R', *port_pairs) | |
178 | |
179 def _RestartUI(self): | |
180 if self._cri: | |
181 logging.info('(Re)starting the ui (logs the user out)') | |
182 if self._cri.IsServiceRunning('ui'): | |
183 self._cri.GetCmdOutput(['restart', 'ui']) | |
184 else: | |
185 self._cri.GetCmdOutput(['start', 'ui']) | |
186 | |
187 | |
188 class SSHForwarder(object): | |
189 def __init__(self, cri, forwarding_flag, *port_pairs): | |
190 self._proc = None | |
191 | |
192 if forwarding_flag == 'R': | |
193 self._host_port = port_pairs[0].remote_port | |
194 command_line = ['-%s%i:localhost:%i' % (forwarding_flag, | |
195 port_pair.remote_port, | |
196 port_pair.local_port) | |
197 for port_pair in port_pairs] | |
198 else: | |
199 self._host_port = port_pairs[0].local_port | |
200 command_line = ['-%s%i:localhost:%i' % (forwarding_flag, | |
201 port_pair.local_port, | |
202 port_pair.remote_port) | |
203 for port_pair in port_pairs] | |
204 | |
205 self._device_port = port_pairs[0].remote_port | |
206 | |
207 self._proc = subprocess.Popen( | |
208 cri.FormSSHCommandLine(['sleep', '999999999'], command_line), | |
209 stdout=subprocess.PIPE, | |
210 stderr=subprocess.PIPE, | |
211 stdin=subprocess.PIPE, | |
212 shell=False) | |
213 | |
214 util.WaitFor(lambda: cri.IsHTTPServerRunningOnPort(self._device_port), 60) | |
215 | |
216 @property | |
217 def url(self): | |
218 assert self._proc | |
219 return 'http://localhost:%i' % self._host_port | |
220 | |
221 def Close(self): | |
222 if self._proc: | |
223 self._proc.kill() | |
224 self._proc = None | |
OLD | NEW |