Bug 1887435 - Ensure the `isPrimary` property of `PointerEvent` is correctly set for touch input; r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D207017
This commit is contained in:
Edgar Chen 2024-04-11 21:49:49 +00:00
Родитель 657e7dfb94
Коммит e7c1812c97
6 изменённых файлов: 101 добавлений и 35 удалений

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

@ -2278,8 +2278,8 @@ void EventStateManager::MaybeFirePointerCancel(WidgetInputEvent* aEvent) {
WidgetPointerEvent event(aTouchEvent->IsTrusted(), ePointerCancel,
aTouchEvent->mWidget);
PointerEventHandler::InitPointerEventFromTouch(
event, *aTouchEvent, *aTouchEvent->mTouches[0], true);
PointerEventHandler::InitPointerEventFromTouch(event, *aTouchEvent,
*aTouchEvent->mTouches[0]);
event.convertToPointer = false;
presShell->HandleEventWithTarget(&event, targetFrame, content, &status);

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

@ -15,6 +15,7 @@
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/MouseEventBinding.h"
namespace mozilla {
@ -80,7 +81,8 @@ void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
// In this case we have to know information about available mouse pointers
sActivePointersIds->InsertOrUpdate(
aEvent->pointerId,
MakeUnique<PointerInfo>(false, aEvent->mInputSource, true, nullptr));
MakeUnique<PointerInfo>(false, aEvent->mInputSource, true, false,
nullptr));
MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId);
break;
@ -94,6 +96,7 @@ void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
pointerEvent->pointerId,
MakeUnique<PointerInfo>(
true, pointerEvent->mInputSource, pointerEvent->mIsPrimary,
pointerEvent->mFromTouchEvent,
aTargetContent ? aTargetContent->OwnerDoc() : nullptr));
MaybeCacheSpoofedPointerID(pointerEvent->mInputSource,
pointerEvent->pointerId);
@ -112,7 +115,8 @@ void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
sActivePointersIds->InsertOrUpdate(
pointerEvent->pointerId,
MakeUnique<PointerInfo>(false, pointerEvent->mInputSource,
pointerEvent->mIsPrimary, nullptr));
pointerEvent->mIsPrimary,
pointerEvent->mFromTouchEvent, nullptr));
} else {
sActivePointersIds->Remove(pointerEvent->pointerId);
}
@ -314,7 +318,7 @@ void PointerEventHandler::ProcessPointerCaptureForTouch(
continue;
}
WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
InitPointerEventFromTouch(event, *aEvent, *touch, i == 0);
InitPointerEventFromTouch(event, *aEvent, *touch);
CheckPointerCaptureState(&event);
}
}
@ -548,7 +552,7 @@ void PointerEventHandler::InitPointerEventFromMouse(
/* static */
void PointerEventHandler::InitPointerEventFromTouch(
WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent,
const mozilla::dom::Touch& aTouch, bool aIsPrimary) {
const mozilla::dom::Touch& aTouch) {
// Use mButton/mButtons only when mButton got a value (from pen input)
int16_t button = aTouchEvent.mMessage == eTouchMove ? MouseButton::eNotPressed
: aTouchEvent.mButton != MouseButton::eNotPressed
@ -560,7 +564,10 @@ void PointerEventHandler::InitPointerEventFromTouch(
? aTouchEvent.mButtons
: MouseButtonsFlag::ePrimaryFlag;
aPointerEvent.mIsPrimary = aIsPrimary;
// Only the first touch would be the primary pointer.
aPointerEvent.mIsPrimary = aTouchEvent.mMessage == eTouchStart
? !HasActiveTouchPointer()
: GetPointerPrimaryState(aTouch.Identifier());
aPointerEvent.pointerId = aTouch.Identifier();
aPointerEvent.mRefPoint = aTouch.mRefPoint;
aPointerEvent.mModifiers = aTouchEvent.mModifiers;
@ -681,7 +688,7 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch(
WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
touchEvent->mWidget);
InitPointerEventFromTouch(event, *touchEvent, *touch, i == 0);
InitPointerEventFromTouch(event, *touchEvent, *touch);
event.convertToPointer = touch->convertToPointer = false;
event.mCoalescedWidgetEvents = touch->mCoalescedWidgetEvents;
if (aMouseOrTouchEvent->mMessage == eTouchStart) {
@ -741,6 +748,15 @@ void PointerEventHandler::NotifyDestroyPresContext(
iter.Remove();
}
}
// Clean up active pointer info
for (auto iter = sActivePointersIds->Iter(); !iter.Done(); iter.Next()) {
PointerInfo* data = iter.UserData();
MOZ_ASSERT(data, "how could we have a null PointerInfo here?");
if (data->mActiveDocument &&
data->mActiveDocument->GetPresContext() == aPresContext) {
iter.Remove();
}
}
}
bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
@ -772,6 +788,16 @@ bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) {
return false;
}
/* static */
bool PointerEventHandler::HasActiveTouchPointer() {
for (auto iter = sActivePointersIds->ConstIter(); !iter.Done(); iter.Next()) {
if (iter.Data()->mFromTouchEvent) {
return true;
}
}
return false;
}
/* static */
void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent,

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

@ -50,13 +50,16 @@ class PointerInfo final {
uint16_t mPointerType;
bool mActiveState;
bool mPrimaryState;
bool mFromTouchEvent;
bool mPreventMouseEventByContent;
WeakPtr<dom::Document> mActiveDocument;
explicit PointerInfo(bool aActiveState, uint16_t aPointerType,
bool aPrimaryState, dom::Document* aActiveDocument)
bool aPrimaryState, bool aFromTouchEvent,
dom::Document* aActiveDocument)
: mPointerType(aPointerType),
mActiveState(aActiveState),
mPrimaryState(aPrimaryState),
mFromTouchEvent(aFromTouchEvent),
mPreventMouseEventByContent(false),
mActiveDocument(aActiveDocument) {}
};
@ -208,8 +211,7 @@ class PointerEventHandler final {
static void InitPointerEventFromTouch(WidgetPointerEvent& aPointerEvent,
const WidgetTouchEvent& aTouchEvent,
const mozilla::dom::Touch& aTouch,
bool aIsPrimary);
const mozilla::dom::Touch& aTouch);
static bool ShouldGeneratePointerEventFromMouse(WidgetGUIEvent* aEvent) {
return aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp ||
@ -250,6 +252,10 @@ class PointerEventHandler final {
// event with pointerId
static bool GetPointerPrimaryState(uint32_t aPointerId);
// HasActiveTouchPointer returns true if there is active pointer event that is
// generated from touch event.
static bool HasActiveTouchPointer();
MOZ_CAN_RUN_SCRIPT
static void DispatchGotOrLostPointerCaptureEvent(
bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent,

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

@ -25,8 +25,7 @@ void CoalescedTouchData::CreateCoalescedTouchEvent(
WidgetPointerEvent* event =
touch->mCoalescedWidgetEvents->mEvents.AppendElement(WidgetPointerEvent(
aEvent.IsTrusted(), ePointerMove, aEvent.mWidget));
PointerEventHandler::InitPointerEventFromTouch(*event, aEvent, *touch,
i == 0);
PointerEventHandler::InitPointerEventFromTouch(*event, aEvent, *touch);
event->mFlags.mBubbles = false;
event->mFlags.mCancelable = false;
}
@ -63,8 +62,7 @@ void CoalescedTouchData::Coalesce(const WidgetTouchEvent& aEvent,
sameTouch->mCoalescedWidgetEvents->mEvents.AppendElement(
WidgetPointerEvent(aEvent.IsTrusted(), ePointerMove,
aEvent.mWidget));
PointerEventHandler::InitPointerEventFromTouch(*event, aEvent, *touch,
i == 0);
PointerEventHandler::InitPointerEventFromTouch(*event, aEvent, *touch);
event->mFlags.mBubbles = false;
event->mFlags.mCancelable = false;
}

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

@ -1,3 +0,0 @@
[pointerevent_pointermove_isprimary_same_as_pointerdown.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]

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

@ -6,6 +6,8 @@
<link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
<meta name="assert" content="The isPrimary attribute of a pointermove event must have the same value as the isPrimary attribute of the last pointerdown event with the same pointerId attribute."/>
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
<meta name="variant" content="?mouse">
<meta name="variant" content="?touch">
<!-- /resources/testharness.js -->
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
@ -22,45 +24,82 @@
// will fail unless the async_test is created with the var name "test_pointerEvent".
add_completion_callback(showPointerTypes);
var pointermove_event = null;
var pointerdown_event = null;
var pointermove_events = new Map();
var pointerdown_events = new Map();
function run() {
var target0 = document.getElementById("target0");
var actions_promise;
on_event(target0, "pointermove", function (event) {
if (pointerdown_event != null) {
let pointerdown_event = pointerdown_events.get(event.pointerId);
if (pointerdown_event) {
test_pointerEvent.step(function () {
assert_equals(event.pointerId, pointerdown_event.pointerId, "pointermove.pointerId should be the same as pointerdown.pointerId.");
assert_equals(event.isPrimary, pointerdown_event.isPrimary, "pointermove.isPrimary should be the same as pointerdown.isPrimary.");
});
pointermove_event = event;
pointermove_events.set(event.pointerId, event);
}
});
on_event(target0, "pointerdown", function (event) {
pointerdown_event = event;
assert_equals(event.isPrimary, !!(pointerdown_events.size == 0), "pointerdown.isPrimary should only be true for first pointer.");
pointerdown_events.set(event.pointerId, event);
detected_pointertypes[event.pointerType] = true;
});
on_event(target0, "pointerup", function (event) {
let pointerdown_event = pointerdown_events.get(event.pointerId);
test_pointerEvent.step(function () {
assert_not_equals(pointermove_event, null, "pointermove event was never received: ");
});
// Make sure the test finishes after all the input actions are completed.
actions_promise.then( () => {
test_pointerEvent.done();
assert_true(!!pointerdown_event, "pointerdown event was received.");
assert_true(!!pointermove_events.size, "pointermove event was received.");
assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerup.pointerId should be the same as pointerdown.pointerId.");
assert_equals(event.isPrimary, pointerdown_event.isPrimary, "pointerup.isPrimary should be the same as pointerdown.isPrimary.");
});
pointermove_events.delete(event.pointerId);
pointerdown_events.delete(event.pointerId);
if (pointerdown_events.size == 0) {
// Make sure the test finishes after all the input actions are completed.
actions_promise.then(() => {
test_pointerEvent.done();
});
}
});
// Dispatch a mouse drag in target0.
actions_promise = new test_driver.Actions()
.pointerMove(0, 0, {origin: target0})
.pointerDown()
.pointerMove(3, 3, {origin: target0})
.pointerUp()
.send();
// Dispatch a mouse/touch drag in target0.
var pointerType = location.search.substring(1);
switch (pointerType) {
case "mouse":
actions_promise = new test_driver.Actions()
.pointerMove(0, 0, {origin: target0})
.pointerDown()
.pointerMove(3, 3, {origin: target0})
.pointerUp()
.send();
break;
case "touch":
actions_promise = new test_driver.Actions()
.addPointer("touchPointer1", "touch")
.pointerMove(0, 0, {origin: target0, sourceName: "touchPointer1"})
.pointerDown({sourceName: "touchPointer1"})
.pointerMove(3, 3, {origin: target0, sourceName: "touchPointer1"})
.addPointer("touchPointer2", "touch")
.pointerMove(0, 0, {origin: target0, sourceName: "touchPointer2"})
.pointerDown({sourceName: "touchPointer2"})
.pointerMove(5, 5, {origin: target0, sourceName: "touchPointer2"})
.addPointer("touchPointer3", "touch")
.pointerMove(0, 0, {origin: target0, sourceName: "touchPointer3"})
.pointerDown({sourceName: "touchPointer3"})
.pointerMove(7, 7, {origin: target0, sourceName: "touchPointer3"})
.pointerUp({sourceName: "touchPointer3"})
.pointerUp({sourceName: "touchPointer1"})
.pointerUp({sourceName: "touchPointer2"})
.send();
break;
default:
assert_true(false, `does support testing ${pointerType} input`);
break;
}
}
</script>
</head>