xref: /aosp_15_r20/external/autotest/client/cros/multimedia/facade_resource.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright 2015 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Li"""A module providing common resources for different facades."""
7*9c5db199SXin Li
8*9c5db199SXin Liimport logging
9*9c5db199SXin Liimport time
10*9c5db199SXin Li
11*9c5db199SXin Lifrom autotest_lib.client.bin import utils
12*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
13*9c5db199SXin Lifrom autotest_lib.client.common_lib.cros import chrome
14*9c5db199SXin Lifrom autotest_lib.client.common_lib.cros import retry
15*9c5db199SXin Lifrom autotest_lib.client.cros import constants
16*9c5db199SXin Lifrom telemetry.internal.backends.chrome_inspector import devtools_http
17*9c5db199SXin Li
18*9c5db199SXin Liimport py_utils
19*9c5db199SXin Li
20*9c5db199SXin Li_FLAKY_CALL_RETRY_TIMEOUT_SEC = 60
21*9c5db199SXin Li_FLAKY_CHROME_CALL_RETRY_DELAY_SEC = 1
22*9c5db199SXin Li
23*9c5db199SXin Liretry_chrome_call = retry.retry(
24*9c5db199SXin Li        (chrome.Error, IndexError, Exception),
25*9c5db199SXin Li        timeout_min=_FLAKY_CALL_RETRY_TIMEOUT_SEC / 60.0,
26*9c5db199SXin Li        delay_sec=_FLAKY_CHROME_CALL_RETRY_DELAY_SEC)
27*9c5db199SXin Li
28*9c5db199SXin Li
29*9c5db199SXin Liclass FacadeResoureError(Exception):
30*9c5db199SXin Li    """Error in FacadeResource."""
31*9c5db199SXin Li    pass
32*9c5db199SXin Li
33*9c5db199SXin Li
34*9c5db199SXin Li_FLAKY_CHROME_START_RETRY_TIMEOUT_SEC = 120
35*9c5db199SXin Li_FLAKY_CHROME_START_RETRY_DELAY_SEC = 10
36*9c5db199SXin Li
37*9c5db199SXin Li
38*9c5db199SXin Li# Telemetry sometimes fails to start Chrome.
39*9c5db199SXin Liretry_start_chrome = retry.retry(
40*9c5db199SXin Li        (Exception,),
41*9c5db199SXin Li        timeout_min=_FLAKY_CHROME_START_RETRY_TIMEOUT_SEC / 60.0,
42*9c5db199SXin Li        delay_sec=_FLAKY_CHROME_START_RETRY_DELAY_SEC,
43*9c5db199SXin Li        exception_to_raise=FacadeResoureError,
44*9c5db199SXin Li        label='Start Chrome')
45*9c5db199SXin Li
46*9c5db199SXin Li
47*9c5db199SXin Liclass FacadeResource(object):
48*9c5db199SXin Li    """This class provides access to telemetry chrome wrapper."""
49*9c5db199SXin Li
50*9c5db199SXin Li    ARC_DISABLED = 'disabled'
51*9c5db199SXin Li    ARC_ENABLED = 'enabled'
52*9c5db199SXin Li    ARC_VERSION = 'CHROMEOS_ARC_VERSION'
53*9c5db199SXin Li    EXTRA_BROWSER_ARGS = ['--enable-gpu-benchmarking', '--use-fake-ui-for-media-stream']
54*9c5db199SXin Li
55*9c5db199SXin Li    def __init__(self, chrome_object=None, restart=False):
56*9c5db199SXin Li        """Initializes a FacadeResource.
57*9c5db199SXin Li
58*9c5db199SXin Li        @param chrome_object: A chrome.Chrome object or None.
59*9c5db199SXin Li        @param restart: Preserve the previous browser state.
60*9c5db199SXin Li
61*9c5db199SXin Li        """
62*9c5db199SXin Li        self._chrome = chrome_object
63*9c5db199SXin Li
64*9c5db199SXin Li    @property
65*9c5db199SXin Li    def _browser(self):
66*9c5db199SXin Li        """Gets the browser object from Chrome."""
67*9c5db199SXin Li        return self._chrome.browser
68*9c5db199SXin Li
69*9c5db199SXin Li
70*9c5db199SXin Li    @retry_start_chrome
71*9c5db199SXin Li    def _start_chrome(self, kwargs):
72*9c5db199SXin Li        """Start a Chrome with given arguments.
73*9c5db199SXin Li
74*9c5db199SXin Li        @param kwargs: A dict of keyword arguments passed to Chrome.
75*9c5db199SXin Li
76*9c5db199SXin Li        @return: A chrome.Chrome object.
77*9c5db199SXin Li
78*9c5db199SXin Li        """
79*9c5db199SXin Li        logging.debug('Try to start Chrome with kwargs: %s', kwargs)
80*9c5db199SXin Li        return chrome.Chrome(**kwargs)
81*9c5db199SXin Li
82*9c5db199SXin Li
83*9c5db199SXin Li    def start_custom_chrome(self, kwargs):
84*9c5db199SXin Li        """Start a custom Chrome with given arguments.
85*9c5db199SXin Li
86*9c5db199SXin Li        @param kwargs: A dict of keyword arguments passed to Chrome.
87*9c5db199SXin Li
88*9c5db199SXin Li        @return: True on success, False otherwise.
89*9c5db199SXin Li
90*9c5db199SXin Li        """
91*9c5db199SXin Li        # Close the previous Chrome.
92*9c5db199SXin Li        if self._chrome:
93*9c5db199SXin Li            self._chrome.close()
94*9c5db199SXin Li
95*9c5db199SXin Li        # Start the new Chrome.
96*9c5db199SXin Li        try:
97*9c5db199SXin Li            self._chrome = self._start_chrome(kwargs)
98*9c5db199SXin Li        except FacadeResoureError:
99*9c5db199SXin Li            logging.error('Failed to start Chrome after retries')
100*9c5db199SXin Li            return False
101*9c5db199SXin Li        else:
102*9c5db199SXin Li            logging.info('Chrome started successfully')
103*9c5db199SXin Li
104*9c5db199SXin Li        # The opened tabs are stored by tab descriptors.
105*9c5db199SXin Li        # Key is the tab descriptor string.
106*9c5db199SXin Li        # We use string as the key because of RPC Call. Client can use the
107*9c5db199SXin Li        # string to locate the tab object.
108*9c5db199SXin Li        # Value is the tab object.
109*9c5db199SXin Li        self._tabs = dict()
110*9c5db199SXin Li
111*9c5db199SXin Li        # Workaround for issue crbug.com/588579.
112*9c5db199SXin Li        # On daisy, Chrome freezes about 30 seconds after login because of
113*9c5db199SXin Li        # TPM error. Avoid test accessing Chrome during this time.
114*9c5db199SXin Li        # Check issue crbug.com/588579 and crbug.com/591646.
115*9c5db199SXin Li        if utils.get_board() == 'daisy':
116*9c5db199SXin Li            logging.warning('Delay 30s for issue 588579 on daisy')
117*9c5db199SXin Li            time.sleep(30)
118*9c5db199SXin Li
119*9c5db199SXin Li        return True
120*9c5db199SXin Li
121*9c5db199SXin Li
122*9c5db199SXin Li    def start_default_chrome(self, restart=False, extra_browser_args=None,
123*9c5db199SXin Li                             disable_arc=False):
124*9c5db199SXin Li        """Start the default Chrome.
125*9c5db199SXin Li
126*9c5db199SXin Li        @param restart: True to start Chrome without clearing previous state.
127*9c5db199SXin Li        @param extra_browser_args: A list containing extra browser args passed
128*9c5db199SXin Li                                   to Chrome. This list will be appened to
129*9c5db199SXin Li                                   default EXTRA_BROWSER_ARGS.
130*9c5db199SXin Li        @param disable_arc: True to disable ARC++.
131*9c5db199SXin Li
132*9c5db199SXin Li        @return: True on success, False otherwise.
133*9c5db199SXin Li
134*9c5db199SXin Li        """
135*9c5db199SXin Li        # TODO: (crbug.com/618111) Add test driven switch for
136*9c5db199SXin Li        # supporting arc_mode enabled or disabled. At this time
137*9c5db199SXin Li        # if ARC build is tested, arc_mode is always enabled.
138*9c5db199SXin Li        if not disable_arc and utils.get_board_property(self.ARC_VERSION):
139*9c5db199SXin Li            arc_mode = self.ARC_ENABLED
140*9c5db199SXin Li        else:
141*9c5db199SXin Li            arc_mode = self.ARC_DISABLED
142*9c5db199SXin Li        kwargs = {
143*9c5db199SXin Li            'extension_paths': [constants.AUDIO_TEST_EXTENSION,
144*9c5db199SXin Li                                constants.DISPLAY_TEST_EXTENSION],
145*9c5db199SXin Li            'extra_browser_args': self.EXTRA_BROWSER_ARGS,
146*9c5db199SXin Li            'clear_enterprise_policy': not restart,
147*9c5db199SXin Li            'arc_mode': arc_mode,
148*9c5db199SXin Li            'autotest_ext': True
149*9c5db199SXin Li        }
150*9c5db199SXin Li        if extra_browser_args:
151*9c5db199SXin Li            kwargs['extra_browser_args'] += extra_browser_args
152*9c5db199SXin Li        return self.start_custom_chrome(kwargs)
153*9c5db199SXin Li
154*9c5db199SXin Li
155*9c5db199SXin Li    def __enter__(self):
156*9c5db199SXin Li        return self
157*9c5db199SXin Li
158*9c5db199SXin Li
159*9c5db199SXin Li    def __exit__(self, *args):
160*9c5db199SXin Li        if self._chrome:
161*9c5db199SXin Li            self._chrome.close()
162*9c5db199SXin Li            self._chrome = None
163*9c5db199SXin Li
164*9c5db199SXin Li
165*9c5db199SXin Li    @staticmethod
166*9c5db199SXin Li    def _generate_tab_descriptor(tab):
167*9c5db199SXin Li        """Generate tab descriptor by tab object.
168*9c5db199SXin Li
169*9c5db199SXin Li        @param tab: the tab object.
170*9c5db199SXin Li        @return a str, the tab descriptor of the tab.
171*9c5db199SXin Li
172*9c5db199SXin Li        """
173*9c5db199SXin Li        return hex(id(tab))
174*9c5db199SXin Li
175*9c5db199SXin Li
176*9c5db199SXin Li    def clean_unexpected_tabs(self):
177*9c5db199SXin Li        """Clean all tabs that are not opened by facade_resource
178*9c5db199SXin Li
179*9c5db199SXin Li        It is used to make sure our chrome browser is clean.
180*9c5db199SXin Li
181*9c5db199SXin Li        """
182*9c5db199SXin Li        # If they have the same length we can assume there is no unexpected
183*9c5db199SXin Li        # tabs.
184*9c5db199SXin Li        browser_tabs = self.get_tabs()
185*9c5db199SXin Li        if len(browser_tabs) == len(self._tabs):
186*9c5db199SXin Li            return
187*9c5db199SXin Li
188*9c5db199SXin Li        for tab in browser_tabs:
189*9c5db199SXin Li            if self._generate_tab_descriptor(tab) not in self._tabs:
190*9c5db199SXin Li                # TODO(mojahsu): Reevaluate this code. crbug.com/719592
191*9c5db199SXin Li                try:
192*9c5db199SXin Li                    tab.Close()
193*9c5db199SXin Li                except py_utils.TimeoutException:
194*9c5db199SXin Li                    logging.warning('close tab timeout %r, %s', tab, tab.url)
195*9c5db199SXin Li
196*9c5db199SXin Li
197*9c5db199SXin Li    @retry_chrome_call
198*9c5db199SXin Li    def get_extension(self, extension_path=None):
199*9c5db199SXin Li        """Gets the extension from the indicated path.
200*9c5db199SXin Li
201*9c5db199SXin Li        @param extension_path: the path of the target extension.
202*9c5db199SXin Li                               Set to None to get autotest extension.
203*9c5db199SXin Li                               Defaults to None.
204*9c5db199SXin Li        @return an extension object.
205*9c5db199SXin Li
206*9c5db199SXin Li        @raise RuntimeError if the extension is not found.
207*9c5db199SXin Li        @raise chrome.Error if the found extension has not yet been
208*9c5db199SXin Li               retrieved succesfully.
209*9c5db199SXin Li
210*9c5db199SXin Li        """
211*9c5db199SXin Li        try:
212*9c5db199SXin Li            if extension_path is None:
213*9c5db199SXin Li                extension = self._chrome.autotest_ext
214*9c5db199SXin Li            else:
215*9c5db199SXin Li                extension = self._chrome.get_extension(extension_path)
216*9c5db199SXin Li        except KeyError as errmsg:
217*9c5db199SXin Li            # Trigger retry_chrome_call to retry to retrieve the
218*9c5db199SXin Li            # found extension.
219*9c5db199SXin Li            raise chrome.Error(errmsg)
220*9c5db199SXin Li        if not extension:
221*9c5db199SXin Li            if extension_path is None:
222*9c5db199SXin Li                raise RuntimeError('Autotest extension not found')
223*9c5db199SXin Li            else:
224*9c5db199SXin Li                raise RuntimeError('Extension not found in %r'
225*9c5db199SXin Li                                    % extension_path)
226*9c5db199SXin Li        return extension
227*9c5db199SXin Li
228*9c5db199SXin Li
229*9c5db199SXin Li    def get_visible_notifications(self):
230*9c5db199SXin Li        """Gets the visible notifications
231*9c5db199SXin Li
232*9c5db199SXin Li        @return: Returns all visible notifications in list format. Ex:
233*9c5db199SXin Li                [{title:'', message:'', prority:'', id:''}]
234*9c5db199SXin Li        """
235*9c5db199SXin Li        return self._chrome.get_visible_notifications()
236*9c5db199SXin Li
237*9c5db199SXin Li
238*9c5db199SXin Li    @retry_chrome_call
239*9c5db199SXin Li    def load_url(self, url):
240*9c5db199SXin Li        """Loads the given url in a new tab. The new tab will be active.
241*9c5db199SXin Li
242*9c5db199SXin Li        @param url: The url to load as a string.
243*9c5db199SXin Li        @return a str, the tab descriptor of the opened tab.
244*9c5db199SXin Li
245*9c5db199SXin Li        """
246*9c5db199SXin Li        tab = self._browser.tabs.New()
247*9c5db199SXin Li        tab.Navigate(url)
248*9c5db199SXin Li        tab.Activate()
249*9c5db199SXin Li        tab.WaitForDocumentReadyStateToBeComplete()
250*9c5db199SXin Li        tab_descriptor = self._generate_tab_descriptor(tab)
251*9c5db199SXin Li        self._tabs[tab_descriptor] = tab
252*9c5db199SXin Li        self.clean_unexpected_tabs()
253*9c5db199SXin Li        return tab_descriptor
254*9c5db199SXin Li
255*9c5db199SXin Li
256*9c5db199SXin Li    def set_http_server_directories(self, directories):
257*9c5db199SXin Li        """Starts an HTTP server.
258*9c5db199SXin Li
259*9c5db199SXin Li        @param directories: Directories to start serving.
260*9c5db199SXin Li
261*9c5db199SXin Li        @return True on success. False otherwise.
262*9c5db199SXin Li
263*9c5db199SXin Li        """
264*9c5db199SXin Li        return self._chrome.browser.platform.SetHTTPServerDirectories(directories)
265*9c5db199SXin Li
266*9c5db199SXin Li
267*9c5db199SXin Li    def http_server_url_of(self, fullpath):
268*9c5db199SXin Li        """Converts a path to a URL.
269*9c5db199SXin Li
270*9c5db199SXin Li        @param fullpath: String containing the full path to the content.
271*9c5db199SXin Li
272*9c5db199SXin Li        @return the URL for the provided path.
273*9c5db199SXin Li
274*9c5db199SXin Li        """
275*9c5db199SXin Li        return self._chrome.browser.platform.http_server.UrlOf(fullpath)
276*9c5db199SXin Li
277*9c5db199SXin Li
278*9c5db199SXin Li    def get_tabs(self):
279*9c5db199SXin Li        """Gets the tabs opened by browser.
280*9c5db199SXin Li
281*9c5db199SXin Li        @returns: The tabs attribute in telemetry browser object.
282*9c5db199SXin Li
283*9c5db199SXin Li        """
284*9c5db199SXin Li        return self._browser.tabs
285*9c5db199SXin Li
286*9c5db199SXin Li
287*9c5db199SXin Li    def get_tab_by_descriptor(self, tab_descriptor):
288*9c5db199SXin Li        """Gets the tab by the tab descriptor.
289*9c5db199SXin Li
290*9c5db199SXin Li        @returns: The tab object indicated by the tab descriptor.
291*9c5db199SXin Li
292*9c5db199SXin Li        """
293*9c5db199SXin Li        return self._tabs[tab_descriptor]
294*9c5db199SXin Li
295*9c5db199SXin Li
296*9c5db199SXin Li    @retry_chrome_call
297*9c5db199SXin Li    def close_tab(self, tab_descriptor):
298*9c5db199SXin Li        """Closes the tab.
299*9c5db199SXin Li
300*9c5db199SXin Li        @param tab_descriptor: Indicate which tab to be closed.
301*9c5db199SXin Li
302*9c5db199SXin Li        """
303*9c5db199SXin Li        if tab_descriptor not in self._tabs:
304*9c5db199SXin Li            raise RuntimeError('There is no tab for %s' % tab_descriptor)
305*9c5db199SXin Li        tab = self._tabs[tab_descriptor]
306*9c5db199SXin Li        del self._tabs[tab_descriptor]
307*9c5db199SXin Li        tab.Close()
308*9c5db199SXin Li        self.clean_unexpected_tabs()
309*9c5db199SXin Li
310*9c5db199SXin Li
311*9c5db199SXin Li    def wait_for_javascript_expression(
312*9c5db199SXin Li            self, tab_descriptor, expression, timeout):
313*9c5db199SXin Li        """Waits for the given JavaScript expression to be True on the given tab
314*9c5db199SXin Li
315*9c5db199SXin Li        @param tab_descriptor: Indicate on which tab to wait for the expression.
316*9c5db199SXin Li        @param expression: Indiate for what expression to wait.
317*9c5db199SXin Li        @param timeout: Indicate the timeout of the expression.
318*9c5db199SXin Li        """
319*9c5db199SXin Li        if tab_descriptor not in self._tabs:
320*9c5db199SXin Li            raise RuntimeError('There is no tab for %s' % tab_descriptor)
321*9c5db199SXin Li        self._tabs[tab_descriptor].WaitForJavaScriptCondition(
322*9c5db199SXin Li                expression, timeout=timeout)
323*9c5db199SXin Li
324*9c5db199SXin Li
325*9c5db199SXin Li    def execute_javascript(self, tab_descriptor, statement, timeout):
326*9c5db199SXin Li        """Executes a JavaScript statement on the given tab.
327*9c5db199SXin Li
328*9c5db199SXin Li        @param tab_descriptor: Indicate on which tab to execute the statement.
329*9c5db199SXin Li        @param statement: Indiate what statement to execute.
330*9c5db199SXin Li        @param timeout: Indicate the timeout of the statement.
331*9c5db199SXin Li        """
332*9c5db199SXin Li        if tab_descriptor not in self._tabs:
333*9c5db199SXin Li            raise RuntimeError('There is no tab for %s' % tab_descriptor)
334*9c5db199SXin Li        self._tabs[tab_descriptor].ExecuteJavaScript(
335*9c5db199SXin Li                statement, timeout=timeout)
336*9c5db199SXin Li
337*9c5db199SXin Li
338*9c5db199SXin Li    def evaluate_javascript(self, tab_descriptor, expression, timeout):
339*9c5db199SXin Li        """Evaluates a JavaScript expression on the given tab.
340*9c5db199SXin Li
341*9c5db199SXin Li        @param tab_descriptor: Indicate on which tab to evaluate the expression.
342*9c5db199SXin Li        @param expression: Indiate what expression to evaluate.
343*9c5db199SXin Li        @param timeout: Indicate the timeout of the expression.
344*9c5db199SXin Li        @return the JSONized result of the given expression
345*9c5db199SXin Li        """
346*9c5db199SXin Li        if tab_descriptor not in self._tabs:
347*9c5db199SXin Li            raise RuntimeError('There is no tab for %s' % tab_descriptor)
348*9c5db199SXin Li        return self._tabs[tab_descriptor].EvaluateJavaScript(
349*9c5db199SXin Li                expression, timeout=timeout)
350*9c5db199SXin Li
351*9c5db199SXin Liclass Application(FacadeResource):
352*9c5db199SXin Li    """ This class provides access to WebStore Applications"""
353*9c5db199SXin Li
354*9c5db199SXin Li    APP_NAME_IDS = {
355*9c5db199SXin Li        'camera' : 'njfbnohfdkmbmnjapinfcopialeghnmh',
356*9c5db199SXin Li        'files' : 'hhaomjibdihmijegdhdafkllkbggdgoj'
357*9c5db199SXin Li    }
358*9c5db199SXin Li    # Time in seconds to load the app
359*9c5db199SXin Li    LOAD_TIME = 5
360*9c5db199SXin Li
361*9c5db199SXin Li    def __init__(self, chrome_object=None):
362*9c5db199SXin Li        super(Application, self).__init__(chrome_object)
363*9c5db199SXin Li
364*9c5db199SXin Li    @retry_chrome_call
365*9c5db199SXin Li    def evaluate_javascript(self, code):
366*9c5db199SXin Li        """Executes javascript and returns some result.
367*9c5db199SXin Li
368*9c5db199SXin Li        Occasionally calls to EvaluateJavascript on the autotest_ext will fail
369*9c5db199SXin Li        to find the extension. Instead of wrapping every call in a try/except,
370*9c5db199SXin Li        calls will go through this function instead.
371*9c5db199SXin Li
372*9c5db199SXin Li        @param code: The javascript string to execute
373*9c5db199SXin Li
374*9c5db199SXin Li        """
375*9c5db199SXin Li        try:
376*9c5db199SXin Li            result = self._chrome.autotest_ext.EvaluateJavaScript(code)
377*9c5db199SXin Li            return result
378*9c5db199SXin Li        except KeyError:
379*9c5db199SXin Li            logging.exception('Could not find autotest_ext')
380*9c5db199SXin Li        except (devtools_http.DevToolsClientUrlError,
381*9c5db199SXin Li                devtools_http.DevToolsClientConnectionError):
382*9c5db199SXin Li            logging.exception('Could not connect to DevTools')
383*9c5db199SXin Li
384*9c5db199SXin Li        raise error.TestError("Could not execute %s" % code)
385*9c5db199SXin Li
386*9c5db199SXin Li    def click_on(self, ui, name, isRegex=False, role=None):
387*9c5db199SXin Li        """
388*9c5db199SXin Li        Click on given role and name matches
389*9c5db199SXin Li
390*9c5db199SXin Li        @ui: ui_utils object
391*9c5db199SXin Li        @param name: item node name.
392*9c5db199SXin Li        @param isRegex: If name is in regex format then isRegex should be
393*9c5db199SXin Li                        True otherwise False.
394*9c5db199SXin Li        @param role: role of the element. Example: button or window etc.
395*9c5db199SXin Li        @raise error.TestError if the test is failed to find given node
396*9c5db199SXin Li        """
397*9c5db199SXin Li        if not ui.item_present(name, isRegex=isRegex, role=role):
398*9c5db199SXin Li            raise error.TestError("name=%s, role=%s did not appeared with in "
399*9c5db199SXin Li                                 "time" % (name, role))
400*9c5db199SXin Li        ui.doDefault_on_obj(name, isRegex=isRegex, role=role)
401*9c5db199SXin Li
402*9c5db199SXin Li    def is_app_opened(self, name):
403*9c5db199SXin Li        """
404*9c5db199SXin Li        Verify if the Webstore app is opened or not
405*9c5db199SXin Li
406*9c5db199SXin Li        @param name: Name of the app to verify.
407*9c5db199SXin Li
408*9c5db199SXin Li        """
409*9c5db199SXin Li        self.evaluate_javascript("var isShown = null;")
410*9c5db199SXin Li        is_app_shown_js = """
411*9c5db199SXin Li            chrome.autotestPrivate.isAppShown('%s',
412*9c5db199SXin Li            function(appShown){isShown = appShown});
413*9c5db199SXin Li            """ % self.APP_NAME_IDS[name.lower()]
414*9c5db199SXin Li        self.evaluate_javascript(is_app_shown_js)
415*9c5db199SXin Li        return self.evaluate_javascript('isShown')
416*9c5db199SXin Li
417*9c5db199SXin Li    def launch_app(self, name):
418*9c5db199SXin Li        """
419*9c5db199SXin Li        Launch the app/extension by its ID and verify that it opens.
420*9c5db199SXin Li
421*9c5db199SXin Li        @param name: Name of the app to launch.
422*9c5db199SXin Li
423*9c5db199SXin Li        """
424*9c5db199SXin Li        logging.info("Launching %s app" % name)
425*9c5db199SXin Li        if name == "camera":
426*9c5db199SXin Li            webapps_js = "chrome.autotestPrivate.waitForSystemWebAppsInstall(" \
427*9c5db199SXin Li                     "function(){})"
428*9c5db199SXin Li            self.evaluate_javascript(webapps_js)
429*9c5db199SXin Li            launch_js = "chrome.autotestPrivate.launchSystemWebApp('%s', '%s', " \
430*9c5db199SXin Li                    "function(){})" % ("Camera",
431*9c5db199SXin Li                                       "chrome://camera-app/views/main.html")
432*9c5db199SXin Li        else:
433*9c5db199SXin Li            launch_js = "chrome.autotestPrivate.launchApp('%s', function(){})" \
434*9c5db199SXin Li                         % self.APP_NAME_IDS[name.lower()]
435*9c5db199SXin Li        self.evaluate_javascript(launch_js)
436*9c5db199SXin Li        def is_app_opened():
437*9c5db199SXin Li            return self.is_app_opened(name)
438*9c5db199SXin Li        utils.poll_for_condition(condition=is_app_opened,
439*9c5db199SXin Li                    desc="%s app is not launched" % name,
440*9c5db199SXin Li                    timeout=self.LOAD_TIME)
441*9c5db199SXin Li        logging.info('%s app is launched', name)
442*9c5db199SXin Li
443*9c5db199SXin Li    def close_app(self, name):
444*9c5db199SXin Li        """
445*9c5db199SXin Li        Close the app/extension by its ID and verify that it closes.
446*9c5db199SXin Li
447*9c5db199SXin Li        @param name: Name of the app to close.
448*9c5db199SXin Li
449*9c5db199SXin Li        """
450*9c5db199SXin Li        close_js = "chrome.autotestPrivate.closeApp('%s', function(){})" \
451*9c5db199SXin Li                    % self.APP_NAME_IDS[name.lower()]
452*9c5db199SXin Li        self.evaluate_javascript(close_js)
453*9c5db199SXin Li        def is_app_closed():
454*9c5db199SXin Li            return not self.is_app_opened(name)
455*9c5db199SXin Li        utils.poll_for_condition(condition=is_app_closed,
456*9c5db199SXin Li                    desc="%s app is not closed" % name,
457*9c5db199SXin Li                    timeout=self.LOAD_TIME)
458*9c5db199SXin Li        logging.info('%s app is closed', name)
459