1# Copyright 2015 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
5"""A module providing common resources for different facades."""
6
7import exceptions
8
9from autotest_lib.client.common_lib.cros import chrome
10from autotest_lib.client.common_lib.cros import retry
11from autotest_lib.client.cros import constants
12
13_FLAKY_CALL_RETRY_TIMEOUT_SEC = 60
14_FLAKY_CHROME_CALL_RETRY_DELAY_SEC = 1
15
16retry_chrome_call = retry.retry(
17        (chrome.Error, exceptions.IndexError, exceptions.Exception),
18        timeout_min=_FLAKY_CALL_RETRY_TIMEOUT_SEC / 60.0,
19        delay_sec=_FLAKY_CHROME_CALL_RETRY_DELAY_SEC)
20
21class FacadeResource(object):
22    """This class provides access to telemetry chrome wrapper."""
23
24    EXTRA_BROWSER_ARGS = ['--enable-gpu-benchmarking']
25
26    def __init__(self, chrome_object=None, restart=False):
27        """Initializes a FacadeResource.
28
29        @param chrome_object: A chrome.Chrome object or None.
30        @param restart: Preserve the previous browser state.
31
32        """
33        if chrome_object:
34            self._chrome = chrome_object
35        else:
36            self._chrome = chrome.Chrome(
37                extension_paths=[constants.MULTIMEDIA_TEST_EXTENSION],
38                extra_browser_args=self.EXTRA_BROWSER_ARGS,
39                clear_enterprise_policy=not restart,
40                autotest_ext=True)
41        self._browser = self._chrome.browser
42        # The opened tabs are stored by tab descriptors.
43        # Key is the tab descriptor string.
44        # We use string as the key because of RPC Call. Client can use the
45        # string to locate the tab object.
46        # Value is the tab object.
47        self._tabs = dict()
48
49
50    def close(self):
51        """Closes Chrome."""
52        self._chrome.close()
53
54
55    def __enter__(self):
56        return self
57
58
59    def __exit__(self, *args):
60        self.close()
61
62
63    @retry_chrome_call
64    def get_extension(self, extension_path=None):
65        """Gets the extension from the indicated path.
66
67        @param extension_path: the path of the target extension.
68                               Set to None to get autotest extension.
69                               Defaults to None.
70        @return an extension object.
71
72        @raise RuntimeError if the extension is not found.
73        @raise chrome.Error if the found extension has not yet been
74               retrieved succesfully.
75
76        """
77        try:
78            if extension_path is None:
79                extension = self._chrome.autotest_ext
80            else:
81                extension = self._chrome.get_extension(extension_path)
82        except KeyError, errmsg:
83            # Trigger retry_chrome_call to retry to retrieve the
84            # found extension.
85            raise chrome.Error(errmsg)
86        if not extension:
87            if extension_path is None:
88                raise RuntimeError('Autotest extension not found')
89            else:
90                raise RuntimeError('Extension not found in %r'
91                                    % extension_path)
92        return extension
93
94
95    @retry_chrome_call
96    def load_url(self, url):
97        """Loads the given url in a new tab. The new tab will be active.
98
99        @param url: The url to load as a string.
100        @return a str, the tab descriptor of the opened tab.
101
102        """
103        tab = self._browser.tabs.New()
104        tab.Navigate(url)
105        tab.Activate()
106        tab_descriptor = hex(id(tab))
107        self._tabs[tab_descriptor] = tab
108        return tab_descriptor
109
110
111    def get_tabs(self):
112        """Gets the tabs opened by browser.
113
114        @returns: The tabs attribute in telemetry browser object.
115
116        """
117        return self._browser.tabs
118
119
120    def get_tab_by_descriptor(self, tab_descriptor):
121        """Gets the tab by the tab descriptor.
122
123        @returns: The tab object indicated by the tab descriptor.
124
125        """
126        return self._tabs[tab_descriptor]
127
128
129    @retry_chrome_call
130    def close_tab(self, tab_descriptor):
131        """Closes the tab.
132
133        @param tab_descriptor: Indicate which tab to be closed.
134
135        """
136        if tab_descriptor not in self._tabs:
137            raise RuntimeError('There is no tab for %s' % tab_descriptor)
138        tab = self._tabs[tab_descriptor]
139        del self._tabs[tab_descriptor]
140        tab.Close()
141