aboutsummaryrefslogtreecommitdiff
path: root/catapult/telemetry/telemetry/internal/actions/scroll_to_element.py
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/telemetry/telemetry/internal/actions/scroll_to_element.py')
-rw-r--r--catapult/telemetry/telemetry/internal/actions/scroll_to_element.py88
1 files changed, 88 insertions, 0 deletions
diff --git a/catapult/telemetry/telemetry/internal/actions/scroll_to_element.py b/catapult/telemetry/telemetry/internal/actions/scroll_to_element.py
new file mode 100644
index 00000000..a3ad1941
--- /dev/null
+++ b/catapult/telemetry/telemetry/internal/actions/scroll_to_element.py
@@ -0,0 +1,88 @@
+# Copyright 2016 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 telemetry.internal.actions import page_action
+from telemetry.internal.actions.scroll import ScrollAction
+from telemetry.util import js_template
+
+
+class ScrollToElementAction(page_action.PageAction):
+
+
+ def __init__(self, selector=None, element_function=None,
+ container_selector=None, container_element_function=None,
+ speed_in_pixels_per_second=800):
+ """Perform scroll gesture on container until an element is in view.
+
+ Both the element and the container can be specified by a CSS selector
+ xor a JavaScript function, provided as a string, which returns an element.
+ The element is required so exactly one of selector and element_function
+ must be provided. The container is optional so at most one of
+ container_selector and container_element_function can be provided.
+ The container defaults to document.scrollingElement or document.body if
+ scrollingElement is not set.
+
+ Args:
+ selector: A CSS selector describing the element.
+ element_function: A JavaScript function (as string) that is used
+ to retrieve the element. For example:
+ 'function() { return foo.element; }'.
+ container_selector: A CSS selector describing the container element.
+ container_element_function: A JavaScript function (as a string) that is
+ used to retrieve the container element.
+ speed_in_pixels_per_second: Speed to scroll.
+ """
+ super(ScrollToElementAction, self).__init__()
+ self._selector = selector
+ self._element_function = element_function
+ self._container_selector = container_selector
+ self._container_element_function = container_element_function
+ self._speed = speed_in_pixels_per_second
+ self._distance = None
+ self._direction = None
+ self._scroller = None
+ assert (self._selector or self._element_function), (
+ 'Must have either selector or element function')
+
+ def WillRunAction(self, tab):
+ if self._selector:
+ element = js_template.Render(
+ 'document.querySelector({{ selector }})', selector=self._selector)
+ else:
+ element = self._element_function
+
+ # TODO(catapult:#3028): Render in JavaScript method when supported by API.
+ get_distance_js = js_template.Render('''
+ (function(elem){
+ var rect = elem.getBoundingClientRect();
+ if (rect.bottom < 0) {
+ // The bottom of the element is above the viewport.
+ // Scroll up until the top of the element is on screen.
+ return rect.top - (window.innerHeight / 2);
+ }
+ if (rect.top - window.innerHeight >= 0) {
+ // rect.top provides the pixel offset of the element from the
+ // top of the page. Because that exceeds the viewport's height,
+ // we know that the element is below the viewport.
+ return rect.top - (window.innerHeight / 2);
+ }
+ return 0;
+ })({{ @element }});
+ ''', element=element)
+
+ self._distance = tab.EvaluateJavaScript(get_distance_js)
+ self._direction = 'down' if self._distance > 0 else 'up'
+ self._distance = abs(self._distance)
+ self._scroller = ScrollAction(
+ direction=self._direction,
+ selector=self._container_selector,
+ element_function=self._container_element_function,
+ distance=self._distance,
+ speed_in_pixels_per_second=self._speed)
+
+ def RunAction(self, tab):
+ if self._distance == 0: # Element is already in view.
+ return
+ self._scroller.WillRunAction(tab)
+ self._scroller.RunAction(tab)