/* -*- 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* 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 behaviors = {mozilla::layers::AllowedTouchBehavior::NONE, mozilla::layers::AllowedTouchBehavior::NONE}; DoPinchTest(false, &behaviors); } TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionZoom) { nsTArray 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 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 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{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()); }