Bug 1036985 - Change the return value of APZCTM::ReceiveInputEvent to return more useful information. r=botond

This commit is contained in:
Kartikaya Gupta 2014-08-01 16:24:50 -04:00
parent 89b4e69b9a
commit f8b53c5e8b
6 changed files with 129 additions and 85 deletions

View File

@ -419,6 +419,7 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
nsEventStatus result = nsEventStatus_eIgnore;
Matrix4x4 transformToApzc;
Matrix4x4 transformToGecko;
bool inOverscrolledApzc = false;
switch (aEvent.mInputType) {
case MULTITOUCH_INPUT: {
MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
@ -426,7 +427,6 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
break;
} case PANGESTURE_INPUT: {
PanGestureInput& panInput = aEvent.AsPanGestureInput();
bool inOverscrolledApzc = false;
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
&inOverscrolledApzc);
if (apzc) {
@ -455,7 +455,6 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
break;
} case PINCHGESTURE_INPUT: {
PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
bool inOverscrolledApzc = false;
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(pinchInput.mFocusPoint,
&inOverscrolledApzc);
if (apzc) {
@ -465,19 +464,15 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
PinchGestureInput inputForApzc(pinchInput);
GetInputTransforms(apzc, transformToApzc, transformToGecko);
ApplyTransform(&(inputForApzc.mFocusPoint), transformToApzc);
apzc->ReceiveInputEvent(inputForApzc);
result = apzc->ReceiveInputEvent(inputForApzc);
// Update the out-parameters so they are what the caller expects.
apzc->GetGuid(aOutTargetGuid);
TransformScreenToGecko(&(pinchInput.mFocusPoint), apzc, this);
}
result = inOverscrolledApzc ? nsEventStatus_eConsumeNoDefault
: apzc ? nsEventStatus_eConsumeDoDefault
: nsEventStatus_eIgnore;
break;
} case TAPGESTURE_INPUT: {
TapGestureInput& tapInput = aEvent.AsTapGestureInput();
bool inOverscrolledApzc = false;
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(ScreenPoint(tapInput.mPoint),
&inOverscrolledApzc);
if (apzc) {
@ -487,18 +482,18 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
TapGestureInput inputForApzc(tapInput);
GetInputTransforms(apzc, transformToApzc, transformToGecko);
ApplyTransform(&(inputForApzc.mPoint), transformToApzc);
apzc->ReceiveInputEvent(inputForApzc);
result = apzc->ReceiveInputEvent(inputForApzc);
// Update the out-parameters so they are what the caller expects.
apzc->GetGuid(aOutTargetGuid);
TransformScreenToGecko(&(tapInput.mPoint), apzc, this);
}
result = inOverscrolledApzc ? nsEventStatus_eConsumeNoDefault
: apzc ? nsEventStatus_eConsumeDoDefault
: nsEventStatus_eIgnore;
break;
}
}
if (inOverscrolledApzc) {
result = nsEventStatus_eConsumeNoDefault;
}
return result;
}
@ -594,6 +589,7 @@ APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
}
}
nsEventStatus result = nsEventStatus_eIgnore;
if (mApzcForInputBlock) {
mApzcForInputBlock->GetGuid(aOutTargetGuid);
// For computing the input for the APZC, used the cached transform.
@ -604,7 +600,7 @@ APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
}
mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
result = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
// For computing the event to pass back to Gecko, use the up-to-date transforms.
// This ensures that transformToApzc and transformToGecko are in sync
@ -616,10 +612,9 @@ APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
ApplyTransform(&(aInput.mTouches[i].mScreenPoint), outTransform);
}
}
nsEventStatus result = mInOverscrolledApzc ? nsEventStatus_eConsumeNoDefault
: mApzcForInputBlock ? nsEventStatus_eConsumeDoDefault
: nsEventStatus_eIgnore;
if (mInOverscrolledApzc) {
result = nsEventStatus_eConsumeNoDefault;
}
if (aInput.mType == MultiTouchInput::MULTITOUCH_END) {
if (mTouchCount >= aInput.mTouches.Length()) {
@ -667,6 +662,7 @@ APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
ScrollableLayerGuid* aOutTargetGuid)
{
MOZ_ASSERT(NS_IsMainThread());
nsEventStatus result = nsEventStatus_eIgnore;
// Transform the refPoint.
// If the event hits an overscrolled APZC, instruct the caller to ignore it.
@ -681,9 +677,10 @@ APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
Matrix4x4 outTransform = transformToApzc * transformToGecko;
ApplyTransform(&(aEvent.refPoint), outTransform);
}
return inOverscrolledApzc ? nsEventStatus_eConsumeNoDefault
: apzc ? nsEventStatus_eConsumeDoDefault
: nsEventStatus_eIgnore;
if (inOverscrolledApzc) {
result = nsEventStatus_eConsumeNoDefault;
}
return result;
}
nsEventStatus

View File

@ -127,6 +127,25 @@ public:
* handle them. The event may need to be converted to a WidgetInputEvent
* by the caller if it wants to do this.
*
* The following values may be returned by this function:
* nsEventStatus_eConsumeNoDefault is returned to indicate the
* caller should discard the event with extreme prejudice.
* Currently this is only returned if the APZ determines that
* something is in overscroll and the event should be ignored entirely.
* There may be other scenarios where this return code might be used in
* the future.
* nsEventStatus_eIgnore is returned to indicate that the APZ code didn't
* use this event. This might be because it was directed at a point on
* the screen where there was no APZ, or because the thing the user was
* trying to do was not allowed. (For example, attempting to pan a
* non-pannable document).
* nsEventStatus_eConsumeDoDefault is returned to indicate that the APZ
* code may have used this event to do some user-visible thing. Note that
* in some cases CONSUMED is returned even if the event was NOT used. This
* is because we cannot always know at the time of event delivery whether
* the event will be used or not. So we err on the side of sending
* CONSUMED when we are uncertain.
*
* @param aEvent input event object; is modified in-place
* @param aOutTargetGuid returns the guid of the apzc this event was
* delivered to. May be null.
@ -149,6 +168,7 @@ public:
* @param aEvent input event object; is modified in-place
* @param aOutTargetGuid returns the guid of the apzc this event was
* delivered to. May be null.
* @return See documentation for other ReceiveInputEvent above.
*/
nsEventStatus ReceiveInputEvent(WidgetInputEvent& aEvent,
ScrollableLayerGuid* aOutTargetGuid);

View File

@ -832,11 +832,49 @@ AsyncPanZoomController::CancelAnimationForHandoffChain()
CancelAnimation();
}
bool
AsyncPanZoomController::ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints) {
if (aTouchPoints == 0) {
// Cant' do anything with zero touch points
return false;
}
// This logic is simplified, erring on the side of returning true
// if we're not sure. It's safer to pretend that we can consume the
// event and then not be able to than vice-versa.
// We could probably enhance this logic to determine things like "we're
// not pannable, so we can only zoom in, and the zoom is already maxed
// out, so we're not zoomable either" but no need for that at this point.
bool pannable = true;
bool zoomable = mZoomConstraints.mAllowZoom;
APZCTreeManager* treeManagerLocal = mTreeManager;
if (!treeManagerLocal || !treeManagerLocal->CanBePanned(this)) {
pannable = false;
}
pannable &= (aBlock->TouchActionAllowsPanningX() || aBlock->TouchActionAllowsPanningY());
zoomable &= (aBlock->TouchActionAllowsPinchZoom());
// XXX once we fix bug 1031443, consumable should be assigned
// pannable || zoomable if aTouchPoints > 1.
bool consumable = (aTouchPoints == 1 ? pannable : zoomable);
if (!consumable) {
return false;
}
return true;
}
nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
AssertOnControllerThread();
if (aEvent.mInputType != MULTITOUCH_INPUT) {
return HandleInputEvent(aEvent);
HandleInputEvent(aEvent);
// The return value for non-touch input isn't really used, so just return
// ConsumeDoDefault for now. This can be changed later if needed.
return nsEventStatus_eConsumeDoDefault;
}
TouchBlockState* block = nullptr;
@ -883,18 +921,23 @@ nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent)
return nsEventStatus_eIgnore;
}
nsEventStatus result = ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())
? nsEventStatus_eConsumeDoDefault
: nsEventStatus_eIgnore;
if (block == CurrentTouchBlock() && block->IsReadyForHandling()) {
APZC_LOG("%p's current touch block is ready with preventdefault %d\n",
this, block->IsDefaultPrevented());
if (block->IsDefaultPrevented()) {
return nsEventStatus_eIgnore;
return result;
}
return HandleInputEvent(aEvent);
HandleInputEvent(aEvent);
return result;
}
// Otherwise, add it to the queue for the touch block
block->AddEvent(aEvent.AsMultiTouchInput());
return nsEventStatus_eConsumeDoDefault;
return result;
}
nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {

View File

@ -115,9 +115,8 @@ public:
* based on what type of input it is. For example, a PinchGestureEvent will
* cause scaling. This should only be called externally to this class.
* HandleInputEvent() should be used internally.
* This function returns nsEventStatus_eIgnore for events that are ignored,
* and nsEventStatus_eConsumeDoDefault for events that are queued for
* processing pending a content response.
* See the documentation on APZCTreeManager::ReceiveInputEvent for info on
* return values from this function.
*/
nsEventStatus ReceiveInputEvent(const InputData& aEvent);
@ -544,6 +543,14 @@ private:
*/
void CancelAnimationForHandoffChain();
/**
* Given the number of touch points in an input event and touch block they
* belong to, check if the event can result in a panning/zooming behavior.
* This is primarily used to figure out when to dispatch the pointercancel
* event for the pointer events spec.
*/
bool ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints);
/**
* Helper to set the current state. Holds the monitor before actually setting
* it and fires content controller events based on state changes. Always set

View File

@ -185,7 +185,8 @@ TouchBlockState::TouchActionAllowsPanningX() const
return true;
}
if (mAllowedTouchBehaviors.IsEmpty()) {
return false;
// Default to allowed
return true;
}
TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
return (flags & AllowedTouchBehavior::HORIZONTAL_PAN);
@ -198,7 +199,8 @@ TouchBlockState::TouchActionAllowsPanningY() const
return true;
}
if (mAllowedTouchBehaviors.IsEmpty()) {
return false;
// Default to allowed
return true;
}
TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
return (flags & AllowedTouchBehavior::VERTICAL_PAN);
@ -211,7 +213,8 @@ TouchBlockState::TouchActionAllowsPanningXY() const
return true;
}
if (mAllowedTouchBehaviors.IsEmpty()) {
return false;
// Default to allowed
return true;
}
TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
return (flags & AllowedTouchBehavior::HORIZONTAL_PAN)

View File

@ -278,8 +278,8 @@ ApzcTapAndCheckStatus(AsyncPanZoomController* aApzc, int aX, int aY, int& aTime,
{
nsEventStatus statuses[2];
ApzcTap(aApzc, aX, aY, aTime, aTapLength, &statuses);
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, statuses[0]);
EXPECT_EQ(nsEventStatus_eIgnore, statuses[1]);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
}
static void
@ -328,7 +328,7 @@ ApzcPan(AsyncPanZoomController* aApzc,
if (!aKeepFingerDown) {
status = ApzcUp(aApzc, 10, aTouchEndY, aTime);
} else {
status = (nsEventStatus)-1;
status = nsEventStatus_eIgnore;
}
if (aOutEventStatuses) {
(*aOutEventStatuses)[3] = status;
@ -346,35 +346,19 @@ ApzcPanAndCheckStatus(AsyncPanZoomController* aApzc,
int& aTime,
int aTouchStartY,
int aTouchEndY,
bool expectIgnoredPan,
bool hasTouchListeners,
bool aExpectConsumed,
nsTArray<uint32_t>* aAllowedTouchBehaviors)
{
nsEventStatus statuses[4]; // down, move, move, up
ApzcPan(aApzc, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses);
nsEventStatus touchStartStatus;
if (hasTouchListeners || gfxPrefs::TouchActionEnabled()) {
// APZC shouldn't consume the start event now, instead queueing it up
// waiting for content's response and/or allowed behavior.
touchStartStatus = nsEventStatus_eConsumeDoDefault;
} else {
// APZC should go into the touching state and therefore consume the event.
touchStartStatus = nsEventStatus_eConsumeNoDefault;
}
EXPECT_EQ(touchStartStatus, statuses[0]);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
nsEventStatus touchMoveStatus;
if (hasTouchListeners) {
// APZC will queue up this event while waiting for content's response.
if (aExpectConsumed) {
touchMoveStatus = nsEventStatus_eConsumeDoDefault;
} else if (expectIgnoredPan) {
// APZC should ignore panning, be in TOUCHING state and therefore return eIgnore.
// The same applies to all consequent touch move events.
touchMoveStatus = nsEventStatus_eIgnore;
} else {
// APZC should go into the panning state and therefore consume the event.
touchMoveStatus = nsEventStatus_eConsumeNoDefault;
touchMoveStatus = nsEventStatus_eIgnore;
}
EXPECT_EQ(touchMoveStatus, statuses[1]);
EXPECT_EQ(touchMoveStatus, statuses[2]);
@ -495,11 +479,12 @@ ApzcPinchWithTouchInputAndCheckStatus(AsyncPanZoomController* aApzc,
nsEventStatus statuses[4]; // down, move, move, up
ApzcPinchWithTouchInput(aApzc, aFocusX, aFocusY, aScale, inputId, aAllowedTouchBehaviors, &statuses);
nsEventStatus expectedStatus = aShouldTriggerPinch
? nsEventStatus_eConsumeNoDefault
nsEventStatus expectedMoveStatus = aShouldTriggerPinch
? nsEventStatus_eConsumeDoDefault
: nsEventStatus_eIgnore;
EXPECT_EQ(statuses[1], expectedStatus);
EXPECT_EQ(statuses[2], expectedStatus);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
EXPECT_EQ(expectedMoveStatus, statuses[1]);
EXPECT_EQ(expectedMoveStatus, statuses[2]);
}
class APZCPinchTester : public APZCBasicTester {
@ -788,7 +773,7 @@ TEST_F(APZCBasicTester, ComplexTransform) {
class APZCPanningTester : public APZCBasicTester {
protected:
void DoPanTest(bool aShouldTriggerScroll, uint32_t aBehavior)
void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed, uint32_t aBehavior)
{
if (aShouldTriggerScroll) {
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
@ -808,7 +793,7 @@ protected:
allowedTouchBehaviors.AppendElement(aBehavior);
// Pan down
ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, aShouldBeConsumed, &allowedTouchBehaviors);
apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
if (aShouldTriggerScroll) {
@ -824,7 +809,7 @@ protected:
apzc->CancelAnimation();
// Pan back
ApzcPanAndCheckStatus(apzc, time, touchEnd, touchStart, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
ApzcPanAndCheckStatus(apzc, time, touchEnd, touchStart, aShouldBeConsumed, &allowedTouchBehaviors);
apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
EXPECT_EQ(ScreenPoint(), pointOut);
@ -844,7 +829,7 @@ protected:
// Pan down
nsTArray<uint32_t> allowedTouchBehaviors;
allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors);
ApzcPanAndCheckStatus(apzc, time, touchStart, touchEnd, true, &allowedTouchBehaviors);
// Send the signal that content has handled and preventDefaulted the touch
// events. This flushes the event queue.
@ -862,7 +847,7 @@ protected:
};
TEST_F(APZCPanningTester, Pan) {
DoPanTest(true, mozilla::layers::AllowedTouchBehavior::NONE);
DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::NONE);
}
// In the each of the following 4 pan tests we are performing two pan gestures: vertical pan from top
@ -870,25 +855,28 @@ TEST_F(APZCPanningTester, Pan) {
// According to the pointer-events/touch-action spec AUTO and PAN_Y touch-action values allow vertical
// scrolling while NONE and PAN_X forbid it. The first parameter of DoPanTest method specifies this
// behavior.
// However, the events will be marked as consumed even if the behavior in PAN_X, because the user could
// move their finger horizontally too - APZ has no way of knowing beforehand and so must consume the
// events.
TEST_F(APZCPanningTester, PanWithTouchActionAuto) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoPanTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
}
TEST_F(APZCPanningTester, PanWithTouchActionNone) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoPanTest(false, 0);
DoPanTest(false, false, 0);
}
TEST_F(APZCPanningTester, PanWithTouchActionPanX) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoPanTest(false, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
DoPanTest(false, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
}
TEST_F(APZCPanningTester, PanWithTouchActionPanY) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoPanTest(true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
}
TEST_F(APZCPanningTester, PanWithPreventDefaultAndTouchAction) {
@ -1160,14 +1148,7 @@ protected:
int time = 0;
nsEventStatus status = ApzcDown(apzc, 10, 10, time);
if (gfxPrefs::TouchActionEnabled()) {
// If touch-action is enabled, then the event is queued until the
// allowed touch behavior is set.
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
} else {
// Otherwise, it is processed immediately.
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
}
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
if (gfxPrefs::TouchActionEnabled()) {
// SetAllowedTouchBehavior() must be called after sending touch-start.
@ -1218,7 +1199,7 @@ protected:
// prevent-defaulted, we should get a long-tap-up event.
check.Call("preHandleLongTapUp");
status = ApzcUp(apzc, 10, 10, time);
EXPECT_EQ(nsEventStatus_eIgnore, status);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
check.Call("postHandleLongTapUp");
apzc->AssertStateIsReset();
@ -1236,14 +1217,7 @@ protected:
int time = 0;
nsEventStatus status = ApzcDown(apzc, touchX, touchStartY, time);
if (gfxPrefs::TouchActionEnabled()) {
// If touch-action is enabled, then the event is queued until the
// allowed touch behavior is set.
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
} else {
// Otherwise, it is processed immediately.
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
}
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
if (gfxPrefs::TouchActionEnabled()) {
// SetAllowedTouchBehavior() must be called after sending touch-start.
@ -1288,11 +1262,11 @@ protected:
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, time, TimeStamp(), 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
status = apzc->ReceiveInputEvent(mti);
EXPECT_EQ(nsEventStatus_eIgnore, status);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
EXPECT_CALL(*mcc, HandleLongTapUp(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0);
status = ApzcUp(apzc, touchX, touchEndY, time);
EXPECT_EQ(nsEventStatus_eIgnore, status);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
ScreenPoint pointOut;
ViewTransform viewTransformOut;