TestScrollHandoff.cpp (36734B)
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 #include "InputUtils.h" 10 11 class APZScrollHandoffTester : public APZCTreeManagerTester { 12 protected: 13 UniquePtr<ScopedLayerTreeRegistration> registration; 14 TestAsyncPanZoomController* rootApzc; 15 16 void CreateScrollHandoffLayerTree1() { 17 const char* treeShape = "x(x)"; 18 LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100), 19 LayerIntRect(0, 50, 100, 50)}; 20 CreateScrollData(treeShape, layerVisibleRect); 21 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 22 CSSRect(0, 0, 200, 200)); 23 SetScrollableFrameMetrics(layers[1], 24 ScrollableLayerGuid::START_SCROLL_ID + 1, 25 CSSRect(0, 0, 100, 100)); 26 SetScrollHandoff(layers[1], root); 27 registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 28 UpdateHitTestingTree(); 29 rootApzc = ApzcOf(root); 30 rootApzc->GetFrameMetrics().SetIsRootContent( 31 true); // make root APZC zoomable 32 } 33 34 void CreateScrollHandoffLayerTree2() { 35 const char* treeShape = "x(x(x))"; 36 LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100), 37 LayerIntRect(0, 0, 100, 100), 38 LayerIntRect(0, 50, 100, 50)}; 39 CreateScrollData(treeShape, layerVisibleRect); 40 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 41 CSSRect(0, 0, 200, 200)); 42 SetScrollableFrameMetrics(layers[1], 43 ScrollableLayerGuid::START_SCROLL_ID + 2, 44 CSSRect(-100, -100, 200, 200)); 45 SetScrollableFrameMetrics(layers[2], 46 ScrollableLayerGuid::START_SCROLL_ID + 1, 47 CSSRect(0, 0, 100, 100)); 48 SetScrollHandoff(layers[1], root); 49 SetScrollHandoff(layers[2], layers[1]); 50 // No ScopedLayerTreeRegistration as that just needs to be done once per 51 // test and this is the second layer tree for a particular test. 52 MOZ_ASSERT(registration); 53 UpdateHitTestingTree(); 54 rootApzc = ApzcOf(root); 55 } 56 57 void CreateScrollHandoffLayerTree3() { 58 const char* treeShape = "x(x(x)x(x))"; 59 LayerIntRect layerVisibleRect[] = { 60 LayerIntRect(0, 0, 100, 100), // root 61 LayerIntRect(0, 0, 100, 50), // scrolling parent 1 62 LayerIntRect(0, 0, 100, 50), // scrolling child 1 63 LayerIntRect(0, 50, 100, 50), // scrolling parent 2 64 LayerIntRect(0, 50, 100, 50) // scrolling child 2 65 }; 66 CreateScrollData(treeShape, layerVisibleRect); 67 SetScrollableFrameMetrics(layers[0], ScrollableLayerGuid::START_SCROLL_ID, 68 CSSRect(0, 0, 100, 100)); 69 SetScrollableFrameMetrics(layers[1], 70 ScrollableLayerGuid::START_SCROLL_ID + 1, 71 CSSRect(0, 0, 100, 100)); 72 SetScrollableFrameMetrics(layers[2], 73 ScrollableLayerGuid::START_SCROLL_ID + 2, 74 CSSRect(0, 0, 100, 100)); 75 SetScrollableFrameMetrics(layers[3], 76 ScrollableLayerGuid::START_SCROLL_ID + 3, 77 CSSRect(0, 50, 100, 100)); 78 SetScrollableFrameMetrics(layers[4], 79 ScrollableLayerGuid::START_SCROLL_ID + 4, 80 CSSRect(0, 50, 100, 100)); 81 SetScrollHandoff(layers[1], layers[0]); 82 SetScrollHandoff(layers[3], layers[0]); 83 SetScrollHandoff(layers[2], layers[1]); 84 SetScrollHandoff(layers[4], layers[3]); 85 registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 86 UpdateHitTestingTree(); 87 } 88 89 // Creates a layer tree with a parent layer that is only scrollable 90 // horizontally, and a child layer that is only scrollable vertically. 91 void CreateScrollHandoffLayerTree4() { 92 const char* treeShape = "x(x)"; 93 LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100), 94 LayerIntRect(0, 0, 100, 100)}; 95 CreateScrollData(treeShape, layerVisibleRect); 96 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 97 CSSRect(0, 0, 200, 100)); 98 SetScrollableFrameMetrics(layers[1], 99 ScrollableLayerGuid::START_SCROLL_ID + 1, 100 CSSRect(0, 0, 100, 200)); 101 SetScrollHandoff(layers[1], root); 102 registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 103 UpdateHitTestingTree(); 104 rootApzc = ApzcOf(root); 105 } 106 107 // Creates a layer tree with a parent layer that is not scrollable, and a 108 // child layer that is only scrollable vertically. 109 void CreateScrollHandoffLayerTree5() { 110 const char* treeShape = "x(x)"; 111 LayerIntRect layerVisibleRect[] = { 112 LayerIntRect(0, 0, 100, 100), // scrolling parent 113 LayerIntRect(0, 50, 100, 50) // scrolling child 114 }; 115 CreateScrollData(treeShape, layerVisibleRect); 116 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 117 CSSRect(0, 0, 100, 100)); 118 SetScrollableFrameMetrics(layers[1], 119 ScrollableLayerGuid::START_SCROLL_ID + 1, 120 CSSRect(0, 0, 100, 200)); 121 SetScrollHandoff(layers[1], root); 122 registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 123 UpdateHitTestingTree(); 124 rootApzc = ApzcOf(root); 125 } 126 127 // Creates a layer tree with a parent layer that is only scrollable 128 // vertically, and a child layer that is only scrollable horizontally. 129 void CreateScrollHandoffLayerTree6() { 130 const char* treeShape = "x(x)"; 131 LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100), 132 LayerIntRect(0, 0, 100, 1000)}; 133 CreateScrollData(treeShape, layerVisibleRect); 134 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 135 CSSRect(0, 0, 100, 1000)); 136 SetScrollableFrameMetrics(layers[1], 137 ScrollableLayerGuid::START_SCROLL_ID + 1, 138 CSSRect(0, 0, 200, 1000)); 139 SetScrollHandoff(layers[1], root); 140 registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 141 UpdateHitTestingTree(); 142 rootApzc = ApzcOf(root); 143 } 144 145 void TestCrossApzcAxisLock() { 146 SCOPED_GFX_PREF_INT("apz.axis_lock.mode", 1); 147 148 CreateScrollHandoffLayerTree1(); 149 150 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 151 Pan(childApzc, ScreenIntPoint(10, 60), ScreenIntPoint(15, 90), 152 PanOptions::KeepFingerDown | PanOptions::ExactCoordinates); 153 154 childApzc->AssertAxisLocked(ScrollDirection::eVertical); 155 childApzc->AssertStateIsPanningLockedY(); 156 } 157 }; 158 159 class APZScrollHandoffTesterMock : public APZScrollHandoffTester { 160 public: 161 APZScrollHandoffTesterMock() { CreateMockHitTester(); } 162 }; 163 164 class APZCNestedFlingScrollHandoffTester : public APZCTreeManagerTester { 165 protected: 166 void SetUp() { 167 APZCTreeManagerTester::SetUp(); 168 const char* treeShape = "x(x)"; 169 LayerIntRect layerVisibleRect[] = { 170 LayerIntRect(0, 0, 800, 1000), 171 LayerIntRect(0, 0, 100, 100), 172 }; 173 174 CreateScrollData(treeShape, layerVisibleRect); 175 176 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, 177 CSSRect(0, 0, 800, 50000)); 178 SetScrollableFrameMetrics(layers[1], 179 ScrollableLayerGuid::START_SCROLL_ID + 1, 180 CSSRect(0, 0, 800, 100)); 181 182 SetScrollHandoff(layers[1], root); 183 184 // Scroll somewhere into the middle of the scroll range, so that we have 185 // lots of space to scroll in both directions. 186 ModifyFrameMetrics(root, [](ScrollMetadata& aSm, FrameMetrics& aMetrics) { 187 aMetrics.SetVisualScrollUpdateType( 188 FrameMetrics::ScrollOffsetUpdateType::eMainThread); 189 aMetrics.SetVisualDestination(CSSPoint(0, 25000)); 190 }); 191 192 registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc); 193 UpdateHitTestingTree(); 194 195 subframeApzc = ApzcOf(layers[1]); 196 } 197 198 void ExecuteDirectionChangingPanGesture( 199 const ScreenIntPoint& aStartPoint, 200 std::initializer_list<int32_t> aXDeltas, 201 std::initializer_list<int32_t> aYDeltas) { 202 APZEventResult result = TouchDown(subframeApzc, aStartPoint, mcc->Time()); 203 204 // Allowed touch behaviours must be set after sending touch-start. 205 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 206 SetDefaultAllowedTouchBehavior(subframeApzc, result.mInputBlockId); 207 } 208 209 const TimeDuration kTouchTimeDelta100Hz = 210 TimeDuration::FromMilliseconds(10); 211 212 ScreenIntPoint currentLocation = aStartPoint; 213 for (int32_t delta : aXDeltas) { 214 mcc->AdvanceBy(kTouchTimeDelta100Hz); 215 if (delta != 0) { 216 currentLocation.x += delta; 217 (void)TouchMove(subframeApzc, currentLocation, mcc->Time()); 218 } 219 } 220 221 ExecuteWait(TimeDuration::FromMilliseconds(255)); 222 223 for (int32_t delta : aYDeltas) { 224 mcc->AdvanceBy(kTouchTimeDelta100Hz); 225 if (delta != 0) { 226 currentLocation.y += delta; 227 (void)TouchMove(subframeApzc, currentLocation, mcc->Time()); 228 } 229 } 230 231 (void)TouchUp(subframeApzc, currentLocation, mcc->Time()); 232 } 233 234 void ExecuteWait(const TimeDuration& aDuration) { 235 TimeDuration remaining = aDuration; 236 const TimeDuration TIME_BETWEEN_FRAMES = 237 TimeDuration::FromSeconds(1) / int64_t(60); 238 while (remaining.ToMilliseconds() > 0) { 239 mcc->AdvanceBy(TIME_BETWEEN_FRAMES); 240 subframeApzc->AdvanceAnimations(mcc->GetSampleTime()); 241 remaining -= TIME_BETWEEN_FRAMES; 242 } 243 } 244 245 RefPtr<TestAsyncPanZoomController> subframeApzc; 246 UniquePtr<ScopedLayerTreeRegistration> registration; 247 }; 248 #ifndef MOZ_WIDGET_ANDROID // Currently fails on Android 249 // Here we test that if the processing of a touch block is deferred while we 250 // wait for content to send a prevent-default message, overscroll is still 251 // handed off correctly when the block is processed. 252 TEST_F(APZScrollHandoffTester, DeferredInputEventProcessing) { 253 SCOPED_GFX_PREF_BOOL("apz.allow_immediate_handoff", true); 254 255 // Set up the APZC tree. 256 CreateScrollHandoffLayerTree1(); 257 258 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 259 260 // Enable touch-listeners so that we can separate the queueing of input 261 // events from them being processed. 262 childApzc->SetWaitForMainThread(); 263 264 // Queue input events for a pan. 265 uint64_t blockId = 0; 266 Pan(childApzc, 90, 30, PanOptions::NoFling, nullptr, nullptr, &blockId); 267 268 // Allow the pan to be processed. 269 childApzc->ContentReceivedInputBlock(blockId, false); 270 childApzc->ConfirmTarget(blockId); 271 272 // Make sure overscroll was handed off correctly. 273 EXPECT_EQ(50, childApzc->GetFrameMetrics().GetVisualScrollOffset().y); 274 EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetVisualScrollOffset().y); 275 } 276 #endif 277 278 #ifndef MOZ_WIDGET_ANDROID // Currently fails on Android 279 // Here we test that if the layer structure changes in between two input 280 // blocks being queued, and the first block is only processed after the second 281 // one has been queued, overscroll handoff for the first block follows 282 // the original layer structure while overscroll handoff for the second block 283 // follows the new layer structure. 284 TEST_F(APZScrollHandoffTester, LayerStructureChangesWhileEventsArePending) { 285 SCOPED_GFX_PREF_BOOL("apz.allow_immediate_handoff", true); 286 287 // Set up an initial APZC tree. 288 CreateScrollHandoffLayerTree1(); 289 290 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 291 292 // Enable touch-listeners so that we can separate the queueing of input 293 // events from them being processed. 294 childApzc->SetWaitForMainThread(); 295 296 // Queue input events for a pan. 297 uint64_t blockId = 0; 298 Pan(childApzc, 90, 30, PanOptions::NoFling, nullptr, nullptr, &blockId); 299 300 // Modify the APZC tree to insert a new APZC 'middle' into the handoff chain 301 // between the child and the root. 302 CreateScrollHandoffLayerTree2(); 303 WebRenderLayerScrollData* middle = layers[1]; 304 childApzc->SetWaitForMainThread(); 305 TestAsyncPanZoomController* middleApzc = ApzcOf(middle); 306 307 // Queue input events for another pan. 308 uint64_t secondBlockId = 0; 309 Pan(childApzc, 30, 90, PanOptions::NoFling, nullptr, nullptr, &secondBlockId); 310 311 // Allow the first pan to be processed. 312 childApzc->ContentReceivedInputBlock(blockId, false); 313 childApzc->ConfirmTarget(blockId); 314 315 // Make sure things have scrolled according to the handoff chain in 316 // place at the time the touch-start of the first pan was queued. 317 EXPECT_EQ(50, childApzc->GetFrameMetrics().GetVisualScrollOffset().y); 318 EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetVisualScrollOffset().y); 319 EXPECT_EQ(0, middleApzc->GetFrameMetrics().GetVisualScrollOffset().y); 320 321 // Allow the second pan to be processed. 322 childApzc->ContentReceivedInputBlock(secondBlockId, false); 323 childApzc->ConfirmTarget(secondBlockId); 324 325 // Make sure things have scrolled according to the handoff chain in 326 // place at the time the touch-start of the second pan was queued. 327 EXPECT_EQ(0, childApzc->GetFrameMetrics().GetVisualScrollOffset().y); 328 EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetVisualScrollOffset().y); 329 EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetVisualScrollOffset().y); 330 } 331 #endif 332 333 #ifndef MOZ_WIDGET_ANDROID // Currently fails on Android 334 // Test that putting a second finger down on an APZC while a down-chain APZC 335 // is overscrolled doesn't result in being stuck in overscroll. 336 TEST_F(APZScrollHandoffTesterMock, StuckInOverscroll_Bug1073250) { 337 // Enable overscrolling. 338 SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled", true); 339 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 340 341 CreateScrollHandoffLayerTree1(); 342 343 TestAsyncPanZoomController* child = ApzcOf(layers[1]); 344 345 // Pan, causing the parent APZC to overscroll. 346 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 347 Pan(manager, 10, 40, PanOptions::KeepFingerDown); 348 EXPECT_FALSE(child->IsOverscrolled()); 349 EXPECT_TRUE(rootApzc->IsOverscrolled()); 350 351 // Put a second finger down. 352 MultiTouchInput secondFingerDown = 353 CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); 354 // Use the same touch identifier for the first touch (0) as Pan(). (A bit 355 // hacky.) 356 secondFingerDown.mTouches.AppendElement( 357 SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0)); 358 secondFingerDown.mTouches.AppendElement( 359 SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0)); 360 manager->ReceiveInputEvent(secondFingerDown); 361 362 // Release the fingers. 363 MultiTouchInput fingersUp = secondFingerDown; 364 fingersUp.mType = MultiTouchInput::MULTITOUCH_END; 365 manager->ReceiveInputEvent(fingersUp); 366 367 // Allow any animations to run their course. 368 child->AdvanceAnimationsUntilEnd(); 369 rootApzc->AdvanceAnimationsUntilEnd(); 370 371 // Make sure nothing is overscrolled. 372 EXPECT_FALSE(child->IsOverscrolled()); 373 EXPECT_FALSE(rootApzc->IsOverscrolled()); 374 } 375 #endif 376 377 #ifndef MOZ_WIDGET_ANDROID // Currently fails on Android 378 // This is almost exactly like StuckInOverscroll_Bug1073250, except the 379 // APZC receiving the input events for the first touch block is the child 380 // (and thus not the same APZC that overscrolls, which is the parent). 381 TEST_F(APZScrollHandoffTesterMock, StuckInOverscroll_Bug1231228) { 382 // Enable overscrolling. 383 SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled", true); 384 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 385 386 CreateScrollHandoffLayerTree1(); 387 388 TestAsyncPanZoomController* child = ApzcOf(layers[1]); 389 390 // Pan, causing the parent APZC to overscroll. 391 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 392 Pan(manager, 60, 90, PanOptions::KeepFingerDown); 393 EXPECT_FALSE(child->IsOverscrolled()); 394 EXPECT_TRUE(rootApzc->IsOverscrolled()); 395 396 // Put a second finger down. 397 MultiTouchInput secondFingerDown = 398 CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); 399 // Use the same touch identifier for the first touch (0) as Pan(). (A bit 400 // hacky.) 401 secondFingerDown.mTouches.AppendElement( 402 SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0)); 403 secondFingerDown.mTouches.AppendElement( 404 SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0)); 405 manager->ReceiveInputEvent(secondFingerDown); 406 407 // Release the fingers. 408 MultiTouchInput fingersUp = secondFingerDown; 409 fingersUp.mType = MultiTouchInput::MULTITOUCH_END; 410 manager->ReceiveInputEvent(fingersUp); 411 412 // Allow any animations to run their course. 413 child->AdvanceAnimationsUntilEnd(); 414 rootApzc->AdvanceAnimationsUntilEnd(); 415 416 // Make sure nothing is overscrolled. 417 EXPECT_FALSE(child->IsOverscrolled()); 418 EXPECT_FALSE(rootApzc->IsOverscrolled()); 419 } 420 #endif 421 422 #ifndef MOZ_WIDGET_ANDROID // Currently fails on Android 423 TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202a) { 424 // Enable overscrolling. 425 SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled", true); 426 427 CreateScrollHandoffLayerTree1(); 428 429 TestAsyncPanZoomController* child = ApzcOf(layers[1]); 430 431 // Pan, causing the parent APZC to overscroll. 432 Pan(manager, 60, 90, PanOptions::KeepFingerDown); 433 EXPECT_FALSE(child->IsOverscrolled()); 434 EXPECT_TRUE(rootApzc->IsOverscrolled()); 435 436 // Lift the finger, triggering an overscroll animation 437 // (but don't allow it to run). 438 TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time()); 439 440 // Put the finger down again, interrupting the animation 441 // and entering the TOUCHING state. 442 TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time()); 443 444 // Lift the finger once again. 445 TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time()); 446 447 // Allow any animations to run their course. 448 child->AdvanceAnimationsUntilEnd(); 449 rootApzc->AdvanceAnimationsUntilEnd(); 450 451 // Make sure nothing is overscrolled. 452 EXPECT_FALSE(child->IsOverscrolled()); 453 EXPECT_FALSE(rootApzc->IsOverscrolled()); 454 } 455 #endif 456 457 #ifndef MOZ_WIDGET_ANDROID // Currently fails on Android 458 TEST_F(APZScrollHandoffTesterMock, StuckInOverscroll_Bug1240202b) { 459 // Enable overscrolling. 460 SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled", true); 461 462 CreateScrollHandoffLayerTree1(); 463 464 TestAsyncPanZoomController* child = ApzcOf(layers[1]); 465 466 // Pan, causing the parent APZC to overscroll. 467 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 468 Pan(manager, 60, 90, PanOptions::KeepFingerDown); 469 EXPECT_FALSE(child->IsOverscrolled()); 470 EXPECT_TRUE(rootApzc->IsOverscrolled()); 471 472 // Lift the finger, triggering an overscroll animation 473 // (but don't allow it to run). 474 TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time()); 475 476 // Put the finger down again, interrupting the animation 477 // and entering the TOUCHING state. 478 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 479 TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time()); 480 481 // Put a second finger down. Since we're in the TOUCHING state, 482 // the "are we panned into overscroll" check will fail and we 483 // will not ignore the second finger, instead entering the 484 // PINCHING state. 485 MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, 486 mcc->Time(), 0); 487 // Use the same touch identifier for the first touch (0) as TouchDown(). (A 488 // bit hacky.) 489 secondFingerDown.mTouches.AppendElement( 490 SingleTouchData(0, ScreenIntPoint(10, 90), ScreenSize(0, 0), 0, 0)); 491 secondFingerDown.mTouches.AppendElement( 492 SingleTouchData(1, ScreenIntPoint(10, 80), ScreenSize(0, 0), 0, 0)); 493 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 494 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 495 manager->ReceiveInputEvent(secondFingerDown); 496 497 // Release the fingers. 498 MultiTouchInput fingersUp = secondFingerDown; 499 fingersUp.mType = MultiTouchInput::MULTITOUCH_END; 500 manager->ReceiveInputEvent(fingersUp); 501 502 // Allow any animations to run their course. 503 child->AdvanceAnimationsUntilEnd(); 504 rootApzc->AdvanceAnimationsUntilEnd(); 505 506 // Make sure nothing is overscrolled. 507 EXPECT_FALSE(child->IsOverscrolled()); 508 EXPECT_FALSE(rootApzc->IsOverscrolled()); 509 } 510 #endif 511 512 #ifndef MOZ_WIDGET_ANDROID // Currently fails on Android 513 TEST_F(APZScrollHandoffTester, OpposingConstrainedAxes_Bug1201098) { 514 // Enable overscrolling. 515 SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled", true); 516 517 CreateScrollHandoffLayerTree4(); 518 519 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 520 521 // Pan, causing the child APZC to overscroll. 522 Pan(childApzc, 50, 60); 523 524 // Make sure only the child is overscrolled. 525 EXPECT_TRUE(childApzc->IsOverscrolled()); 526 EXPECT_FALSE(rootApzc->IsOverscrolled()); 527 } 528 #endif 529 530 // Test the behaviour when flinging diagonally and there is room to scroll in 531 // one direction but not the other. In the direction where there is room to 532 // scroll, the fling should continue. In the direction where there is no room 533 // to scroll, the fling should stop without being handed off to the parent. 534 TEST_F(APZScrollHandoffTesterMock, DiagonalFlingNoHandoff) { 535 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 536 537 CreateScrollHandoffLayerTree1(); 538 539 // Fling up and to the left. The child APZC has room to scroll up, but not 540 // to the left. 541 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 542 Pan(manager, ScreenIntPoint(90, 90), ScreenIntPoint(55, 55)); 543 544 RefPtr<TestAsyncPanZoomController> parent = ApzcOf(layers[0]); 545 RefPtr<TestAsyncPanZoomController> child = ApzcOf(layers[1]); 546 547 // Advance the child's fling animation once to give any potential handoff 548 // a chance to occur. 549 mcc->AdvanceByMillis(10); 550 child->AdvanceAnimations(mcc->GetSampleTime()); 551 552 // Assert that the child is still flinging but the parent is not (no handoff 553 // occurred). 554 child->AssertStateIsFling(); 555 parent->AssertStateIsReset(); 556 } 557 558 // Here we test that if two flings are happening simultaneously, overscroll 559 // is handed off correctly for each. 560 TEST_F(APZScrollHandoffTester, SimultaneousFlings) { 561 SCOPED_GFX_PREF_BOOL("apz.allow_immediate_handoff", true); 562 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 563 564 // Set up an initial APZC tree. 565 CreateScrollHandoffLayerTree3(); 566 567 RefPtr<TestAsyncPanZoomController> parent1 = ApzcOf(layers[1]); 568 RefPtr<TestAsyncPanZoomController> child1 = ApzcOf(layers[2]); 569 RefPtr<TestAsyncPanZoomController> parent2 = ApzcOf(layers[3]); 570 RefPtr<TestAsyncPanZoomController> child2 = ApzcOf(layers[4]); 571 572 // Pan on the lower child. 573 Pan(child2, 45, 5); 574 575 // Pan on the upper child. 576 Pan(child1, 95, 55); 577 578 // Check that child1 and child2 are in a FLING state. 579 child1->AssertStateIsFling(); 580 child2->AssertStateIsFling(); 581 582 // Advance the animations on child1 and child2 until their end. 583 child1->AdvanceAnimationsUntilEnd(); 584 child2->AdvanceAnimationsUntilEnd(); 585 586 // Check that the flings have been handed off to the parents. 587 child1->AssertStateIsReset(); 588 parent1->AssertStateIsFling(); 589 child2->AssertStateIsReset(); 590 parent2->AssertStateIsFling(); 591 } 592 593 TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Pan) { 594 SCOPED_GFX_PREF_BOOL("apz.allow_immediate_handoff", false); 595 596 CreateScrollHandoffLayerTree1(); 597 598 RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(layers[0]); 599 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 600 601 // Pan on the child, enough to scroll it to its end and have scroll 602 // left to hand off. Since immediate handoff is disallowed, we expect 603 // the leftover scroll not to be handed off. 604 Pan(childApzc, 60, 5); 605 606 // Verify that the parent has not scrolled. 607 EXPECT_EQ(50, childApzc->GetFrameMetrics().GetVisualScrollOffset().y); 608 EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetVisualScrollOffset().y); 609 610 // Pan again on the child. This time, since the child was scrolled to 611 // its end when the gesture began, we expect the scroll to be handed off. 612 Pan(childApzc, 60, 50); 613 614 // Verify that the parent scrolled. 615 EXPECT_EQ(10, parentApzc->GetFrameMetrics().GetVisualScrollOffset().y); 616 } 617 618 TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Fling) { 619 SCOPED_GFX_PREF_BOOL("apz.allow_immediate_handoff", false); 620 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 621 622 CreateScrollHandoffLayerTree1(); 623 624 RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(layers[0]); 625 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 626 627 // Pan on the child, enough to get very close to the end, so that the 628 // subsequent fling reaches the end and has leftover velocity to hand off. 629 Pan(childApzc, 60, 2); 630 631 // Allow the fling to run its course. 632 childApzc->AdvanceAnimationsUntilEnd(); 633 parentApzc->AdvanceAnimationsUntilEnd(); 634 635 // Verify that the parent has not scrolled. 636 // The first comparison needs to be an ASSERT_NEAR because the fling 637 // computations are such that the final scroll position can be within 638 // COORDINATE_EPSILON of the end rather than right at the end. 639 ASSERT_NEAR(50, childApzc->GetFrameMetrics().GetVisualScrollOffset().y, 640 COORDINATE_EPSILON); 641 EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetVisualScrollOffset().y); 642 643 // Pan again on the child. This time, since the child was scrolled to 644 // its end when the gesture began, we expect the scroll to be handed off. 645 Pan(childApzc, 60, 40); 646 647 // Allow the fling to run its course. The fling should also be handed off. 648 childApzc->AdvanceAnimationsUntilEnd(); 649 parentApzc->AdvanceAnimationsUntilEnd(); 650 651 // Verify that the parent scrolled from the fling. 652 EXPECT_GT(parentApzc->GetFrameMetrics().GetVisualScrollOffset().y, 10); 653 } 654 655 TEST_F(APZScrollHandoffTester, CrossApzcAxisLock_TouchAction) { 656 TestCrossApzcAxisLock(); 657 } 658 659 TEST_F(APZScrollHandoffTesterMock, WheelHandoffAfterDirectionReversal) { 660 // Explicitly set the wheel transaction timeout pref because the test relies 661 // on its value. 662 SCOPED_GFX_PREF_INT("mousewheel.transaction.timeout", 1500); 663 664 // Set up a basic scroll handoff layer tree. 665 CreateScrollHandoffLayerTree1(); 666 667 rootApzc = ApzcOf(layers[0]); 668 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 669 FrameMetrics& rootMetrics = rootApzc->GetFrameMetrics(); 670 FrameMetrics& childMetrics = childApzc->GetFrameMetrics(); 671 CSSRect childScrollRange = childMetrics.CalculateScrollRange(); 672 673 EXPECT_EQ(0, rootMetrics.GetVisualScrollOffset().y); 674 EXPECT_EQ(0, childMetrics.GetVisualScrollOffset().y); 675 676 ScreenIntPoint cursorLocation(10, 60); // positioned to hit the subframe 677 ScreenPoint upwardDelta(0, -10); 678 ScreenPoint downwardDelta(0, 10); 679 680 // First wheel upwards. This will have no effect because we're already 681 // scrolled to the top. 682 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 683 Wheel(manager, cursorLocation, upwardDelta, mcc->Time()); 684 EXPECT_EQ(0, rootMetrics.GetVisualScrollOffset().y); 685 EXPECT_EQ(0, childMetrics.GetVisualScrollOffset().y); 686 687 // Now wheel downwards 6 times. This should scroll the child, and get it 688 // to the bottom of its 50px scroll range. 689 for (size_t i = 0; i < 6; ++i) { 690 mcc->AdvanceByMillis(100); 691 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 692 Wheel(manager, cursorLocation, downwardDelta, mcc->Time()); 693 } 694 EXPECT_EQ(0, rootMetrics.GetVisualScrollOffset().y); 695 EXPECT_EQ(childScrollRange.YMost(), childMetrics.GetVisualScrollOffset().y); 696 697 // Wheel downwards an additional 16 times, with 100ms increments. 698 // This should be enough to overcome the 1500ms wheel transaction timeout 699 // and start scrolling the root. 700 for (size_t i = 0; i < 16; ++i) { 701 mcc->AdvanceByMillis(100); 702 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 703 Wheel(manager, cursorLocation, downwardDelta, mcc->Time()); 704 } 705 EXPECT_EQ(childScrollRange.YMost(), childMetrics.GetVisualScrollOffset().y); 706 EXPECT_GT(rootMetrics.GetVisualScrollOffset().y, 0); 707 } 708 709 TEST_F(APZScrollHandoffTesterMock, WheelHandoffNonscrollable) { 710 // Set up a basic scroll layer tree. 711 CreateScrollHandoffLayerTree5(); 712 713 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 714 FrameMetrics& childMetrics = childApzc->GetFrameMetrics(); 715 716 EXPECT_EQ(0, childMetrics.GetVisualScrollOffset().y); 717 718 ScreenPoint downwardDelta(0, 10); 719 // Positioned to hit the nonscrollable parent frame 720 ScreenIntPoint nonscrollableLocation(40, 10); 721 // Positioned to hit the scrollable subframe 722 ScreenIntPoint scrollableLocation(40, 60); 723 724 // Start the wheel transaction on a nonscrollable parent frame. 725 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); 726 Wheel(manager, nonscrollableLocation, downwardDelta, mcc->Time()); 727 EXPECT_EQ(0, childMetrics.GetVisualScrollOffset().y); 728 729 // Mouse moves to a scrollable subframe. This should end the transaction. 730 mcc->AdvanceByMillis(100); 731 MouseInput mouseInput(MouseInput::MOUSE_MOVE, 732 MouseInput::ButtonType::PRIMARY_BUTTON, 0, 0, 733 scrollableLocation, mcc->Time(), 0); 734 WidgetMouseEvent mouseEvent = 735 mouseInput.ToWidgetEvent<WidgetMouseEvent>(nullptr); 736 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 737 ((APZInputBridge*)manager.get())->ReceiveInputEvent(mouseEvent); 738 739 // Wheel downward should scroll the subframe. 740 mcc->AdvanceByMillis(100); 741 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 742 Wheel(manager, scrollableLocation, downwardDelta, mcc->Time()); 743 EXPECT_GT(childMetrics.GetVisualScrollOffset().y, 0); 744 } 745 746 TEST_F(APZScrollHandoffTesterMock, ChildCloseToEndOfScrollRange) { 747 SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled", true); 748 749 CreateScrollHandoffLayerTree1(); 750 751 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 752 753 FrameMetrics& rootMetrics = rootApzc->GetFrameMetrics(); 754 FrameMetrics& childMetrics = childApzc->GetFrameMetrics(); 755 756 // Zoom the page in by 3x. This needs to be reflected in the zoom level 757 // and composition bounds of both APZCs. 758 rootMetrics.SetZoom(CSSToParentLayerScale(3.0)); 759 rootMetrics.SetCompositionBounds(ParentLayerRect(0, 0, 300, 300)); 760 childMetrics.SetZoom(CSSToParentLayerScale(3.0)); 761 childMetrics.SetCompositionBounds(ParentLayerRect(0, 150, 300, 150)); 762 763 // Scroll the child APZC very close to the end of the scroll range. 764 // The scroll offset is chosen such that in CSS pixels it has 0.01 pixels 765 // room to scroll (less than COORDINATE_EPSILON = 0.02), but in ParentLayer 766 // pixels it has 0.03 pixels room (greater than COORDINATE_EPSILON). 767 childMetrics.SetVisualScrollOffset(CSSPoint(0, 49.99)); 768 769 EXPECT_FALSE(childApzc->IsOverscrolled()); 770 771 CSSPoint childBefore = childApzc->GetFrameMetrics().GetVisualScrollOffset(); 772 CSSPoint parentBefore = rootApzc->GetFrameMetrics().GetVisualScrollOffset(); 773 774 // Synthesize a pan gesture that tries to scroll the child further down. 775 PanGesture(PanGestureInput::PANGESTURE_START, childApzc, 776 ScreenIntPoint(10, 20), ScreenPoint(0, 40), mcc->Time()); 777 mcc->AdvanceByMillis(5); 778 childApzc->AdvanceAnimations(mcc->GetSampleTime()); 779 780 PanGesture(PanGestureInput::PANGESTURE_END, childApzc, ScreenIntPoint(10, 21), 781 ScreenPoint(0, 0), mcc->Time()); 782 783 CSSPoint childAfter = childApzc->GetFrameMetrics().GetVisualScrollOffset(); 784 CSSPoint parentAfter = rootApzc->GetFrameMetrics().GetVisualScrollOffset(); 785 786 bool childScrolled = (childBefore != childAfter); 787 bool parentScrolled = (parentBefore != parentAfter); 788 789 // Check that either the child or the parent scrolled. 790 // (With the current implementation of comparing quantities to 791 // COORDINATE_EPSILON in CSS units, it will be the parent, but the important 792 // thing is that at least one of the child or parent scroll, i.e. we're not 793 // stuck in a situation where no scroll offset is changing). 794 EXPECT_TRUE(childScrolled || parentScrolled); 795 } 796 797 TEST_F(APZScrollHandoffTesterMock, ScrollJump_Bug1812227) { 798 // Set the touch start tolerance to 10 pixels. 799 SCOPED_GFX_PREF_FLOAT("apz.touch_start_tolerance", 10 / manager->GetDPI()); 800 801 CreateScrollHandoffLayerTree6(); 802 RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); 803 804 // Throughout the test, we record the composited vertical scroll position 805 // of the root scroll frame after every event or animation frame. 806 std::vector<CSSCoord> rootYScrollPositions; 807 auto SampleScrollPosition = [&]() { 808 rootYScrollPositions.push_back( 809 rootApzc->GetFrameMetrics().GetVisualScrollOffset().y); 810 }; 811 812 // Helper function to perform a light upward flick (finger moves upward 813 // ==> page will scroll downward). 814 auto DoLightUpwardFlick = [&](bool aSimulatePaint = false) { 815 // Don't use Pan() because it decreases the touch start tolerance 816 // to almost zero, and here we want to test a codepath related to 817 // the touch start tolerance. 818 819 mcc->AdvanceByMillis(16); 820 QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); 821 TouchDown(manager, {30, 30}, mcc->Time()); 822 SampleScrollPosition(); 823 824 // If aSimulatePaint=true, simulate a main-thread paint arriving in between 825 // the touch-down (when the input block is created and the cached value 826 // InputBlockState::mTransformToApzc is set) and the first touch-move which 827 // overcomes the touch-tolerance threshold and synthesizes an additional 828 // touch-move event at the threshold. The paint has the effect of resetting 829 // transform to the APZC to zero. The bug occurs if the synthesized 830 // touch-move event incorrectly uses the up-to-date transform to the APZC 831 // rather than the value cached in InputBlockState::mTrasnformToApzc. 832 if (aSimulatePaint) { 833 // For simplicity, simulate a paint with the latest metrics stored on the 834 // APZC. In practice, what would be painted would be from a frame or two 835 // ago, but for reproducing this bug it does not matter. 836 ModifyFrameMetrics(root, [&](ScrollMetadata&, FrameMetrics& aMetrics) { 837 aMetrics = rootApzc->GetFrameMetrics(); 838 }); 839 ModifyFrameMetrics(layers[1], 840 [&](ScrollMetadata&, FrameMetrics& aMetrics) { 841 aMetrics = childApzc->GetFrameMetrics(); 842 }); 843 UpdateHitTestingTree(); 844 } 845 846 mcc->AdvanceByMillis(16); 847 TouchMove(manager, {30, 10}, mcc->Time()); 848 SampleScrollPosition(); 849 850 mcc->AdvanceByMillis(16); 851 TouchUp(manager, {30, 10}, mcc->Time()); 852 SampleScrollPosition(); 853 854 // The root APZC should be flinging. 855 rootApzc->AssertStateIsFling(); 856 }; 857 858 // Peform one flick. 859 DoLightUpwardFlick(); 860 861 // Sample the resulting fling partway. Testing shows it goes well past 862 // y=100, so sample it until y=100. 863 while (SampleAnimationsOnce() && rootYScrollPositions.back() < 100) { 864 SampleScrollPosition(); 865 } 866 867 // Perform a second flick, this time simulating a paint in between 868 // the touch-start and touch-move. 869 DoLightUpwardFlick(true); 870 871 // Sample the fling until its completion. 872 while (SampleAnimationsOnce()) { 873 SampleScrollPosition(); 874 } 875 876 // Check that the vertical root scroll position is non-decreasing 877 // throughout the course of the test, i.e. it never jumps back up. 878 for (size_t i = 0; i < (rootYScrollPositions.size() - 1); ++i) { 879 CSSCoord before = rootYScrollPositions[i]; 880 CSSCoord after = rootYScrollPositions[i + 1]; 881 EXPECT_LE(before, after); 882 } 883 } 884 885 TEST_F(APZCNestedFlingScrollHandoffTester, FlingInOppositeDirection) { 886 RefPtr<TestAsyncPanZoomController> rootApzc = ApzcOf(root); 887 888 ParentLayerPoint startRootOffset = rootApzc->GetCurrentAsyncScrollOffset( 889 AsyncTransformConsumer::eForEventHandling); 890 ExecuteDirectionChangingPanGesture( 891 ScreenIntPoint{569, 710}, 892 {-11, -2, -107, -18, -148, -57, -133, -159, -21}, {11, 2, 42, 107, 148}); 893 // Let any animation start and run for a few frames 894 ExecuteWait(TimeDuration::FromMilliseconds(154)); 895 auto vel = subframeApzc->GetVelocityVector(); 896 897 ParentLayerPoint endRootOffset = rootApzc->GetCurrentAsyncScrollOffset( 898 AsyncTransformConsumer::eForEventHandling); 899 900 EXPECT_EQ(vel.y, 0.0); 901 rootApzc->AssertStateIsReset(); 902 subframeApzc->AssertStateIsReset(); 903 EXPECT_EQ(startRootOffset.y, endRootOffset.y); 904 }