tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }