blob: b6d0576e78cf565e35d3ab91be2a34ade91ecacc [file] [log] [blame]
# 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())