tor-browser

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

TestScrollHandoff.cpp (36734B)


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