/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=8 et 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" class APZCPinchTester : public APZCBasicTester { public: explicit APZCPinchTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES) : APZCBasicTester(aGestureBehavior) { } protected: FrameMetrics GetPinchableFrameMetrics() { FrameMetrics fm; fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200)); fm.SetScrollableRect(CSSRect(0, 0, 980, 1000)); fm.SetScrollOffset(CSSPoint(300, 300)); fm.SetZoom(CSSToParentLayerScale2D(2.0, 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); } FrameMetrics fm = apzc->GetFrameMetrics(); if (aShouldTriggerPinch) { // the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80 EXPECT_EQ(2.5f, fm.GetZoom().ToScaleFactor().scale); EXPECT_EQ(305, fm.GetScrollOffset().x); EXPECT_EQ(310, fm.GetScrollOffset().y); } else { // The frame metrics should stay the same since touch-action:none makes // apzc ignore pinch gestures. EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale); EXPECT_EQ(300, fm.GetScrollOffset().x); EXPECT_EQ(300, fm.GetScrollOffset().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(CSSToParentLayerScale2D(2.0, 2.0)); fm.SetScrollOffset(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); } fm = apzc->GetFrameMetrics(); if (aShouldTriggerPinch) { // the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200 EXPECT_EQ(1.0f, fm.GetZoom().ToScaleFactor().scale); EXPECT_EQ(880, fm.GetScrollOffset().x); EXPECT_EQ(0, fm.GetScrollOffset().y); } else { EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale); EXPECT_EQ(930, fm.GetScrollOffset().x); EXPECT_EQ(5, fm.GetScrollOffset().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.GetScrollOffset().x, fm.GetScrollOffset().x); EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y); apzc->AssertStateIsReset(); } }; TEST_F(APZCPinchTester, Pinch_DefaultGestures_NoTouchAction) { SCOPED_GFX_PREF(TouchActionEnabled, bool, false); DoPinchTest(true); } TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_NoTouchAction) { SCOPED_GFX_PREF(TouchActionEnabled, bool, false); DoPinchTest(true); } TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone) { SCOPED_GFX_PREF(TouchActionEnabled, bool, true); nsTArray behaviors = { mozilla::layers::AllowedTouchBehavior::NONE, mozilla::layers::AllowedTouchBehavior::NONE }; DoPinchTest(false, &behaviors); } TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionZoom) { SCOPED_GFX_PREF(TouchActionEnabled, bool, true); 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) { SCOPED_GFX_PREF(TouchActionEnabled, bool, true); nsTArray behaviors; behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN); behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM); DoPinchTest(false, &behaviors); } TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone_NoAPZZoom) { SCOPED_GFX_PREF(TouchActionEnabled, bool, true); SCOPED_GFX_PREF(APZAllowZooming, bool, 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 // APZAllowZooming 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(APZAllowZooming, bool, false); // Since we are preventing the pinch action we should not be sending the pinch // gesture notifications that would normally be sent when APZAllowZooming is // false. EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0); DoPinchWithPreventDefaultTest(); } 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.GetScrollOffset().x); EXPECT_EQ(325, fm.GetScrollOffset().y); EXPECT_EQ(2.0, fm.GetZoom().ToScaleFactor().scale); } TEST_F(APZCPinchGestureDetectorTester, Pinch_APZZoom_Disabled) { SCOPED_GFX_PREF(APZAllowZooming, bool, false); FrameMetrics originalMetrics = GetPinchableFrameMetrics(); apzc->SetFrameMetrics(originalMetrics); // When APZAllowZooming is false, the ZoomConstraintsClient produces // ZoomConstraints with mAllowZoom set to false. MakeApzcUnzoomable(); // With APZAllowZooming 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.GetScrollOffset().x, fm.GetScrollOffset().x); EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y); apzc->AssertStateIsReset(); } TEST_F(APZCPinchGestureDetectorTester, Pinch_NoSpan) { SCOPED_GFX_PREF(APZAllowZooming, bool, false); SCOPED_GFX_PREF(TouchActionEnabled, bool, false); FrameMetrics originalMetrics = GetPinchableFrameMetrics(); apzc->SetFrameMetrics(originalMetrics); // When APZAllowZooming is false, the ZoomConstraintsClient produces // ZoomConstraints with mAllowZoom set to false. MakeApzcUnzoomable(); // With APZAllowZooming 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 MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0); mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, focus)); mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus)); apzc->ReceiveInputEvent(mtiStart, nullptr); focus.y -= 35 + 1; // this is to get over the PINCH_START_THRESHOLD in GestureEventListener.cpp MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0); mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, focus)); mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus)); apzc->ReceiveInputEvent(mtiMove1, nullptr); focus.y -= 100; // do a two-finger scroll of 100 screen pixels MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0); mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, focus)); mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus)); apzc->ReceiveInputEvent(mtiMove2, nullptr); MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0); mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, focus)); mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus)); apzc->ReceiveInputEvent(mtiEnd, nullptr); // 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.GetScrollOffset().x, fm.GetScrollOffset().x); EXPECT_EQ(originalMetrics.GetScrollOffset().y + 50, fm.GetScrollOffset().y); apzc->AssertStateIsReset(); }