tor-browser

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

TestHitTesting.cpp (14493B)


      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 
     10 #include "InputUtils.h"
     11 
     12 class APZHitTestingTester : public APZCTreeManagerTester {
     13 protected:
     14  ScreenToParentLayerMatrix4x4 transformToApzc;
     15  ParentLayerToScreenMatrix4x4 transformToGecko;
     16 
     17  already_AddRefed<AsyncPanZoomController> GetTargetAPZC(
     18      const ScreenPoint& aPoint) {
     19    RefPtr<AsyncPanZoomController> hit =
     20        manager->GetTargetAPZC(aPoint).mTargetApzc;
     21    if (hit) {
     22      transformToApzc = manager->GetScreenToApzcTransform(hit.get());
     23      transformToGecko =
     24          manager->GetApzcToGeckoTransform(hit.get(), LayoutAndVisual);
     25    }
     26    return hit.forget();
     27  }
     28 
     29 protected:
     30  void DisableApzOn(WebRenderLayerScrollData* aLayer) {
     31    ModifyFrameMetrics(aLayer, [](ScrollMetadata& aSm, FrameMetrics&) {
     32      aSm.SetForceDisableApz(true);
     33    });
     34  }
     35 
     36  void CreateComplexMultiLayerTree() {
     37    const char* treeShape = "x(xx(x)xx(x(x)xx))";
     38    // LayerID               0 12 3 45 6 7 89
     39    LayerIntRect layerVisibleRect[] = {
     40        LayerIntRect(0, 0, 300, 400),    // root(0)
     41        LayerIntRect(0, 0, 100, 100),    // layer(1) in top-left
     42        LayerIntRect(50, 50, 200, 300),  // layer(2) centered in root(0)
     43        LayerIntRect(50, 50, 200,
     44                     300),  // layer(3) fully occupying parent layer(2)
     45        LayerIntRect(0, 200, 100, 100),  // layer(4) in bottom-left
     46        LayerIntRect(200, 0, 100,
     47                     400),  // layer(5) along the right 100px of root(0)
     48        LayerIntRect(200, 0, 100, 200),  // layer(6) taking up the top
     49                                         // half of parent layer(5)
     50        LayerIntRect(200, 0, 100,
     51                     200),  // layer(7) fully occupying parent layer(6)
     52        LayerIntRect(200, 200, 100,
     53                     100),  // layer(8) in bottom-right (below (6))
     54        LayerIntRect(200, 300, 100,
     55                     100),  // layer(9) in bottom-right (below (8))
     56    };
     57    CreateScrollData(treeShape, layerVisibleRect);
     58    SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID);
     59    SetScrollableFrameMetrics(layers[2], ScrollableLayerGuid::START_SCROLL_ID);
     60    SetScrollableFrameMetrics(layers[4],
     61                              ScrollableLayerGuid::START_SCROLL_ID + 1);
     62    SetScrollableFrameMetrics(layers[6],
     63                              ScrollableLayerGuid::START_SCROLL_ID + 1);
     64    SetScrollableFrameMetrics(layers[7],
     65                              ScrollableLayerGuid::START_SCROLL_ID + 2);
     66    SetScrollableFrameMetrics(layers[8],
     67                              ScrollableLayerGuid::START_SCROLL_ID + 1);
     68    SetScrollableFrameMetrics(layers[9],
     69                              ScrollableLayerGuid::START_SCROLL_ID + 3);
     70  }
     71 
     72  void CreateBug1148350LayerTree() {
     73    const char* treeShape = "x(x)";
     74    // LayerID               0 1
     75    LayerIntRect layerVisibleRect[] = {
     76        LayerIntRect(0, 0, 200, 200),
     77        LayerIntRect(0, 0, 200, 200),
     78    };
     79    CreateScrollData(treeShape, layerVisibleRect);
     80    SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID);
     81  }
     82 };
     83 
     84 TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
     85  CreateComplexMultiLayerTree();
     86  ScopedLayerTreeRegistration registration(LayersId{0}, mcc);
     87  UpdateHitTestingTree();
     88 
     89  /* The layer tree looks like this:
     90 
     91                0
     92        |----|--+--|----|
     93        1    2     4    5
     94             |         /|\
     95             3        6 8 9
     96                      |
     97                      7
     98 
     99     Layers 1,2 have the same APZC
    100     Layers 4,6,8 have the same APZC
    101     Layer 7 has an APZC
    102     Layer 9 has an APZC
    103  */
    104 
    105  TestAsyncPanZoomController* nullAPZC = nullptr;
    106  // Ensure all the scrollable layers have an APZC
    107 
    108  EXPECT_FALSE(HasScrollableFrameMetrics(layers[0]));
    109  EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
    110  EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
    111  EXPECT_FALSE(HasScrollableFrameMetrics(layers[3]));
    112  EXPECT_NE(nullAPZC, ApzcOf(layers[4]));
    113  EXPECT_FALSE(HasScrollableFrameMetrics(layers[5]));
    114  EXPECT_NE(nullAPZC, ApzcOf(layers[6]));
    115  EXPECT_NE(nullAPZC, ApzcOf(layers[7]));
    116  EXPECT_NE(nullAPZC, ApzcOf(layers[8]));
    117  EXPECT_NE(nullAPZC, ApzcOf(layers[9]));
    118  // Ensure those that scroll together have the same APZCs
    119  EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
    120  EXPECT_EQ(ApzcOf(layers[4]), ApzcOf(layers[6]));
    121  EXPECT_EQ(ApzcOf(layers[8]), ApzcOf(layers[6]));
    122  // Ensure those that don't scroll together have different APZCs
    123  EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[4]));
    124  EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[7]));
    125  EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[9]));
    126  EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[7]));
    127  EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[9]));
    128  EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
    129  // Ensure the APZC parent chains are set up correctly
    130  TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
    131  TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
    132  TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
    133  TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]);
    134  EXPECT_EQ(nullptr, layers1_2->GetParent());
    135  EXPECT_EQ(nullptr, layers4_6_8->GetParent());
    136  EXPECT_EQ(layers4_6_8, layer7->GetParent());
    137  EXPECT_EQ(nullptr, layer9->GetParent());
    138  // Ensure the hit-testing tree looks like the layer tree
    139  RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
    140  RefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
    141  RefPtr<HitTestingTreeNode> node4 = node5->GetPrevSibling();
    142  RefPtr<HitTestingTreeNode> node2 = node4->GetPrevSibling();
    143  RefPtr<HitTestingTreeNode> node1 = node2->GetPrevSibling();
    144  RefPtr<HitTestingTreeNode> node3 = node2->GetLastChild();
    145  RefPtr<HitTestingTreeNode> node9 = node5->GetLastChild();
    146  RefPtr<HitTestingTreeNode> node8 = node9->GetPrevSibling();
    147  RefPtr<HitTestingTreeNode> node6 = node8->GetPrevSibling();
    148  RefPtr<HitTestingTreeNode> node7 = node6->GetLastChild();
    149  EXPECT_EQ(nullptr, node1->GetPrevSibling());
    150  EXPECT_EQ(nullptr, node3->GetPrevSibling());
    151  EXPECT_EQ(nullptr, node6->GetPrevSibling());
    152  EXPECT_EQ(nullptr, node7->GetPrevSibling());
    153  EXPECT_EQ(nullptr, node1->GetLastChild());
    154  EXPECT_EQ(nullptr, node3->GetLastChild());
    155  EXPECT_EQ(nullptr, node4->GetLastChild());
    156  EXPECT_EQ(nullptr, node7->GetLastChild());
    157  EXPECT_EQ(nullptr, node8->GetLastChild());
    158  EXPECT_EQ(nullptr, node9->GetLastChild());
    159 
    160  // Assertions about hit-testing have been ported to mochitest,
    161  // in helper_hittest_bug1730606-4.html.
    162 }
    163 
    164 TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
    165  // The main purpose of this test is to verify that touch-start events (or
    166  // anything that starts a new input block) don't ever get untransformed. This
    167  // should always hold because the APZ code should flush repaints when we start
    168  // a new input block and the transform to gecko space should be empty.
    169 
    170  CreateSimpleScrollingLayer();
    171  ScopedLayerTreeRegistration registration(LayersId{0}, mcc);
    172  UpdateHitTestingTree();
    173  RefPtr<TestAsyncPanZoomController> apzcroot = ApzcOf(root);
    174 
    175  // At this point, the following holds (all coordinates in screen pixels):
    176  // layers[0] has content from (0,0)-(500,500), clipped by composition bounds
    177  // (0,0)-(200,200)
    178 
    179  MockFunction<void(std::string checkPointName)> check;
    180 
    181  {
    182    InSequence s;
    183 
    184    EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
    185    EXPECT_CALL(check, Call("post-first-touch-start"));
    186    EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
    187    EXPECT_CALL(check, Call("post-second-fling"));
    188    EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
    189    EXPECT_CALL(check, Call("post-second-touch-start"));
    190  }
    191 
    192  // This first pan will move the APZC by 50 pixels, and dispatch a paint
    193  // request.
    194  Pan(apzcroot, 100, 50, PanOptions::NoFling);
    195 
    196  // Verify that a touch start doesn't get untransformed
    197  ScreenIntPoint touchPoint(50, 50);
    198  MultiTouchInput mti =
    199      CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
    200  mti.mTouches.AppendElement(
    201      SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0));
    202 
    203  EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
    204            manager->ReceiveInputEvent(mti).GetStatus());
    205  EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
    206  check.Call("post-first-touch-start");
    207 
    208  // Send a touchend to clear state
    209  mti.mType = MultiTouchInput::MULTITOUCH_END;
    210  manager->ReceiveInputEvent(mti);
    211 
    212  mcc->AdvanceByMillis(1000);
    213 
    214  // Now do two pans. The first of these will dispatch a repaint request, as
    215  // above. The second will get stuck in the paint throttler because the first
    216  // one doesn't get marked as "completed", so this will result in a non-empty
    217  // LD transform. (Note that any outstanding repaint requests from the first
    218  // half of this test don't impact this half because we advance the time by 1
    219  // second, which will trigger the max-wait-exceeded codepath in the paint
    220  // throttler).
    221  Pan(apzcroot, 100, 50, PanOptions::NoFling);
    222  check.Call("post-second-fling");
    223  Pan(apzcroot, 100, 50, PanOptions::NoFling);
    224 
    225  // Ensure that a touch start again doesn't get untransformed by flushing
    226  // a repaint
    227  mti.mType = MultiTouchInput::MULTITOUCH_START;
    228  EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
    229            manager->ReceiveInputEvent(mti).GetStatus());
    230  EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
    231  check.Call("post-second-touch-start");
    232 
    233  mti.mType = MultiTouchInput::MULTITOUCH_END;
    234  EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
    235            manager->ReceiveInputEvent(mti).GetStatus());
    236  EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
    237 }
    238 
    239 TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) {
    240  // The purpose of this test is to ensure that wheel events trigger a repaint
    241  // flush as per bug 1166871, and that the wheel event untransform is a no-op.
    242 
    243  CreateSimpleScrollingLayer();
    244  ScopedLayerTreeRegistration registration(LayersId{0}, mcc);
    245  UpdateHitTestingTree();
    246  TestAsyncPanZoomController* apzcroot = ApzcOf(root);
    247 
    248  EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3));
    249  ScreenPoint origin(100, 50);
    250  for (int i = 0; i < 3; i++) {
    251    ScrollWheelInput swi(mcc->Time(), 0, ScrollWheelInput::SCROLLMODE_INSTANT,
    252                         ScrollWheelInput::SCROLLDELTA_PIXEL, origin, 0, 10,
    253                         false, WheelDeltaAdjustmentStrategy::eNone);
    254    EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
    255              manager->ReceiveInputEvent(swi).GetStatus());
    256    EXPECT_EQ(origin, swi.mOrigin);
    257 
    258    AsyncTransform viewTransform;
    259    ParentLayerPoint point;
    260    apzcroot->SampleContentTransformForFrame(&viewTransform, point);
    261    EXPECT_EQ(0, point.x);
    262    EXPECT_EQ((i + 1) * 10, point.y);
    263    EXPECT_EQ(0, viewTransform.mTranslation.x);
    264    EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y);
    265 
    266    mcc->AdvanceByMillis(5);
    267  }
    268 }
    269 
    270 TEST_F(APZHitTestingTester, TestForceDisableApz) {
    271  CreateSimpleScrollingLayer();
    272  ScopedLayerTreeRegistration registration(LayersId{0}, mcc);
    273  UpdateHitTestingTree();
    274  DisableApzOn(root);
    275  TestAsyncPanZoomController* apzcroot = ApzcOf(root);
    276 
    277  ScreenPoint origin(100, 50);
    278  ScrollWheelInput swi(mcc->Time(), 0, ScrollWheelInput::SCROLLMODE_INSTANT,
    279                       ScrollWheelInput::SCROLLDELTA_PIXEL, origin, 0, 10,
    280                       false, WheelDeltaAdjustmentStrategy::eNone);
    281  EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
    282            manager->ReceiveInputEvent(swi).GetStatus());
    283  EXPECT_EQ(origin, swi.mOrigin);
    284 
    285  AsyncTransform viewTransform;
    286  ParentLayerPoint point;
    287  apzcroot->SampleContentTransformForFrame(&viewTransform, point);
    288  // Since APZ is force-disabled, we expect to see the async transform via
    289  // the NORMAL AsyncMode, but not via the RESPECT_FORCE_DISABLE AsyncMode.
    290  EXPECT_EQ(0, point.x);
    291  EXPECT_EQ(10, point.y);
    292  EXPECT_EQ(0, viewTransform.mTranslation.x);
    293  EXPECT_EQ(-10, viewTransform.mTranslation.y);
    294  viewTransform = apzcroot->GetCurrentAsyncTransform(
    295      AsyncPanZoomController::eForCompositing);
    296  point = apzcroot->GetCurrentAsyncScrollOffset(
    297      AsyncPanZoomController::eForCompositing);
    298  EXPECT_EQ(0, point.x);
    299  EXPECT_EQ(0, point.y);
    300  EXPECT_EQ(0, viewTransform.mTranslation.x);
    301  EXPECT_EQ(0, viewTransform.mTranslation.y);
    302 
    303  mcc->AdvanceByMillis(10);
    304 
    305  // With untransforming events we should get normal behaviour (in this case,
    306  // no noticeable untransform, because the repaint request already got
    307  // flushed).
    308  swi = ScrollWheelInput(mcc->Time(), 0, ScrollWheelInput::SCROLLMODE_INSTANT,
    309                         ScrollWheelInput::SCROLLDELTA_PIXEL, origin, 0, 0,
    310                         false, WheelDeltaAdjustmentStrategy::eNone);
    311  EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
    312            manager->ReceiveInputEvent(swi).GetStatus());
    313  EXPECT_EQ(origin, swi.mOrigin);
    314 }
    315 
    316 TEST_F(APZHitTestingTester, Bug1148350) {
    317  CreateBug1148350LayerTree();
    318  ScopedLayerTreeRegistration registration(LayersId{0}, mcc);
    319  UpdateHitTestingTree();
    320 
    321  MockFunction<void(std::string checkPointName)> check;
    322  {
    323    InSequence s;
    324    EXPECT_CALL(*mcc,
    325                HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0,
    326                          ApzcOf(layers[1])->GetGuid(), _, _))
    327        .Times(1);
    328    EXPECT_CALL(check, Call("Tapped without transform"));
    329    EXPECT_CALL(*mcc,
    330                HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0,
    331                          ApzcOf(layers[1])->GetGuid(), _, _))
    332        .Times(1);
    333    EXPECT_CALL(check, Call("Tapped with interleaved transform"));
    334  }
    335 
    336  Tap(manager, ScreenIntPoint(100, 100), TimeDuration::FromMilliseconds(100));
    337  mcc->RunThroughDelayedTasks();
    338  check.Call("Tapped without transform");
    339 
    340  uint64_t blockId =
    341      TouchDown(manager, ScreenIntPoint(100, 100), mcc->Time()).mInputBlockId;
    342  SetDefaultAllowedTouchBehavior(manager, blockId);
    343  mcc->AdvanceByMillis(100);
    344 
    345  layers[0]->SetVisibleRect(LayerIntRect(0, 50, 200, 150));
    346  layers[0]->SetTransform(Matrix4x4::Translation(0, 50, 0));
    347  UpdateHitTestingTree();
    348 
    349  TouchUp(manager, ScreenIntPoint(100, 100), mcc->Time());
    350  mcc->RunThroughDelayedTasks();
    351  check.Call("Tapped with interleaved transform");
    352 }