| # 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)) |