| # 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. |
| |
| import six |
| |
| 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) |
| DEFAULT_TIMEOUT = 60 |
| |
| |
| class PageActionNotSupported(Exception): |
| pass |
| |
| |
| class PageActionFailed(Exception): |
| pass |
| |
| |
| class PageAction(six.with_metaclass(trace_event.TracedMetaClass, object)): |
| """Represents an action that a user might try to perform to a page.""" |
| |
| def __init__(self, timeout=DEFAULT_TIMEOUT): |
| """Initialization function. |
| |
| Args: |
| timeout: number of seconds to wait for the action to finish. |
| """ |
| self.timeout = timeout |
| |
| 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 __str__(self): |
| return self.__class__.__name__ |
| |
| |
| class ElementPageAction(PageAction): |
| """A PageAction which acts on DOM elements""" |
| |
| def __init__(self, selector=None, text=None, element_function=None, |
| timeout=DEFAULT_TIMEOUT): |
| super(ElementPageAction, self).__init__(timeout) |
| self._selector = selector |
| self._text = text |
| self._element_function = element_function |
| |
| def RunAction(self, tab): |
| raise NotImplementedError() |
| |
| def HasElementSelector(self): |
| return (self._selector is not None or |
| self._text is not None or |
| self._element_function is not None) |
| |
| def EvaluateCallback(self, tab, code, **kwargs): |
| return EvaluateCallbackWithElement( |
| tab, code, selector=self._selector, text=self._text, |
| element_function=self._element_function, **kwargs) |
| |
| def __str__(self): |
| query_string = '' |
| if self._selector is not None: |
| query_string = self._selector |
| if self._text is not None: |
| query_string = 'text=%s' % self._text |
| if self._element_function: |
| query_string = 'element_function=%s' % self._element_function |
| return '%s(%s)' % (self.__class__.__name__, query_string) |
| |
| |
| def EvaluateCallbackWithElement( |
| tab, callback_js, selector=None, text=None, element_function=None, |
| wait=False, timeout_in_seconds=DEFAULT_TIMEOUT, user_gesture=False): |
| """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). |
| user_gesture: Whether execution should be treated as initiated by user |
| in the UI. Code that plays media or requests fullscreen may not take |
| effects without user_gesture set to True. |
| """ |
| 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.WaitForJavaScriptCondition(code, timeout=timeout_in_seconds) |
| return True |
| else: |
| return tab.EvaluateJavaScript(code, user_gesture=user_gesture) |
| |
| |
| @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') |
| |
| return tab.EvaluateJavaScript( |
| """ |
| chrome.gpuBenchmarking.gestureSourceTypeSupported( |
| chrome.gpuBenchmarking.{{ @gesture_source_type }}_INPUT)""", |
| gesture_source_type=gesture_source_type.upper()) |