tor-browser

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

TestPinching.cpp (26717B)


      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 #include "InputUtils.h"
     10 #include "mozilla/StaticPrefs_apz.h"
     11 
     12 class APZCPinchTester : public APZCBasicTester {
     13 private:
     14  // This (multiplied by apz.touch_start_tolerance) needs to be the hypotenuse
     15  // in a Pythagorean triple, along with overcomeTouchToleranceX and
     16  // overcomeTouchToleranceY from APZCTesterBase::Pan().
     17  // This is because APZCTesterBase::Pan(), when run without the
     18  // PanOptions::ExactCoordinates option, will need to first overcome the
     19  // touch start tolerance by performing a move of exactly
     20  // (apz.touch_start_tolerance * DPI) length.
     21  // When moving on both axes at once, we need to use integers for both legs
     22  // (overcomeTouchToleranceX and overcomeTouchToleranceY) while making sure
     23  // that the hypotenuse is also a round integer number (hence Pythagorean
     24  // triples). (The hypotenuse is the length of the movement in this case.)
     25  static const int mDPI = 100;
     26 
     27 public:
     28  explicit APZCPinchTester(
     29      AsyncPanZoomController::GestureBehavior aGestureBehavior =
     30          AsyncPanZoomController::DEFAULT_GESTURES)
     31      : APZCBasicTester(aGestureBehavior) {}
     32 
     33  void SetUp() override {
     34    APZCBasicTester::SetUp();
     35    tm->SetDPI(mDPI);
     36  }
     37 
     38 protected:
     39  FrameMetrics GetPinchableFrameMetrics() {
     40    FrameMetrics fm;
     41    fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 200));
     42    fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
     43    fm.SetVisualScrollOffset(CSSPoint(300, 300));
     44    fm.SetLayoutViewport(CSSRect(300, 300, 100, 200));
     45    fm.SetZoom(CSSToParentLayerScale(2.0));
     46    // APZC only allows zooming on the root scrollable frame.
     47    fm.SetIsRootContent(true);
     48    // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
     49    return fm;
     50  }
     51 
     52  void DoPinchTest(bool aShouldTriggerPinch,
     53                   nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr) {
     54    apzc->SetFrameMetrics(GetPinchableFrameMetrics());
     55    MakeApzcZoomable();
     56 
     57    if (aShouldTriggerPinch) {
     58      // One repaint request for each gesture.
     59      EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
     60    } else {
     61      EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
     62    }
     63 
     64    int touchInputId = 0;
     65    if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
     66      PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
     67                                        touchInputId, aShouldTriggerPinch,
     68                                        aAllowedTouchBehaviors);
     69    } else {
     70      PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
     71                                        aShouldTriggerPinch);
     72    }
     73 
     74    apzc->AssertStateIsReset();
     75 
     76    FrameMetrics fm = apzc->GetFrameMetrics();
     77 
     78    if (aShouldTriggerPinch) {
     79      // the visible area of the document in CSS pixels is now x=325 y=330 w=40
     80      // h=80
     81      EXPECT_EQ(2.5f, fm.GetZoom().scale);
     82      EXPECT_EQ(325, fm.GetVisualScrollOffset().x);
     83      EXPECT_EQ(330, fm.GetVisualScrollOffset().y);
     84    } else {
     85      // The frame metrics should stay the same since touch-action:none makes
     86      // apzc ignore pinch gestures.
     87      EXPECT_EQ(2.0f, fm.GetZoom().scale);
     88      EXPECT_EQ(300, fm.GetVisualScrollOffset().x);
     89      EXPECT_EQ(300, fm.GetVisualScrollOffset().y);
     90    }
     91 
     92    // part 2 of the test, move to the top-right corner of the page and pinch
     93    // and make sure we stay in the correct spot
     94    fm.SetZoom(CSSToParentLayerScale(2.0));
     95    fm.SetVisualScrollOffset(CSSPoint(930, 5));
     96    apzc->SetFrameMetrics(fm);
     97    // the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100
     98 
     99    if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
    100      PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
    101                                        touchInputId, aShouldTriggerPinch,
    102                                        aAllowedTouchBehaviors);
    103    } else {
    104      PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
    105                                        aShouldTriggerPinch);
    106    }
    107 
    108    apzc->AssertStateIsReset();
    109 
    110    fm = apzc->GetFrameMetrics();
    111 
    112    if (aShouldTriggerPinch) {
    113      // the visible area of the document in CSS pixels is now x=805 y=0 w=100
    114      // h=200
    115      EXPECT_EQ(1.0f, fm.GetZoom().scale);
    116      EXPECT_EQ(805, fm.GetVisualScrollOffset().x);
    117      EXPECT_EQ(0, fm.GetVisualScrollOffset().y);
    118    } else {
    119      EXPECT_EQ(2.0f, fm.GetZoom().scale);
    120      EXPECT_EQ(930, fm.GetVisualScrollOffset().x);
    121      EXPECT_EQ(5, fm.GetVisualScrollOffset().y);
    122    }
    123  }
    124 };
    125 
    126 class APZCPinchGestureDetectorTester : public APZCPinchTester {
    127 public:
    128  APZCPinchGestureDetectorTester()
    129      : APZCPinchTester(AsyncPanZoomController::USE_GESTURE_DETECTOR) {}
    130 
    131  void DoPinchWithPreventDefaultTest() {
    132    FrameMetrics originalMetrics = GetPinchableFrameMetrics();
    133    apzc->SetFrameMetrics(originalMetrics);
    134 
    135    MakeApzcWaitForMainThread();
    136    MakeApzcZoomable();
    137 
    138    uint64_t blockId = 0;
    139    PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25,
    140                        PinchOptions().OutInputBlockId(&blockId));
    141 
    142    // Send the prevent-default notification for the touch block
    143    apzc->ContentReceivedInputBlock(blockId, true);
    144 
    145    // verify the metrics didn't change (i.e. the pinch was ignored)
    146    FrameMetrics fm = apzc->GetFrameMetrics();
    147    EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
    148    EXPECT_EQ(originalMetrics.GetVisualScrollOffset().x,
    149              fm.GetVisualScrollOffset().x);
    150    EXPECT_EQ(originalMetrics.GetVisualScrollOffset().y,
    151              fm.GetVisualScrollOffset().y);
    152 
    153    apzc->AssertStateIsReset();
    154  }
    155 };
    156 
    157 class APZCPinchLockingTester : public APZCPinchTester {
    158 private:
    159  ScreenIntPoint mFocus;
    160  float mSpan;
    161  int mPinchLockBufferMaxAge;
    162 
    163 public:
    164  APZCPinchLockingTester()
    165      : APZCPinchTester(AsyncPanZoomController::USE_GESTURE_DETECTOR),
    166        mFocus(ScreenIntPoint(200, 300)),
    167        mSpan(10.0) {}
    168 
    169  virtual void SetUp() {
    170    mPinchLockBufferMaxAge =
    171        StaticPrefs::apz_pinch_lock_buffer_max_age_AtStartup();
    172 
    173    APZCPinchTester::SetUp();
    174    apzc->SetFrameMetrics(GetPinchableFrameMetrics());
    175    MakeApzcZoomable();
    176 
    177    auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
    178                                         mFocus, mSpan, mSpan, mcc->Time());
    179    apzc->ReceiveInputEvent(event);
    180    mcc->AdvanceBy(TimeDuration::FromMilliseconds(mPinchLockBufferMaxAge + 1));
    181  }
    182 
    183  void twoFingerPan() {
    184    ScreenCoord panDistance =
    185        StaticPrefs::apz_pinch_lock_scroll_lock_threshold() * 1.2 *
    186        tm->GetDPI();
    187 
    188    mFocus = ScreenIntPoint((int)(mFocus.x.value + panDistance),
    189                            (int)(mFocus.y.value));
    190 
    191    auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
    192                                         mFocus, mSpan, mSpan, mcc->Time());
    193    apzc->ReceiveInputEvent(event);
    194    mcc->AdvanceBy(TimeDuration::FromMilliseconds(mPinchLockBufferMaxAge + 1));
    195  }
    196 
    197  void twoFingerZoom() {
    198    float pinchDistance =
    199        StaticPrefs::apz_pinch_lock_span_breakout_threshold() * 1.2 *
    200        tm->GetDPI();
    201 
    202    float newSpan = mSpan + pinchDistance;
    203 
    204    auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
    205                                         mFocus, newSpan, mSpan, mcc->Time());
    206    apzc->ReceiveInputEvent(event);
    207    mcc->AdvanceBy(TimeDuration::FromMilliseconds(mPinchLockBufferMaxAge + 1));
    208    mSpan = newSpan;
    209  }
    210 
    211  bool isPinchLockActive() {
    212    FrameMetrics originalMetrics = apzc->GetFrameMetrics();
    213 
    214    // Send a small scale input to the APZC
    215    float pinchDistance =
    216        StaticPrefs::apz_pinch_lock_span_breakout_threshold() * 0.8 *
    217        tm->GetDPI();
    218    auto event =
    219        CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE, mFocus,
    220                                mSpan + pinchDistance, mSpan, mcc->Time());
    221    apzc->ReceiveInputEvent(event);
    222 
    223    FrameMetrics result = apzc->GetFrameMetrics();
    224    bool lockActive = originalMetrics.GetZoom() == result.GetZoom() &&
    225                      originalMetrics.GetVisualScrollOffset().x ==
    226                          result.GetVisualScrollOffset().x &&
    227                      originalMetrics.GetVisualScrollOffset().y ==
    228                          result.GetVisualScrollOffset().y;
    229 
    230    // Avoid side effects, reset to original frame metrics
    231    apzc->SetFrameMetrics(originalMetrics);
    232    return lockActive;
    233  }
    234 };
    235 
    236 TEST_F(APZCPinchGestureDetectorTester,
    237       Pinch_UseGestureDetector_TouchActionNone) {
    238  nsTArray<uint32_t> behaviors = {mozilla::layers::AllowedTouchBehavior::NONE,
    239                                  mozilla::layers::AllowedTouchBehavior::NONE};
    240  DoPinchTest(false, &behaviors);
    241 }
    242 
    243 TEST_F(APZCPinchGestureDetectorTester,
    244       Pinch_UseGestureDetector_TouchActionZoom) {
    245  nsTArray<uint32_t> behaviors;
    246  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
    247  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
    248  DoPinchTest(true, &behaviors);
    249 }
    250 
    251 TEST_F(APZCPinchGestureDetectorTester,
    252       Pinch_UseGestureDetector_TouchActionNotAllowZoom) {
    253  nsTArray<uint32_t> behaviors;
    254  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
    255  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
    256  DoPinchTest(false, &behaviors);
    257 }
    258 
    259 TEST_F(APZCPinchGestureDetectorTester,
    260       Pinch_UseGestureDetector_TouchActionNone_NoAPZZoom) {
    261  SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false);
    262 
    263  // Since we are preventing the pinch action via touch-action we should not be
    264  // sending the pinch gesture notifications that would normally be sent when
    265  // apz_allow_zooming is false.
    266  EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _, _)).Times(0);
    267  nsTArray<uint32_t> behaviors = {mozilla::layers::AllowedTouchBehavior::NONE,
    268                                  mozilla::layers::AllowedTouchBehavior::NONE};
    269  DoPinchTest(false, &behaviors);
    270 }
    271 
    272 TEST_F(
    273    APZCPinchGestureDetectorTester,
    274    Pinch_UseGestureDetector_TouchActionPanY_APZZoom_When_ForceUserScalable) {
    275  SCOPED_GFX_PREF_BOOL("browser.ui.zoom.force-user-scalable", true);
    276 
    277  nsTArray<uint32_t> behaviors = {
    278      mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN,
    279      mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN};
    280  DoPinchTest(true, &behaviors);
    281 }
    282 
    283 TEST_F(
    284    APZCPinchGestureDetectorTester,
    285    Pinch_UseGestureDetector_TouchActionNone_NoAPZZoom_When_ForceUserScalable) {
    286  SCOPED_GFX_PREF_BOOL("browser.ui.zoom.force-user-scalable", true);
    287 
    288  EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _, _)).Times(0);
    289  nsTArray<uint32_t> behaviors = {
    290      {mozilla::layers::AllowedTouchBehavior::NONE,
    291       mozilla::layers::AllowedTouchBehavior::NONE},
    292  };
    293  DoPinchTest(false, &behaviors);
    294 }
    295 
    296 TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
    297  DoPinchWithPreventDefaultTest();
    298 }
    299 
    300 TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault_NoAPZZoom) {
    301  SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false);
    302 
    303  // Since we are preventing the pinch action we should not be sending the pinch
    304  // gesture notifications that would normally be sent when apz_allow_zooming is
    305  // false.
    306  EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _, _)).Times(0);
    307 
    308  DoPinchWithPreventDefaultTest();
    309 }
    310 
    311 TEST_F(APZCPinchGestureDetectorTester, Panning_TwoFingerFling_ZoomDisabled) {
    312  SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f);
    313 
    314  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
    315  MakeApzcUnzoomable();
    316 
    317  // Perform a two finger pan
    318  PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), 1,
    319                      PinchOptions().SecondFocus(ScreenIntPoint(100, 100)));
    320 
    321  // Expect to be in a flinging state
    322  apzc->AssertStateIsFling();
    323 }
    324 
    325 TEST_F(APZCPinchGestureDetectorTester, Pinch_DoesntFling_ZoomDisabled) {
    326  SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f);
    327 
    328  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
    329  MakeApzcUnzoomable();
    330 
    331  // Perform a pinch
    332  PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), 2,
    333                      PinchOptions()
    334                          .Flags(PinchFlags::LiftFinger2)
    335                          .Vertical(true)
    336                          .SecondFocus(ScreenIntPoint(100, 100)));
    337 
    338  // Lift second finger after a pause
    339  mcc->AdvanceBy(TimeDuration::FromMilliseconds(50));
    340  TouchUp(apzc, ScreenIntPoint(100, 100), mcc->Time());
    341 
    342  // Pinch should not trigger a fling
    343  EXPECT_EQ(apzc->GetVelocityVector().y, 0);
    344 }
    345 
    346 TEST_F(APZCPinchGestureDetectorTester, Panning_TwoFingerFling_ZoomEnabled) {
    347  SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f);
    348 
    349  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
    350  MakeApzcZoomable();
    351 
    352  // Perform a two finger pan
    353  PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), 1,
    354                      PinchOptions().SecondFocus(ScreenIntPoint(100, 100)));
    355 
    356  // Expect to NOT be in flinging state
    357  apzc->AssertStateIsReset();
    358 }
    359 
    360 TEST_F(APZCPinchGestureDetectorTester,
    361       Panning_TwoThenOneFingerFling_ZoomEnabled) {
    362  SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f);
    363 
    364  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
    365  MakeApzcZoomable();
    366 
    367  // Perform a two finger pan lifting only the first finger
    368  PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), 1,
    369                      PinchOptions()
    370                          .Flags(PinchFlags::LiftFinger2)
    371                          .SecondFocus(ScreenIntPoint(100, 100)));
    372 
    373  // Lift second finger after a pause
    374  mcc->AdvanceBy(TimeDuration::FromMilliseconds(50));
    375  TouchUp(apzc, ScreenIntPoint(100, 100), mcc->Time());
    376 
    377  // This gesture should activate the pinch lock, and result
    378  // in a fling even if the page is zoomable.
    379  apzc->AssertStateIsFling();
    380 }
    381 
    382 TEST_F(APZCPinchGestureDetectorTester,
    383       Panning_TwoThenOneFingerFling_ZoomDisabled) {
    384  SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f);
    385 
    386  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
    387  MakeApzcUnzoomable();
    388 
    389  // Perform a two finger pan lifting only the first finger
    390  PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), 1,
    391                      PinchOptions()
    392                          .Flags(PinchFlags::LiftFinger2)
    393                          .SecondFocus(ScreenIntPoint(100, 100)));
    394 
    395  // Lift second finger after a pause
    396  mcc->AdvanceBy(TimeDuration::FromMilliseconds(50));
    397  TouchUp(apzc, ScreenIntPoint(100, 100), mcc->Time());
    398 
    399  // This gesture should activate the pinch lock and result in a fling
    400  apzc->AssertStateIsFling();
    401 }
    402 
    403 TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
    404  // set up APZ
    405  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
    406  MakeApzcUnzoomable();
    407 
    408  nsEventStatus statuses[3];  // scalebegin, scale, scaleend
    409  PinchWithPinchInput(apzc, ScreenIntPoint(250, 350), ScreenIntPoint(200, 300),
    410                      10, &statuses);
    411 
    412  FrameMetrics fm = apzc->GetFrameMetrics();
    413 
    414  // It starts from (300, 300), then moves the focus point from (250, 350) to
    415  // (200, 300) pans by (50, 50) screen pixels, but there is a 2x zoom, which
    416  // causes the scroll offset to change by half of that (25, 25) pixels.
    417  EXPECT_EQ(325, fm.GetVisualScrollOffset().x);
    418  EXPECT_EQ(325, fm.GetVisualScrollOffset().y);
    419  EXPECT_EQ(2.0, fm.GetZoom().scale);
    420 }
    421 
    422 TEST_F(APZCPinchTester, Panning_Beyond_LayoutViewport) {
    423  SCOPED_GFX_PREF_INT("apz.axis_lock.mode", 0);
    424 
    425  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
    426  MakeApzcZoomable();
    427 
    428  // Case 1 - visual viewport is still inside layout viewport.
    429  Pan(apzc, 350, 300, PanOptions::NoFling);
    430  FrameMetrics fm = apzc->GetFrameMetrics();
    431  // It starts from (300, 300) pans by (0, 50) screen pixels, but there is a
    432  // 2x zoom, which causes the scroll offset to change by half of that (0, 25).
    433  // But the visual viewport is still inside the layout viewport.
    434  EXPECT_EQ(300, fm.GetVisualScrollOffset().x);
    435  EXPECT_EQ(325, fm.GetVisualScrollOffset().y);
    436  EXPECT_EQ(300, fm.GetLayoutViewport().X());
    437  EXPECT_EQ(300, fm.GetLayoutViewport().Y());
    438 
    439  // Case 2 - visual viewport crosses the bottom boundary of the layout
    440  // viewport.
    441  Pan(apzc, 525, 325, PanOptions::NoFling);
    442  fm = apzc->GetFrameMetrics();
    443  // It starts from (300, 325) pans by (0, 200) screen pixels, but there is a
    444  // 2x zoom, which causes the scroll offset to change by half of that
    445  // (0, 100). The visual viewport crossed the bottom boundary of the layout
    446  // viewport by 25px.
    447  EXPECT_EQ(300, fm.GetVisualScrollOffset().x);
    448  EXPECT_EQ(425, fm.GetVisualScrollOffset().y);
    449  EXPECT_EQ(300, fm.GetLayoutViewport().X());
    450  EXPECT_EQ(325, fm.GetLayoutViewport().Y());
    451 
    452  // Case 3 - visual viewport crosses the top boundary of the layout viewport.
    453  Pan(apzc, 425, 775, PanOptions::NoFling);
    454  fm = apzc->GetFrameMetrics();
    455  // It starts from (300, 425) pans by (0, -350) screen pixels, but there is a
    456  // 2x zoom, which causes the scroll offset to change by half of that
    457  // (0, -175). The visual viewport crossed the top of the layout viewport by
    458  // 75px.
    459  EXPECT_EQ(300, fm.GetVisualScrollOffset().x);
    460  EXPECT_EQ(250, fm.GetVisualScrollOffset().y);
    461  EXPECT_EQ(300, fm.GetLayoutViewport().X());
    462  EXPECT_EQ(250, fm.GetLayoutViewport().Y());
    463 
    464  // Case 4 - visual viewport crosses the left boundary of the layout viewport.
    465  Pan(apzc, ScreenIntPoint(150, 10), ScreenIntPoint(350, 10),
    466      PanOptions::NoFling);
    467  fm = apzc->GetFrameMetrics();
    468  // It starts from (300, 250) pans by (-200, 0) screen pixels, but there is a
    469  // 2x zoom, which causes the scroll offset to change by half of that
    470  // (-100, 0). The visual viewport crossed the left boundary of the layout
    471  // viewport by 100px.
    472  EXPECT_EQ(200, fm.GetVisualScrollOffset().x);
    473  EXPECT_EQ(250, fm.GetVisualScrollOffset().y);
    474  EXPECT_EQ(200, fm.GetLayoutViewport().X());
    475  EXPECT_EQ(250, fm.GetLayoutViewport().Y());
    476 
    477  // Case 5 - visual viewport crosses the right boundary of the layout viewport.
    478  Pan(apzc, ScreenIntPoint(350, 10), ScreenIntPoint(150, 10),
    479      PanOptions::NoFling);
    480  fm = apzc->GetFrameMetrics();
    481  // It starts from (200, 250) pans by (200, 0) screen pixels, but there is a
    482  // 2x zoom, which causes the scroll offset to change by half of that
    483  // (100, 0). The visual viewport crossed the right boundary of the layout
    484  // viewport by 50px.
    485  EXPECT_EQ(300, fm.GetVisualScrollOffset().x);
    486  EXPECT_EQ(250, fm.GetVisualScrollOffset().y);
    487  EXPECT_EQ(250, fm.GetLayoutViewport().X());
    488  EXPECT_EQ(250, fm.GetLayoutViewport().Y());
    489 
    490  // Case 6 - visual viewport crosses both the vertical and horizontal
    491  // boundaries of the layout viewport by moving diagonally towards the
    492  // top-right corner.
    493  Pan(apzc, ScreenIntPoint(350, 200), ScreenIntPoint(150, 400),
    494      PanOptions::NoFling);
    495  fm = apzc->GetFrameMetrics();
    496  // It starts from (300, 250) pans by (200, -200) screen pixels, but there is
    497  // a 2x zoom, which causes the scroll offset to change by half of that
    498  // (100, -100). The visual viewport moved by (100, -100) outside the
    499  // boundary of the layout viewport.
    500  EXPECT_EQ(400, fm.GetVisualScrollOffset().x);
    501  EXPECT_EQ(150, fm.GetVisualScrollOffset().y);
    502  EXPECT_EQ(350, fm.GetLayoutViewport().X());
    503  EXPECT_EQ(150, fm.GetLayoutViewport().Y());
    504 }
    505 
    506 TEST_F(APZCPinchGestureDetectorTester, Pinch_APZZoom_Disabled) {
    507  SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false);
    508 
    509  FrameMetrics originalMetrics = GetPinchableFrameMetrics();
    510  apzc->SetFrameMetrics(originalMetrics);
    511 
    512  // When apz_allow_zooming is false, the ZoomConstraintsClient produces
    513  // ZoomConstraints with mAllowZoom set to false.
    514  MakeApzcUnzoomable();
    515 
    516  // With apz_allow_zooming false, we expect the NotifyPinchGesture function to
    517  // get called as the pinch progresses, but the metrics shouldn't change.
    518  EXPECT_CALL(*mcc,
    519              NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START,
    520                                 apzc->GetGuid(), _, LayoutDeviceCoord(0), _))
    521      .Times(1);
    522  EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE,
    523                                       apzc->GetGuid(), _, _, _))
    524      .Times(AtLeast(1));
    525  EXPECT_CALL(*mcc,
    526              NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END,
    527                                 apzc->GetGuid(), _, LayoutDeviceCoord(0), _))
    528      .Times(1);
    529 
    530  PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25);
    531 
    532  // verify the metrics didn't change (i.e. the pinch was ignored inside APZ)
    533  FrameMetrics fm = apzc->GetFrameMetrics();
    534  EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
    535  EXPECT_EQ(originalMetrics.GetVisualScrollOffset().x,
    536            fm.GetVisualScrollOffset().x);
    537  EXPECT_EQ(originalMetrics.GetVisualScrollOffset().y,
    538            fm.GetVisualScrollOffset().y);
    539 
    540  apzc->AssertStateIsReset();
    541 }
    542 
    543 TEST_F(APZCPinchGestureDetectorTester, Pinch_NoSpan) {
    544  SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false);
    545 
    546  FrameMetrics originalMetrics = GetPinchableFrameMetrics();
    547  apzc->SetFrameMetrics(originalMetrics);
    548 
    549  // When apz_allow_zooming is false, the ZoomConstraintsClient produces
    550  // ZoomConstraints with mAllowZoom set to false.
    551  MakeApzcUnzoomable();
    552 
    553  // With apz_allow_zooming false, we expect the NotifyPinchGesture function to
    554  // get called as the pinch progresses, but the metrics shouldn't change.
    555  EXPECT_CALL(*mcc,
    556              NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START,
    557                                 apzc->GetGuid(), _, LayoutDeviceCoord(0), _))
    558      .Times(1);
    559  EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE,
    560                                       apzc->GetGuid(), _, _, _))
    561      .Times(AtLeast(1));
    562  EXPECT_CALL(*mcc,
    563              NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END,
    564                                 apzc->GetGuid(), _, LayoutDeviceCoord(0), _))
    565      .Times(1);
    566 
    567  int inputId = 0;
    568  ScreenIntPoint focus(250, 300);
    569 
    570  // Do a pinch holding a zero span and moving the focus by y=100
    571 
    572  const TimeDuration TIME_BETWEEN_TOUCH_EVENT =
    573      TimeDuration::FromMilliseconds(50);
    574  const auto touchBehaviors = Some(nsTArray<uint32_t>{kDefaultTouchBehavior});
    575 
    576  MultiTouchInput mtiStart =
    577      MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, mcc->Time(), 0);
    578  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
    579  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
    580  apzc->ReceiveInputEvent(mtiStart, touchBehaviors);
    581  mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
    582 
    583  focus.y -= 35 + 1;  // this is to get over the PINCH_START_THRESHOLD in
    584                      // GestureEventListener.cpp
    585  MultiTouchInput mtiMove1 =
    586      MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0);
    587  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
    588  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
    589  apzc->ReceiveInputEvent(mtiMove1);
    590  mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
    591 
    592  focus.y -= 100;  // do a two-finger scroll of 100 screen pixels
    593  MultiTouchInput mtiMove2 =
    594      MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0);
    595  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
    596  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
    597  apzc->ReceiveInputEvent(mtiMove2);
    598  mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
    599 
    600  MultiTouchInput mtiEnd =
    601      MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, mcc->Time(), 0);
    602  mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
    603  mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
    604  apzc->ReceiveInputEvent(mtiEnd);
    605 
    606  // Done, check the metrics to make sure we scrolled by 100 screen pixels,
    607  // which is 50 CSS pixels for the pinchable frame metrics.
    608 
    609  FrameMetrics fm = apzc->GetFrameMetrics();
    610  EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
    611  EXPECT_EQ(originalMetrics.GetVisualScrollOffset().x,
    612            fm.GetVisualScrollOffset().x);
    613  EXPECT_EQ(originalMetrics.GetVisualScrollOffset().y + 50,
    614            fm.GetVisualScrollOffset().y);
    615 
    616  apzc->AssertStateIsReset();
    617 }
    618 
    619 TEST_F(APZCPinchTester, Pinch_TwoFinger_APZZoom_Disabled_Bug1354185) {
    620  // Set up APZ such that mZoomConstraints.mAllowZoom is false.
    621  SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false);
    622  apzc->SetFrameMetrics(GetPinchableFrameMetrics());
    623  MakeApzcUnzoomable();
    624 
    625  // We expect a repaint request for scrolling.
    626  EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
    627 
    628  // Send only the PINCHGESTURE_START and PINCHGESTURE_SCALE events,
    629  // in order to trigger a call to AsyncPanZoomController::OnScale
    630  // but not to AsyncPanZoomController::OnScaleEnd.
    631  ScreenIntPoint aFocus(250, 350);
    632  ScreenIntPoint aSecondFocus(200, 300);
    633  float aScale = 10;
    634  auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
    635                                       aFocus, 10.0, 10.0, mcc->Time());
    636  apzc->ReceiveInputEvent(event);
    637 
    638  event =
    639      CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
    640                              aSecondFocus, 10.0f * aScale, 10.0, mcc->Time());
    641  apzc->ReceiveInputEvent(event);
    642 }
    643 
    644 TEST_F(APZCPinchLockingTester, Pinch_Locking_Free) {
    645  SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 0);  // PINCH_FREE
    646 
    647  twoFingerPan();
    648  EXPECT_FALSE(isPinchLockActive());
    649 }
    650 
    651 TEST_F(APZCPinchLockingTester, Pinch_Locking_Normal_Lock) {
    652  SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 1);  // PINCH_NORMAL
    653 
    654  twoFingerPan();
    655  EXPECT_TRUE(isPinchLockActive());
    656 }
    657 
    658 TEST_F(APZCPinchLockingTester, Pinch_Locking_Normal_Lock_Break) {
    659  SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 1);  // PINCH_NORMAL
    660 
    661  twoFingerPan();
    662  twoFingerZoom();
    663  EXPECT_TRUE(isPinchLockActive());
    664 }
    665 
    666 TEST_F(APZCPinchLockingTester, Pinch_Locking_Sticky_Lock) {
    667  SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 2);  // PINCH_STICKY
    668 
    669  twoFingerPan();
    670  EXPECT_TRUE(isPinchLockActive());
    671 }
    672 
    673 TEST_F(APZCPinchLockingTester, Pinch_Locking_Sticky_Lock_Break) {
    674  SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 2);  // PINCH_STICKY
    675 
    676  twoFingerPan();
    677  twoFingerZoom();
    678  EXPECT_FALSE(isPinchLockActive());
    679 }
    680 
    681 TEST_F(APZCPinchLockingTester, Pinch_Locking_Sticky_Lock_Break_Lock) {
    682  SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 2);  // PINCH_STICKY
    683 
    684  twoFingerPan();
    685  twoFingerZoom();
    686  twoFingerPan();
    687  EXPECT_TRUE(isPinchLockActive());
    688 }