chrome.py revision 2165ae169273ec1742d270cfdc8be33922372907
1# 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
5import logging, os
6
7from autotest_lib.client.bin import utils
8from telemetry.core import cros_interface, exceptions, util
9from telemetry.internal.browser import browser_finder, browser_options
10from telemetry.internal.browser import extension_to_load
11
12Error = exceptions.Error
13
14
15class Chrome(object):
16    """Wrapper for creating a telemetry browser instance with extensions."""
17
18
19    BROWSER_TYPE_LOGIN = 'system'
20    BROWSER_TYPE_GUEST = 'system-guest'
21
22
23    def __init__(self, logged_in=True, extension_paths=[], autotest_ext=False,
24                 is_component=True, num_tries=3, extra_browser_args=None,
25                 clear_enterprise_policy=True, dont_override_profile=False,
26                 disable_gaia_services=True, disable_default_apps = True,
27                 auto_login=True, gaia_login=False,
28                 username=None, password=None):
29        """
30        Constructor of telemetry wrapper.
31
32        @param logged_in: Regular user (True) or guest user (False).
33        @param extension_paths: path of unpacked extension to install.
34        @param autotest_ext: Load a component extension with privileges to
35                             invoke chrome.autotestPrivate.
36        @param is_component: Whether extensions should be loaded as component
37                             extensions.
38        @param num_tries: Number of attempts to log in.
39        @param extra_browser_args: Additional argument(s) to pass to the
40                                   browser. It can be a string or a list.
41        @param clear_enterprise_policy: Clear enterprise policy before
42                                        logging in.
43        @param dont_override_profile: Don't delete cryptohome before login.
44                                      Telemetry will output a warning with this
45                                      option.
46        @param disable_gaia_services: For enterprise autotests, this option may
47                                      be used to enable policy fetch.
48        @param disable_default_apps: For tests that exercise default apps.
49        @param auto_login: Does not login automatically if this is False.
50                           Useful if you need to examine oobe.
51        @param gaia_login: Logs in to real gaia.
52        @param username: Log in using this username instead of the default.
53        @param username: Log in using this password instead of the default.
54        """
55        self._autotest_ext_path = None
56        if autotest_ext:
57            self._autotest_ext_path = os.path.join(os.path.dirname(__file__),
58                                                   'autotest_private_ext')
59            extension_paths.append(self._autotest_ext_path)
60
61        finder_options = browser_options.BrowserFinderOptions()
62        self._browser_type = (self.BROWSER_TYPE_LOGIN
63                if logged_in else self.BROWSER_TYPE_GUEST)
64        finder_options.browser_type = self.browser_type
65        if extra_browser_args:
66            finder_options.browser_options.AppendExtraBrowserArgs(
67                    extra_browser_args)
68
69        if logged_in:
70            extensions_to_load = finder_options.extensions_to_load
71            for path in extension_paths:
72                extension = extension_to_load.ExtensionToLoad(
73                        path, self.browser_type, is_component=is_component)
74                extensions_to_load.append(extension)
75            self._extensions_to_load = extensions_to_load
76
77        # finder options must be set before parse_args(), browser options must
78        # be set before Create().
79        # TODO(crbug.com/360890) Below MUST be '2' so that it doesn't inhibit
80        # autotest debug logs
81        finder_options.verbosity = 2
82        finder_options.CreateParser().parse_args(args=[])
83        b_options = finder_options.browser_options
84        b_options.disable_component_extensions_with_background_pages = False
85        b_options.create_browser_with_oobe = True
86        b_options.clear_enterprise_policy = clear_enterprise_policy
87        b_options.dont_override_profile = dont_override_profile
88        b_options.disable_gaia_services = disable_gaia_services
89        b_options.disable_default_apps = disable_default_apps
90        b_options.disable_component_extensions_with_background_pages = disable_default_apps
91
92        b_options.auto_login = auto_login
93        b_options.gaia_login = gaia_login
94        self.username = b_options.username if username is None else username
95        self.password = b_options.password if password is None else password
96        b_options.username = self.username
97        b_options.password = self.password
98
99        # TODO(dgarrett): Reenable, crbug.com/527144
100        # Turn on collection of Chrome coredumps via creation of a magic file.
101        # (Without this, Chrome coredumps are trashed.)
102        # open(constants.CHROME_CORE_MAGIC_FILE, 'w').close()
103
104        for i in range(num_tries):
105            try:
106                browser_to_create = browser_finder.FindBrowser(finder_options)
107                self._browser = browser_to_create.Create(finder_options)
108                break
109            except (exceptions.LoginException) as e:
110                logging.error('Timed out logging in, tries=%d, error=%s',
111                              i, repr(e))
112                if i == num_tries-1:
113                    raise
114
115
116    def __enter__(self):
117        return self
118
119
120    def __exit__(self, *args):
121        self.close()
122
123
124    @property
125    def browser(self):
126        """Returns a telemetry browser instance."""
127        return self._browser
128
129
130    def get_extension(self, extension_path):
131        """Fetches a telemetry extension instance given the extension path."""
132        for ext in self._extensions_to_load:
133            if extension_path == ext.path:
134                return self.browser.extensions[ext]
135        return None
136
137
138    @property
139    def autotest_ext(self):
140        """Returns the autotest extension."""
141        return self.get_extension(self._autotest_ext_path)
142
143
144    @property
145    def login_status(self):
146        """Returns login status."""
147        ext = self.autotest_ext
148        if not ext:
149            return None
150
151        ext.ExecuteJavaScript('''
152            window.__login_status = null;
153            chrome.autotestPrivate.loginStatus(function(s) {
154              window.__login_status = s;
155            });
156        ''')
157        return ext.EvaluateJavaScript('window.__login_status')
158
159
160    @property
161    def browser_type(self):
162        """Returns the browser_type."""
163        return self._browser_type
164
165
166    @staticmethod
167    def did_browser_crash(func):
168        """Runs func, returns True if the browser crashed, False otherwise.
169
170        @param func: function to run.
171
172        """
173        try:
174            func()
175        except (Error):
176            return True
177        return False
178
179
180    @staticmethod
181    def wait_for_browser_restart(func):
182        """Runs func, and waits for a browser restart.
183
184        @param func: function to run.
185
186        """
187        _cri = cros_interface.CrOSInterface()
188        pid = _cri.GetChromePid()
189        Chrome.did_browser_crash(func)
190        utils.poll_for_condition(lambda: pid != _cri.GetChromePid(), timeout=60)
191
192
193    def wait_for_browser_to_come_up(self):
194        """Waits for the browser to come up. This should only be called after a
195        browser crash.
196        """
197        def _BrowserReady(cr):
198            tabs = []  # Wrapper for pass by reference.
199            if self.did_browser_crash(
200                    lambda: tabs.append(cr.browser.tabs.New())):
201                return False
202            try:
203                tabs[0].Close()
204            except:
205                # crbug.com/350941
206                logging.error('Timed out closing tab')
207            return True
208        util.WaitFor(lambda: _BrowserReady(self), timeout=10)
209
210
211    def close(self):
212        """Closes the browser."""
213        self._browser.Close()
214