TestSnapping.cpp (12758B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "APZCTreeManagerTester.h" 8 #include "APZTestCommon.h" 9 10 #include "InputUtils.h" 11 #include "mozilla/StaticPrefs_layout.h" 12 #include "mozilla/StaticPrefs_mousewheel.h" 13 14 class APZCSnappingTesterMock : public APZCTreeManagerTester { 15 public: 16 APZCSnappingTesterMock() { CreateMockHitTester(); } 17 }; 18 19 TEST_F(APZCSnappingTesterMock, Bug1265510) { 20 // Needed because the test uses SmoothWheel() 21 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 22 23 const char* treeShape = "x(x)"; 24 LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100), 25 LayerIntRect(0, 100, 100, 100)}; 26 CreateScrollData(treeShape, layerVisibleRect); 27 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 28 CSSRect(0, 0, 100, 200)); 29 SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1, 30 CSSRect(0, 0, 100, 200)); 31 SetScrollHandoff(layers[1], root); 32 33 ScrollSnapInfo snap; 34 snap.mScrollSnapStrictnessY = StyleScrollSnapStrictness::Mandatory; 35 snap.mSnapportSize = 36 CSSSize::ToAppUnits(layerVisibleRect[0].Size() * LayerToCSSScale(1.0)); 37 38 snap.mSnapTargets.AppendElement(ScrollSnapInfo::SnapTarget( 39 Nothing(), Some(0 * AppUnitsPerCSSPixel()), 40 CSSRect::ToAppUnits(CSSRect(0, 0, 10, 10)), StyleScrollSnapStop::Normal, 41 ScrollSnapTargetId{1})); 42 snap.mSnapTargets.AppendElement(ScrollSnapInfo::SnapTarget( 43 Nothing(), Some(100 * AppUnitsPerCSSPixel()), 44 CSSRect::ToAppUnits(CSSRect(0, 100, 10, 10)), StyleScrollSnapStop::Normal, 45 ScrollSnapTargetId{2})); 46 47 ModifyFrameMetrics(root, [&](ScrollMetadata& aSm, FrameMetrics&) { 48 aSm.SetSnapInfo(ScrollSnapInfo(snap)); 49 }); 50 51 UniquePtr<ScopedLayerTreeRegistration> registration = 52 MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 53 UpdateHitTestingTree(); 54 55 TestAsyncPanZoomController* outer = ApzcOf(layers[0]); 56 TestAsyncPanZoomController* inner = ApzcOf(layers[1]); 57 58 // Position the mouse near the bottom of the outer frame and scroll by 60px. 59 // (6 lines of 10px each). APZC will actually scroll to y=100 because of the 60 // mandatory snap coordinate there. 61 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 62 SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), mcc->Time()); 63 // Advance in 5ms increments until we've scrolled by 70px. At this point, the 64 // closest snap point is y=100, and the inner frame should be under the mouse 65 // cursor. 66 while (outer 67 ->GetCurrentAsyncScrollOffset( 68 AsyncTransformConsumer::eForEventHandling) 69 .y < 70) { 70 mcc->AdvanceByMillis(5); 71 outer->AdvanceAnimations(mcc->GetSampleTime()); 72 } 73 // Now do another wheel in a new transaction. This should start scrolling the 74 // inner frame; we verify that it does by checking the inner scroll position. 75 mcc->AdvanceBy(TimeDuration::FromMilliseconds( 76 StaticPrefs::mousewheel_transaction_timeout() + 100)); 77 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 78 SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), mcc->Time()); 79 mcc->AdvanceByMillis(5); 80 inner->AdvanceAnimationsUntilEnd(); 81 EXPECT_LT(0.0f, inner 82 ->GetCurrentAsyncScrollOffset( 83 AsyncTransformConsumer::eForEventHandling) 84 .y); 85 86 // However, the outer frame should also continue to the snap point, otherwise 87 // it is demonstrating incorrect behaviour by violating the mandatory 88 // snapping. 89 outer->AdvanceAnimationsUntilEnd(); 90 EXPECT_EQ(100.0f, outer 91 ->GetCurrentAsyncScrollOffset( 92 AsyncTransformConsumer::eForEventHandling) 93 .y); 94 } 95 96 TEST_F(APZCSnappingTesterMock, Snap_After_Pinch) { 97 const char* treeShape = "x"; 98 LayerIntRect layerVisibleRect[] = { 99 LayerIntRect(0, 0, 100, 100), 100 }; 101 CreateScrollData(treeShape, layerVisibleRect); 102 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 103 CSSRect(0, 0, 100, 200)); 104 105 // Set up some basic scroll snapping 106 ScrollSnapInfo snap; 107 snap.mScrollSnapStrictnessY = StyleScrollSnapStrictness::Mandatory; 108 snap.mSnapportSize = 109 CSSSize::ToAppUnits(layerVisibleRect[0].Size() * LayerToCSSScale(1.0)); 110 111 snap.mSnapTargets.AppendElement(ScrollSnapInfo::SnapTarget( 112 Nothing(), Some(0 * AppUnitsPerCSSPixel()), 113 CSSRect::ToAppUnits(CSSRect(0, 0, 10, 10)), StyleScrollSnapStop::Normal, 114 ScrollSnapTargetId{1})); 115 snap.mSnapTargets.AppendElement(ScrollSnapInfo::SnapTarget( 116 Nothing(), Some(100 * AppUnitsPerCSSPixel()), 117 CSSRect::ToAppUnits(CSSRect(0, 100, 10, 10)), StyleScrollSnapStop::Normal, 118 ScrollSnapTargetId{2})); 119 120 // Save the scroll snap info on the root APZC. 121 // Also mark the root APZC as "root content", since APZC only allows 122 // zooming on the root content APZC. 123 ModifyFrameMetrics(root, [&](ScrollMetadata& aSm, FrameMetrics& aMetrics) { 124 aSm.SetSnapInfo(ScrollSnapInfo(snap)); 125 aMetrics.SetIsRootContent(true); 126 }); 127 128 UniquePtr<ScopedLayerTreeRegistration> registration = 129 MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 130 UpdateHitTestingTree(); 131 132 RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root); 133 134 // Allow zooming 135 apzc->UpdateZoomConstraints(ZoomConstraints( 136 true, true, CSSToParentLayerScale(0.25f), CSSToParentLayerScale(4.0f))); 137 138 PinchWithPinchInput(apzc, ScreenIntPoint(50, 50), ScreenIntPoint(50, 50), 139 1.2f); 140 141 apzc->AssertInSmoothMsdScroll(); 142 } 143 144 // Currently fails on Android because on the platform we have a different 145 // VelocityTracker. 146 #ifndef MOZ_WIDGET_ANDROID 147 TEST_F(APZCSnappingTesterMock, SnapOnPanEndWithZeroVelocity) { 148 // Use pref values for desktop everywhere. 149 SCOPED_GFX_PREF_FLOAT("apz.fling_friction", 0.002); 150 SCOPED_GFX_PREF_FLOAT("apz.fling_stopped_threshold", 0.01); 151 SCOPED_GFX_PREF_FLOAT("apz.fling_curve_function_x1", 0.0); 152 SCOPED_GFX_PREF_FLOAT("apz.fling_curve_function_x2", 1.0); 153 SCOPED_GFX_PREF_FLOAT("apz.fling_curve_function_y1", 0.0); 154 SCOPED_GFX_PREF_FLOAT("apz.fling_curve_function_y2", 1.0); 155 SCOPED_GFX_PREF_INT("apz.velocity_relevance_time_ms", 100); 156 157 const char* treeShape = "x"; 158 LayerIntRect layerVisibleRect[] = { 159 LayerIntRect(0, 0, 100, 100), 160 }; 161 CreateScrollData(treeShape, layerVisibleRect); 162 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 163 CSSRect(0, 0, 100, 400)); 164 165 // Set up two snap points, 30 and 100. 166 ScrollSnapInfo snap; 167 snap.mScrollSnapStrictnessY = StyleScrollSnapStrictness::Mandatory; 168 snap.mSnapportSize = 169 CSSSize::ToAppUnits(layerVisibleRect[0].Size() * LayerToCSSScale(1.0)); 170 snap.mSnapTargets.AppendElement(ScrollSnapInfo::SnapTarget( 171 Nothing(), Some(30 * AppUnitsPerCSSPixel()), 172 CSSRect::ToAppUnits(CSSRect(0, 30, 10, 10)), StyleScrollSnapStop::Normal, 173 ScrollSnapTargetId{1})); 174 snap.mSnapTargets.AppendElement(ScrollSnapInfo::SnapTarget( 175 Nothing(), Some(100 * AppUnitsPerCSSPixel()), 176 CSSRect::ToAppUnits(CSSRect(0, 100, 10, 10)), StyleScrollSnapStop::Normal, 177 ScrollSnapTargetId{2})); 178 179 // Save the scroll snap info on the root APZC. 180 ModifyFrameMetrics(root, [&](ScrollMetadata& aSm, FrameMetrics& aMetrics) { 181 aSm.SetSnapInfo(ScrollSnapInfo(snap)); 182 }); 183 184 UniquePtr<ScopedLayerTreeRegistration> registration = 185 MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 186 UpdateHitTestingTree(); 187 188 RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root); 189 190 // Send a series of pan gestures to scroll to position at 50. 191 const ScreenIntPoint position = ScreenIntPoint(50, 30); 192 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 193 PanGesture(PanGestureInput::PANGESTURE_START, manager, position, 194 ScreenPoint(0, 10), mcc->Time()); 195 mcc->AdvanceByMillis(5); 196 apzc->AdvanceAnimations(mcc->GetSampleTime()); 197 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 198 PanGesture(PanGestureInput::PANGESTURE_PAN, manager, position, 199 ScreenPoint(0, 40), mcc->Time()); 200 mcc->AdvanceByMillis(5); 201 apzc->AdvanceAnimations(mcc->GetSampleTime()); 202 203 // Make sure the velocity just before sending a pan-end is zero. 204 EXPECT_EQ(apzc->GetVelocityVector().y, 0); 205 206 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 207 PanGesture(PanGestureInput::PANGESTURE_END, manager, position, 208 ScreenPoint(0, 0), mcc->Time()); 209 210 // Now a smooth animation has been triggered for snapping to 30. 211 apzc->AssertInSmoothMsdScroll(); 212 213 apzc->AdvanceAnimationsUntilEnd(); 214 // The snapped position should be 30 rather than 100 because it's the nearest 215 // snap point. 216 EXPECT_EQ(apzc->GetCurrentAsyncScrollOffset( 217 AsyncPanZoomController::eForEventHandling) 218 .y, 219 30); 220 } 221 222 // Smililar to above SnapOnPanEndWithZeroVelocity but with positive velocity so 223 // that the snap position would be the one in the scrolling direction. 224 TEST_F(APZCSnappingTesterMock, SnapOnPanEndWithPositiveVelocity) { 225 // Use pref values for desktop everywhere. 226 SCOPED_GFX_PREF_FLOAT("apz.fling_friction", 0.002); 227 SCOPED_GFX_PREF_FLOAT("apz.fling_stopped_threshold", 0.01); 228 SCOPED_GFX_PREF_FLOAT("apz.fling_curve_function_x1", 0.0); 229 SCOPED_GFX_PREF_FLOAT("apz.fling_curve_function_x2", 1.0); 230 SCOPED_GFX_PREF_FLOAT("apz.fling_curve_function_y1", 0.0); 231 SCOPED_GFX_PREF_FLOAT("apz.fling_curve_function_y2", 1.0); 232 SCOPED_GFX_PREF_INT("apz.velocity_relevance_time_ms", 100); 233 234 const char* treeShape = "x"; 235 LayerIntRect layerVisibleRect[] = { 236 LayerIntRect(0, 0, 100, 100), 237 }; 238 CreateScrollData(treeShape, layerVisibleRect); 239 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 240 CSSRect(0, 0, 100, 400)); 241 242 // Set up two snap points, 30 and 100. 243 ScrollSnapInfo snap; 244 snap.mScrollSnapStrictnessY = StyleScrollSnapStrictness::Mandatory; 245 snap.mSnapportSize = 246 CSSSize::ToAppUnits(layerVisibleRect[0].Size() * LayerToCSSScale(1.0)); 247 snap.mSnapTargets.AppendElement(ScrollSnapInfo::SnapTarget( 248 Nothing(), Some(30 * AppUnitsPerCSSPixel()), 249 CSSRect::ToAppUnits(CSSRect(0, 30, 10, 10)), StyleScrollSnapStop::Normal, 250 ScrollSnapTargetId{1})); 251 snap.mSnapTargets.AppendElement(ScrollSnapInfo::SnapTarget( 252 Nothing(), Some(100 * AppUnitsPerCSSPixel()), 253 CSSRect::ToAppUnits(CSSRect(0, 100, 10, 10)), StyleScrollSnapStop::Normal, 254 ScrollSnapTargetId{2})); 255 256 // Save the scroll snap info on the root APZC. 257 ModifyFrameMetrics(root, [&](ScrollMetadata& aSm, FrameMetrics& aMetrics) { 258 aSm.SetSnapInfo(ScrollSnapInfo(snap)); 259 }); 260 261 UniquePtr<ScopedLayerTreeRegistration> registration = 262 MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 263 UpdateHitTestingTree(); 264 265 RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root); 266 267 // Send a series of pan gestures that a pan-end event happens at 65 268 const ScreenIntPoint position = ScreenIntPoint(50, 30); 269 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 270 PanGesture(PanGestureInput::PANGESTURE_START, manager, position, 271 ScreenPoint(0, 10), mcc->Time()); 272 mcc->AdvanceByMillis(5); 273 apzc->AdvanceAnimations(mcc->GetSampleTime()); 274 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 275 PanGesture(PanGestureInput::PANGESTURE_PAN, manager, position, 276 ScreenPoint(0, 35), mcc->Time()); 277 mcc->AdvanceByMillis(5); 278 apzc->AdvanceAnimations(mcc->GetSampleTime()); 279 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 280 PanGesture(PanGestureInput::PANGESTURE_PAN, manager, position, 281 ScreenPoint(0, 20), mcc->Time()); 282 mcc->AdvanceByMillis(5); 283 apzc->AdvanceAnimations(mcc->GetSampleTime()); 284 285 // There should be positive velocity in this case. 286 EXPECT_GT(apzc->GetVelocityVector().y, 0); 287 288 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 289 PanGesture(PanGestureInput::PANGESTURE_END, manager, position, 290 ScreenPoint(0, 0), mcc->Time()); 291 mcc->AdvanceByMillis(5); 292 293 // A smooth animation has been triggered by the pan-end event above. 294 apzc->AssertInSmoothMsdScroll(); 295 296 apzc->AdvanceAnimationsUntilEnd(); 297 EXPECT_EQ(apzc->GetCurrentAsyncScrollOffset( 298 AsyncPanZoomController::eForEventHandling) 299 .y, 300 100); 301 } 302 #endif