1# Copyright 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
5import re
6
7from telemetry import decorators
8
9
10class PageActionNotSupported(Exception):
11  pass
12
13class PageActionFailed(Exception):
14  pass
15
16
17class PageAction(object):
18  """Represents an action that a user might try to perform to a page."""
19
20  def WillRunAction(self, tab):
21    """Override to do action-specific setup before
22    Test.WillRunAction is called."""
23    pass
24
25  def RunAction(self, tab):
26    raise NotImplementedError()
27
28  def CleanUp(self, tab):
29    pass
30
31def EvaluateCallbackWithElement(
32    tab, callback_js, selector=None, text=None, element_function=None,
33    wait=False, timeout_in_seconds=60):
34  """Evaluates the JavaScript callback with the given element.
35
36  The element may be selected via selector, text, or element_function.
37  Only one of these arguments must be specified.
38
39  Returns:
40    The callback's return value, if any. The return value must be
41    convertible to JSON.
42
43  Args:
44    tab: A telemetry.core.Tab object.
45    callback_js: The JavaScript callback to call (as string).
46        The callback receive 2 parameters: the element, and information
47        string about what method was used to retrieve the element.
48        Example: '''
49          function(element, info) {
50            if (!element) {
51              throw Error('Can not find element: ' + info);
52            }
53            element.click()
54          }'''
55    selector: A CSS selector describing the element.
56    text: The element must contains this exact text.
57    element_function: A JavaScript function (as string) that is used
58        to retrieve the element. For example:
59        '(function() { return foo.element; })()'.
60    wait: Whether to wait for the return value to be true.
61    timeout_in_seconds: The timeout for wait (if waiting).
62  """
63  count = 0
64  info_msg = ''
65  if element_function is not None:
66    count = count + 1
67    info_msg = 'using element_function "%s"' % re.escape(element_function)
68  if selector is not None:
69    count = count + 1
70    info_msg = 'using selector "%s"' % _EscapeSelector(selector)
71    element_function = 'document.querySelector(\'%s\')' % _EscapeSelector(
72        selector)
73  if text is not None:
74    count = count + 1
75    info_msg = 'using exact text match "%s"' % re.escape(text)
76    element_function = '''
77        (function() {
78          function _findElement(element, text) {
79            if (element.innerHTML == text) {
80              return element;
81            }
82
83            var childNodes = element.childNodes;
84            for (var i = 0, len = childNodes.length; i < len; ++i) {
85              var found = _findElement(childNodes[i], text);
86              if (found) {
87                return found;
88              }
89            }
90            return null;
91          }
92          return _findElement(document, '%s');
93        })()''' % text
94
95  if count != 1:
96    raise PageActionFailed(
97        'Must specify 1 way to retrieve element, but %s was specified.' % count)
98
99  code = '''
100      (function() {
101        var element = %s;
102        var callback = %s;
103        return callback(element, '%s');
104      })()''' % (element_function, callback_js, info_msg)
105
106  if wait:
107    tab.WaitForJavaScriptExpression(code, timeout_in_seconds)
108    return True
109  else:
110    return tab.EvaluateJavaScript(code)
111
112def _EscapeSelector(selector):
113  return selector.replace('\'', '\\\'')
114
115def GetGestureSourceTypeFromOptions(tab):
116  gesture_source_type = tab.browser.synthetic_gesture_source_type
117  return 'chrome.gpuBenchmarking.' + gesture_source_type.upper() + '_INPUT'
118
119@decorators.Cache
120def IsGestureSourceTypeSupported(tab, gesture_source_type):
121  # TODO(dominikg): remove once support for
122  #                 'chrome.gpuBenchmarking.gestureSourceTypeSupported' has
123  #                 been rolled into reference build.
124  if tab.EvaluateJavaScript("""
125      typeof chrome.gpuBenchmarking.gestureSourceTypeSupported ===
126          'undefined'"""):
127    return (tab.browser.platform.GetOSName() != 'mac' or
128            gesture_source_type.lower() != 'touch')
129
130  return tab.EvaluateJavaScript("""
131      chrome.gpuBenchmarking.gestureSourceTypeSupported(
132          chrome.gpuBenchmarking.%s_INPUT)"""
133      % (gesture_source_type.upper()))
134