Bug 914829 - MetroInput should forward touch input to apz first, then to content. r=kats, tabraldes

This commit is contained in:
Jim Mathies 2013-09-12 06:31:34 -05:00
parent 29cf090b6f
commit 217abb186c
5 changed files with 132 additions and 19 deletions

View File

@ -213,6 +213,7 @@ ApplyTransform(nsIntPoint* aPoint, const gfx3DMatrix& aMatrix)
nsEventStatus nsEventStatus
APZCTreeManager::ReceiveInputEvent(const InputData& aEvent) APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
{ {
nsEventStatus result = nsEventStatus_eIgnore;
gfx3DMatrix transformToApzc; gfx3DMatrix transformToApzc;
gfx3DMatrix transformToScreen; gfx3DMatrix transformToScreen;
switch (aEvent.mInputType) { switch (aEvent.mInputType) {
@ -238,7 +239,7 @@ APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) { for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc); ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
} }
mApzcForInputBlock->ReceiveInputEvent(inputForApzc); result = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
// If we have an mApzcForInputBlock and it's the end of the touch sequence // If we have an mApzcForInputBlock and it's the end of the touch sequence
// then null it out so we don't keep a dangling reference and leak things. // then null it out so we don't keep a dangling reference and leak things.
if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL || if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL ||
@ -254,7 +255,7 @@ APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
GetInputTransforms(apzc, transformToApzc, transformToScreen); GetInputTransforms(apzc, transformToApzc, transformToScreen);
PinchGestureInput inputForApzc(pinchInput); PinchGestureInput inputForApzc(pinchInput);
ApplyTransform(&(inputForApzc.mFocusPoint), transformToApzc); ApplyTransform(&(inputForApzc.mFocusPoint), transformToApzc);
apzc->ReceiveInputEvent(inputForApzc); result = apzc->ReceiveInputEvent(inputForApzc);
} }
break; break;
} case TAPGESTURE_INPUT: { } case TAPGESTURE_INPUT: {
@ -264,12 +265,12 @@ APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
GetInputTransforms(apzc, transformToApzc, transformToScreen); GetInputTransforms(apzc, transformToApzc, transformToScreen);
TapGestureInput inputForApzc(tapInput); TapGestureInput inputForApzc(tapInput);
ApplyTransform(&(inputForApzc.mPoint), transformToApzc); ApplyTransform(&(inputForApzc.mPoint), transformToApzc);
apzc->ReceiveInputEvent(inputForApzc); result = apzc->ReceiveInputEvent(inputForApzc);
} }
break; break;
} }
} }
return nsEventStatus_eIgnore; return result;
} }
nsEventStatus nsEventStatus

View File

@ -179,6 +179,20 @@ namespace {
aData->mChanged = false; aData->mChanged = false;
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
// Helper for making sure event ptrs get freed.
class AutoDeleteEvent
{
public:
AutoDeleteEvent(nsGUIEvent* aPtr) :
mPtr(aPtr) {}
~AutoDeleteEvent() {
if (mPtr) {
delete mPtr;
}
}
nsGUIEvent* mPtr;
};
} }
namespace mozilla { namespace mozilla {
@ -431,6 +445,8 @@ MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender,
mTouchStartDefaultPrevented = false; mTouchStartDefaultPrevented = false;
mTouchMoveDefaultPrevented = false; mTouchMoveDefaultPrevented = false;
mIsFirstTouchMove = true; mIsFirstTouchMove = true;
mCancelable = true;
mTouchCancelSent = false;
InitTouchEventTouchList(touchEvent); InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEventWithCallback(touchEvent, &MetroInput::OnPointerPressedCallback); DispatchAsyncTouchEventWithCallback(touchEvent, &MetroInput::OnPointerPressedCallback);
} else { } else {
@ -449,10 +465,12 @@ MetroInput::OnPointerPressedCallback()
{ {
nsEventStatus status = DeliverNextQueuedTouchEvent(); nsEventStatus status = DeliverNextQueuedTouchEvent();
mTouchStartDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status); mTouchStartDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status);
// If content cancelled the first touchstart don't generate any gesture based
// input - clear the recognizer state without sending any events.
if (mTouchStartDefaultPrevented) { if (mTouchStartDefaultPrevented) {
// If content canceled the first touchstart don't generate any gesture based
// input - clear the recognizer state without sending any events.
mGestureRecognizer->CompleteGesture(); mGestureRecognizer->CompleteGesture();
// Let the apz know content wants to consume touch events.
mWidget->ApzContentConsumingTouch();
} }
} }
@ -549,12 +567,15 @@ MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
void void
MetroInput::OnFirstPointerMoveCallback() MetroInput::OnFirstPointerMoveCallback()
{ {
nsTouchEvent* event = static_cast<nsTouchEvent*>(mInputEventQueue.PopFront()); nsEventStatus status = DeliverNextQueuedTouchEvent();
MOZ_ASSERT(event); mCancelable = false;
nsEventStatus status;
mWidget->DispatchEvent(event, status);
mTouchMoveDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status); mTouchMoveDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status);
delete event; // Let the apz know whether content wants to consume touch events
if (mTouchMoveDefaultPrevented) {
mWidget->ApzContentConsumingTouch();
} else if (!mTouchMoveDefaultPrevented && !mTouchStartDefaultPrevented) {
mWidget->ApzContentIgnoringTouch();
}
} }
// This event is raised when the user lifts the left mouse button, lifts a // This event is raised when the user lifts the left mouse button, lifts a
@ -1096,19 +1117,69 @@ MetroInput::DispatchAsyncTouchEventIgnoreStatus(nsTouchEvent* aEvent)
nsEventStatus nsEventStatus
MetroInput::DeliverNextQueuedTouchEvent() MetroInput::DeliverNextQueuedTouchEvent()
{ {
nsEventStatus status;
nsTouchEvent* event = static_cast<nsTouchEvent*>(mInputEventQueue.PopFront()); nsTouchEvent* event = static_cast<nsTouchEvent*>(mInputEventQueue.PopFront());
MOZ_ASSERT(event); MOZ_ASSERT(event);
nsEventStatus status;
AutoDeleteEvent wrap(event);
/*
* We go through states here and make different decisions in each:
*
* 1) delivering first touchpoint touchstart or its first touchmove
* Our callers (OnFirstPointerMoveCallback, OnPointerPressedCallback) will
* check our result and set mTouchStartDefaultPrevented or
* mTouchMoveDefaultPrevented appropriately. Deliver touch events to the apz
* (ignoring return result) and to content and return the content event
* status result to our caller.
* 2) mTouchStartDefaultPrevented or mTouchMoveDefaultPrevented are true
* Deliver touch directly to content and bypass the apz. Our callers
* handle calling cancel for the touch sequence on the apz.
* 3) mTouchStartDefaultPrevented and mTouchMoveDefaultPrevented are false
* Deliver events to the apz. If the apz returns eConsumeNoDefault dispatch
* a touchcancel to content and do not deliver any additional events there.
* (If the apz is doing something with the events we can save ourselves
* the overhead of delivering dom events.)
*/
// Check if content called preventDefault on touchstart or first touchmove. If so
// send directly to content, do not forward to the apz.
if (mTouchStartDefaultPrevented || mTouchMoveDefaultPrevented) {
// continue delivering events to content
mWidget->DispatchEvent(event, status); mWidget->DispatchEvent(event, status);
// Deliver to the apz if content has *not* cancelled touchstart or the first touchmove.
if (!mTouchStartDefaultPrevented && !mTouchMoveDefaultPrevented && MetroWidget::sAPZC) {
MultiTouchInput inputData(*event);
MetroWidget::sAPZC->ReceiveInputEvent(inputData);
}
delete event;
return status; return status;
} }
// Forward event data to apz. If the apz consumes the event, don't forward to
// content if this is not a cancelable event.
status = mWidget->ApzReceiveInputEvent(event);
if (!mCancelable && status == nsEventStatus_eConsumeNoDefault) {
if (!mTouchCancelSent) {
mTouchCancelSent = true;
DispatchTouchCancel();
}
return status;
}
// Deliver event to content
mWidget->DispatchEvent(event, status);
return status;
}
void
MetroInput::DispatchTouchCancel()
{
LogFunction();
// From the spec: The touch point or points that were removed must be
// included in the changedTouches attribute of the TouchEvent, and must
// not be included in the touches and targetTouches attributes.
// (We are 'removing' all touch points that have been sent to content
// thus far.)
nsTouchEvent touchEvent(true, NS_TOUCH_CANCEL, mWidget.Get());
InitTouchEventTouchList(&touchEvent);
mWidget->DispatchEvent(&touchEvent, sThrowawayStatus);
}
void void
MetroInput::DispatchAsyncTouchEventWithCallback(nsTouchEvent* aEvent, void (MetroInput::*Callback)()) MetroInput::DispatchAsyncTouchEventWithCallback(nsTouchEvent* aEvent, void (MetroInput::*Callback)())
{ {

View File

@ -196,6 +196,8 @@ private:
bool mTouchStartDefaultPrevented; bool mTouchStartDefaultPrevented;
bool mTouchMoveDefaultPrevented; bool mTouchMoveDefaultPrevented;
bool mIsFirstTouchMove; bool mIsFirstTouchMove;
bool mCancelable;
bool mTouchCancelSent;
// In the old Win32 way of doing things, we would receive a WM_TOUCH event // In the old Win32 way of doing things, we would receive a WM_TOUCH event
// that told us the state of every touchpoint on the touch surface. If // that told us the state of every touchpoint on the touch surface. If
@ -271,6 +273,7 @@ private:
// Sync event dispatching // Sync event dispatching
void DispatchEventIgnoreStatus(nsGUIEvent *aEvent); void DispatchEventIgnoreStatus(nsGUIEvent *aEvent);
void DispatchTouchCancel();
nsDeque mInputEventQueue; nsDeque mInputEventQueue;
static nsEventStatus sThrowawayStatus; static nsEventStatus sThrowawayStatus;

View File

@ -31,6 +31,7 @@
#endif #endif
#include "UIABridgePrivate.h" #include "UIABridgePrivate.h"
#include "WinMouseScrollHandler.h" #include "WinMouseScrollHandler.h"
#include "InputData.h"
using namespace Microsoft::WRL; using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers; using namespace Microsoft::WRL::Wrappers;
@ -978,6 +979,39 @@ CompositorParent* MetroWidget::NewCompositorParent(int aSurfaceWidth, int aSurfa
return compositor; return compositor;
} }
void
MetroWidget::ApzContentConsumingTouch()
{
LogFunction();
if (!MetroWidget::sAPZC) {
return;
}
MetroWidget::sAPZC->ContentReceivedTouch(mRootLayerTreeId, true);
}
void
MetroWidget::ApzContentIgnoringTouch()
{
LogFunction();
if (!MetroWidget::sAPZC) {
return;
}
MetroWidget::sAPZC->ContentReceivedTouch(mRootLayerTreeId, false);
}
nsEventStatus
MetroWidget::ApzReceiveInputEvent(nsTouchEvent* aEvent)
{
MOZ_ASSERT(aEvent);
if (!MetroWidget::sAPZC) {
return nsEventStatus_eIgnore;
}
MultiTouchInput inputData(*aEvent);
return MetroWidget::sAPZC->ReceiveInputEvent(inputData);
}
LayerManager* LayerManager*
MetroWidget::GetLayerManager(PLayerTransactionChild* aShadowManager, MetroWidget::GetLayerManager(PLayerTransactionChild* aShadowManager,
LayersBackend aBackendHint, LayersBackend aBackendHint,

View File

@ -196,6 +196,10 @@ public:
virtual void SetTransparencyMode(nsTransparencyMode aMode); virtual void SetTransparencyMode(nsTransparencyMode aMode);
virtual nsTransparencyMode GetTransparencyMode(); virtual nsTransparencyMode GetTransparencyMode();
// APZ related apis
void ApzContentConsumingTouch();
void ApzContentIgnoringTouch();
nsEventStatus ApzReceiveInputEvent(nsTouchEvent* aEvent);
nsresult RequestContentScroll(); nsresult RequestContentScroll();
void RequestContentRepaintImplMainThread(); void RequestContentRepaintImplMainThread();