TestBasic.cpp (44229B)
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 "APZTestCommon.h" 9 10 #include "InputUtils.h" 11 #include "mozilla/ScrollPositionUpdate.h" 12 #include "mozilla/layers/ScrollableLayerGuid.h" 13 14 using LayersUpdateFlags = AsyncPanZoomController::LayersUpdateFlags; 15 16 TEST_F(APZCBasicTester, Overzoom) { 17 // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100 18 FrameMetrics fm; 19 fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100)); 20 fm.SetScrollableRect(CSSRect(0, 0, 125, 150)); 21 fm.SetVisualScrollOffset(CSSPoint(10, 0)); 22 fm.SetZoom(CSSToParentLayerScale(1.0)); 23 fm.SetIsRootContent(true); 24 apzc->SetFrameMetrics(fm); 25 26 MakeApzcZoomable(); 27 28 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); 29 30 PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(50, 50), 0.5, true); 31 32 fm = apzc->GetFrameMetrics(); 33 EXPECT_EQ(0.8f, fm.GetZoom().scale); 34 // bug 936721 - PGO builds introduce rounding error so 35 // use a fuzzy match instead 36 EXPECT_LT(std::abs(fm.GetVisualScrollOffset().x), 1e-5); 37 EXPECT_LT(std::abs(fm.GetVisualScrollOffset().y), 1e-5); 38 } 39 40 TEST_F(APZCBasicTester, ZoomLimits) { 41 SCOPED_GFX_PREF_FLOAT("apz.min_zoom", 0.9f); 42 SCOPED_GFX_PREF_FLOAT("apz.max_zoom", 2.0f); 43 44 // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100 45 FrameMetrics fm; 46 fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100)); 47 fm.SetScrollableRect(CSSRect(0, 0, 125, 150)); 48 fm.SetZoom(CSSToParentLayerScale(1.0)); 49 fm.SetIsRootContent(true); 50 apzc->SetFrameMetrics(fm); 51 52 MakeApzcZoomable(); 53 54 // This should take the zoom scale to 0.8, but we've capped it at 0.9. 55 PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(50, 50), 0.5, true); 56 57 fm = apzc->GetFrameMetrics(); 58 EXPECT_EQ(0.9f, fm.GetZoom().scale); 59 60 // This should take the zoom scale to 2.7, but we've capped it at 2. 61 PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(50, 50), 3, true); 62 63 fm = apzc->GetFrameMetrics(); 64 EXPECT_EQ(2.0f, fm.GetZoom().scale); 65 } 66 67 TEST_F(APZCBasicTester, SimpleTransform) { 68 ParentLayerPoint pointOut; 69 AsyncTransform viewTransformOut; 70 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 71 72 EXPECT_EQ(ParentLayerPoint(), pointOut); 73 EXPECT_EQ(AsyncTransform(), viewTransformOut); 74 } 75 76 TEST_F(APZCBasicTester, ComplexTransform) { 77 // This test assumes there is a page that gets rendered to 78 // two layers. In CSS pixels, the first layer is 50x50 and 79 // the second layer is 25x50. The widget scale factor is 3.0 80 // and the presShell resolution is 2.0. Therefore, these layers 81 // end up being 300x300 and 150x300 in layer pixels. 82 // 83 // The second (child) layer has an additional CSS transform that 84 // stretches it by 2.0 on the x-axis. Therefore, after applying 85 // CSS transforms, the two layers are the same size in screen 86 // pixels. 87 // 88 // The screen itself is 24x24 in screen pixels (therefore 4x4 in 89 // CSS pixels). The displayport is 1 extra CSS pixel on all 90 // sides. 91 92 RefPtr<TestAsyncPanZoomController> childApzc = 93 new TestAsyncPanZoomController(LayersId{0}, mcc, tm); 94 95 const char* treeShape = "x(x)"; 96 // LayerID 0 1 97 LayerIntRect layerVisibleRect[] = { 98 LayerIntRect(0, 0, 300, 300), 99 LayerIntRect(0, 0, 150, 300), 100 }; 101 Matrix4x4 transforms[] = { 102 Matrix4x4(), 103 Matrix4x4(), 104 }; 105 transforms[0].PostScale( 106 0.5f, 0.5f, 107 1.0f); // this results from the 2.0 resolution on the root layer 108 transforms[1].PostScale( 109 2.0f, 1.0f, 110 1.0f); // this is the 2.0 x-axis CSS transform on the child layer 111 112 auto layers = TestWRScrollData::Create(treeShape, *updater, layerVisibleRect, 113 transforms); 114 115 ScrollMetadata metadata; 116 FrameMetrics& metrics = metadata.GetMetrics(); 117 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 24, 24)); 118 metrics.SetDisplayPort(CSSRect(-1, -1, 6, 6)); 119 metrics.SetVisualScrollOffset(CSSPoint(10, 10)); 120 metrics.SetLayoutViewport(CSSRect(10, 10, 8, 8)); 121 metrics.SetScrollableRect(CSSRect(0, 0, 50, 50)); 122 metrics.SetCumulativeResolution(LayoutDeviceToLayerScale(2)); 123 metrics.SetPresShellResolution(2.0f); 124 metrics.SetZoom(CSSToParentLayerScale(6)); 125 metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(3)); 126 metrics.SetScrollId(ScrollableLayerGuid::START_SCROLL_ID); 127 128 ScrollMetadata childMetadata = metadata; 129 FrameMetrics& childMetrics = childMetadata.GetMetrics(); 130 childMetrics.SetScrollId(ScrollableLayerGuid::START_SCROLL_ID + 1); 131 132 layers[0]->AppendScrollMetadata(layers, metadata); 133 layers[1]->AppendScrollMetadata(layers, childMetadata); 134 135 ParentLayerPoint pointOut; 136 AsyncTransform viewTransformOut; 137 138 // Both the parent and child layer should behave exactly the same here, 139 // because the CSS transform on the child layer does not affect the 140 // SampleContentTransformForFrame code 141 142 // initial transform 143 apzc->SetFrameMetrics(metrics); 144 apzc->NotifyLayersUpdated( 145 metadata, 146 LayersUpdateFlags{.mIsFirstPaint = true, .mThisLayerTreeUpdated = true}); 147 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 148 EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), 149 viewTransformOut); 150 EXPECT_EQ(ParentLayerPoint(60, 60), pointOut); 151 152 childApzc->SetFrameMetrics(childMetrics); 153 childApzc->NotifyLayersUpdated( 154 childMetadata, 155 LayersUpdateFlags{.mIsFirstPaint = true, .mThisLayerTreeUpdated = true}); 156 childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 157 EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), 158 viewTransformOut); 159 EXPECT_EQ(ParentLayerPoint(60, 60), pointOut); 160 161 // do an async scroll by 5 pixels and check the transform 162 metrics.ScrollBy(CSSPoint(5, 0)); 163 apzc->SetFrameMetrics(metrics); 164 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 165 EXPECT_EQ( 166 AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), 167 viewTransformOut); 168 EXPECT_EQ(ParentLayerPoint(90, 60), pointOut); 169 170 childMetrics.ScrollBy(CSSPoint(5, 0)); 171 childApzc->SetFrameMetrics(childMetrics); 172 childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 173 EXPECT_EQ( 174 AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), 175 viewTransformOut); 176 EXPECT_EQ(ParentLayerPoint(90, 60), pointOut); 177 178 // do an async zoom of 1.5x and check the transform 179 metrics.ZoomBy(1.5f); 180 apzc->SetFrameMetrics(metrics); 181 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 182 EXPECT_EQ( 183 AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), 184 viewTransformOut); 185 EXPECT_EQ(ParentLayerPoint(135, 90), pointOut); 186 187 childMetrics.ZoomBy(1.5f); 188 childApzc->SetFrameMetrics(childMetrics); 189 childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 190 EXPECT_EQ( 191 AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), 192 viewTransformOut); 193 EXPECT_EQ(ParentLayerPoint(135, 90), pointOut); 194 195 childApzc->Destroy(); 196 } 197 198 TEST_F(APZCBasicTester, Fling) { 199 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 200 int touchStart = 50; 201 int touchEnd = 10; 202 ParentLayerPoint pointOut; 203 AsyncTransform viewTransformOut; 204 205 // Fling down. Each step scroll further down 206 Pan(apzc, touchStart, touchEnd); 207 ParentLayerPoint lastPoint; 208 for (int i = 1; i < 50; i += 1) { 209 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, 210 TimeDuration::FromMilliseconds(1)); 211 EXPECT_GT(pointOut.y, lastPoint.y); 212 lastPoint = pointOut; 213 } 214 } 215 216 #ifndef MOZ_WIDGET_ANDROID // Maybe fails on Android 217 static ScrollGenerationCounter sGenerationCounter; 218 219 TEST_F(APZCBasicTester, ResumeInterruptedTouchDrag_Bug1592435) { 220 // Start a touch-drag and scroll some amount, not lifting the finger. 221 SCOPED_GFX_PREF_FLOAT("apz.touch_start_tolerance", 1.0f / 1000.0f); 222 ScreenIntPoint touchPos(10, 50); 223 uint64_t touchBlock = TouchDown(apzc, touchPos, mcc->Time()).mInputBlockId; 224 SetDefaultAllowedTouchBehavior(apzc, touchBlock); 225 for (int i = 0; i < 20; ++i) { 226 touchPos.y -= 1; 227 mcc->AdvanceByMillis(1); 228 TouchMove(apzc, touchPos, mcc->Time()); 229 } 230 231 // Take note of the scroll offset before the interruption. 232 CSSPoint scrollOffsetBeforeInterruption = 233 apzc->GetFrameMetrics().GetVisualScrollOffset(); 234 235 // Have the main thread interrupt the touch-drag by sending 236 // a main thread scroll update to a nearby location. 237 CSSPoint mainThreadOffset = scrollOffsetBeforeInterruption; 238 mainThreadOffset.y -= 5; 239 ScrollMetadata metadata = apzc->GetScrollMetadata(); 240 metadata.GetMetrics().SetLayoutScrollOffset(mainThreadOffset); 241 nsTArray<ScrollPositionUpdate> scrollUpdates; 242 scrollUpdates.AppendElement(ScrollPositionUpdate::NewScroll( 243 ScrollOrigin::Other, CSSPoint::ToAppUnits(mainThreadOffset))); 244 metadata.SetScrollUpdates(scrollUpdates); 245 metadata.GetMetrics().SetScrollGeneration( 246 scrollUpdates.LastElement().GetGeneration()); 247 apzc->NotifyLayersUpdated( 248 metadata, 249 LayersUpdateFlags{.mIsFirstPaint = false, .mThisLayerTreeUpdated = true}); 250 251 // Continue and finish the touch-drag gesture. 252 for (int i = 0; i < 20; ++i) { 253 touchPos.y -= 1; 254 mcc->AdvanceByMillis(1); 255 TouchMove(apzc, touchPos, mcc->Time()); 256 } 257 258 // Check that the portion of the touch-drag that occurred after 259 // the interruption caused additional scrolling. 260 CSSPoint finalScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); 261 EXPECT_GT(finalScrollOffset.y, scrollOffsetBeforeInterruption.y); 262 263 // Now do the same thing, but for a visual scroll update. 264 scrollOffsetBeforeInterruption = 265 apzc->GetFrameMetrics().GetVisualScrollOffset(); 266 mainThreadOffset = scrollOffsetBeforeInterruption; 267 mainThreadOffset.y -= 5; 268 metadata = apzc->GetScrollMetadata(); 269 metadata.GetMetrics().SetVisualDestination(mainThreadOffset); 270 metadata.GetMetrics().SetScrollGeneration( 271 sGenerationCounter.NewMainThreadGeneration()); 272 metadata.GetMetrics().SetVisualScrollUpdateType(FrameMetrics::eMainThread); 273 scrollUpdates.Clear(); 274 metadata.SetScrollUpdates(scrollUpdates); 275 apzc->NotifyLayersUpdated( 276 metadata, 277 LayersUpdateFlags{.mIsFirstPaint = false, .mThisLayerTreeUpdated = true}); 278 for (int i = 0; i < 20; ++i) { 279 touchPos.y -= 1; 280 mcc->AdvanceByMillis(1); 281 TouchMove(apzc, touchPos, mcc->Time()); 282 } 283 finalScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); 284 EXPECT_GT(finalScrollOffset.y, scrollOffsetBeforeInterruption.y); 285 286 // Clean up by ending the touch gesture. 287 mcc->AdvanceByMillis(1); 288 TouchUp(apzc, touchPos, mcc->Time()); 289 } 290 #endif 291 292 TEST_F(APZCBasicTester, RelativeScrollOffset) { 293 // Set up initial conditions: zoomed in, layout offset at (100, 100), 294 // visual offset at (120, 120); the relative offset is therefore (20, 20). 295 ScrollMetadata metadata; 296 FrameMetrics& metrics = metadata.GetMetrics(); 297 metrics.SetScrollableRect(CSSRect(0, 0, 1000, 1000)); 298 metrics.SetLayoutViewport(CSSRect(100, 100, 100, 100)); 299 metrics.SetZoom(CSSToParentLayerScale(2.0)); 300 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100)); 301 metrics.SetVisualScrollOffset(CSSPoint(120, 120)); 302 metrics.SetIsRootContent(true); 303 apzc->SetFrameMetrics(metrics); 304 305 // Scroll the layout viewport to (200, 200). 306 ScrollMetadata mainThreadMetadata = metadata; 307 FrameMetrics& mainThreadMetrics = mainThreadMetadata.GetMetrics(); 308 mainThreadMetrics.SetLayoutScrollOffset(CSSPoint(200, 200)); 309 nsTArray<ScrollPositionUpdate> scrollUpdates; 310 scrollUpdates.AppendElement(ScrollPositionUpdate::NewScroll( 311 ScrollOrigin::Other, CSSPoint::ToAppUnits(CSSPoint(200, 200)))); 312 mainThreadMetadata.SetScrollUpdates(scrollUpdates); 313 mainThreadMetrics.SetScrollGeneration( 314 scrollUpdates.LastElement().GetGeneration()); 315 apzc->NotifyLayersUpdated( 316 mainThreadMetadata, 317 LayersUpdateFlags{.mIsFirstPaint = false, .mThisLayerTreeUpdated = true}); 318 319 // Check that the relative offset has been preserved. 320 metrics = apzc->GetFrameMetrics(); 321 EXPECT_EQ(metrics.GetLayoutScrollOffset(), CSSPoint(200, 200)); 322 EXPECT_EQ(metrics.GetVisualScrollOffset(), CSSPoint(220, 220)); 323 } 324 325 TEST_F(APZCBasicTester, MultipleSmoothScrollsSmooth) { 326 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 327 // We want to test that if we send multiple smooth scroll requests that we 328 // still smoothly animate, ie that we get non-zero change every frame while 329 // the animation is running. 330 331 ScrollMetadata metadata; 332 FrameMetrics& metrics = metadata.GetMetrics(); 333 metrics.SetScrollableRect(CSSRect(0, 0, 100, 10000)); 334 metrics.SetLayoutViewport(CSSRect(0, 0, 100, 100)); 335 metrics.SetZoom(CSSToParentLayerScale(1.0)); 336 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100)); 337 metrics.SetVisualScrollOffset(CSSPoint(0, 0)); 338 metrics.SetIsRootContent(true); 339 apzc->SetFrameMetrics(metrics); 340 341 // Structure of this test. 342 // -send a pure relative smooth scroll request via NotifyLayersUpdated 343 // -advance animations a few times, check that scroll offset is increasing 344 // after the first few advances 345 // -send a pure relative smooth scroll request via NotifyLayersUpdated 346 // -advance animations a few times, check that scroll offset is increasing 347 // -send a pure relative smooth scroll request via NotifyLayersUpdated 348 // -advance animations a few times, check that scroll offset is increasing 349 350 ScrollMetadata metadata2 = metadata; 351 nsTArray<ScrollPositionUpdate> scrollUpdates2; 352 scrollUpdates2.AppendElement(ScrollPositionUpdate::NewPureRelativeScroll( 353 ScrollOrigin::Other, ScrollMode::Smooth, 354 CSSPoint::ToAppUnits(CSSPoint(0, 200)))); 355 metadata2.SetScrollUpdates(scrollUpdates2); 356 metadata2.GetMetrics().SetScrollGeneration( 357 scrollUpdates2.LastElement().GetGeneration()); 358 apzc->NotifyLayersUpdated( 359 metadata2, 360 LayersUpdateFlags{.mIsFirstPaint = false, .mThisLayerTreeUpdated = true}); 361 362 // Get the animation going 363 for (uint32_t i = 0; i < 3; i++) { 364 SampleAnimationOneFrame(); 365 } 366 367 float offset = 368 apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing) 369 .y; 370 ASSERT_GT(offset, 0); 371 float lastOffset = offset; 372 373 for (uint32_t i = 0; i < 2; i++) { 374 for (uint32_t j = 0; j < 3; j++) { 375 SampleAnimationOneFrame(); 376 offset = apzc->GetCurrentAsyncScrollOffset( 377 AsyncPanZoomController::eForCompositing) 378 .y; 379 ASSERT_GT(offset, lastOffset); 380 lastOffset = offset; 381 } 382 383 ScrollMetadata metadata3 = metadata; 384 nsTArray<ScrollPositionUpdate> scrollUpdates3; 385 scrollUpdates3.AppendElement(ScrollPositionUpdate::NewPureRelativeScroll( 386 ScrollOrigin::Other, ScrollMode::Smooth, 387 CSSPoint::ToAppUnits(CSSPoint(0, 200)))); 388 metadata3.SetScrollUpdates(scrollUpdates3); 389 metadata3.GetMetrics().SetScrollGeneration( 390 scrollUpdates3.LastElement().GetGeneration()); 391 apzc->NotifyLayersUpdated(metadata3, 392 LayersUpdateFlags{.mIsFirstPaint = false, 393 .mThisLayerTreeUpdated = true}); 394 } 395 396 for (uint32_t j = 0; j < 7; j++) { 397 SampleAnimationOneFrame(); 398 offset = apzc->GetCurrentAsyncScrollOffset( 399 AsyncPanZoomController::eForCompositing) 400 .y; 401 ASSERT_GT(offset, lastOffset); 402 lastOffset = offset; 403 } 404 } 405 406 TEST_F(APZCBasicTester, NotifyLayersUpdate_WithScrollUpdate) { 407 // Set an empty metadata as if the APZC is now newly created. 408 // This replicates when a document in a background tab now becomes forground. 409 ScrollMetadata metadata; 410 apzc->SetScrollMetadata(metadata); 411 ASSERT_TRUE(apzc->GetScrollMetadata().IsDefault()); 412 413 FrameMetrics& metrics = metadata.GetMetrics(); 414 metrics.SetDisplayPort(CSSRect(0, 0, 10, 10)); 415 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 10, 10)); 416 metrics.SetScrollableRect(CSSRect(0, 0, 100, 100)); 417 418 // Set layout/visual scroll offsets as if the document has scrolled when the 419 // document was foregound. 420 metrics.SetVisualScrollOffset(CSSPoint(10, 10)); 421 metrics.SetLayoutViewport(CSSRect(10, 10, 10, 10)); 422 metrics.SetScrollId(ScrollableLayerGuid::START_SCROLL_ID); 423 424 // Add a new relative scroll update (10, 10) -> (15, 15). 425 AutoTArray<ScrollPositionUpdate, 1> scrollUpdates; 426 scrollUpdates.AppendElement(ScrollPositionUpdate::NewRelativeScroll( 427 CSSPoint::ToAppUnits(CSSPoint(10, 10)), 428 CSSPoint::ToAppUnits(CSSPoint(15, 15)))); 429 metadata.SetScrollUpdates(scrollUpdates); 430 metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration()); 431 // With the above scroll update, now the layout/visual scroll offsets (on the 432 // main-thread) need to be updated. 433 metrics.SetVisualScrollOffset(CSSPoint(15, 15)); 434 metrics.SetLayoutViewport(CSSRect(15, 15, 10, 10)); 435 436 // It's not first-paint when switching tab. 437 apzc->NotifyLayersUpdated( 438 metadata, 439 LayersUpdateFlags{.mIsFirstPaint = false, .mThisLayerTreeUpdated = true}); 440 441 // The layout/visual scroll ofsets and the relative scroll update need to be 442 // reflected. 443 ASSERT_EQ(apzc->GetFrameMetrics().GetLayoutScrollOffset(), CSSPoint(15, 15)); 444 ASSERT_EQ(apzc->GetFrameMetrics().GetVisualScrollOffset(), CSSPoint(15, 15)); 445 } 446 447 TEST_F(APZCBasicTester, NotifyLayersUpdate_WithMultipleScrollUpdates) { 448 // Set an empty metadata as if the APZC is now newly created. 449 // This replicates when a document in a background tab now becomes foreground. 450 ScrollMetadata metadata; 451 apzc->SetScrollMetadata(metadata); 452 ASSERT_TRUE(apzc->GetScrollMetadata().IsDefault()); 453 454 FrameMetrics& metrics = metadata.GetMetrics(); 455 metrics.SetDisplayPort(CSSRect(0, 0, 10, 10)); 456 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 10, 10)); 457 metrics.SetScrollableRect(CSSRect(0, 0, 100, 100)); 458 459 metrics.SetVisualScrollOffset(CSSPoint(0, 0)); 460 metrics.SetLayoutViewport(CSSRect(0, 0, 10, 10)); 461 metrics.SetScrollId(ScrollableLayerGuid::START_SCROLL_ID); 462 463 AutoTArray<ScrollPositionUpdate, 2> scrollUpdates; 464 // Append a new scroll frame as if the scroll frame was reconstructed. 465 scrollUpdates.AppendElement(ScrollPositionUpdate::NewScrollframe( 466 CSSPoint::ToAppUnits(CSSPoint(0, 0)))); 467 // Append a new relative scroll update (0, 0) -> (20, 20). 468 scrollUpdates.AppendElement(ScrollPositionUpdate::NewRelativeScroll( 469 CSSPoint::ToAppUnits(CSSPoint(0, 0)), 470 CSSPoint::ToAppUnits(CSSPoint(20, 20)))); 471 metadata.SetScrollUpdates(scrollUpdates); 472 metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration()); 473 // With the above scroll updates, now the layout/visual scroll offsets (on the 474 // main-thread) need to be updated. 475 metrics.SetVisualScrollOffset(CSSPoint(20, 20)); 476 metrics.SetLayoutViewport(CSSRect(20, 20, 10, 10)); 477 478 // It's not first-paint when switching tab. 479 apzc->NotifyLayersUpdated( 480 metadata, 481 LayersUpdateFlags{.mIsFirstPaint = false, .mThisLayerTreeUpdated = true}); 482 483 // The layout/visual scroll ofsets and the relative scroll update need to be 484 // reflected. 485 ASSERT_EQ(apzc->GetFrameMetrics().GetLayoutScrollOffset(), CSSPoint(20, 20)); 486 ASSERT_EQ(apzc->GetFrameMetrics().GetVisualScrollOffset(), CSSPoint(20, 20)); 487 } 488 489 class APZCSmoothScrollTester : public APZCBasicTester { 490 public: 491 // Test that a smooth scroll animation correctly handles its destination 492 // being updated by a relative scroll delta from the main thread (a "content 493 // shift"). 494 void TestContentShift() { 495 // Set up scroll frame. Starting scroll position is (0, 0). 496 ScrollMetadata metadata; 497 FrameMetrics& metrics = metadata.GetMetrics(); 498 metrics.SetScrollableRect(CSSRect(0, 0, 100, 10000)); 499 metrics.SetLayoutViewport(CSSRect(0, 0, 100, 100)); 500 metrics.SetZoom(CSSToParentLayerScale(1.0)); 501 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100)); 502 metrics.SetVisualScrollOffset(CSSPoint(0, 0)); 503 metrics.SetIsRootContent(true); 504 apzc->SetFrameMetrics(metrics); 505 506 // Start smooth scroll via main-thread request. 507 nsTArray<ScrollPositionUpdate> scrollUpdates; 508 scrollUpdates.AppendElement(ScrollPositionUpdate::NewPureRelativeScroll( 509 ScrollOrigin::Other, ScrollMode::Smooth, 510 CSSPoint::ToAppUnits(CSSPoint(0, 1000)))); 511 metadata.SetScrollUpdates(scrollUpdates); 512 metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration()); 513 apzc->NotifyLayersUpdated(metadata, 514 LayersUpdateFlags{.mIsFirstPaint = false, 515 .mThisLayerTreeUpdated = true}); 516 517 // Sample the smooth scroll animation until we get past y=500. 518 apzc->AssertInSmoothScroll(); 519 float y = 0; 520 while (y < 500) { 521 SampleAnimationOneFrame(); 522 y = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 523 } 524 525 // Send a relative scroll of y = -400. 526 scrollUpdates.Clear(); 527 scrollUpdates.AppendElement(ScrollPositionUpdate::NewRelativeScroll( 528 CSSPoint::ToAppUnits(CSSPoint(0, 500)), 529 CSSPoint::ToAppUnits(CSSPoint(0, 100)))); 530 metadata.SetScrollUpdates(scrollUpdates); 531 metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration()); 532 apzc->NotifyLayersUpdated( 533 metadata, LayersUpdateFlags{.mIsFirstPaint = false, 534 .mThisLayerTreeUpdated = false}); 535 536 // Verify the relative scroll was applied but didn't cancel the animation. 537 float y2 = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 538 ASSERT_EQ(y2, y - 400); 539 apzc->AssertInSmoothScroll(); 540 541 // Sample the animation again and check that it respected the relative 542 // scroll. 543 SampleAnimationOneFrame(); 544 float y3 = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 545 ASSERT_GT(y3, y2); 546 ASSERT_LT(y3, 500); 547 548 // Continue animation until done and check that it ended up at a correctly 549 // adjusted destination. 550 apzc->AdvanceAnimationsUntilEnd(); 551 float y4 = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 552 ASSERT_EQ(y4, 600); // 1000 (initial destination) - 400 (relative scroll) 553 } 554 555 // Test that a smooth scroll animation correctly handles a content 556 // shift, followed by an UpdateDelta due to a new input event. 557 void TestContentShiftThenUpdateDelta() { 558 // Set up scroll frame. Starting position is (0, 0). 559 ScrollMetadata metadata; 560 FrameMetrics& metrics = metadata.GetMetrics(); 561 metrics.SetScrollableRect(CSSRect(0, 0, 1000, 10000)); 562 metrics.SetLayoutViewport(CSSRect(0, 0, 1000, 1000)); 563 metrics.SetZoom(CSSToParentLayerScale(1.0)); 564 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 1000, 1000)); 565 metrics.SetVisualScrollOffset(CSSPoint(0, 0)); 566 metrics.SetIsRootContent(true); 567 // Set the line scroll amount to 100 pixels. Note that SmoothWheel() takes 568 // a delta denominated in lines. 569 metadata.SetLineScrollAmount({100, 100}); 570 // The page scroll amount also needs to be set, otherwise the wheel handling 571 // code will get confused by things like the "don't scroll more than one 572 // page" check. 573 metadata.SetPageScrollAmount({1000, 1000}); 574 apzc->SetScrollMetadata(metadata); 575 576 // Send a wheel event to trigger smooth scrolling by 5 lines (= 500 pixels). 577 SmoothWheel(apzc, ScreenIntPoint(50, 50), ScreenPoint(0, 5), mcc->Time()); 578 apzc->AssertInWheelScroll(); 579 580 // Sample the wheel scroll animation until we get past y=200. 581 float y = 0; 582 while (y < 200) { 583 SampleAnimationOneFrame(); 584 y = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 585 } 586 587 // Apply a content shift of y=100. 588 nsTArray<ScrollPositionUpdate> scrollUpdates; 589 scrollUpdates.AppendElement(ScrollPositionUpdate::NewRelativeScroll( 590 CSSPoint::ToAppUnits(CSSPoint(0, 200)), 591 CSSPoint::ToAppUnits(CSSPoint(0, 300)))); 592 metadata.SetScrollUpdates(scrollUpdates); 593 metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration()); 594 apzc->NotifyLayersUpdated(metadata, 595 LayersUpdateFlags{.mIsFirstPaint = false, 596 .mThisLayerTreeUpdated = true}); 597 598 // Check that the content shift was applied but didn't cancel the animation. 599 // At this point, the animation's internal state should be targeting a 600 // destination of y=600. 601 float y2 = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 602 ASSERT_EQ(y2, y + 100); 603 apzc->AssertInWheelScroll(); 604 605 // Sample the animation until we get past y=400. 606 while (y < 400) { 607 SampleAnimationOneFrame(); 608 y = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 609 } 610 611 // Send another wheel event to trigger smooth scrolling by another 5 lines 612 // (=500 pixels). This should update the animation to target a destination 613 // of y=1100. 614 SmoothWheel(apzc, ScreenIntPoint(50, 50), ScreenPoint(0, 5), mcc->Time()); 615 616 // Continue the animation until done and check that it ended up at y=1100. 617 apzc->AdvanceAnimationsUntilEnd(); 618 float yEnd = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 619 ASSERT_EQ(yEnd, 1100); 620 } 621 622 // Test that a content shift does not cause a smooth scroll animation to 623 // overshoot its (updated) destination. 624 void TestContentShiftDoesNotCauseOvershoot() { 625 // Follow the same steps as in TestContentShiftThenUpdateDelta(), 626 // except use a content shift of y=1000. 627 ScrollMetadata metadata; 628 FrameMetrics& metrics = metadata.GetMetrics(); 629 metrics.SetScrollableRect(CSSRect(0, 0, 1000, 10000)); 630 metrics.SetLayoutViewport(CSSRect(0, 0, 1000, 1000)); 631 metrics.SetZoom(CSSToParentLayerScale(1.0)); 632 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 1000, 1000)); 633 metrics.SetVisualScrollOffset(CSSPoint(0, 0)); 634 metrics.SetIsRootContent(true); 635 metadata.SetLineScrollAmount({100, 100}); 636 metadata.SetPageScrollAmount({1000, 1000}); 637 apzc->SetScrollMetadata(metadata); 638 639 // First wheel event, smooth scroll destination is y=500. 640 SmoothWheel(apzc, ScreenIntPoint(50, 50), ScreenPoint(0, 5), mcc->Time()); 641 apzc->AssertInWheelScroll(); 642 643 // Sample until we get past y=200. 644 float y = 0; 645 while (y < 200) { 646 SampleAnimationOneFrame(); 647 y = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 648 } 649 650 // Apply a content shift of y=1000. The current scroll position is now 651 // y>1200, and the updated destination is y=1500. 652 nsTArray<ScrollPositionUpdate> scrollUpdates; 653 scrollUpdates.AppendElement(ScrollPositionUpdate::NewRelativeScroll( 654 CSSPoint::ToAppUnits(CSSPoint(0, 200)), 655 CSSPoint::ToAppUnits(CSSPoint(0, 1200)))); 656 metadata.SetScrollUpdates(scrollUpdates); 657 metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration()); 658 apzc->NotifyLayersUpdated(metadata, 659 LayersUpdateFlags{.mIsFirstPaint = false, 660 .mThisLayerTreeUpdated = true}); 661 float y2 = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 662 ASSERT_EQ(y2, y + 1000); 663 apzc->AssertInWheelScroll(); 664 665 // Sample until we get past y=1300. 666 while (y < 1300) { 667 SampleAnimationOneFrame(); 668 y = apzc->GetFrameMetrics().GetVisualScrollOffset().y; 669 } 670 671 // Second wheel event, destination is now y=2000. 672 // MSD physics has a bug where the UpdateDelta() causes the content shift 673 // to be applied in duplicate on the next sample, causing the scroll 674 // position to be y>2000! 675 SmoothWheel(apzc, ScreenIntPoint(50, 50), ScreenPoint(0, 5), mcc->Time()); 676 677 // Check that the scroll position remains <= 2000 until the end of the 678 // animation. 679 while (apzc->IsWheelScrollAnimationRunning()) { 680 SampleAnimationOneFrame(); 681 ASSERT_LE(apzc->GetFrameMetrics().GetVisualScrollOffset().y, 2000); 682 } 683 ASSERT_EQ(2000, apzc->GetFrameMetrics().GetVisualScrollOffset().y); 684 } 685 686 // Test that receiving a wheel event with a timestamp far in the future does 687 // not cause scrolling to get stuck. 688 void TestWheelEventWithFutureStamp() { 689 // Set up scroll frame. Starting scroll position is (0, 0). 690 ScrollMetadata metadata; 691 FrameMetrics& metrics = metadata.GetMetrics(); 692 metrics.SetScrollableRect(CSSRect(0, 0, 1000, 10000)); 693 metrics.SetLayoutViewport(CSSRect(0, 0, 1000, 1000)); 694 metrics.SetZoom(CSSToParentLayerScale(1.0)); 695 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 1000, 1000)); 696 metrics.SetVisualScrollOffset(CSSPoint(0, 0)); 697 metrics.SetIsRootContent(true); 698 // Set the line scroll amount to 100 pixels. Note that SmoothWheel() takes 699 // a delta denominated in lines. 700 metadata.SetLineScrollAmount({100, 100}); 701 // The page scroll amount also needs to be set, otherwise the wheel handling 702 // code will get confused by things like the "don't scroll more than one 703 // page" check. 704 metadata.SetPageScrollAmount({1000, 1000}); 705 apzc->SetScrollMetadata(metadata); 706 707 // Send a wheel event to trigger smooth scrolling by 5 lines (= 500 pixels). 708 // Give the wheel event a timestamp "far" (here, 1 minute) into the future. 709 // This simulates a scenario, observed in bug 1926830, where a bug in the 710 // system toolkit or widget layers causes something to introduce a skew into 711 // the timestamps received from widget code. 712 TimeStamp futureTimeStamp = mcc->Time() + TimeDuration::FromSeconds(60); 713 SmoothWheel(apzc, ScreenIntPoint(50, 50), ScreenPoint(0, 5), 714 futureTimeStamp); 715 apzc->AssertInWheelScroll(); 716 717 // Sample the animation 10 frames (a shorter overall duration than the 718 // timestamp skew). 719 for (int i = 0; i < 10; ++i) { 720 SampleAnimationOneFrame(); 721 } 722 723 // Assert that we have scrolled. Without a mitigation in place for the 724 // timestamp skew, we may wait for the frame (vsync) time to catch up with 725 // the event's timestamp before doing any scrolling. 726 ASSERT_GT(apzc->GetFrameMetrics().GetVisualScrollOffset().y, 0); 727 728 // Clean up by letting the animation run until completion. 729 apzc->AdvanceAnimationsUntilEnd(); 730 } 731 732 // Test that receiving a key event with a timestamp far in the future does 733 // not cause scrolling to get stuck. 734 void TestKeyEventWithFutureStamp() { 735 // Set up scroll frame. Starting scroll position is (0, 0). 736 ScrollMetadata metadata; 737 FrameMetrics& metrics = metadata.GetMetrics(); 738 metrics.SetScrollableRect(CSSRect(0, 0, 1000, 10000)); 739 metrics.SetLayoutViewport(CSSRect(0, 0, 1000, 1000)); 740 metrics.SetZoom(CSSToParentLayerScale(1.0)); 741 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 1000, 1000)); 742 metrics.SetVisualScrollOffset(CSSPoint(0, 0)); 743 metrics.SetScrollId(ScrollableLayerGuid::START_SCROLL_ID); 744 metrics.SetIsRootContent(true); 745 // Set the line scroll amount to 100 pixels. The key event we send 746 // will scroll by a multiple of this amount. 747 metadata.SetLineScrollAmount({100, 100}); 748 apzc->SetScrollMetadata(metadata); 749 750 // Note that, since we are sending the key event to the APZC instance 751 // directly, we don't need to set up a keyboard map or focus state. 752 753 // Send a key event to trigger smooth scrolling by a few lines (the number 754 // of lines is determined by toolkit.scrollbox.verticalScrollDistance). 755 WidgetKeyboardEvent keyEvent(true, eKeyDown, nullptr); 756 // Give the key event a timestamp "far" (here, 1 minute) into the future. 757 // This simulates a scenario, observed in bug 1926830, where a bug in the 758 // system toolkit or widget layers causes something to introduce a skew into 759 // the timestamps received from widget code. 760 TimeStamp futureTimeStamp = mcc->Time() + TimeDuration::FromSeconds(60); 761 keyEvent.mTimeStamp = futureTimeStamp; 762 KeyboardInput keyInput(keyEvent); 763 // The KeyboardScrollAction needs to be specified on the event explicitly, 764 // since the mapping from eKeyDown to it happens in APZCTreeManager which 765 // we are bypassing here. 766 keyInput.mAction = {KeyboardScrollAction::eScrollLine, /*aForward=*/true}; 767 (void)apzc->ReceiveInputEvent(keyInput); 768 apzc->AssertInKeyboardScroll(); 769 770 // Sample the animation 10 frames (a shorter overall duration than the 771 // timestamp skew). 772 for (int i = 0; i < 10; ++i) { 773 SampleAnimationOneFrame(); 774 } 775 776 // Assert that we have scrolled. Without a mitigation in place for the 777 // timestamp skew, we may wait for the frame (vsync) time to catch up with 778 // the event's timestamp before doing any scrolling. 779 ASSERT_GT(apzc->GetFrameMetrics().GetVisualScrollOffset().y, 0); 780 781 // Clean up by letting the animation run until completion. 782 apzc->AdvanceAnimationsUntilEnd(); 783 } 784 }; 785 786 TEST_F(APZCSmoothScrollTester, ContentShiftBezier) { 787 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 788 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", false); 789 TestContentShift(); 790 } 791 792 TEST_F(APZCSmoothScrollTester, ContentShiftMsd) { 793 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 794 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", true); 795 TestContentShift(); 796 } 797 798 TEST_F(APZCSmoothScrollTester, ContentShiftThenUpdateDeltaBezier) { 799 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 800 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", false); 801 TestContentShiftThenUpdateDelta(); 802 } 803 804 TEST_F(APZCSmoothScrollTester, ContentShiftThenUpdateDeltaMsd) { 805 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 806 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", true); 807 TestContentShiftThenUpdateDelta(); 808 } 809 810 TEST_F(APZCSmoothScrollTester, ContentShiftDoesNotCauseOvershootBezier) { 811 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 812 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", false); 813 TestContentShiftDoesNotCauseOvershoot(); 814 } 815 816 TEST_F(APZCSmoothScrollTester, ContentShiftDoesNotCauseOvershootMsd) { 817 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 818 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", true); 819 TestContentShiftDoesNotCauseOvershoot(); 820 } 821 822 TEST_F(APZCSmoothScrollTester, FutureWheelTimestampBezier) { 823 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 824 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", false); 825 TestWheelEventWithFutureStamp(); 826 } 827 828 TEST_F(APZCSmoothScrollTester, FutureWheelTimestampMsd) { 829 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 830 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", true); 831 TestWheelEventWithFutureStamp(); 832 } 833 834 TEST_F(APZCSmoothScrollTester, FutureKeyTimestampBezier) { 835 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 836 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", false); 837 TestKeyEventWithFutureStamp(); 838 } 839 840 TEST_F(APZCSmoothScrollTester, FutureKeyTimestampMsd) { 841 SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); 842 SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", true); 843 TestKeyEventWithFutureStamp(); 844 } 845 846 TEST_F(APZCBasicTester, ZoomAndScrollableRectChangeAfterZoomChange) { 847 // We want to check that a small scrollable rect change (which causes us to 848 // reclamp our scroll position, including in the sampled state) does not move 849 // the scroll offset in the sample state based the zoom in the apzc, only 850 // based on the zoom in the sampled state. 851 852 // First we zoom in to the right hand side. Then start zooming out, then send 853 // a scrollable rect change and check that it doesn't change the sampled state 854 // scroll offset. 855 856 ScrollMetadata metadata; 857 FrameMetrics& metrics = metadata.GetMetrics(); 858 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100)); 859 metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000)); 860 metrics.SetLayoutViewport(CSSRect(0, 0, 100, 100)); 861 metrics.SetVisualScrollOffset(CSSPoint(0, 0)); 862 metrics.SetZoom(CSSToParentLayerScale(1.0)); 863 metrics.SetIsRootContent(true); 864 apzc->SetFrameMetrics(metrics); 865 866 MakeApzcZoomable(); 867 868 // Zoom to right side. 869 ZoomTarget zoomTarget{CSSRect(75, 25, 25, 25)}; 870 apzc->ZoomToRect(zoomTarget, 0); 871 872 // Run the animation to completion, should take 250ms/16.67ms = 15 frames, but 873 // do extra to make sure. 874 for (uint32_t i = 0; i < 30; i++) { 875 SampleAnimationOneFrame(); 876 } 877 878 EXPECT_FALSE(apzc->IsAsyncZooming()); 879 880 // Zoom out. 881 ZoomTarget zoomTarget2{CSSRect(0, 0, 100, 100)}; 882 apzc->ZoomToRect(zoomTarget2, 0); 883 884 // Run the animation a few times to get it going. 885 for (uint32_t i = 0; i < 2; i++) { 886 SampleAnimationOneFrame(); 887 } 888 889 // Check that it is decreasing in scale. 890 float prevScale = 891 apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing) 892 .scale; 893 for (uint32_t i = 0; i < 2; i++) { 894 SampleAnimationOneFrame(); 895 float scale = 896 apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing) 897 .scale; 898 ASSERT_GT(prevScale, scale); 899 prevScale = scale; 900 } 901 902 float offset = 903 apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing) 904 .x; 905 906 // Change the scrollable rect slightly to trigger a reclamp. 907 ScrollMetadata metadata2 = metadata; 908 metadata2.GetMetrics().SetScrollableRect(CSSRect(0, 0, 100, 1000.2)); 909 apzc->NotifyLayersUpdated( 910 metadata2, 911 LayersUpdateFlags{.mIsFirstPaint = false, .mThisLayerTreeUpdated = true}); 912 913 float newOffset = 914 apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing) 915 .x; 916 917 ASSERT_EQ(newOffset, offset); 918 } 919 920 TEST_F(APZCBasicTester, ZoomToRectAndCompositionBoundsChange) { 921 // We want to check that content sending a composition bounds change (due to 922 // addition of scrollbars) during a zoom animation does not cause us to take 923 // the out of date content resolution. 924 925 ScrollMetadata metadata; 926 FrameMetrics& metrics = metadata.GetMetrics(); 927 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100)); 928 metrics.SetCompositionBoundsWidthIgnoringScrollbars(ParentLayerCoord{100}); 929 metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000)); 930 metrics.SetLayoutViewport(CSSRect(0, 0, 100, 100)); 931 metrics.SetVisualScrollOffset(CSSPoint(0, 0)); 932 metrics.SetZoom(CSSToParentLayerScale(1.0)); 933 metrics.SetIsRootContent(true); 934 apzc->SetFrameMetrics(metrics); 935 936 MakeApzcZoomable(); 937 938 // Start a zoom to a rect. 939 ZoomTarget zoomTarget{CSSRect(25, 25, 25, 25)}; 940 apzc->ZoomToRect(zoomTarget, 0); 941 942 // Run the animation a few times to get it going. 943 // Check that it is increasing in scale. 944 float prevScale = 945 apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing) 946 .scale; 947 for (uint32_t i = 0; i < 3; i++) { 948 SampleAnimationOneFrame(); 949 float scale = 950 apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing) 951 .scale; 952 ASSERT_GE(scale, prevScale); 953 prevScale = scale; 954 } 955 956 EXPECT_TRUE(apzc->IsAsyncZooming()); 957 958 // Simulate the appearance of a scrollbar by reducing the width of 959 // the composition bounds, while keeping 960 // mCompositionBoundsWidthIgnoringScrollbars unchanged. 961 ScrollMetadata metadata2 = metadata; 962 metadata2.GetMetrics().SetCompositionBounds(ParentLayerRect(0, 0, 90, 100)); 963 apzc->NotifyLayersUpdated( 964 metadata2, 965 LayersUpdateFlags{.mIsFirstPaint = false, .mThisLayerTreeUpdated = true}); 966 967 float scale = 968 apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing) 969 .scale; 970 971 ASSERT_EQ(scale, prevScale); 972 973 // Run the rest of the animation to completion, should take 250ms/16.67ms = 15 974 // frames total, but do extra to make sure. 975 for (uint32_t i = 0; i < 30; i++) { 976 SampleAnimationOneFrame(); 977 scale = 978 apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing) 979 .scale; 980 ASSERT_GE(scale, prevScale); 981 prevScale = scale; 982 } 983 984 EXPECT_FALSE(apzc->IsAsyncZooming()); 985 } 986 987 TEST_F(APZCBasicTester, StartTolerance) { 988 SCOPED_GFX_PREF_FLOAT("apz.touch_start_tolerance", 10 / tm->GetDPI()); 989 990 FrameMetrics fm; 991 fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100)); 992 fm.SetScrollableRect(CSSRect(0, 0, 100, 300)); 993 fm.SetVisualScrollOffset(CSSPoint(0, 50)); 994 fm.SetIsRootContent(true); 995 apzc->SetFrameMetrics(fm); 996 997 uint64_t touchBlock = TouchDown(apzc, {50, 50}, mcc->Time()).mInputBlockId; 998 SetDefaultAllowedTouchBehavior(apzc, touchBlock); 999 1000 CSSPoint initialScrollOffset = 1001 apzc->GetFrameMetrics().GetVisualScrollOffset(); 1002 1003 mcc->AdvanceByMillis(1); 1004 TouchMove(apzc, {50, 70}, mcc->Time()); 1005 1006 // Expect 10 pixels of scrolling: the distance from (50,50) to (50,70) 1007 // minus the 10-pixel touch start tolerance. 1008 ASSERT_EQ(initialScrollOffset.y - 10, 1009 apzc->GetFrameMetrics().GetVisualScrollOffset().y); 1010 1011 mcc->AdvanceByMillis(1); 1012 TouchMove(apzc, {50, 90}, mcc->Time()); 1013 1014 // Expect 30 pixels of scrolling: the distance from (50,50) to (50,90) 1015 // minus the 10-pixel touch start tolerance. 1016 ASSERT_EQ(initialScrollOffset.y - 30, 1017 apzc->GetFrameMetrics().GetVisualScrollOffset().y); 1018 1019 // Clean up by ending the touch gesture. 1020 mcc->AdvanceByMillis(1); 1021 TouchUp(apzc, {50, 90}, mcc->Time()); 1022 } 1023 1024 // A helper class for the ImmediatelyInterruptedSmoothScroll_Bug1984589 1025 // test below, which overrides APZCTreeManager::GetFrameTime() to 1026 // advance the time by 1ms every time GetFrameTime() is queried. This 1027 // is needed to reproduce the bug (specifically to ensure that in the 1028 // NotifyLayersUpdated call with two scroll updates, some time has 1029 // elapsed between the two updates). 1030 class APZCFrameTimeTester : public APZCBasicTester { 1031 class FrameTimeAPZCTreeManager : public TestAPZCTreeManager { 1032 public: 1033 explicit FrameTimeAPZCTreeManager(MockContentControllerDelayed* aMcc) 1034 : TestAPZCTreeManager(aMcc) {} 1035 1036 protected: 1037 SampleTime GetFrameTime() override { 1038 SampleTime result = mcc->GetSampleTime(); 1039 mcc->AdvanceByMillis(1); 1040 return result; 1041 } 1042 }; 1043 1044 protected: 1045 TestAPZCTreeManager* CreateTreeManager() override { 1046 return new FrameTimeAPZCTreeManager(mcc); 1047 } 1048 }; 1049 1050 TEST_F(APZCFrameTimeTester, ImmediatelyInterruptedSmoothScroll_Bug1984589) { 1051 // Set up a vertically scrollable scroll frame, with the starting scroll 1052 // position at the bottom. 1053 ScrollMetadata metadata; 1054 FrameMetrics& metrics = metadata.GetMetrics(); 1055 metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000)); 1056 metrics.SetLayoutViewport(CSSRect(0, 900, 100, 100)); 1057 metrics.SetZoom(CSSToParentLayerScale(1.0)); 1058 metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100)); 1059 metrics.SetVisualScrollOffset(CSSPoint(0, 900)); 1060 metrics.SetIsRootContent(true); 1061 apzc->SetFrameMetrics(metrics); 1062 1063 // Simulate a main-thread transaction with two absolute scroll updates with 1064 // SmoothMsd scroll mode: one that scrolls back to the top, and one that 1065 // "interrupts" it by scrolling back to the bottom. 1066 nsTArray<ScrollPositionUpdate> scrollUpdates; 1067 scrollUpdates.AppendElement(ScrollPositionUpdate::NewSmoothScroll( 1068 ScrollMode::SmoothMsd, ScrollOrigin::Other, 1069 CSSPoint::ToAppUnits(CSSPoint(0, 0)), ScrollTriggeredByScript::Yes, 1070 nullptr, ViewportType::Layout)); 1071 scrollUpdates.AppendElement(ScrollPositionUpdate::NewSmoothScroll( 1072 ScrollMode::SmoothMsd, ScrollOrigin::Other, 1073 CSSPoint::ToAppUnits(CSSPoint(0, 900)), ScrollTriggeredByScript::Yes, 1074 nullptr, ViewportType::Layout)); 1075 metadata.SetScrollUpdates(scrollUpdates); 1076 metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration()); 1077 apzc->NotifyLayersUpdated( 1078 metadata, 1079 LayersUpdateFlags{.mIsFirstPaint = false, .mThisLayerTreeUpdated = true}); 1080 1081 // Sample smooth scroll animations until they complete, 1082 // and assert that at no point does the scroll position leave (0, 900). 1083 do { 1084 ASSERT_EQ(apzc->GetFrameMetrics().GetVisualScrollOffset().y, 900); 1085 } while (SampleAnimationOneFrame()); 1086 }