TestPanning.cpp (22943B)
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 "APZCBasicTester.h" 8 #include "APZCTreeManagerTester.h" 9 #include "APZTestCommon.h" 10 #include "FrameMetrics.h" 11 #include "InputUtils.h" 12 #include "gtest/gtest.h" 13 #include "mozilla/ScrollSnapInfo.h" 14 #include "mozilla/ServoComputedData.h" 15 #include "mozilla/gfx/CompositorHitTestInfo.h" 16 #include "mozilla/layers/ScrollableLayerGuid.h" 17 18 class APZCPanningTester : public APZCBasicTester { 19 protected: 20 void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed, 21 uint32_t aBehavior) { 22 if (aShouldTriggerScroll) { 23 // Three repaint request for each pan. 24 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(6); 25 } else { 26 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0); 27 } 28 29 int touchStart = 50; 30 int touchEnd = 10; 31 ParentLayerPoint pointOut; 32 AsyncTransform viewTransformOut; 33 34 nsTArray<uint32_t> allowedTouchBehaviors; 35 allowedTouchBehaviors.AppendElement(aBehavior); 36 37 // Pan down 38 PanAndCheckStatus(apzc, touchStart, touchEnd, aShouldBeConsumed, 39 &allowedTouchBehaviors); 40 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 41 42 if (aShouldTriggerScroll) { 43 EXPECT_EQ(ParentLayerPoint(0, -(touchEnd - touchStart)), pointOut); 44 EXPECT_NE(AsyncTransform(), viewTransformOut); 45 } else { 46 EXPECT_EQ(ParentLayerPoint(), pointOut); 47 EXPECT_EQ(AsyncTransform(), viewTransformOut); 48 } 49 50 // Clear the fling from the previous pan, or stopping it will 51 // consume the next touchstart 52 apzc->CancelAnimation(); 53 54 // Pan back 55 PanAndCheckStatus(apzc, touchEnd, touchStart, aShouldBeConsumed, 56 &allowedTouchBehaviors); 57 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 58 59 EXPECT_EQ(ParentLayerPoint(), pointOut); 60 EXPECT_EQ(AsyncTransform(), viewTransformOut); 61 } 62 63 void DoPanWithPreventDefaultTest() { 64 MakeApzcWaitForMainThread(); 65 66 int touchStart = 50; 67 int touchEnd = 10; 68 ParentLayerPoint pointOut; 69 AsyncTransform viewTransformOut; 70 uint64_t blockId = 0; 71 72 // Pan down 73 nsTArray<uint32_t> allowedTouchBehaviors; 74 allowedTouchBehaviors.AppendElement( 75 mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN); 76 PanAndCheckStatus(apzc, touchStart, touchEnd, true, &allowedTouchBehaviors, 77 &blockId); 78 79 // Send the signal that content has handled and preventDefaulted the touch 80 // events. This flushes the event queue. 81 apzc->ContentReceivedInputBlock(blockId, true); 82 83 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 84 EXPECT_EQ(ParentLayerPoint(), pointOut); 85 EXPECT_EQ(AsyncTransform(), viewTransformOut); 86 87 apzc->AssertStateIsReset(); 88 } 89 90 void PanWithFling() { 91 // Send a pan gesture that triggers a fling animation at the end. 92 // Note that we need at least two _PAN events to have enough samples 93 // in the velocity tracker to compute a fling velocity. 94 PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80), 95 ScreenPoint(0, 2), mcc->Time()); 96 mcc->AdvanceByMillis(5); 97 apzc->AdvanceAnimations(mcc->GetSampleTime()); 98 PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80), 99 ScreenPoint(0, 10), mcc->Time()); 100 mcc->AdvanceByMillis(5); 101 apzc->AdvanceAnimations(mcc->GetSampleTime()); 102 PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80), 103 ScreenPoint(0, 10), mcc->Time()); 104 mcc->AdvanceByMillis(5); 105 apzc->AdvanceAnimations(mcc->GetSampleTime()); 106 PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80), 107 ScreenPoint(0, 0), mcc->Time(), MODIFIER_NONE, 108 /*aSimulateMomentum=*/true); 109 } 110 }; 111 112 // In the each of the following 4 pan tests we are performing two pan gestures: 113 // vertical pan from top to bottom and back - from bottom to top. According to 114 // the pointer-events/touch-action spec AUTO and PAN_Y touch-action values allow 115 // vertical scrolling while NONE and PAN_X forbid it. The first parameter of 116 // DoPanTest method specifies this behavior. However, the events will be marked 117 // as consumed even if the behavior in PAN_X, because the user could move their 118 // finger horizontally too - APZ has no way of knowing beforehand and so must 119 // consume the events. 120 TEST_F(APZCPanningTester, PanWithTouchActionAuto) { 121 // Velocity bias can cause extra repaint requests. 122 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0); 123 DoPanTest(true, true, 124 mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN | 125 mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN); 126 } 127 128 TEST_F(APZCPanningTester, PanWithTouchActionNone) { 129 // Velocity bias can cause extra repaint requests. 130 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0); 131 DoPanTest(false, false, 0); 132 } 133 134 TEST_F(APZCPanningTester, PanWithTouchActionPanX) { 135 // Velocity bias can cause extra repaint requests. 136 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0); 137 DoPanTest(false, false, 138 mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN); 139 } 140 141 TEST_F(APZCPanningTester, PanWithTouchActionPanY) { 142 // Velocity bias can cause extra repaint requests. 143 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0); 144 DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN); 145 } 146 147 TEST_F(APZCPanningTester, PanWithPreventDefault) { 148 DoPanWithPreventDefaultTest(); 149 } 150 151 TEST_F(APZCPanningTester, PanWithHistoricalTouchData) { 152 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0); 153 154 // Simulate the same pan gesture, in three different ways. 155 // We start at y=50, with a 50ms resting period at the start of the pan. 156 // Then we accelerate the finger upwards towards y=10, reaching a 10px/10ms 157 // velocity towards the end of the panning motion. 158 // 159 // The first simulation fires touch move events with 10ms gaps. 160 // The second simulation skips two of the touch move events, simulating 161 // "jank". The third simulation also skips those two events, but reports the 162 // missed positions in the following event's historical coordinates. 163 // 164 // Consequently, the first and third simulation should estimate the same 165 // velocities, whereas the second simulation should estimate a different 166 // velocity because it is missing data. 167 168 // First simulation: full data 169 170 APZEventResult result = TouchDown(apzc, ScreenIntPoint(0, 50), mcc->Time()); 171 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 172 SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId); 173 } 174 175 mcc->AdvanceByMillis(50); 176 result = TouchMove(apzc, ScreenIntPoint(0, 45), mcc->Time()); 177 mcc->AdvanceByMillis(10); 178 result = TouchMove(apzc, ScreenIntPoint(0, 40), mcc->Time()); 179 mcc->AdvanceByMillis(10); 180 result = TouchMove(apzc, ScreenIntPoint(0, 30), mcc->Time()); 181 mcc->AdvanceByMillis(10); 182 result = TouchMove(apzc, ScreenIntPoint(0, 20), mcc->Time()); 183 result = TouchUp(apzc, ScreenIntPoint(0, 20), mcc->Time()); 184 auto velocityFromFullDataAsSeparateEvents = apzc->GetVelocityVector(); 185 apzc->CancelAnimation(); 186 187 mcc->AdvanceByMillis(100); 188 189 // Second simulation: partial data 190 191 result = TouchDown(apzc, ScreenIntPoint(0, 50), mcc->Time()); 192 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 193 SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId); 194 } 195 196 mcc->AdvanceByMillis(50); 197 result = TouchMove(apzc, ScreenIntPoint(0, 45), mcc->Time()); 198 mcc->AdvanceByMillis(30); 199 result = TouchMove(apzc, ScreenIntPoint(0, 20), mcc->Time()); 200 result = TouchUp(apzc, ScreenIntPoint(0, 20), mcc->Time()); 201 auto velocityFromPartialData = apzc->GetVelocityVector(); 202 apzc->CancelAnimation(); 203 204 mcc->AdvanceByMillis(100); 205 206 // Third simulation: full data via historical data 207 208 result = TouchDown(apzc, ScreenIntPoint(0, 50), mcc->Time()); 209 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 210 SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId); 211 } 212 213 mcc->AdvanceByMillis(50); 214 result = TouchMove(apzc, ScreenIntPoint(0, 45), mcc->Time()); 215 mcc->AdvanceByMillis(30); 216 217 MultiTouchInput mti = 218 CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); 219 auto singleTouchData = CreateSingleTouchData(0, ScreenIntPoint(0, 20)); 220 singleTouchData.mHistoricalData.AppendElement( 221 SingleTouchData::HistoricalTouchData{ 222 mcc->Time() - TimeDuration::FromMilliseconds(20), 223 ScreenIntPoint(0, 40), 224 {}, 225 {}, 226 0.0f, 227 0.0f}); 228 singleTouchData.mHistoricalData.AppendElement( 229 SingleTouchData::HistoricalTouchData{ 230 mcc->Time() - TimeDuration::FromMilliseconds(10), 231 ScreenIntPoint(0, 30), 232 {}, 233 {}, 234 0.0f, 235 0.0f}); 236 mti.mTouches.AppendElement(singleTouchData); 237 result = apzc->ReceiveInputEvent(mti); 238 239 result = TouchUp(apzc, ScreenIntPoint(0, 20), mcc->Time()); 240 auto velocityFromFullDataViaHistory = apzc->GetVelocityVector(); 241 apzc->CancelAnimation(); 242 243 EXPECT_EQ(velocityFromFullDataAsSeparateEvents, 244 velocityFromFullDataViaHistory); 245 EXPECT_NE(velocityFromPartialData, velocityFromFullDataViaHistory); 246 } 247 248 TEST_F(APZCPanningTester, DuplicatePanEndEvents_Bug1833950) { 249 // Send a pan gesture that triggers a fling animation at the end. 250 PanWithFling(); 251 252 // Give the fling animation a chance to start. 253 SampleAnimationOnce(); 254 apzc->AssertStateIsFling(); 255 256 // Send a duplicate pan-end event. 257 // This test is just intended to check that doing this doesn't 258 // trigger an assertion failure in debug mode. 259 PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80), 260 ScreenPoint(0, 0), mcc->Time(), MODIFIER_NONE, 261 /*aSimulateMomentum=*/true); 262 } 263 264 class APZCPanningTesterMock : public APZCTreeManagerTester { 265 public: 266 APZCPanningTesterMock() { CreateMockHitTester(); } 267 }; 268 269 TEST_F(APZCPanningTester, HoldGesture_HoldAndRelease) { 270 // Send a pan gesture that triggers a fling animation at the end. 271 PanWithFling(); 272 273 // Give the fling animation a chance to start. 274 SampleAnimationOnce(); 275 apzc->AssertStateIsFling(); 276 277 // Send a PANGESTURE_MAYSTART event, signifying that the fingers went back 278 // down on the touchpad. 279 PanGesture(PanGestureInput::PANGESTURE_MAYSTART, apzc, ScreenIntPoint(50, 80), 280 ScreenPoint(0, 0), mcc->Time()); 281 282 // This should have had the effect of cancelling the fling animation. 283 apzc->AssertStateIsReset(); 284 285 // Send a PANGESTURE_CANCELLED event, signifying that the fingers have been 286 // lifted without any scrolling. This should have no effect on the gesture 287 // state. 288 mcc->AdvanceByMillis(5); 289 apzc->AdvanceAnimations(mcc->GetSampleTime()); 290 PanGesture(PanGestureInput::PANGESTURE_CANCELLED, apzc, 291 ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time()); 292 apzc->AssertStateIsReset(); 293 } 294 295 TEST_F(APZCPanningTester, HoldGesture_HoldAndScroll) { 296 // Send a pan gesture that triggers a fling animation at the end. 297 PanWithFling(); 298 299 // Give the fling animation a chance to start. 300 SampleAnimationOnce(); 301 apzc->AssertStateIsFling(); 302 303 // Record the scroll offset before the fingers go back. 304 float scrollYBefore = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 305 EXPECT_GT(scrollYBefore, 0); 306 307 // Send a PANGESTURE_MAYSTART event, signifying that the fingers went back 308 // down on the touchpad. 309 PanGesture(PanGestureInput::PANGESTURE_MAYSTART, apzc, ScreenIntPoint(50, 80), 310 ScreenPoint(0, 0), mcc->Time()); 311 312 // This should have had the effect of cancelling the fling animation. 313 apzc->AssertStateIsReset(); 314 315 // Do actual panning as part of the same gesture. 316 mcc->AdvanceByMillis(5); 317 apzc->AdvanceAnimations(mcc->GetSampleTime()); 318 PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80), 319 ScreenPoint(0, 2), mcc->Time()); 320 mcc->AdvanceByMillis(5); 321 apzc->AdvanceAnimations(mcc->GetSampleTime()); 322 PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80), 323 ScreenPoint(0, 10), mcc->Time()); 324 mcc->AdvanceByMillis(5); 325 apzc->AdvanceAnimations(mcc->GetSampleTime()); 326 PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80), 327 ScreenPoint(0, 0), mcc->Time(), MODIFIER_NONE, 328 /*aSimulateMomentum=*/true); 329 330 // Check that we've done additional scrolling. 331 float scrollYAfter = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 332 EXPECT_GT(scrollYAfter, scrollYBefore); 333 } 334 335 TEST_F(APZCPanningTesterMock, HoldGesture_ActiveWheelListener) { 336 // Explicitly set the content response timeout. 337 // The value should be greater than the sum of the AdvanceByMillis() 338 // intervals from the MAYSTART event until the content response. 339 SCOPED_GFX_PREF_INT("apz.content_response_timeout", 100); 340 341 CreateSimpleScrollingLayer(); 342 ScopedLayerTreeRegistration registration(LayersId{0}, mcc); 343 UpdateHitTestingTree(); 344 345 RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root); 346 ScrollableLayerGuid::ViewID scrollId = ScrollableLayerGuid::START_SCROLL_ID; 347 ScreenIntPoint panPoint(50, 80); 348 349 // Simulate an active wheel listener by having the MockHitTester 350 // return eApzAwareListeners as the hit-test result for every event. 351 gfx::CompositorHitTestInfo dispatchToContent{ 352 CompositorHitTestFlags::eVisibleToHitTest, 353 CompositorHitTestFlags::eApzAwareListeners}; 354 355 // Send a MAYSTART. Note that this has zero delta and does not 356 // result in an event sent to web content (so it will not itself 357 // result in a content response). 358 QueueMockHitResult(scrollId, dispatchToContent); 359 PanGesture(PanGestureInput::PANGESTURE_MAYSTART, manager, panPoint, 360 ScreenPoint(0, 0), mcc->Time()); 361 362 #ifdef MOZ_WIDGET_GTK 363 // On Linux, send a CANCELLED. This signifies the end of the hold gesture. 364 // (On Mac the widget code goes directly to sending START.) 365 mcc->AdvanceByMillis(5); 366 QueueMockHitResult(scrollId, dispatchToContent); 367 PanGesture(PanGestureInput::PANGESTURE_CANCELLED, manager, panPoint, 368 ScreenPoint(0, 0), mcc->Time()); 369 #endif 370 371 // Send a START. This does result in an event sent to web 372 // content if there is a nonzero delta. 373 mcc->AdvanceByMillis(5); 374 QueueMockHitResult(scrollId, dispatchToContent); 375 auto startResult = PanGesture(PanGestureInput::PANGESTURE_START, manager, 376 panPoint, ScreenPoint(0, 10), mcc->Time()); 377 378 // Send a couple of PAN events. 379 mcc->AdvanceByMillis(5); 380 QueueMockHitResult(scrollId, dispatchToContent); 381 PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint, 382 ScreenPoint(0, 10), mcc->Time()); 383 mcc->AdvanceByMillis(5); 384 QueueMockHitResult(scrollId, dispatchToContent); 385 PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint, 386 ScreenPoint(0, 10), mcc->Time()); 387 388 // Simulate a content response which allows the scroll. 389 manager->SetTargetAPZC(startResult.mInputBlockId, {startResult.mTargetGuid}); 390 manager->ContentReceivedInputBlock(startResult.mInputBlockId, 391 /*aPreventDefault=*/false); 392 393 // Check that we did scroll. 394 // In the buggy scenario for which this test case is written, 395 // the input block for the hold gesture was never confirmed, 396 // stalling the input queue so that we don't scroll until that input 397 // block is timed out. 398 EXPECT_GT(apzc->GetFrameMetrics().GetVisualScrollOffset().y, 0); 399 400 // Clean up by sending an END event. 401 QueueMockHitResult(scrollId, dispatchToContent); 402 PanGesture(PanGestureInput::PANGESTURE_END, manager, panPoint, 403 ScreenPoint(0, 0), mcc->Time()); 404 apzc->AssertStateIsReset(); 405 } 406 407 TEST_F(APZCPanningTesterMock, HoldGesture_PreventDefaultAfterLongHold) { 408 // Explicitly set a content response timeout. 409 SCOPED_GFX_PREF_INT("apz.content_response_timeout", 20); 410 411 CreateSimpleScrollingLayer(); 412 ScopedLayerTreeRegistration registration(LayersId{0}, mcc); 413 UpdateHitTestingTree(); 414 415 RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root); 416 ScrollableLayerGuid::ViewID scrollId = ScrollableLayerGuid::START_SCROLL_ID; 417 ScreenIntPoint panPoint(50, 80); 418 419 // Simulate an active wheel listener by having the MockHitTester 420 // return eApzAwareListeners as the hit-test result for every event. 421 gfx::CompositorHitTestInfo dispatchToContent{ 422 CompositorHitTestFlags::eVisibleToHitTest, 423 CompositorHitTestFlags::eApzAwareListeners}; 424 425 // Send a MAYSTART. Note that this has zero delta and does not 426 // result in an event sent to web content (so it will not itself 427 // result in a content response). 428 QueueMockHitResult(scrollId, dispatchToContent); 429 PanGesture(PanGestureInput::PANGESTURE_MAYSTART, manager, panPoint, 430 ScreenPoint(0, 0), mcc->Time()); 431 432 // Allow enough time to pass for the content response timeout to be reached. 433 mcc->AdvanceByMillis(30); 434 435 #ifdef MOZ_WIDGET_GTK 436 // On Linux, send a CANCELLED. This signifies the end of the hold gesture. 437 // (On Mac the widget code goes directly to sending START.) 438 QueueMockHitResult(scrollId, dispatchToContent); 439 PanGesture(PanGestureInput::PANGESTURE_CANCELLED, manager, panPoint, 440 ScreenPoint(0, 0), mcc->Time()); 441 #endif 442 443 // Send a START. This does result in an event sent to web 444 // content if there is a nonzero delta. 445 mcc->AdvanceByMillis(5); 446 QueueMockHitResult(scrollId, dispatchToContent); 447 auto startResult = PanGesture(PanGestureInput::PANGESTURE_START, manager, 448 panPoint, ScreenPoint(0, 10), mcc->Time()); 449 450 // Send a couple of PAN events. 451 mcc->AdvanceByMillis(5); 452 QueueMockHitResult(scrollId, dispatchToContent); 453 PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint, 454 ScreenPoint(0, 10), mcc->Time()); 455 mcc->AdvanceByMillis(5); 456 QueueMockHitResult(scrollId, dispatchToContent); 457 PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint, 458 ScreenPoint(0, 10), mcc->Time()); 459 460 // Simulate a content response which does NOT allow the scroll. 461 manager->SetTargetAPZC(startResult.mInputBlockId, {startResult.mTargetGuid}); 462 manager->ContentReceivedInputBlock(startResult.mInputBlockId, 463 /*aPreventDefault=*/true); 464 465 // Check that we did NOT scroll. 466 // In the buggy scenario for which this test case is written, 467 // the hold gesture and the scroll go into the same input block, 468 // for which the content response times out during the hold gesture, 469 // and we don't wait for the content response for the scroll. 470 EXPECT_EQ(apzc->GetFrameMetrics().GetVisualScrollOffset().y, 0); 471 472 // Clean up by sending an END event. 473 QueueMockHitResult(scrollId, dispatchToContent); 474 PanGesture(PanGestureInput::PANGESTURE_END, manager, panPoint, 475 ScreenPoint(0, 0), mcc->Time()); 476 apzc->AssertStateIsReset(); 477 } 478 479 TEST_F(APZCPanningTesterMock, HoldGesture_SubframeTargeting) { 480 // Set up a layer tree with a scrollable subframe handing off to a root frame. 481 const char* treeShape = "x(x)"; 482 LayerIntRect layerVisibleRect[] = { 483 LayerIntRect(0, 0, 100, 100), 484 LayerIntRect(0, 0, 100, 100), 485 }; 486 CreateScrollData(treeShape, layerVisibleRect); 487 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 488 CSSRect(0, 0, 100, 100)); 489 SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1, 490 CSSRect(0, 0, 100, 200)); 491 SetScrollHandoff(layers[1], root); 492 ScopedLayerTreeRegistration registration(LayersId{0}, mcc); 493 UpdateHitTestingTree(); 494 495 auto* rootApzc = ApzcOf(root); 496 auto* subframeApzc = ApzcOf(layers[1]); 497 rootApzc->GetFrameMetrics().SetIsRootContent(true); 498 499 // Mark the subframe as overscroll-behavior:none. This is important to 500 // trigger the codepath in FindFirstScrollable() that exposes the bug. 501 subframeApzc->GetScrollMetadata().SetOverscrollBehavior( 502 OverscrollBehaviorInfo::FromStyleConstants( 503 StyleOverscrollBehavior::None, StyleOverscrollBehavior::None)); 504 505 ScrollableLayerGuid::ViewID subframeScrollId = 506 ScrollableLayerGuid::START_SCROLL_ID + 1; 507 ScreenIntPoint panPoint(50, 50); 508 509 // Send a MAYSTART. Note that this has zero delta, and causes its input 510 // block to be marked as having empty mAllowedScrollDirections because the 511 // subframe fails the "can this APZC be scrolled by this event" check 512 // and is overscroll-behavior:none. 513 QueueMockHitResult(subframeScrollId, 514 CompositorHitTestFlags::eVisibleToHitTest); 515 PanGesture(PanGestureInput::PANGESTURE_MAYSTART, manager, panPoint, 516 ScreenPoint(0, 0), mcc->Time()); 517 518 #ifdef MOZ_WIDGET_GTK 519 // On Linux, send a CANCELLED. This signifies the end of the hold gesture. 520 // (On Mac the widget code goes directly to sending START.) 521 mcc->AdvanceByMillis(5); 522 QueueMockHitResult(subframeScrollId, 523 CompositorHitTestFlags::eVisibleToHitTest); 524 PanGesture(PanGestureInput::PANGESTURE_CANCELLED, manager, panPoint, 525 ScreenPoint(0, 0), mcc->Time()); 526 #endif 527 528 // Send a START. In the buggy scenario, this gets added to the same input 529 // block as the MAYSTART, which has been marked as having empty 530 // mAllowedScrollDirections, and thus fails to scroll anything. 531 mcc->AdvanceByMillis(5); 532 QueueMockHitResult(subframeScrollId, 533 CompositorHitTestFlags::eVisibleToHitTest); 534 PanGesture(PanGestureInput::PANGESTURE_START, manager, panPoint, 535 ScreenPoint(0, 10), mcc->Time()); 536 537 // Check that the subframe scrolled. 538 EXPECT_GT(subframeApzc->GetFrameMetrics().GetVisualScrollOffset().y, 0); 539 540 // Clean up by sending an END event. 541 QueueMockHitResult(subframeScrollId, 542 CompositorHitTestFlags::eVisibleToHitTest); 543 PanGesture(PanGestureInput::PANGESTURE_END, manager, panPoint, 544 ScreenPoint(0, 0), mcc->Time()); 545 } 546 547 TEST_F(APZCPanningTester, HoldGesture_DuringAutoscrollAnimation) { 548 // Tell APZ about the current mouse position. This is needed for 549 // autoscroll to work correctly. 550 tm->SetCurrentMousePosition(ScreenPoint(5, 5)); 551 552 // Start an autoscroll animation. 553 apzc->StartAutoscroll(ScreenPoint(5, 5)); 554 apzc->AssertStateIsAutoscroll(); 555 556 // Send a PANGESTURE_MAYSTART event. With a touchpad, this can happen 557 // when you start moving the cursor after a three-finger gesture to 558 // start autoscroll. 559 PanGesture(PanGestureInput::PANGESTURE_MAYSTART, apzc, ScreenIntPoint(50, 80), 560 ScreenPoint(0, 0), mcc->Time()); 561 562 // Check that this did NOT cancel the autoscroll animation. 563 apzc->AssertStateIsAutoscroll(); 564 }