Bug 1340085 - [Pointer Event] Stop firing pointer events after firing eTouchCancel. f=smaug. r=kats

--HG--
extra : rebase_source : 95539f13dd0316cb4c01382a4c0213b00dcb64b0
This commit is contained in:
Stone Shih 2017-02-16 15:05:09 +08:00
parent da577361e9
commit f3b56f1a20
7 changed files with 94 additions and 33 deletions

View File

@ -368,7 +368,7 @@ APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
aApzResponse == nsEventStatus_eConsumeDoDefault &&
gfxPrefs::PointerEventsEnabled()) {
WidgetTouchEvent cancelEvent(aEvent);
cancelEvent.mMessage = eTouchCancel;
cancelEvent.mMessage = eTouchPointerCancel;
cancelEvent.mFlags.mCancelable = false; // mMessage != eTouchCancel;
for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) {
if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) {

View File

@ -7026,6 +7026,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell,
pointerMessage = ePointerDown;
break;
case eTouchCancel:
case eTouchPointerCancel:
pointerMessage = ePointerCancel;
break;
default:
@ -7034,7 +7035,7 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell,
for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
mozilla::dom::Touch* touch = touchEvent->mTouches[i];
if (!touch || !touch->convertToPointer) {
if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
continue;
}

View File

@ -140,7 +140,8 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
touch->mChanged = true;
}
touch->mMessage = aEvent->mMessage;
TouchInfo info = { touch, GetNonAnonymousAncestor(touch->mTarget) };
TouchInfo info = { touch, GetNonAnonymousAncestor(touch->mTarget),
true };
sCaptureTouchList->Put(id, info);
}
break;
@ -247,6 +248,26 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent,
AppendToTouchList(&touches);
break;
}
case eTouchPointerCancel: {
// Don't generate pointer events by touch events after eTouchPointerCancel
// is received.
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
for (uint32_t i = 0; i < touches.Length(); ++i) {
Touch* touch = touches[i];
if (!touch) {
continue;
}
int32_t id = touch->Identifier();
TouchInfo info;
if (!sCaptureTouchList->Get(id, &info)) {
continue;
}
info.mConvertToPointer = false;
sCaptureTouchList->Put(id, info);
}
break;
}
default:
break;
}
@ -290,4 +311,24 @@ TouchManager::GetCapturedTouch(int32_t aId)
return touch.forget();
}
/*static*/ bool
TouchManager::ShouldConvertTouchToPointer(const Touch* aTouch,
const WidgetTouchEvent* aEvent)
{
if (!aTouch || !aTouch->convertToPointer) {
return false;
}
TouchInfo info;
if (!sCaptureTouchList->Get(aTouch->Identifier(), &info)) {
// This check runs before the TouchManager has the touch registered in its
// touch list. It's because we dispatching pointer events before handling
// touch events. So we convert eTouchStart to pointerdown even it's not
// registered.
// Check WidgetTouchEvent::mMessage because Touch::mMessage is assigned when
// pre-handling touch events.
return aEvent->mMessage == eTouchStart;
}
return info.mConvertToPointer;
}
} // namespace mozilla

View File

@ -40,7 +40,8 @@ public:
static already_AddRefed<nsIContent> GetAnyCapturedTouchTarget();
static bool HasCapturedTouch(int32_t aId);
static already_AddRefed<dom::Touch> GetCapturedTouch(int32_t aId);
static bool ShouldConvertTouchToPointer(const dom::Touch* aTouch,
const WidgetTouchEvent* aEvent);
private:
void EvictTouches();
static void EvictTouchPoint(RefPtr<dom::Touch>& aTouch,
@ -54,6 +55,7 @@ private:
{
RefPtr<mozilla::dom::Touch> mTouch;
nsCOMPtr<nsIContent> mNonAnonymousTarget;
bool mConvertToPointer;
};
static nsDataHashtable<nsUint32HashKey, TouchInfo>* sCaptureTouchList;
};

View File

@ -114,44 +114,58 @@ function runTests() {
var touchCancelTriggered = 0;
var pointerCancelTriggered = 0;
d0.onmousedown = function(e) {
mouseDownTriggered = 1;
is(pointerDownTriggered , 1, "Mouse event must be triggered after pointer event!");
};
d0.ontouchstart = function(e) {
touchDownTriggered = 1;
is(touchDownTriggered , 1, "Touch event must be triggered after pointer event!");
}
d0.ontouchcancel = function(e) {
touchCancelTriggered = 1;
is(pointerCancelTriggered, 1, "Touch cancel event must be triggered after pointer event!");
}
d0.onpointerdown = function(e) {
pointerDownTriggered = 1;
is(mouseDownTriggered, 0, "Pointer event must be triggered before mouse event!");
is(touchDownTriggered, 0, "Pointer event must be triggered before touch event!");
};
d0.addEventListener("pointercancel", function(ev) {
is(ev.pointerId, 0, "Correct default pointerId");
is(ev.bubbles, true, "bubbles should be true");
is(ev.cancelable, false, "pointercancel cancelable should be false ");
pointerCancelTriggered = 1;
is(touchCancelTriggered, 0, "Pointer event must be triggered before touch event!");
// Test pointer event generated from mouse event
d0.addEventListener("mousedown", (e) => {
++mouseDownTriggered;
is(pointerDownTriggered , mouseDownTriggered, "Mouse event must be triggered after pointer event!");
}, {once: true});
d0.addEventListener("pointerdown", (e) => {
++pointerDownTriggered;
is(pointerDownTriggered, mouseDownTriggered + 1, "Pointer event must be triggered before mouse event!");
}, {once: true});
// Test pointer event generated from mouse event
synthesizeMouse(d1, 3, 3, { type: "mousemove"});
synthesizeMouse(d1, 3, 3, { type: "mousedown"});
synthesizeMouse(d1, 3, 3, { type: "mouseup"});
// Test pointer event generated from touch event
pointerDownTriggered = 0;
mouseDownTriggered = 0;
pointerDownTriggered = 0;
d0.addEventListener("touchstart", (e) => {
++touchDownTriggered;
is(pointerDownTriggered, touchDownTriggered, "Touch event must be triggered after pointer event!");
}, {once: true});
d0.addEventListener("mousedown", (e) => {
++mouseDownTriggered;
is(pointerDownTriggered , mouseDownTriggered, "Mouse event must be triggered after pointer event!");
}, {once: true});
d0.addEventListener("pointerdown", (e) => {
++pointerDownTriggered;
is(pointerDownTriggered, touchDownTriggered + 1, "Pointer event must be triggered before mouse event!");
is(pointerDownTriggered, mouseDownTriggered + 1, "Pointer event must be triggered before mouse event!");
}, {once: true});
d0.addEventListener("touchcancel", (e) => {
++touchCancelTriggered;
is(pointerCancelTriggered, touchCancelTriggered, "Touch cancel event must be triggered after pointer event!");
}, {once: true});
d0.addEventListener("pointercancel", function(ev) {
is(ev.pointerId, 0, "Correct default pointerId");
is(ev.bubbles, true, "bubbles should be true");
is(ev.cancelable, false, "pointercancel cancelable should be false ");
++pointerCancelTriggered;
is(pointerCancelTriggered, touchCancelTriggered + 1, "Pointer event must be triggered before touch event!");
}, {once: true});
var cwu = SpecialPowers.getDOMWindowUtils(window);
var event1 = getTouchEventForTarget(d1, cwu, 0);
sendTouchEvent(cwu, "touchmove", event1, 0);
sendTouchEvent(cwu, "touchstart", event1, 0);
sendTouchEvent(cwu, "touchmove", event1, 0);
// Test Touch to Pointer Cancel
sendTouchEvent(cwu, "touchcancel", event1, 0);
@ -228,6 +242,7 @@ function runTests() {
};
synthesizeMouse(d1, 3, 3, { type: "mousemove"});
sendTouchEvent(cwu, "touchstart", getTouchEventForTarget(d3, cwu, 3), 0);
sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d3, cwu, 3), 0);
is(touchPointerEnterLeaveCount, 1, "Wrong touch enterLeave count for!");
is(mousePointerEnterLeaveCount, 2, "Wrong mouse enterLeave count for!");
@ -291,8 +306,8 @@ function runTests() {
is(e.pointerType, "mouse", "Wrong Pointer type, should be mouse type");
}
pointerOutTriggeredForCancelEvent = 1;
};
d1.onpointerleave = function(e) {
};
d1.onpointerleave = function(e) {
is(pointerOutTriggeredForCancelEvent, 1, "Pointer Out must be dispatched bedore Pointer leave");
if (pointerLeaveTriggeredForCancelEvent == 0) {
is(e.pointerId, 3, "Wrong Pointer type, should be id from Touch event");

View File

@ -398,6 +398,7 @@ NS_EVENT_MESSAGE(eTouchStart)
NS_EVENT_MESSAGE(eTouchMove)
NS_EVENT_MESSAGE(eTouchEnd)
NS_EVENT_MESSAGE(eTouchCancel)
NS_EVENT_MESSAGE(eTouchPointerCancel)
// Pointerlock DOM API
NS_EVENT_MESSAGE(ePointerLockChange)

View File

@ -439,7 +439,8 @@ WidgetEvent::IsAllowedToDispatchDOMEvent() const
return wheelEvent->mDeltaX != 0.0 || wheelEvent->mDeltaY != 0.0 ||
wheelEvent->mDeltaZ != 0.0;
}
case eTouchEventClass:
return mMessage != eTouchPointerCancel;
// Following events are handled in EventStateManager, so, we don't need to
// dispatch DOM event for them into the DOM tree.
case eQueryContentEventClass: