aboutsummaryrefslogtreecommitdiff
path: root/catapult/telemetry/telemetry/internal/actions/page_action.py
blob: 3e17c84b6ae744a544f265c094875e9abd8526cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# Copyright 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

from py_trace_event import trace_event

from telemetry import decorators
from telemetry.util import js_template


GESTURE_SOURCE_DEFAULT = 'DEFAULT'
GESTURE_SOURCE_MOUSE = 'MOUSE'
GESTURE_SOURCE_TOUCH = 'TOUCH'
SUPPORTED_GESTURE_SOURCES = (GESTURE_SOURCE_DEFAULT,
                             GESTURE_SOURCE_MOUSE,
                             GESTURE_SOURCE_TOUCH)

class PageActionNotSupported(Exception):
  pass

class PageActionFailed(Exception):
  pass


class PageAction(object):
  """Represents an action that a user might try to perform to a page."""

  __metaclass__ = trace_event.TracedMetaClass

  def WillRunAction(self, tab):
    """Override to do action-specific setup before
    Test.WillRunAction is called."""
    pass

  def RunAction(self, tab):
    raise NotImplementedError()

  def CleanUp(self, tab):
    pass

def EvaluateCallbackWithElement(
    tab, callback_js, selector=None, text=None, element_function=None,
    wait=False, timeout_in_seconds=60):
  """Evaluates the JavaScript callback with the given element.

  The element may be selected via selector, text, or element_function.
  Only one of these arguments must be specified.

  Returns:
    The callback's return value, if any. The return value must be
    convertible to JSON.

  Args:
    tab: A telemetry.core.Tab object.
    callback_js: The JavaScript callback to call (as string).
        The callback receive 2 parameters: the element, and information
        string about what method was used to retrieve the element.
        Example: '''
          function(element, info) {
            if (!element) {
              throw Error('Can not find element: ' + info);
            }
            element.click()
          }'''
    selector: A CSS selector describing the element.
    text: The element must contains this exact text.
    element_function: A JavaScript function (as string) that is used
        to retrieve the element. For example:
        '(function() { return foo.element; })()'.
    wait: Whether to wait for the return value to be true.
    timeout_in_seconds: The timeout for wait (if waiting).
  """
  count = 0
  info_msg = ''
  if element_function is not None:
    count = count + 1
    info_msg = js_template.Render(
        'using element_function: {{ @code }}', code=element_function)
  if selector is not None:
    count = count + 1
    info_msg = js_template.Render(
        'using selector {{ selector }}', selector=selector)
    element_function = js_template.Render(
        'document.querySelector({{ selector }})', selector=selector)
  if text is not None:
    count = count + 1
    info_msg = js_template.Render(
        'using exact text match {{ text }}', text=text)
    element_function = js_template.Render('''
        (function() {
          function _findElement(element, text) {
            if (element.innerHTML == text) {
              return element;
            }

            var childNodes = element.childNodes;
            for (var i = 0, len = childNodes.length; i < len; ++i) {
              var found = _findElement(childNodes[i], text);
              if (found) {
                return found;
              }
            }
            return null;
          }
          return _findElement(document, {{ text }});
        })()''',
        text=text)

  if count != 1:
    raise PageActionFailed(
        'Must specify 1 way to retrieve element, but %s was specified.' % count)

  code = js_template.Render('''
      (function() {
        var element = {{ @element_function }};
        var callback = {{ @callback_js }};
        return callback(element, {{ info_msg }});
      })()''',
      element_function=element_function,
      callback_js=callback_js,
      info_msg=info_msg)

  if wait:
    tab.WaitForJavaScriptExpression(code, timeout_in_seconds)
    return True
  else:
    return tab.EvaluateJavaScript(code)


@decorators.Cache
def IsGestureSourceTypeSupported(tab, gesture_source_type):
  # TODO(dominikg): remove once support for
  #                 'chrome.gpuBenchmarking.gestureSourceTypeSupported' has
  #                 been rolled into reference build.
  if tab.EvaluateJavaScript("""
      typeof chrome.gpuBenchmarking.gestureSourceTypeSupported ===
          'undefined'"""):
    return (tab.browser.platform.GetOSName() != 'mac' or
            gesture_source_type.lower() != 'touch')

  # TODO(catapult:#3028): Render in JavaScript method when supported by API.
  code = js_template.Render("""
      chrome.gpuBenchmarking.gestureSourceTypeSupported(
          chrome.gpuBenchmarking.{{ @gesture_source_type }}_INPUT)""",
      gesture_source_type=gesture_source_type.upper())
  return tab.EvaluateJavaScript(code)