diff options
Diffstat (limited to 'gfx/layers/apz/test/gtest/TestPinching.cpp')
-rw-r--r-- | gfx/layers/apz/test/gtest/TestPinching.cpp | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/gfx/layers/apz/test/gtest/TestPinching.cpp b/gfx/layers/apz/test/gtest/TestPinching.cpp new file mode 100644 index 0000000000..f6d1280bf3 --- /dev/null +++ b/gfx/layers/apz/test/gtest/TestPinching.cpp @@ -0,0 +1,675 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "APZCBasicTester.h" +#include "APZTestCommon.h" +#include "InputUtils.h" +#include "mozilla/StaticPrefs_apz.h" + +class APZCPinchTester : public APZCBasicTester { + private: + // This (multiplied by apz.touch_start_tolerance) needs to be the hypotenuse + // in a Pythagorean triple, along with overcomeTouchToleranceX and + // overcomeTouchToleranceY from APZCTesterBase::Pan(). + // This is because APZCTesterBase::Pan(), when run without the + // PanOptions::ExactCoordinates option, will need to first overcome the + // touch start tolerance by performing a move of exactly + // (apz.touch_start_tolerance * DPI) length. + // When moving on both axes at once, we need to use integers for both legs + // (overcomeTouchToleranceX and overcomeTouchToleranceY) while making sure + // that the hypotenuse is also a round integer number (hence Pythagorean + // triples). (The hypotenuse is the length of the movement in this case.) + static const int mDPI = 100; + + public: + explicit APZCPinchTester( + AsyncPanZoomController::GestureBehavior aGestureBehavior = + AsyncPanZoomController::DEFAULT_GESTURES) + : APZCBasicTester(aGestureBehavior) {} + + void SetUp() override { + APZCBasicTester::SetUp(); + tm->SetDPI(mDPI); + } + + protected: + FrameMetrics GetPinchableFrameMetrics() { + FrameMetrics fm; + fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 200)); + fm.SetScrollableRect(CSSRect(0, 0, 980, 1000)); + fm.SetVisualScrollOffset(CSSPoint(300, 300)); + fm.SetLayoutViewport(CSSRect(300, 300, 100, 200)); + fm.SetZoom(CSSToParentLayerScale(2.0)); + // APZC only allows zooming on the root scrollable frame. + fm.SetIsRootContent(true); + // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100 + return fm; + } + + void DoPinchTest(bool aShouldTriggerPinch, + nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr) { + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcZoomable(); + + if (aShouldTriggerPinch) { + // One repaint request for each gesture. + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2); + } else { + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0); + } + + int touchInputId = 0; + if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) { + PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25, + touchInputId, aShouldTriggerPinch, + aAllowedTouchBehaviors); + } else { + PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25, + aShouldTriggerPinch); + } + + apzc->AssertStateIsReset(); + + FrameMetrics fm = apzc->GetFrameMetrics(); + + if (aShouldTriggerPinch) { + // the visible area of the document in CSS pixels is now x=325 y=330 w=40 + // h=80 + EXPECT_EQ(2.5f, fm.GetZoom().scale); + EXPECT_EQ(325, fm.GetVisualScrollOffset().x); + EXPECT_EQ(330, fm.GetVisualScrollOffset().y); + } else { + // The frame metrics should stay the same since touch-action:none makes + // apzc ignore pinch gestures. + EXPECT_EQ(2.0f, fm.GetZoom().scale); + EXPECT_EQ(300, fm.GetVisualScrollOffset().x); + EXPECT_EQ(300, fm.GetVisualScrollOffset().y); + } + + // part 2 of the test, move to the top-right corner of the page and pinch + // and make sure we stay in the correct spot + fm.SetZoom(CSSToParentLayerScale(2.0)); + fm.SetVisualScrollOffset(CSSPoint(930, 5)); + apzc->SetFrameMetrics(fm); + // the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100 + + if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) { + PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5, + touchInputId, aShouldTriggerPinch, + aAllowedTouchBehaviors); + } else { + PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5, + aShouldTriggerPinch); + } + + apzc->AssertStateIsReset(); + + fm = apzc->GetFrameMetrics(); + + if (aShouldTriggerPinch) { + // the visible area of the document in CSS pixels is now x=805 y=0 w=100 + // h=200 + EXPECT_EQ(1.0f, fm.GetZoom().scale); + EXPECT_EQ(805, fm.GetVisualScrollOffset().x); + EXPECT_EQ(0, fm.GetVisualScrollOffset().y); + } else { + EXPECT_EQ(2.0f, fm.GetZoom().scale); + EXPECT_EQ(930, fm.GetVisualScrollOffset().x); + EXPECT_EQ(5, fm.GetVisualScrollOffset().y); + } + } +}; + +class APZCPinchGestureDetectorTester : public APZCPinchTester { + public: + APZCPinchGestureDetectorTester() + : APZCPinchTester(AsyncPanZoomController::USE_GESTURE_DETECTOR) {} + + void DoPinchWithPreventDefaultTest() { + FrameMetrics originalMetrics = GetPinchableFrameMetrics(); + apzc->SetFrameMetrics(originalMetrics); + + MakeApzcWaitForMainThread(); + MakeApzcZoomable(); + + int touchInputId = 0; + uint64_t blockId = 0; + PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId, + nullptr, nullptr, &blockId); + + // Send the prevent-default notification for the touch block + apzc->ContentReceivedInputBlock(blockId, true); + + // verify the metrics didn't change (i.e. the pinch was ignored) + FrameMetrics fm = apzc->GetFrameMetrics(); + EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom()); + EXPECT_EQ(originalMetrics.GetVisualScrollOffset().x, + fm.GetVisualScrollOffset().x); + EXPECT_EQ(originalMetrics.GetVisualScrollOffset().y, + fm.GetVisualScrollOffset().y); + + apzc->AssertStateIsReset(); + } +}; + +class APZCPinchLockingTester : public APZCPinchTester { + private: + ScreenIntPoint mFocus; + float mSpan; + int mPinchLockBufferMaxAge; + + public: + APZCPinchLockingTester() + : APZCPinchTester(AsyncPanZoomController::USE_GESTURE_DETECTOR), + mFocus(ScreenIntPoint(200, 300)), + mSpan(10.0) {} + + virtual void SetUp() { + mPinchLockBufferMaxAge = + StaticPrefs::apz_pinch_lock_buffer_max_age_AtStartup(); + + APZCPinchTester::SetUp(); + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcZoomable(); + + auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START, + mFocus, mSpan, mSpan, mcc->Time()); + apzc->ReceiveInputEvent(event); + mcc->AdvanceBy(TimeDuration::FromMilliseconds(mPinchLockBufferMaxAge + 1)); + } + + void twoFingerPan() { + ScreenCoord panDistance = + StaticPrefs::apz_pinch_lock_scroll_lock_threshold() * 1.2 * + tm->GetDPI(); + + mFocus = ScreenIntPoint((int)(mFocus.x.value + panDistance), + (int)(mFocus.y.value)); + + auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE, + mFocus, mSpan, mSpan, mcc->Time()); + apzc->ReceiveInputEvent(event); + mcc->AdvanceBy(TimeDuration::FromMilliseconds(mPinchLockBufferMaxAge + 1)); + } + + void twoFingerZoom() { + float pinchDistance = + StaticPrefs::apz_pinch_lock_span_breakout_threshold() * 1.2 * + tm->GetDPI(); + + float newSpan = mSpan + pinchDistance; + + auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE, + mFocus, newSpan, mSpan, mcc->Time()); + apzc->ReceiveInputEvent(event); + mcc->AdvanceBy(TimeDuration::FromMilliseconds(mPinchLockBufferMaxAge + 1)); + mSpan = newSpan; + } + + bool isPinchLockActive() { + FrameMetrics originalMetrics = apzc->GetFrameMetrics(); + + // Send a small scale input to the APZC + float pinchDistance = + StaticPrefs::apz_pinch_lock_span_breakout_threshold() * 0.8 * + tm->GetDPI(); + auto event = + CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE, mFocus, + mSpan + pinchDistance, mSpan, mcc->Time()); + apzc->ReceiveInputEvent(event); + + FrameMetrics result = apzc->GetFrameMetrics(); + bool lockActive = originalMetrics.GetZoom() == result.GetZoom() && + originalMetrics.GetVisualScrollOffset().x == + result.GetVisualScrollOffset().x && + originalMetrics.GetVisualScrollOffset().y == + result.GetVisualScrollOffset().y; + + // Avoid side effects, reset to original frame metrics + apzc->SetFrameMetrics(originalMetrics); + return lockActive; + } +}; + +TEST_F(APZCPinchGestureDetectorTester, + Pinch_UseGestureDetector_TouchActionNone) { + nsTArray<uint32_t> behaviors = {mozilla::layers::AllowedTouchBehavior::NONE, + mozilla::layers::AllowedTouchBehavior::NONE}; + DoPinchTest(false, &behaviors); +} + +TEST_F(APZCPinchGestureDetectorTester, + Pinch_UseGestureDetector_TouchActionZoom) { + nsTArray<uint32_t> behaviors; + behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM); + behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM); + DoPinchTest(true, &behaviors); +} + +TEST_F(APZCPinchGestureDetectorTester, + Pinch_UseGestureDetector_TouchActionNotAllowZoom) { + nsTArray<uint32_t> behaviors; + behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE); + behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM); + DoPinchTest(false, &behaviors); +} + +TEST_F(APZCPinchGestureDetectorTester, + Pinch_UseGestureDetector_TouchActionNone_NoAPZZoom) { + SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false); + + // Since we are preventing the pinch action via touch-action we should not be + // sending the pinch gesture notifications that would normally be sent when + // apz_allow_zooming is false. + EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _, _)).Times(0); + nsTArray<uint32_t> behaviors = {mozilla::layers::AllowedTouchBehavior::NONE, + mozilla::layers::AllowedTouchBehavior::NONE}; + DoPinchTest(false, &behaviors); +} + +TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) { + DoPinchWithPreventDefaultTest(); +} + +TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault_NoAPZZoom) { + SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false); + + // Since we are preventing the pinch action we should not be sending the pinch + // gesture notifications that would normally be sent when apz_allow_zooming is + // false. + EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _, _)).Times(0); + + DoPinchWithPreventDefaultTest(); +} + +TEST_F(APZCPinchGestureDetectorTester, Panning_TwoFingerFling_ZoomDisabled) { + SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); + + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcUnzoomable(); + + // Perform a two finger pan + int touchInputId = 0; + uint64_t blockId = 0; + PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), ScreenIntPoint(100, 100), + 1, touchInputId, nullptr, nullptr, &blockId); + + // Expect to be in a flinging state + apzc->AssertStateIsFling(); +} + +TEST_F(APZCPinchGestureDetectorTester, Pinch_DoesntFling_ZoomDisabled) { + SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); + + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcUnzoomable(); + + // Perform a pinch + int touchInputId = 0; + uint64_t blockId = 0; + + PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), ScreenIntPoint(100, 100), + 2, touchInputId, nullptr, nullptr, &blockId, + PinchOptions::LiftFinger2, true); + + // Lift second finger after a pause + mcc->AdvanceBy(TimeDuration::FromMilliseconds(50)); + TouchUp(apzc, ScreenIntPoint(100, 100), mcc->Time()); + + // Pinch should not trigger a fling + EXPECT_EQ(apzc->GetVelocityVector().y, 0); +} + +TEST_F(APZCPinchGestureDetectorTester, Panning_TwoFingerFling_ZoomEnabled) { + SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); + + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcZoomable(); + + // Perform a two finger pan + int touchInputId = 0; + uint64_t blockId = 0; + PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), ScreenIntPoint(100, 100), + 1, touchInputId, nullptr, nullptr, &blockId); + + // Expect to NOT be in flinging state + apzc->AssertStateIsReset(); +} + +TEST_F(APZCPinchGestureDetectorTester, + Panning_TwoThenOneFingerFling_ZoomEnabled) { + SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); + + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcZoomable(); + + // Perform a two finger pan lifting only the first finger + int touchInputId = 0; + uint64_t blockId = 0; + PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), ScreenIntPoint(100, 100), + 1, touchInputId, nullptr, nullptr, &blockId, + PinchOptions::LiftFinger2); + + // Lift second finger after a pause + mcc->AdvanceBy(TimeDuration::FromMilliseconds(50)); + TouchUp(apzc, ScreenIntPoint(100, 100), mcc->Time()); + + // This gesture should activate the pinch lock, and result + // in a fling even if the page is zoomable. + apzc->AssertStateIsFling(); +} + +TEST_F(APZCPinchGestureDetectorTester, + Panning_TwoThenOneFingerFling_ZoomDisabled) { + SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); + + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcUnzoomable(); + + // Perform a two finger pan lifting only the first finger + int touchInputId = 0; + uint64_t blockId = 0; + PinchWithTouchInput(apzc, ScreenIntPoint(100, 200), ScreenIntPoint(100, 100), + 1, touchInputId, nullptr, nullptr, &blockId, + PinchOptions::LiftFinger2); + + // Lift second finger after a pause + mcc->AdvanceBy(TimeDuration::FromMilliseconds(50)); + TouchUp(apzc, ScreenIntPoint(100, 100), mcc->Time()); + + // This gesture should activate the pinch lock and result in a fling + apzc->AssertStateIsFling(); +} + +TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) { + // set up APZ + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcUnzoomable(); + + nsEventStatus statuses[3]; // scalebegin, scale, scaleend + PinchWithPinchInput(apzc, ScreenIntPoint(250, 350), ScreenIntPoint(200, 300), + 10, &statuses); + + FrameMetrics fm = apzc->GetFrameMetrics(); + + // It starts from (300, 300), then moves the focus point from (250, 350) to + // (200, 300) pans by (50, 50) screen pixels, but there is a 2x zoom, which + // causes the scroll offset to change by half of that (25, 25) pixels. + EXPECT_EQ(325, fm.GetVisualScrollOffset().x); + EXPECT_EQ(325, fm.GetVisualScrollOffset().y); + EXPECT_EQ(2.0, fm.GetZoom().scale); +} + +TEST_F(APZCPinchTester, Panning_Beyond_LayoutViewport) { + SCOPED_GFX_PREF_INT("apz.axis_lock.mode", 0); + + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcZoomable(); + + // Case 1 - visual viewport is still inside layout viewport. + Pan(apzc, 350, 300, PanOptions::NoFling); + FrameMetrics fm = apzc->GetFrameMetrics(); + // It starts from (300, 300) pans by (0, 50) screen pixels, but there is a + // 2x zoom, which causes the scroll offset to change by half of that (0, 25). + // But the visual viewport is still inside the layout viewport. + EXPECT_EQ(300, fm.GetVisualScrollOffset().x); + EXPECT_EQ(325, fm.GetVisualScrollOffset().y); + EXPECT_EQ(300, fm.GetLayoutViewport().X()); + EXPECT_EQ(300, fm.GetLayoutViewport().Y()); + + // Case 2 - visual viewport crosses the bottom boundary of the layout + // viewport. + Pan(apzc, 525, 325, PanOptions::NoFling); + fm = apzc->GetFrameMetrics(); + // It starts from (300, 325) pans by (0, 200) screen pixels, but there is a + // 2x zoom, which causes the scroll offset to change by half of that + // (0, 100). The visual viewport crossed the bottom boundary of the layout + // viewport by 25px. + EXPECT_EQ(300, fm.GetVisualScrollOffset().x); + EXPECT_EQ(425, fm.GetVisualScrollOffset().y); + EXPECT_EQ(300, fm.GetLayoutViewport().X()); + EXPECT_EQ(325, fm.GetLayoutViewport().Y()); + + // Case 3 - visual viewport crosses the top boundary of the layout viewport. + Pan(apzc, 425, 775, PanOptions::NoFling); + fm = apzc->GetFrameMetrics(); + // It starts from (300, 425) pans by (0, -350) screen pixels, but there is a + // 2x zoom, which causes the scroll offset to change by half of that + // (0, -175). The visual viewport crossed the top of the layout viewport by + // 75px. + EXPECT_EQ(300, fm.GetVisualScrollOffset().x); + EXPECT_EQ(250, fm.GetVisualScrollOffset().y); + EXPECT_EQ(300, fm.GetLayoutViewport().X()); + EXPECT_EQ(250, fm.GetLayoutViewport().Y()); + + // Case 4 - visual viewport crosses the left boundary of the layout viewport. + Pan(apzc, ScreenIntPoint(150, 10), ScreenIntPoint(350, 10), + PanOptions::NoFling); + fm = apzc->GetFrameMetrics(); + // It starts from (300, 250) pans by (-200, 0) screen pixels, but there is a + // 2x zoom, which causes the scroll offset to change by half of that + // (-100, 0). The visual viewport crossed the left boundary of the layout + // viewport by 100px. + EXPECT_EQ(200, fm.GetVisualScrollOffset().x); + EXPECT_EQ(250, fm.GetVisualScrollOffset().y); + EXPECT_EQ(200, fm.GetLayoutViewport().X()); + EXPECT_EQ(250, fm.GetLayoutViewport().Y()); + + // Case 5 - visual viewport crosses the right boundary of the layout viewport. + Pan(apzc, ScreenIntPoint(350, 10), ScreenIntPoint(150, 10), + PanOptions::NoFling); + fm = apzc->GetFrameMetrics(); + // It starts from (200, 250) pans by (200, 0) screen pixels, but there is a + // 2x zoom, which causes the scroll offset to change by half of that + // (100, 0). The visual viewport crossed the right boundary of the layout + // viewport by 50px. + EXPECT_EQ(300, fm.GetVisualScrollOffset().x); + EXPECT_EQ(250, fm.GetVisualScrollOffset().y); + EXPECT_EQ(250, fm.GetLayoutViewport().X()); + EXPECT_EQ(250, fm.GetLayoutViewport().Y()); + + // Case 6 - visual viewport crosses both the vertical and horizontal + // boundaries of the layout viewport by moving diagonally towards the + // top-right corner. + Pan(apzc, ScreenIntPoint(350, 200), ScreenIntPoint(150, 400), + PanOptions::NoFling); + fm = apzc->GetFrameMetrics(); + // It starts from (300, 250) pans by (200, -200) screen pixels, but there is + // a 2x zoom, which causes the scroll offset to change by half of that + // (100, -100). The visual viewport moved by (100, -100) outside the + // boundary of the layout viewport. + EXPECT_EQ(400, fm.GetVisualScrollOffset().x); + EXPECT_EQ(150, fm.GetVisualScrollOffset().y); + EXPECT_EQ(350, fm.GetLayoutViewport().X()); + EXPECT_EQ(150, fm.GetLayoutViewport().Y()); +} + +TEST_F(APZCPinchGestureDetectorTester, Pinch_APZZoom_Disabled) { + SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false); + + FrameMetrics originalMetrics = GetPinchableFrameMetrics(); + apzc->SetFrameMetrics(originalMetrics); + + // When apz_allow_zooming is false, the ZoomConstraintsClient produces + // ZoomConstraints with mAllowZoom set to false. + MakeApzcUnzoomable(); + + // With apz_allow_zooming false, we expect the NotifyPinchGesture function to + // get called as the pinch progresses, but the metrics shouldn't change. + EXPECT_CALL(*mcc, + NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, + apzc->GetGuid(), _, LayoutDeviceCoord(0), _)) + .Times(1); + EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, + apzc->GetGuid(), _, _, _)) + .Times(AtLeast(1)); + EXPECT_CALL(*mcc, + NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, + apzc->GetGuid(), _, LayoutDeviceCoord(0), _)) + .Times(1); + + int touchInputId = 0; + uint64_t blockId = 0; + PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId, + nullptr, nullptr, &blockId); + + // verify the metrics didn't change (i.e. the pinch was ignored inside APZ) + FrameMetrics fm = apzc->GetFrameMetrics(); + EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom()); + EXPECT_EQ(originalMetrics.GetVisualScrollOffset().x, + fm.GetVisualScrollOffset().x); + EXPECT_EQ(originalMetrics.GetVisualScrollOffset().y, + fm.GetVisualScrollOffset().y); + + apzc->AssertStateIsReset(); +} + +TEST_F(APZCPinchGestureDetectorTester, Pinch_NoSpan) { + SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false); + + FrameMetrics originalMetrics = GetPinchableFrameMetrics(); + apzc->SetFrameMetrics(originalMetrics); + + // When apz_allow_zooming is false, the ZoomConstraintsClient produces + // ZoomConstraints with mAllowZoom set to false. + MakeApzcUnzoomable(); + + // With apz_allow_zooming false, we expect the NotifyPinchGesture function to + // get called as the pinch progresses, but the metrics shouldn't change. + EXPECT_CALL(*mcc, + NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, + apzc->GetGuid(), _, LayoutDeviceCoord(0), _)) + .Times(1); + EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, + apzc->GetGuid(), _, _, _)) + .Times(AtLeast(1)); + EXPECT_CALL(*mcc, + NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, + apzc->GetGuid(), _, LayoutDeviceCoord(0), _)) + .Times(1); + + int inputId = 0; + ScreenIntPoint focus(250, 300); + + // Do a pinch holding a zero span and moving the focus by y=100 + + const TimeDuration TIME_BETWEEN_TOUCH_EVENT = + TimeDuration::FromMilliseconds(50); + const auto touchBehaviors = Some(nsTArray<uint32_t>{kDefaultTouchBehavior}); + + MultiTouchInput mtiStart = + MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, mcc->Time(), 0); + mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, focus)); + mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus)); + apzc->ReceiveInputEvent(mtiStart, touchBehaviors); + mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); + + focus.y -= 35 + 1; // this is to get over the PINCH_START_THRESHOLD in + // GestureEventListener.cpp + MultiTouchInput mtiMove1 = + MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0); + mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, focus)); + mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus)); + apzc->ReceiveInputEvent(mtiMove1); + mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); + + focus.y -= 100; // do a two-finger scroll of 100 screen pixels + MultiTouchInput mtiMove2 = + MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0); + mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, focus)); + mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus)); + apzc->ReceiveInputEvent(mtiMove2); + mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); + + MultiTouchInput mtiEnd = + MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, mcc->Time(), 0); + mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, focus)); + mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus)); + apzc->ReceiveInputEvent(mtiEnd); + + // Done, check the metrics to make sure we scrolled by 100 screen pixels, + // which is 50 CSS pixels for the pinchable frame metrics. + + FrameMetrics fm = apzc->GetFrameMetrics(); + EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom()); + EXPECT_EQ(originalMetrics.GetVisualScrollOffset().x, + fm.GetVisualScrollOffset().x); + EXPECT_EQ(originalMetrics.GetVisualScrollOffset().y + 50, + fm.GetVisualScrollOffset().y); + + apzc->AssertStateIsReset(); +} + +TEST_F(APZCPinchTester, Pinch_TwoFinger_APZZoom_Disabled_Bug1354185) { + // Set up APZ such that mZoomConstraints.mAllowZoom is false. + SCOPED_GFX_PREF_BOOL("apz.allow_zooming", false); + apzc->SetFrameMetrics(GetPinchableFrameMetrics()); + MakeApzcUnzoomable(); + + // We expect a repaint request for scrolling. + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); + + // Send only the PINCHGESTURE_START and PINCHGESTURE_SCALE events, + // in order to trigger a call to AsyncPanZoomController::OnScale + // but not to AsyncPanZoomController::OnScaleEnd. + ScreenIntPoint aFocus(250, 350); + ScreenIntPoint aSecondFocus(200, 300); + float aScale = 10; + auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START, + aFocus, 10.0, 10.0, mcc->Time()); + apzc->ReceiveInputEvent(event); + + event = + CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE, + aSecondFocus, 10.0f * aScale, 10.0, mcc->Time()); + apzc->ReceiveInputEvent(event); +} + +TEST_F(APZCPinchLockingTester, Pinch_Locking_Free) { + SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 0); // PINCH_FREE + + twoFingerPan(); + EXPECT_FALSE(isPinchLockActive()); +} + +TEST_F(APZCPinchLockingTester, Pinch_Locking_Normal_Lock) { + SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 1); // PINCH_NORMAL + + twoFingerPan(); + EXPECT_TRUE(isPinchLockActive()); +} + +TEST_F(APZCPinchLockingTester, Pinch_Locking_Normal_Lock_Break) { + SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 1); // PINCH_NORMAL + + twoFingerPan(); + twoFingerZoom(); + EXPECT_TRUE(isPinchLockActive()); +} + +TEST_F(APZCPinchLockingTester, Pinch_Locking_Sticky_Lock) { + SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 2); // PINCH_STICKY + + twoFingerPan(); + EXPECT_TRUE(isPinchLockActive()); +} + +TEST_F(APZCPinchLockingTester, Pinch_Locking_Sticky_Lock_Break) { + SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 2); // PINCH_STICKY + + twoFingerPan(); + twoFingerZoom(); + EXPECT_FALSE(isPinchLockActive()); +} + +TEST_F(APZCPinchLockingTester, Pinch_Locking_Sticky_Lock_Break_Lock) { + SCOPED_GFX_PREF_INT("apz.pinch_lock.mode", 2); // PINCH_STICKY + + twoFingerPan(); + twoFingerZoom(); + twoFingerPan(); + EXPECT_TRUE(isPinchLockActive()); +} |