blob: 7705b4a8079864aab835c451d46b26866fcf11a8 [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.
from __future__ import division
from telemetry import decorators
from telemetry.internal.actions import page_action
from telemetry.internal.actions import scroll
from telemetry.internal.actions import utils
from telemetry.testing import tab_test_case
class ScrollActionTest(tab_test_case.TabTestCase):
def _MakePageVerticallyScrollable(self):
# Make page taller than window so it's scrollable vertically.
self._tab.ExecuteJavaScript(
'document.body.style.height ='
'(3 * __GestureCommon_GetWindowHeight() + 1) + "px";')
def _MakePageHorizontallyScrollable(self):
# Make page wider than window so it's scrollable horizontally.
self._tab.ExecuteJavaScript(
'document.body.style.width ='
'(3 * __GestureCommon_GetWindowWidth() + 1) + "px";')
def setUp(self):
tab_test_case.TabTestCase.setUp(self)
self.Navigate('blank.html')
utils.InjectJavaScript(self._tab, 'gesture_common.js')
def _RunScrollDistanceTest(self, distance, speed, source, maxError):
# TODO(bokan): Distance tests will fail on versions of Chrome that haven't
# been fixed. The fixes landed at the same time as the
# setBrowserControlsShown method was added so only run the test if that's
# available. Once that rolls into ref builds we can remove this check.
distanceFixedInChrome = self._tab.EvaluateJavaScript(
"'setBrowserControlsShown' in chrome.gpuBenchmarking")
if not distanceFixedInChrome:
return
# Hide the URL bar so we can measure scrolled distance without worrying
# about the URL bar consuming delta.
self._tab.ExecuteJavaScript(
'chrome.gpuBenchmarking.setBrowserControlsShown(false);')
# Make the document tall enough to accomodate the requested distance but
# also leave enough space so we can tell if the scroll overshoots the
# target.
screenHeight = self._tab.EvaluateJavaScript('window.visualViewport.height')
documentHeight = (screenHeight + distance) * 2
self._tab.ExecuteJavaScript(
'document.body.style.height = "' + str(documentHeight) + 'px";')
self.assertEquals(
self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
# Allow for some visual viewport offset. For example, if the test doesn't
# want any visual viewport offset due to animation handoff error between
# the two viewports.
start_offset = self._tab.EvaluateJavaScript('window.visualViewport.pageTop')
i = scroll.ScrollAction(
distance=distance,
direction="down",
speed_in_pixels_per_second=speed,
synthetic_gesture_source=source)
i.WillRunAction(self._tab)
i.RunAction(self._tab)
actual = self._tab.EvaluateJavaScript(
'window.visualViewport.pageTop') - start_offset
# TODO(bokan): setBrowserControlsShown isn't quite enough. Chrome will hide
# the browser controls but then they animate in after a timeout. We'll need
# to add a way to lock them to hidden. Until then, just increase the
# allowed error.
urlBarError = 150
self.assertAlmostEqual(distance, actual, delta=maxError + urlBarError)
@decorators.Disabled('chromeos', 'linux') # crbug.com/1006789
def testScrollDistanceFastTouch(self):
# Just pass the test on platforms that don't support touch (i.e. Mac)
if not page_action.IsGestureSourceTypeSupported(self._tab, 'touch'):
return
# Scrolling distance for touch will have some error from the excess delta
# of the event that crosses the slop threshold but isn't applied, also
# scroll resampling can increase the error amount..
self._RunScrollDistanceTest(
500000, 200000, page_action.GESTURE_SOURCE_TOUCH, 200)
@decorators.Disabled('android-reference') # crbug.com/934649
def testScrollDistanceFastWheel(self):
# Wheel scrolling will have a much greater error than touch. There's 2
# reasons: 1) synthetic wheel gesture accumulate the sent deltas and use
# that to determine how much delta to send at each event dispatch time.
# This assumes that the entire sent delta is applied which is wrong due to
# physical pixel snapping which accumulates over the gesture.
# 2) We can only send delta as ticks of the wheel. If the total delta is
# not a multiple of the tick size, we'll "lose" the remainder.
self._RunScrollDistanceTest(
500000, 200000, page_action.GESTURE_SOURCE_MOUSE, 15000)
def testScrollDistanceSlowTouch(self):
# Just pass the test on platforms that don't support touch (i.e. Mac)
if not page_action.IsGestureSourceTypeSupported(self._tab, 'touch'):
return
# Scrolling slowly produces larger error since each event will have a
# smaller delta. Thus error from snapping in each event will be a larger
# share of the total delta.
self._RunScrollDistanceTest(
1000, 300, page_action.GESTURE_SOURCE_TOUCH, 10)
@decorators.Disabled('android-reference') # crbug.com/934649
def testScrollDistanceSlowWheel(self):
self._RunScrollDistanceTest(
1000, 300, page_action.GESTURE_SOURCE_MOUSE, 200)
@decorators.Disabled('android-reference') # crbug.com/934649
@decorators.Disabled('win-reference') # crbug.com/805523
def testWheelScrollDistanceWhileZoomed(self):
# TODO(bokan): This API was added recently so only run the test once it's
# available. Remove this check once it rolls into stable builds.
chromeSupportsSetPageScaleFactor = self._tab.EvaluateJavaScript(
"'setPageScaleFactor' in chrome.gpuBenchmarking")
if not chromeSupportsSetPageScaleFactor:
return
self._tab.EvaluateJavaScript('chrome.gpuBenchmarking.setPageScaleFactor(2)')
# Wheel scrolling can cause animated scrolls. This is a problem here since
# Chrome currently doesn't hand off the animation between the visual and
# layout viewports. To account for this, scroll the visual viewport to it's
# maximum extent so that the entire scroll goes to the layout viewport.
screenHeight = self._tab.EvaluateJavaScript('window.visualViewport.height')
i = scroll.ScrollAction(
distance=screenHeight*2,
direction="down",
speed_in_pixels_per_second=5000,
synthetic_gesture_source=page_action.GESTURE_SOURCE_MOUSE)
i.WillRunAction(self._tab)
i.RunAction(self._tab)
# Ensure the layout viewport isn't scrolled but the visual is.
self.assertGreater(
self._tab.EvaluateJavaScript('window.visualViewport.offsetTop'),
screenHeight // 2 - 1)
self.assertEqual(self._tab.EvaluateJavaScript('window.scrollY'), 0)
self._RunScrollDistanceTest(
2000, 2000, page_action.GESTURE_SOURCE_MOUSE, 60)
def testTouchScrollDistanceWhileZoomed(self):
# Just pass the test on platforms that don't support touch (i.e. Mac)
if not page_action.IsGestureSourceTypeSupported(self._tab, 'touch'):
return
# TODO(bokan): This API was added recently so only run the test once it's
# available. Remove this check once it rolls into stable builds.
chromeSupportsSetPageScaleFactor = self._tab.EvaluateJavaScript(
"'setPageScaleFactor' in chrome.gpuBenchmarking")
if not chromeSupportsSetPageScaleFactor:
return
self._tab.EvaluateJavaScript('chrome.gpuBenchmarking.setPageScaleFactor(2)')
self._RunScrollDistanceTest(
2000, 2000, page_action.GESTURE_SOURCE_TOUCH, 20)
def testScrollAction(self):
self._MakePageVerticallyScrollable()
self.assertEquals(
self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
i = scroll.ScrollAction()
i.WillRunAction(self._tab)
self._tab.ExecuteJavaScript("""
window.__scrollAction.beginMeasuringHook = function() {
window.__didBeginMeasuring = true;
};
window.__scrollAction.endMeasuringHook = function() {
window.__didEndMeasuring = true;
};""")
i.RunAction(self._tab)
self.assertTrue(self._tab.EvaluateJavaScript('window.__didBeginMeasuring'))
self.assertTrue(self._tab.EvaluateJavaScript('window.__didEndMeasuring'))
scroll_position = self._tab.EvaluateJavaScript(
'document.scrollingElement.scrollTop')
self.assertTrue(
scroll_position != 0, msg='scroll_position=%d;' % (scroll_position))
# https://github.com/catapult-project/catapult/issues/3099
@decorators.Disabled('android')
@decorators.Disabled('chromeos') # crbug.com/984016
def testDiagonalScrollAction(self):
self._MakePageVerticallyScrollable()
self.assertEquals(
self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
self._MakePageHorizontallyScrollable()
self.assertEquals(
self._tab.EvaluateJavaScript('document.scrollingElement.scrollLeft'), 0)
i = scroll.ScrollAction(direction='downright')
i.WillRunAction(self._tab)
i.RunAction(self._tab)
viewport_top = self._tab.EvaluateJavaScript(
'document.scrollingElement.scrollTop')
self.assertTrue(viewport_top != 0, msg='viewport_top=%d;' % viewport_top)
viewport_left = self._tab.EvaluateJavaScript(
'document.scrollingElement.scrollLeft')
self.assertTrue(viewport_left != 0, msg='viewport_left=%d;' % viewport_left)
def testBoundingClientRect(self):
# Verify that the rect returned by getBoundingVisibleRect() in scroll.js is
# completely contained within the viewport. Scroll events dispatched by the
# scrolling API use the center of this rect as their location, and this
# location needs to be within the viewport bounds to correctly decide
# between main-thread and impl-thread scroll. If the scrollable area were
# not clipped to the viewport bounds, then the instance used here (the
# scrollable area being more than twice as tall as the viewport) would
# result in a scroll location outside of the viewport bounds.
self._MakePageVerticallyScrollable()
self.assertEquals(
self._tab.EvaluateJavaScript('document.scrollingElement.scrollTop'), 0)
self._MakePageHorizontallyScrollable()
self.assertEquals(
self._tab.EvaluateJavaScript('document.scrollingElement.scrollLeft'), 0)
self._tab.ExecuteJavaScript("""
window.scrollTo(__GestureCommon_GetWindowWidth(),
__GestureCommon_GetWindowHeight());""")
rect_top = int(
self._tab.EvaluateJavaScript(
'__GestureCommon_GetBoundingVisibleRect(document.body).top'))
rect_height = int(
self._tab.EvaluateJavaScript(
'__GestureCommon_GetBoundingVisibleRect(document.body).height'))
rect_bottom = rect_top + rect_height
rect_left = int(
self._tab.EvaluateJavaScript(
'__GestureCommon_GetBoundingVisibleRect(document.body).left'))
rect_width = int(
self._tab.EvaluateJavaScript(
'__GestureCommon_GetBoundingVisibleRect(document.body).width'))
rect_right = rect_left + rect_width
viewport_height = int(
self._tab.EvaluateJavaScript('__GestureCommon_GetWindowHeight()'))
viewport_width = int(
self._tab.EvaluateJavaScript('__GestureCommon_GetWindowWidth()'))
self.assertTrue(rect_top >= 0, msg='%s >= %s' % (rect_top, 0))
self.assertTrue(rect_left >= 0, msg='%s >= %s' % (rect_left, 0))
self.assertTrue(
rect_bottom <= viewport_height,
msg='%s + %s <= %s' % (rect_top, rect_height, viewport_height))
self.assertTrue(
rect_right <= viewport_width,
msg='%s + %s <= %s' % (rect_left, rect_width, viewport_width))