Home | History | Annotate | Download | only in cros
      1 #!/usr/bin/env python
      2 
      3 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 
      7 import cgi
      8 import json
      9 import logging
     10 import logging.handlers
     11 import os
     12 import sys
     13 
     14 import common
     15 from autotest_lib.client.bin import utils
     16 from autotest_lib.client.common_lib.cros import chrome, xmlrpc_server
     17 from autotest_lib.client.cros import constants
     18 
     19 
     20 class InteractiveXmlRpcDelegate(xmlrpc_server.XmlRpcDelegate):
     21     """Exposes methods called remotely to create interactive tests.
     22 
     23     All instance methods of this object without a preceding '_' are exposed via
     24     an XML-RPC server. This is not a stateless handler object, which means that
     25     if you store state inside the delegate, that state will remain around for
     26     future calls.
     27     """
     28 
     29     def login(self):
     30         """Login to the system and open a tab.
     31 
     32         The tab opened is used by other methods on this server to interact
     33         with the user.
     34 
     35         @return True.
     36 
     37         """
     38         self._chrome = chrome.Chrome(init_network_controller=True)
     39         self._chrome.browser.platform.SetHTTPServerDirectories(
     40                 os.path.dirname(sys.argv[0]))
     41         self._tab = self._chrome.browser.tabs[0]
     42         self._tab.Navigate(
     43                 self._chrome.browser.platform.http_server.UrlOf('shell.html'))
     44 
     45         return True
     46 
     47 
     48     def set_output(self, html):
     49         """Replace the contents of the tab.
     50 
     51         @param html: HTML document to replace tab contents with.
     52 
     53         @return True.
     54 
     55         """
     56         # JSON does a better job of escaping HTML for JavaScript than we could
     57         # with string.replace().
     58         html_escaped = json.dumps(html)
     59         # Use JavaScript to append the output and scroll to the bottom of the
     60         # open tab.
     61         self._tab.ExecuteJavaScript('document.body.innerHTML = %s; ' %
     62                                     html_escaped)
     63         self._tab.Activate()
     64         self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
     65         return True
     66 
     67 
     68     def append_output(self, html):
     69         """Append HTML to the contents of the tab.
     70 
     71         @param html: HTML to append to the existing tab contents.
     72 
     73         @return True.
     74 
     75         """
     76         # JSON does a better job of escaping HTML for JavaScript than we could
     77         # with string.replace().
     78         html_escaped = json.dumps(html)
     79         # Use JavaScript to append the output and scroll to the bottom of the
     80         # open tab.
     81         self._tab.ExecuteJavaScript(
     82                 ('document.body.innerHTML += %s; ' % html_escaped) +
     83                 'window.scrollTo(0, document.body.scrollHeight);')
     84         self._tab.Activate()
     85         self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
     86         return True
     87 
     88 
     89     def append_buttons(self, *args):
     90         """Append confirmation buttons to the tab.
     91 
     92         Each button is given an index, 0 for the first button, 1 for the second,
     93         and so on.
     94 
     95         @param title...: Title of button to append.
     96 
     97         @return True.
     98 
     99         """
    100         html = ''
    101         index = 0
    102         for title in args:
    103             onclick = 'submit_button(%d)' % index
    104             html += ('<input type="button" value="%s" onclick="%s">' % (
    105                      cgi.escape(title),
    106                      cgi.escape(onclick)))
    107             index += 1
    108         return self.append_output(html)
    109 
    110 
    111     def wait_for_button(self, timeout):
    112         """Wait for a button to be clicked.
    113 
    114         Call append_buttons() before this to add buttons to the document.
    115 
    116         @param timeout: Maximum time, in seconds, to wait for a click.
    117 
    118         @return index of button that was clicked.
    119 
    120         """
    121         # Wait for the button to be clicked.
    122         utils.poll_for_condition(
    123                 condition=lambda:
    124                     self._tab.EvaluateJavaScript('window.__ready') == 1,
    125                 desc='User clicked on button.',
    126                 timeout=timeout)
    127         # Fetch the result.
    128         result = self._tab.EvaluateJavaScript('window.__result')
    129         # Reset for the next button.
    130         self._tab.ExecuteJavaScript(
    131                 'window.__ready = 0; '
    132                 'window.__result = null;')
    133         return result
    134 
    135 
    136     def check_for_button(self):
    137         """Check whether a button has been clicked.
    138 
    139         Call append_buttons() before this to add buttons to the document.
    140 
    141         @return index of button that was clicked or -1 if no button
    142             has been clicked.
    143 
    144         """
    145         if not self._tab.EvaluateJavaScript('window.__ready'):
    146             return -1
    147         # Fetch the result.
    148         result = self._tab.EvaluateJavaScript('window.__result')
    149         # Reset for the next button.
    150         self._tab.ExecuteJavaScript(
    151                 'window.__ready = 0; '
    152                 'window.__result = null;')
    153         return result
    154 
    155 
    156     def append_list(self, name):
    157         """Append a results list to the contents of the tab.
    158 
    159         @param name: Name to use for making modifications to the list.
    160 
    161         @return True.
    162 
    163         """
    164         html = '<div id="%s"></div>' % cgi.escape(name)
    165         return self.append_output(html)
    166 
    167 
    168     def append_list_item(self, list_name, item_name, html):
    169         """Append an item to a results list.
    170 
    171         @param list_name: Name of list provided to append_list().
    172         @param item_name: Name to use for making modifications to the item.
    173         @param html: HTML to place in the list item.
    174 
    175         @return True.
    176 
    177         """
    178         # JSON does a better job of escaping HTML for JavaScript than we could
    179         # with string.replace().
    180         item_html = '"<div id=\\"%s\\"></div>"' % cgi.escape(item_name)
    181         # Use JavaScript to append the output.
    182         self._tab.ExecuteJavaScript(
    183                 'document.getElementById("%s").innerHTML += %s; ' % (
    184                         cgi.escape(list_name),
    185                         item_html))
    186         self._tab.Activate()
    187         self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
    188         return self.replace_list_item(item_name, html)
    189 
    190 
    191     def replace_list_item(self, item_name, html):
    192         """Replace an item in a results list.
    193 
    194         @param item_name: Name of item provided to append_list_item().
    195         @param html: HTML to place in the list item.
    196 
    197         @return True.
    198 
    199         """
    200         # JSON does a better job of escaping HTML for JavaScript than we could
    201         # with string.replace().
    202         html_escaped = json.dumps(html)
    203         # Use JavaScript to append the output.
    204         self._tab.ExecuteJavaScript(
    205                 'document.getElementById("%s").innerHTML = %s; ' % (
    206                         cgi.escape(item_name),
    207                         html_escaped))
    208         self._tab.Activate()
    209         self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
    210         return True
    211 
    212 
    213     def close(self):
    214         """Close the browser.
    215 
    216         @return True.
    217 
    218         """
    219         if hasattr(self, '_chrome'):
    220             self._chrome.close();
    221         return True
    222 
    223 
    224 if __name__ == '__main__':
    225     logging.basicConfig(level=logging.DEBUG)
    226     handler = logging.handlers.SysLogHandler(address='/dev/log')
    227     formatter = logging.Formatter(
    228             'interactive_xmlrpc_server: [%(levelname)s] %(message)s')
    229     handler.setFormatter(formatter)
    230     logging.getLogger().addHandler(handler)
    231     logging.debug('interactive_xmlrpc_server main...')
    232     server = xmlrpc_server.XmlRpcServer(
    233             'localhost',
    234             constants.INTERACTIVE_XMLRPC_SERVER_PORT)
    235     server.register_delegate(InteractiveXmlRpcDelegate())
    236     server.run()
    237