Bug 964750 - Make GestureEventListener rely on the EventStatus returned from the apzc. Add a check to apzc if touch-action allows zooming. Update apzc tests accordingly. r=drs

This commit is contained in:
Nick Lebedev 2014-05-05 11:23:00 -04:00
Родитель 78fb1d66b0
Коммит 3d81d33195
3 изменённых файлов: 214 добавлений и 86 удалений

Просмотреть файл

@ -880,6 +880,11 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEve
nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
APZC_LOG("%p got a scale in state %d\n", this, mState);
if (!TouchActionAllowPinchZoom()) {
return nsEventStatus_eIgnore;
}
if (mState != PINCHING) {
return nsEventStatus_eConsumeNoDefault;
}
@ -959,6 +964,10 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
APZC_LOG("%p got a scale-end in state %d\n", this, mState);
if (!TouchActionAllowPinchZoom()) {
return nsEventStatus_eIgnore;
}
SetState(NOTHING);
{

Просмотреть файл

@ -221,9 +221,9 @@ nsEventStatus GestureEventListener::HandleInputTouchMove()
currentSpan,
mLastTouchInput.modifiers);
mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
}
rv = nsEventStatus_eConsumeNoDefault;
mPreviousSpan = currentSpan;
break;
}
@ -245,8 +245,7 @@ nsEventStatus GestureEventListener::HandleInputTouchMove()
mPreviousSpan,
mLastTouchInput.modifiers);
mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
rv = nsEventStatus_eConsumeNoDefault;
rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
mPreviousSpan = currentSpan;
break;
@ -277,8 +276,8 @@ nsEventStatus GestureEventListener::HandleInputTouchEnd()
mLastTouchInput.mTime,
mLastTouchInput.mTouches[0].mScreenPoint,
mLastTouchInput.modifiers);
nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent(tapEvent);
if (tapupStatus == nsEventStatus_eIgnore) {
rv = mAsyncPanZoomController->HandleGestureEvent(tapEvent);
if (rv == nsEventStatus_eIgnore) {
SetState(GESTURE_FIRST_SINGLE_TOUCH_UP);
CreateMaxTapTimeoutTask();
} else {
@ -295,7 +294,7 @@ nsEventStatus GestureEventListener::HandleInputTouchEnd()
mLastTouchInput.mTime,
mLastTouchInput.mTouches[0].mScreenPoint,
mLastTouchInput.modifiers);
mAsyncPanZoomController->HandleGestureEvent(tapEvent);
rv = mAsyncPanZoomController->HandleGestureEvent(tapEvent);
break;
}
@ -311,7 +310,7 @@ nsEventStatus GestureEventListener::HandleInputTouchEnd()
mLastTouchInput.mTime,
mLastTouchInput.mTouches[0].mScreenPoint,
mLastTouchInput.modifiers);
mAsyncPanZoomController->HandleGestureEvent(tapEvent);
rv = mAsyncPanZoomController->HandleGestureEvent(tapEvent);
break;
}
@ -330,9 +329,9 @@ nsEventStatus GestureEventListener::HandleInputTouchEnd()
1.0f,
1.0f,
mLastTouchInput.modifiers);
mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
}
rv = nsEventStatus_eConsumeNoDefault;
break;
default:

Просмотреть файл

@ -290,21 +290,36 @@ void DoPanTest(bool aShouldTriggerScroll, bool aShouldUseTouchAction, uint32_t a
apzc->Destroy();
}
static void
ApzcPinch(AsyncPanZoomController* aApzc, int aFocusX, int aFocusY, float aScale) {
aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
static void ApzcPinchWithPinchInput(AsyncPanZoomController* aApzc,
int aFocusX,
int aFocusY,
float aScale,
bool aShouldTriggerPinch,
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr) {
if (aAllowedTouchBehaviors) {
aApzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors);
}
nsEventStatus expectedStatus = aShouldTriggerPinch
? nsEventStatus_eConsumeNoDefault
: nsEventStatus_eIgnore;
nsEventStatus actualStatus;
actualStatus = aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
0,
ScreenPoint(aFocusX, aFocusY),
10.0,
10.0,
0));
aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
EXPECT_EQ(actualStatus, expectedStatus);
actualStatus = aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
0,
ScreenPoint(aFocusX, aFocusY),
10.0 * aScale,
10.0,
0));
aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
EXPECT_EQ(actualStatus, expectedStatus);
actualStatus = aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
0,
ScreenPoint(aFocusX, aFocusY),
// note: negative values here tell APZC
@ -312,6 +327,138 @@ ApzcPinch(AsyncPanZoomController* aApzc, int aFocusX, int aFocusY, float aScale)
-1.0,
-1.0,
0));
EXPECT_EQ(actualStatus, expectedStatus);
}
static void ApzcPinchWithTouchMoveInput(AsyncPanZoomController* aApzc,
int aFocusX,
int aFocusY,
float aScale,
int& inputId,
bool aShouldTriggerPinch,
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr) {
// Having pinch coordinates in float type may cause problems with high-precision scale values
// since SingleTouchData accepts integer value. But for trivial tests it should be ok.
float pinchLength = 100.0;
float pinchLengthScaled = pinchLength * aScale;
nsEventStatus expectedStatus = aShouldTriggerPinch
? nsEventStatus_eConsumeNoDefault
: nsEventStatus_eIgnore;
nsEventStatus actualStatus;
MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, 0);
mtiStart.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX, aFocusY), ScreenSize(0, 0), 0, 0));
mtiStart.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX, aFocusY), ScreenSize(0, 0), 0, 0));
aApzc->ReceiveInputEvent(mtiStart);
if (aAllowedTouchBehaviors) {
aApzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors);
}
MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, 0);
mtiMove1.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLength, aFocusY), ScreenSize(0, 0), 0, 0));
mtiMove1.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLength, aFocusY), ScreenSize(0, 0), 0, 0));
actualStatus = aApzc->ReceiveInputEvent(mtiMove1);
EXPECT_EQ(actualStatus, expectedStatus);
MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, 0);
mtiMove2.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
mtiMove2.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
actualStatus = aApzc->ReceiveInputEvent(mtiMove2);
EXPECT_EQ(actualStatus, expectedStatus);
MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, 0);
mtiEnd.mTouches.AppendElement(SingleTouchData(inputId, ScreenIntPoint(aFocusX - pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
mtiEnd.mTouches.AppendElement(SingleTouchData(inputId + 1, ScreenIntPoint(aFocusX + pinchLengthScaled, aFocusY), ScreenSize(0, 0), 0, 0));
actualStatus = aApzc->ReceiveInputEvent(mtiEnd);
EXPECT_EQ(actualStatus, expectedStatus);
inputId += 2;
}
static
void DoPinchTest(bool aUseGestureRecognizer, bool aShouldTriggerPinch,
nsTArray<uint32_t> *aAllowedTouchBehaviors = nullptr) {
nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm,
aUseGestureRecognizer
? AsyncPanZoomController::USE_GESTURE_DETECTOR
: AsyncPanZoomController::DEFAULT_GESTURES);
FrameMetrics fm;
fm.mViewport = CSSRect(0, 0, 980, 480);
fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200);
fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
fm.SetScrollOffset(CSSPoint(300, 300));
fm.SetZoom(CSSToScreenScale(2.0));
apzc->SetFrameMetrics(fm);
apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
// the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
if (aShouldTriggerPinch) {
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
} else {
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtMost(2));
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
}
if (aAllowedTouchBehaviors) {
apzc->SetTouchActionEnabled(true);
} else {
apzc->SetTouchActionEnabled(false);
}
int touchInputId = 0;
if (aUseGestureRecognizer) {
ApzcPinchWithTouchMoveInput(apzc, 250, 300, 1.25, touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
} else {
ApzcPinchWithPinchInput(apzc, 250, 300, 1.25, aShouldTriggerPinch, aAllowedTouchBehaviors);
}
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().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().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(CSSToScreenScale(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 (aUseGestureRecognizer) {
ApzcPinchWithTouchMoveInput(apzc, 250, 300, 0.5, touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
} else {
ApzcPinchWithPinchInput(apzc, 250, 300, 0.5, aShouldTriggerPinch, aAllowedTouchBehaviors);
}
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().scale);
EXPECT_EQ(880, fm.GetScrollOffset().x);
EXPECT_EQ(0, fm.GetScrollOffset().y);
} else {
EXPECT_EQ(2.0f, fm.GetZoom().scale);
EXPECT_EQ(930, fm.GetScrollOffset().x);
EXPECT_EQ(5, fm.GetScrollOffset().y);
}
apzc->Destroy();
}
static nsEventStatus
@ -351,81 +498,54 @@ TEST_F(AsyncPanZoomControllerTester, Constructor) {
apzc->SetFrameMetrics(TestFrameMetrics());
}
TEST_F(AsyncPanZoomControllerTester, Pinch) {
nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
FrameMetrics fm;
fm.mViewport = CSSRect(0, 0, 980, 480);
fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200);
fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
fm.SetScrollOffset(CSSPoint(300, 300));
fm.SetZoom(CSSToScreenScale(2.0));
apzc->SetFrameMetrics(fm);
apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
// the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
ApzcPinch(apzc, 250, 300, 1.25);
// the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
fm = apzc->GetFrameMetrics();
EXPECT_EQ(2.5f, fm.GetZoom().scale);
EXPECT_EQ(305, fm.GetScrollOffset().x);
EXPECT_EQ(310, 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(CSSToScreenScale(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
ApzcPinch(apzc, 250, 300, 0.5);
// the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200
fm = apzc->GetFrameMetrics();
EXPECT_EQ(1.0f, fm.GetZoom().scale);
EXPECT_EQ(880, fm.GetScrollOffset().x);
EXPECT_EQ(0, fm.GetScrollOffset().y);
apzc->Destroy();
TEST_F(AsyncPanZoomControllerTester, Pinch_DefaultGestures_NoTouchAction) {
DoPinchTest(false, true);
}
TEST_F(AsyncPanZoomControllerTester, PinchWithTouchActionNone) {
nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
TEST_F(AsyncPanZoomControllerTester, Pinch_DefaultGestures_TouchActionNone) {
nsTArray<uint32_t> behaviors;
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
DoPinchTest(false, false, &behaviors);
}
FrameMetrics fm;
fm.mViewport = CSSRect(0, 0, 980, 480);
fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200);
fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
fm.SetScrollOffset(CSSPoint(300, 300));
fm.SetZoom(CSSToScreenScale(2.0));
apzc->SetFrameMetrics(fm);
// the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
TEST_F(AsyncPanZoomControllerTester, Pinch_DefaultGestures_TouchActionZoom) {
nsTArray<uint32_t> behaviors;
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
DoPinchTest(false, true, &behaviors);
}
// Apzc's OnScaleEnd method calls once SendAsyncScrollDOMEvent and RequestContentRepaint methods,
// therefore we're setting these specific values.
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtMost(1));
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtMost(1));
TEST_F(AsyncPanZoomControllerTester, Pinch_DefaultGestures_TouchActionNotAllowZoom) {
nsTArray<uint32_t> behaviors;
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
DoPinchTest(false, false, &behaviors);
}
nsTArray<uint32_t> values;
values.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
values.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
apzc->SetTouchActionEnabled(true);
TEST_F(AsyncPanZoomControllerTester, Pinch_UseGestureDetector_NoTouchAction) {
DoPinchTest(true, true);
}
apzc->SetAllowedTouchBehavior(values);
ApzcPinch(apzc, 250, 300, 1.25);
TEST_F(AsyncPanZoomControllerTester, Pinch_UseGestureDetector_TouchActionNone) {
nsTArray<uint32_t> behaviors;
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
DoPinchTest(true, false, &behaviors);
}
// The frame metrics should stay the same since touch-action:none makes
// apzc ignore pinch gestures.
fm = apzc->GetFrameMetrics();
EXPECT_EQ(2.0f, fm.GetZoom().scale);
EXPECT_EQ(300, fm.GetScrollOffset().x);
EXPECT_EQ(300, fm.GetScrollOffset().y);
TEST_F(AsyncPanZoomControllerTester, Pinch_UseGestureDetector_TouchActionZoom) {
nsTArray<uint32_t> behaviors;
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
DoPinchTest(true, true, &behaviors);
}
TEST_F(AsyncPanZoomControllerTester, Pinch_UseGestureDetector_TouchActionNotAllowZoom) {
nsTArray<uint32_t> behaviors;
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
DoPinchTest(true, false, &behaviors);
}
TEST_F(AsyncPanZoomControllerTester, Overzoom) {
@ -445,7 +565,7 @@ TEST_F(AsyncPanZoomControllerTester, Overzoom) {
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
ApzcPinch(apzc, 50, 50, 0.5);
ApzcPinchWithPinchInput(apzc, 50, 50, 0.5, true);
fm = apzc->GetFrameMetrics();
EXPECT_EQ(0.8f, fm.GetZoom().scale);
@ -707,7 +827,7 @@ TEST_F(AsyncPanZoomControllerTester, ShortPress) {
int time = 0;
nsEventStatus status = ApzcTap(apzc, 10, 10, time, 100, mcc.get());
EXPECT_EQ(nsEventStatus_eIgnore, status);
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
// This verifies that the single tap notification is sent after the
// touchdown is fully processed. The ordering here is important.
@ -731,7 +851,7 @@ TEST_F(AsyncPanZoomControllerTester, MediumPress) {
int time = 0;
nsEventStatus status = ApzcTap(apzc, 10, 10, time, 400, mcc.get());
EXPECT_EQ(nsEventStatus_eIgnore, status);
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
// This verifies that the single tap notification is sent after the
// touchdown is fully processed. The ordering here is important.