Bug 1550462 - part 2: Make PresShell::HandleEvent dispatch preceding pointerrawupdate event r=smaug,dom-core,edgar
This patch tries to dispatch ePointerRawUpdate with PresShell::EventHandler::DispatchPrecedingPointerEvent as same as usual pointer events. For using the path, this patch adds 2 internal events, eMouseRawUpdate and eTouchRawUpdate which are never dispatched into the DOM because PresShell::EventHandler::DispatchPrecedingPointerEvent will return false for that and then the caller will stop handling the internal events. There are 3 dispatchers of the internal raw update events. One is PresShell::EnsurePrecedingPointerRawUpdate(). This dispatches the internal event if and only if the coming event of PresShell::HandleEvent will cause ePointerMove. The reason why PresShell::HandleEvent handles the preceding raw-update event is, we should support ePointerRawUpdate events for synthesized events for tests (in-process ones) and in the main process. Additionally, if a pointerrawupdate event may destroy the target <iframe>. In such ase, the following pointermove may need to be dispatched on its parent window or another <iframe> window. Therefore, we need to dispatch the internal raw update event before considering the target window (PresShell) and handling the capturing element. The others are BrowserChild::RecvRealMouseMoveEvent and BrowserChild::RecvRealTouchMoveEvent. They dispatch the internal events when they won't dispatch the received event immediately to coalesce with further similar input. For avoiding to dispatch the internal event for same source event, this adds WidgetPointerHelper::convertToPointerRawUpdate member to check it in PresShell::HandlePrecedingPointerRawUpdate. Differential Revision: https://phabricator.services.mozilla.com/D243404
This commit is contained in:
committed by
masayuki@d-toybox.com
parent
0835ca3b3f
commit
90cd469f14
@@ -995,6 +995,7 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||||||
if (touchEvent->mMessage == eTouchMove) {
|
if (touchEvent->mMessage == eTouchMove) {
|
||||||
GenerateDragGesture(aPresContext, touchEvent);
|
GenerateDragGesture(aPresContext, touchEvent);
|
||||||
} else {
|
} else {
|
||||||
|
MOZ_ASSERT(touchEvent->mMessage != eTouchRawUpdate);
|
||||||
mInTouchDrag = false;
|
mInTouchDrag = false;
|
||||||
StopTrackingDragGesture(true);
|
StopTrackingDragGesture(true);
|
||||||
}
|
}
|
||||||
@@ -1142,7 +1143,8 @@ nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||||||
}
|
}
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case eMouseMove:
|
case eMouseMove:
|
||||||
case ePointerMove: {
|
case ePointerMove:
|
||||||
|
case ePointerRawUpdate: {
|
||||||
if (aEvent->mMessage == ePointerMove) {
|
if (aEvent->mMessage == ePointerMove) {
|
||||||
PointerEventHandler::UpdateActivePointerState(mouseEvent,
|
PointerEventHandler::UpdateActivePointerState(mouseEvent,
|
||||||
aTargetContent);
|
aTargetContent);
|
||||||
@@ -5564,7 +5566,8 @@ void EventStateManager::GeneratePointerEnterExit(EventMessage aMessage,
|
|||||||
/* static */
|
/* static */
|
||||||
void EventStateManager::UpdateLastRefPointOfMouseEvent(
|
void EventStateManager::UpdateLastRefPointOfMouseEvent(
|
||||||
WidgetMouseEvent* aMouseEvent) {
|
WidgetMouseEvent* aMouseEvent) {
|
||||||
if (aMouseEvent->mMessage != eMouseMove &&
|
if (aMouseEvent->mMessage != ePointerRawUpdate &&
|
||||||
|
aMouseEvent->mMessage != eMouseMove &&
|
||||||
aMouseEvent->mMessage != ePointerMove) {
|
aMouseEvent->mMessage != ePointerMove) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -5597,7 +5600,8 @@ void EventStateManager::UpdateLastRefPointOfMouseEvent(
|
|||||||
void EventStateManager::ResetPointerToWindowCenterWhilePointerLocked(
|
void EventStateManager::ResetPointerToWindowCenterWhilePointerLocked(
|
||||||
WidgetMouseEvent* aMouseEvent) {
|
WidgetMouseEvent* aMouseEvent) {
|
||||||
MOZ_ASSERT(PointerLockManager::IsLocked());
|
MOZ_ASSERT(PointerLockManager::IsLocked());
|
||||||
if ((aMouseEvent->mMessage != eMouseMove &&
|
if ((aMouseEvent->mMessage != ePointerRawUpdate &&
|
||||||
|
aMouseEvent->mMessage != eMouseMove &&
|
||||||
aMouseEvent->mMessage != ePointerMove) ||
|
aMouseEvent->mMessage != ePointerMove) ||
|
||||||
!aMouseEvent->mWidget) {
|
!aMouseEvent->mWidget) {
|
||||||
return;
|
return;
|
||||||
@@ -5658,6 +5662,7 @@ void EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) {
|
|||||||
switch (aMouseEvent->mMessage) {
|
switch (aMouseEvent->mMessage) {
|
||||||
case eMouseMove:
|
case eMouseMove:
|
||||||
case ePointerMove:
|
case ePointerMove:
|
||||||
|
case ePointerRawUpdate:
|
||||||
case ePointerDown:
|
case ePointerDown:
|
||||||
case ePointerGotCapture: {
|
case ePointerGotCapture: {
|
||||||
// Get the target content target (mousemove target == mouseover target)
|
// Get the target content target (mousemove target == mouseover target)
|
||||||
|
|||||||
@@ -385,7 +385,9 @@ void PointerEvent::GetCoalescedEvents(
|
|||||||
|
|
||||||
void PointerEvent::EnsureFillingCoalescedEvents(
|
void PointerEvent::EnsureFillingCoalescedEvents(
|
||||||
WidgetPointerEvent& aWidgetEvent) {
|
WidgetPointerEvent& aWidgetEvent) {
|
||||||
if (!aWidgetEvent.IsTrusted() || aWidgetEvent.mMessage != ePointerMove ||
|
if (!aWidgetEvent.IsTrusted() ||
|
||||||
|
(aWidgetEvent.mMessage != ePointerMove &&
|
||||||
|
aWidgetEvent.mMessage != ePointerRawUpdate) ||
|
||||||
!mCoalescedEvents.IsEmpty() ||
|
!mCoalescedEvents.IsEmpty() ||
|
||||||
(aWidgetEvent.mCoalescedWidgetEvents &&
|
(aWidgetEvent.mCoalescedWidgetEvents &&
|
||||||
!aWidgetEvent.mCoalescedWidgetEvents->mEvents.IsEmpty()) ||
|
!aWidgetEvent.mCoalescedWidgetEvents->mEvents.IsEmpty()) ||
|
||||||
|
|||||||
@@ -715,7 +715,9 @@ void PointerEventHandler::InitPointerEventFromTouch(
|
|||||||
WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent,
|
WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent,
|
||||||
const mozilla::dom::Touch& aTouch) {
|
const mozilla::dom::Touch& aTouch) {
|
||||||
// Use mButton/mButtons only when mButton got a value (from pen input)
|
// Use mButton/mButtons only when mButton got a value (from pen input)
|
||||||
int16_t button = aTouchEvent.mMessage == eTouchMove ? MouseButton::eNotPressed
|
int16_t button = aTouchEvent.mMessage == eTouchRawUpdate ||
|
||||||
|
aTouchEvent.mMessage == eTouchMove
|
||||||
|
? MouseButton::eNotPressed
|
||||||
: aTouchEvent.mButton != MouseButton::eNotPressed
|
: aTouchEvent.mButton != MouseButton::eNotPressed
|
||||||
? aTouchEvent.mButton
|
? aTouchEvent.mButton
|
||||||
: MouseButton::ePrimary;
|
: MouseButton::ePrimary;
|
||||||
@@ -783,6 +785,9 @@ EventMessage PointerEventHandler::ToPointerEventMessage(
|
|||||||
MOZ_ASSERT(aMouseOrTouchEvent);
|
MOZ_ASSERT(aMouseOrTouchEvent);
|
||||||
|
|
||||||
switch (aMouseOrTouchEvent->mMessage) {
|
switch (aMouseOrTouchEvent->mMessage) {
|
||||||
|
case eMouseRawUpdate:
|
||||||
|
case eTouchRawUpdate:
|
||||||
|
return ePointerRawUpdate;
|
||||||
case eMouseMove:
|
case eMouseMove:
|
||||||
return ePointerMove;
|
return ePointerMove;
|
||||||
case eMouseUp:
|
case eMouseUp:
|
||||||
@@ -809,6 +814,15 @@ EventMessage PointerEventHandler::ToPointerEventMessage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool PointerEventHandler::NeedToDispatchPointerRawUpdate(
|
||||||
|
const Document* aDocument) {
|
||||||
|
const nsPIDOMWindowInner* const innerWindow =
|
||||||
|
aDocument ? aDocument->GetInnerWindow() : nullptr;
|
||||||
|
return innerWindow && innerWindow->HasPointerRawUpdateEventListeners() &&
|
||||||
|
innerWindow->IsSecureContext();
|
||||||
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
void PointerEventHandler::DispatchPointerFromMouseOrTouch(
|
void PointerEventHandler::DispatchPointerFromMouseOrTouch(
|
||||||
PresShell* aShell, nsIFrame* aEventTargetFrame,
|
PresShell* aShell, nsIFrame* aEventTargetFrame,
|
||||||
@@ -838,10 +852,8 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. If it is not mouse then it is likely will come as touch event
|
// 1. If it is not mouse then it is likely will come as touch event
|
||||||
// 2. We don't synthesize pointer events for those events that are not
|
// 2. We don't synthesize pointer events for synthesized mouse move
|
||||||
// dispatched to DOM.
|
if (!mouseEvent->convertToPointer || mouseEvent->IsSynthesized()) {
|
||||||
if (!mouseEvent->convertToPointer ||
|
|
||||||
!aMouseOrTouchEvent->IsAllowedToDispatchDOMEvent()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,6 +861,30 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch(
|
|||||||
if (pointerMessage == eVoidEvent) {
|
if (pointerMessage == eVoidEvent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (pointerMessage == ePointerRawUpdate) {
|
||||||
|
const nsIContent* const targetContent =
|
||||||
|
aEventTargetContent ? aEventTargetContent
|
||||||
|
: aEventTargetFrame->GetContent();
|
||||||
|
NS_ASSERTION(targetContent, "Where do we want to try to dispatch?");
|
||||||
|
if (targetContent) {
|
||||||
|
NS_ASSERTION(
|
||||||
|
targetContent->IsInComposedDoc(),
|
||||||
|
nsPrintfCString("Do we want to dispatch ePointerRawUpdate onto "
|
||||||
|
"disconnected content? (targetContent=%s)",
|
||||||
|
ToString(*targetContent).c_str())
|
||||||
|
.get());
|
||||||
|
if (!NeedToDispatchPointerRawUpdate(targetContent->OwnerDoc())) {
|
||||||
|
NS_ASSERTION(
|
||||||
|
false,
|
||||||
|
nsPrintfCString(
|
||||||
|
"Did we fail to retarget the document? (targetContent=%s)",
|
||||||
|
ToString(*targetContent).c_str())
|
||||||
|
.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // #ifdef DEBUG
|
||||||
WidgetPointerEvent event(*mouseEvent);
|
WidgetPointerEvent event(*mouseEvent);
|
||||||
InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
|
InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
|
||||||
event.convertToPointer = mouseEvent->convertToPointer = false;
|
event.convertToPointer = mouseEvent->convertToPointer = false;
|
||||||
@@ -927,7 +963,7 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch(
|
|||||||
// all pointer events should be dispatched to the same target as their
|
// all pointer events should be dispatched to the same target as their
|
||||||
// corresponding touch events. Call PresShell::HandleEvent so that we do
|
// corresponding touch events. Call PresShell::HandleEvent so that we do
|
||||||
// hit test for pointer events.
|
// hit test for pointer events.
|
||||||
// FIXME: If aDontRetargetEvents is true and the event is fired on
|
// FIXME: If aDontRetargetEvents is false and the event is fired on
|
||||||
// different document, we cannot track the pointer event target when
|
// different document, we cannot track the pointer event target when
|
||||||
// it's removed from the tree.
|
// it's removed from the tree.
|
||||||
PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
|
PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
|
||||||
@@ -992,9 +1028,15 @@ void PointerEventHandler::NotifyDestroyPresContext(
|
|||||||
bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
|
bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
|
||||||
// We shouldn't start a drag session if the event is synthesized one because
|
// We shouldn't start a drag session if the event is synthesized one because
|
||||||
// aEvent doesn't have enough information for initializing the ePointerCancel.
|
// aEvent doesn't have enough information for initializing the ePointerCancel.
|
||||||
if (!aEvent.IsReal()) {
|
if (aEvent.IsSynthesized()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// And we should not start with raw update events, which should be used only
|
||||||
|
// for notifying web apps of the pointer state changes ASAP.
|
||||||
|
if (aEvent.mMessage == ePointerRawUpdate) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(aEvent.mMessage != eMouseRawUpdate);
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
|
if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
|
||||||
// WM_POINTER does not support drag and drop, see bug 1692277
|
// WM_POINTER does not support drag and drop, see bug 1692277
|
||||||
|
|||||||
@@ -270,14 +270,16 @@ class PointerEventHandler final {
|
|||||||
const WidgetPointerEvent& aSourceEvent);
|
const WidgetPointerEvent& aSourceEvent);
|
||||||
|
|
||||||
static bool ShouldGeneratePointerEventFromMouse(WidgetGUIEvent* aEvent) {
|
static bool ShouldGeneratePointerEventFromMouse(WidgetGUIEvent* aEvent) {
|
||||||
return aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp ||
|
return aEvent->mMessage == eMouseRawUpdate ||
|
||||||
|
aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp ||
|
||||||
(aEvent->mMessage == eMouseMove &&
|
(aEvent->mMessage == eMouseMove &&
|
||||||
aEvent->AsMouseEvent()->IsReal()) ||
|
aEvent->AsMouseEvent()->IsReal()) ||
|
||||||
aEvent->mMessage == eMouseExitFromWidget;
|
aEvent->mMessage == eMouseExitFromWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ShouldGeneratePointerEventFromTouch(WidgetGUIEvent* aEvent) {
|
static bool ShouldGeneratePointerEventFromTouch(WidgetGUIEvent* aEvent) {
|
||||||
return aEvent->mMessage == eTouchStart || aEvent->mMessage == eTouchMove ||
|
return aEvent->mMessage == eTouchRawUpdate ||
|
||||||
|
aEvent->mMessage == eTouchStart || aEvent->mMessage == eTouchMove ||
|
||||||
aEvent->mMessage == eTouchEnd || aEvent->mMessage == eTouchCancel ||
|
aEvent->mMessage == eTouchEnd || aEvent->mMessage == eTouchCancel ||
|
||||||
aEvent->mMessage == eTouchPointerCancel;
|
aEvent->mMessage == eTouchPointerCancel;
|
||||||
}
|
}
|
||||||
@@ -290,11 +292,18 @@ class PointerEventHandler final {
|
|||||||
|
|
||||||
static bool IsDragAndDropEnabled(WidgetMouseEvent& aEvent);
|
static bool IsDragAndDropEnabled(WidgetMouseEvent& aEvent);
|
||||||
|
|
||||||
private:
|
|
||||||
// Get proper pointer event message for a mouse or touch event.
|
// Get proper pointer event message for a mouse or touch event.
|
||||||
static EventMessage ToPointerEventMessage(
|
[[nodiscard]] static EventMessage ToPointerEventMessage(
|
||||||
const WidgetGUIEvent* aMouseOrTouchEvent);
|
const WidgetGUIEvent* aMouseOrTouchEvent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the window containing aDocument has had a
|
||||||
|
* `pointerrawupdate` event listener.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] static bool NeedToDispatchPointerRawUpdate(
|
||||||
|
const dom::Document* aDocument);
|
||||||
|
|
||||||
|
private:
|
||||||
// Set pointer capture of the specified pointer by the element.
|
// Set pointer capture of the specified pointer by the element.
|
||||||
static void SetPointerCaptureById(uint32_t aPointerId,
|
static void SetPointerCaptureById(uint32_t aPointerId,
|
||||||
dom::Element* aElement);
|
dom::Element* aElement);
|
||||||
|
|||||||
@@ -100,6 +100,23 @@ skip-if = [
|
|||||||
|
|
||||||
["test_pointermove_isPrimary_subsequent_pens.html"]
|
["test_pointermove_isPrimary_subsequent_pens.html"]
|
||||||
|
|
||||||
|
["test_pointerrawupdate_event_count.html"]
|
||||||
|
scheme = "https"
|
||||||
|
skip-if = [
|
||||||
|
"os == 'android'", # Bug 1312791
|
||||||
|
"xorigin", # Bug 1958029
|
||||||
|
]
|
||||||
|
|
||||||
|
["test_pointerrawupdate_event_count_touch.html"]
|
||||||
|
scheme = "https"
|
||||||
|
skip-if = [
|
||||||
|
"os == 'android'", # Bug 1312791
|
||||||
|
"verify && os == 'win'", # Bug 1659744
|
||||||
|
"win11_2009", # Bug 1781388
|
||||||
|
"os == 'win' && os_version == '11.26100' && processor == 'x86_64' && opt", # Bug 1781388
|
||||||
|
]
|
||||||
|
support-files = ["!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"]
|
||||||
|
|
||||||
["test_remove_frame_when_got_pointer_capture.html"]
|
["test_remove_frame_when_got_pointer_capture.html"]
|
||||||
|
|
||||||
["test_synthesized_touch.html"]
|
["test_synthesized_touch.html"]
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Test for number of pointerrawupdate events</title>
|
||||||
|
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||||
|
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="target0" style="width: 50px; height: 50px; background: green"></div>
|
||||||
|
<script>
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
SimpleTest.requestCompleteLog();
|
||||||
|
SimpleTest.waitForFocus(async () => {
|
||||||
|
await SpecialPowers.pushPrefEnv({"set": [
|
||||||
|
["dom.event.pointer.rawupdate.enabled", true],
|
||||||
|
["dom.events.coalesce.mousemove", true],
|
||||||
|
["test.events.async.enabled", true],
|
||||||
|
]});
|
||||||
|
|
||||||
|
const target0 = window.document.getElementById("target0");
|
||||||
|
|
||||||
|
// First for flushing pending events in the main process, we should synthesize
|
||||||
|
// a simple click and wait for that.
|
||||||
|
info("Waiting for a click for waiting for stable state...");
|
||||||
|
await new Promise(resolve => {
|
||||||
|
target0.addEventListener("click", resolve, {once: true});
|
||||||
|
synthesizeMouseAtCenter(target0, {});
|
||||||
|
});
|
||||||
|
info("Got a click which must be synthesized by us!");
|
||||||
|
|
||||||
|
const utils = SpecialPowers.getDOMWindowUtils(window);
|
||||||
|
utils.advanceTimeAndRefresh(0);
|
||||||
|
await new Promise(resolve => SimpleTest.executeSoon(resolve));
|
||||||
|
|
||||||
|
function stringifyPointerEvent(event) {
|
||||||
|
return `{ screenX: ${event.screenX}, screenY: ${
|
||||||
|
event.screenY
|
||||||
|
}, clientX: ${event.clientX}, clientY:${event.clientY}, buttons:${
|
||||||
|
event.buttons
|
||||||
|
} }`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allEvents = [];
|
||||||
|
const pointerRawUpdateEvents = [];
|
||||||
|
function onPointerRawUpdate(event) {
|
||||||
|
allEvents.push(event);
|
||||||
|
pointerRawUpdateEvents.push(event);
|
||||||
|
// Currently, we need to compute the coordinates of the coalesced events
|
||||||
|
// while the host event is being dispatched. See bug 1960530.
|
||||||
|
event.getCoalescedEvents();
|
||||||
|
if (pointerRawUpdateEvents.length == 4) {
|
||||||
|
utils.restoreNormalRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target0.addEventListener("pointerrawupdate", onPointerRawUpdate);
|
||||||
|
|
||||||
|
const coalescedPointerMoveEvents = [];
|
||||||
|
const waitForPointerMove = new Promise(function (resolve) {
|
||||||
|
function onPointerMove(event) {
|
||||||
|
allEvents.push(event);
|
||||||
|
for (const coalescedEvent of event.getCoalescedEvents()) {
|
||||||
|
coalescedPointerMoveEvents.push(coalescedEvent);
|
||||||
|
}
|
||||||
|
if (coalescedPointerMoveEvents.length == 4) {
|
||||||
|
target0.removeEventListener("pointermove", onPointerMove);
|
||||||
|
target0.removeEventListener("pointerrawupdate", onPointerRawUpdate);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
target0.addEventListener("pointermove", onPointerMove);
|
||||||
|
});
|
||||||
|
|
||||||
|
info("Synthesizing mouse moves....");
|
||||||
|
synthesizeMouse(target0, 5, 5, {type: "mousemove"});
|
||||||
|
synthesizeMouse(target0, 10, 10, {type: "mousemove"});
|
||||||
|
synthesizeMouse(target0, 15, 15, {type: "mousemove"});
|
||||||
|
synthesizeMouse(target0, 20, 20, {type: "mousemove"});
|
||||||
|
info("Waiting for 4 coalesced pointermove events...");
|
||||||
|
await waitForPointerMove;
|
||||||
|
|
||||||
|
for (const event of allEvents) {
|
||||||
|
info(`${event.type}: ${stringifyPointerEvent(event)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(!!pointerRawUpdateEvents.length, "At least one pointerrawupdate event should be fired");
|
||||||
|
is(
|
||||||
|
pointerRawUpdateEvents.length,
|
||||||
|
coalescedPointerMoveEvents.length,
|
||||||
|
`pointermove.getCoalescedEvents().length should be same as the number of preceding pointerrawupdate`
|
||||||
|
);
|
||||||
|
{
|
||||||
|
let i = 0;
|
||||||
|
for (const pointerRawUpdateEvent of pointerRawUpdateEvents) {
|
||||||
|
const coalescedEvents = pointerRawUpdateEvent.getCoalescedEvents();
|
||||||
|
is(
|
||||||
|
coalescedEvents.length,
|
||||||
|
1,
|
||||||
|
`pointerrawupdate(${i}): should have only one coalesced event`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
`${coalescedEvents[0].type}: ${stringifyPointerEvent(coalescedEvents[0])}`,
|
||||||
|
`${pointerRawUpdateEvent.type}: ${stringifyPointerEvent(pointerRawUpdateEvent)}`,
|
||||||
|
`pointerrawupdate(${i++}): the coalesced event should have same values as the host event`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < Math.min(pointerRawUpdateEvents.length, coalescedPointerMoveEvents.length); i++) {
|
||||||
|
is(
|
||||||
|
stringifyPointerEvent(pointerRawUpdateEvents[i]),
|
||||||
|
stringifyPointerEvent(coalescedPointerMoveEvents[i]),
|
||||||
|
`pointerrawupdate(${i++}): should have same values as coalesced pointermove events`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
info("Waiting for a click for waiting for stable state after the test...");
|
||||||
|
await new Promise(resolve => {
|
||||||
|
target0.addEventListener("click", resolve, {once: true});
|
||||||
|
synthesizeMouseAtCenter(target0, {});
|
||||||
|
});
|
||||||
|
info("Got a click after the test!");
|
||||||
|
utils.restoreNormalRefresh();
|
||||||
|
SimpleTest.finish();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Test for number of pointerrawupdate events of touch</title>
|
||||||
|
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||||
|
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="target0" style="margin: 50px; width: 50px; height: 50px; background: green"></div>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
SimpleTest.requestCompleteLog();
|
||||||
|
|
||||||
|
if (!window.opener) {
|
||||||
|
// the utils function in apz can't not be used in remote iframe, so run the
|
||||||
|
// test in a new tab.
|
||||||
|
info("run tests in a new tab");
|
||||||
|
window.open("test_pointerrawupdate_event_count_touch.html");
|
||||||
|
} else {
|
||||||
|
function runTests() {
|
||||||
|
const target0 = window.document.getElementById("target0");
|
||||||
|
const utils = SpecialPowers.getDOMWindowUtils(window);
|
||||||
|
utils.advanceTimeAndRefresh(0);
|
||||||
|
const allEvents = [];
|
||||||
|
const pointerRawUpdateEvents = [];
|
||||||
|
const coalescedPointerMoveEvents = [];
|
||||||
|
|
||||||
|
function stringifyPointerEvent(event) {
|
||||||
|
return `{ screenX: ${event.screenX}, screenY: ${
|
||||||
|
event.screenY
|
||||||
|
}, clientX: ${event.clientX}, clientY:${event.clientY}, buttons:${
|
||||||
|
event.buttons
|
||||||
|
} }`;
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleTest.executeSoon(async () => {
|
||||||
|
function onPointerRawUpdate(event) {
|
||||||
|
allEvents.push(event);
|
||||||
|
pointerRawUpdateEvents.push(event);
|
||||||
|
// Currently, we need to compute the coordinates of the coalesced events
|
||||||
|
// while the host event is being dispatched. See bug 1960530.
|
||||||
|
event.getCoalescedEvents();
|
||||||
|
}
|
||||||
|
target0.addEventListener("pointerrawupdate", onPointerRawUpdate);
|
||||||
|
const waitForPointerMove = new Promise(resolve => {
|
||||||
|
function onPointerMove(event) {
|
||||||
|
allEvents.push(event);
|
||||||
|
for (const coalescedEvent of event.getCoalescedEvents()) {
|
||||||
|
coalescedPointerMoveEvents.push(coalescedEvent);
|
||||||
|
}
|
||||||
|
if (pointerRawUpdateEvents.length > 1) {
|
||||||
|
target0.removeEventListener("pointermove", onPointerMove);
|
||||||
|
target0.removeEventListener("pointerrawupdate", onPointerRawUpdate);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target0.addEventListener("pointermove", onPointerMove);
|
||||||
|
});
|
||||||
|
|
||||||
|
target0.addEventListener("pointerup", async event => {
|
||||||
|
utils.restoreNormalRefresh();
|
||||||
|
await waitForPointerMove;
|
||||||
|
for (const event of allEvents) {
|
||||||
|
info(`${event.type}: ${stringifyPointerEvent(event)}`);
|
||||||
|
}
|
||||||
|
opener.ok(!!pointerRawUpdateEvents.length, "At least one pointerrawupdate event should be fired");
|
||||||
|
opener.is(
|
||||||
|
pointerRawUpdateEvents.length,
|
||||||
|
coalescedPointerMoveEvents.length,
|
||||||
|
`pointermove.getCoalescedEvents().length should be same as the number of preceding pointerrawupdate`
|
||||||
|
);
|
||||||
|
{
|
||||||
|
let i = 0;
|
||||||
|
for (const pointerRawUpdateEvent of pointerRawUpdateEvents) {
|
||||||
|
const coalescedEvents = pointerRawUpdateEvent.getCoalescedEvents();
|
||||||
|
opener.is(
|
||||||
|
coalescedEvents.length,
|
||||||
|
1,
|
||||||
|
`pointerrawupdate(${i}): should have only one coalesced event`
|
||||||
|
);
|
||||||
|
opener.is(
|
||||||
|
`${coalescedEvents[0].type}: ${stringifyPointerEvent(coalescedEvents[0])}`,
|
||||||
|
`${pointerRawUpdateEvent.type}: ${stringifyPointerEvent(pointerRawUpdateEvent)}`,
|
||||||
|
`pointerrawupdate(${i++}): the coalesced event should have same values as the host event`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < Math.min(pointerRawUpdateEvents.length, coalescedPointerMoveEvents.length); i++) {
|
||||||
|
opener.is(
|
||||||
|
stringifyPointerEvent(pointerRawUpdateEvents[i]),
|
||||||
|
stringifyPointerEvent(coalescedPointerMoveEvents[i]),
|
||||||
|
`pointerrawupdate(${i++}): should have same values as coalesced pointermove events`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
opener.SimpleTest.finish();
|
||||||
|
window.close();
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
let positions = [];
|
||||||
|
for (let i = 10; i <= 40; i+=5) {
|
||||||
|
positions.push([{ x: i, y: i }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
await synthesizeNativeTouchSequences(target0, positions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleTest.waitForFocus(() => {
|
||||||
|
SpecialPowers.pushPrefEnv({"set": [
|
||||||
|
["dom.event.pointer.rawupdate.enabled", true],
|
||||||
|
["dom.events.coalesce.touchmove", true],
|
||||||
|
["dom.events.compress.touchmove", false],
|
||||||
|
]}, runTests);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -4894,13 +4894,16 @@ void HTMLMediaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
|
|||||||
// element, allowing media control exclusive consumption on these events,
|
// element, allowing media control exclusive consumption on these events,
|
||||||
// and preventing the content from handling them.
|
// and preventing the content from handling them.
|
||||||
switch (aVisitor.mEvent->mMessage) {
|
switch (aVisitor.mEvent->mMessage) {
|
||||||
case ePointerDown:
|
case eTouchRawUpdate:
|
||||||
case ePointerUp:
|
MOZ_FALLTHROUGH_ASSERT(
|
||||||
case eTouchEnd:
|
"eTouchRawUpdate event shouldn't be dispatched into the DOM");
|
||||||
// Always prevent touchmove captured in video element from being handled by
|
// Always prevent touchmove captured in video element from being handled by
|
||||||
// content, since we always do that for touchstart.
|
// content, since we always do that for touchstart.
|
||||||
case eTouchMove:
|
case eTouchMove:
|
||||||
|
case eTouchEnd:
|
||||||
case eTouchStart:
|
case eTouchStart:
|
||||||
|
case ePointerDown:
|
||||||
|
case ePointerUp:
|
||||||
case ePointerClick:
|
case ePointerClick:
|
||||||
case eMouseDoubleClick:
|
case eMouseDoubleClick:
|
||||||
case eMouseDown:
|
case eMouseDown:
|
||||||
@@ -4908,9 +4911,13 @@ void HTMLMediaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
|
|||||||
aVisitor.mCanHandle = false;
|
aVisitor.mCanHandle = false;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// The *move events however are only comsumed when the range input is being
|
// The *move events however are only consumed when the range input is being
|
||||||
// dragged.
|
// dragged.
|
||||||
|
case eMouseRawUpdate:
|
||||||
|
MOZ_FALLTHROUGH_ASSERT(
|
||||||
|
"eMouseRawUpdate event shouldn't be dispatched into the DOM");
|
||||||
case ePointerMove:
|
case ePointerMove:
|
||||||
|
case ePointerRawUpdate:
|
||||||
case eMouseMove: {
|
case eMouseMove: {
|
||||||
nsINode* node =
|
nsINode* node =
|
||||||
nsINode::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
|
nsINode::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
|
||||||
|
|||||||
@@ -1489,7 +1489,7 @@ void BrowserChild::ProcessPendingCoalescedMouseDataAndDispatchEvents() {
|
|||||||
// mToBeDispatchedMouseData while dispatching an event.
|
// mToBeDispatchedMouseData while dispatching an event.
|
||||||
|
|
||||||
// We may have some pending coalesced data while dispatch an event and reentry
|
// We may have some pending coalesced data while dispatch an event and reentry
|
||||||
// the event loop. In that case we don't have chance to consume the remainding
|
// the event loop. In that case we don't have chance to consume the remaining
|
||||||
// pending data until we get new mouse events. Get some helps from
|
// pending data until we get new mouse events. Get some helps from
|
||||||
// mCoalescedMouseEventFlusher to trigger it.
|
// mCoalescedMouseEventFlusher to trigger it.
|
||||||
mCoalescedMouseEventFlusher->StartObserver();
|
mCoalescedMouseEventFlusher->StartObserver();
|
||||||
@@ -1500,6 +1500,13 @@ void BrowserChild::ProcessPendingCoalescedMouseDataAndDispatchEvents() {
|
|||||||
|
|
||||||
UniquePtr<WidgetMouseEvent> event = data->TakeCoalescedEvent();
|
UniquePtr<WidgetMouseEvent> event = data->TakeCoalescedEvent();
|
||||||
if (event) {
|
if (event) {
|
||||||
|
// When the real mouse event receivers put the received event into the
|
||||||
|
// queue, they should dispatch eMouseRawUpdate event immediately (if and
|
||||||
|
// only if it's required). Therefore, unless the event is the last one
|
||||||
|
// of the queue, the pending events should've been marked as "Do not
|
||||||
|
// convert to "pointerrawupdate".
|
||||||
|
MOZ_ASSERT_IF(mToBeDispatchedMouseData.GetSize() > 0,
|
||||||
|
!event->convertToPointerRawUpdate);
|
||||||
// Dispatch the pending events. Using HandleRealMouseButtonEvent
|
// Dispatch the pending events. Using HandleRealMouseButtonEvent
|
||||||
// to bypass the coalesce handling in RecvRealMouseMoveEvent. Can't use
|
// to bypass the coalesce handling in RecvRealMouseMoveEvent. Can't use
|
||||||
// RecvRealMouseButtonEvent because we may also put some mouse events
|
// RecvRealMouseButtonEvent because we may also put some mouse events
|
||||||
@@ -1556,14 +1563,20 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEvent(
|
|||||||
CoalescedMouseData* data =
|
CoalescedMouseData* data =
|
||||||
mCoalescedMouseData.GetOrInsertNew(aEvent.pointerId);
|
mCoalescedMouseData.GetOrInsertNew(aEvent.pointerId);
|
||||||
MOZ_ASSERT(data);
|
MOZ_ASSERT(data);
|
||||||
if (data->CanCoalesce(aEvent, aGuid, aInputBlockId)) {
|
if (data->CanCoalesce(aEvent, aGuid, aInputBlockId,
|
||||||
data->Coalesce(aEvent, aGuid, aInputBlockId);
|
mCoalescedMouseEventFlusher->GetRefreshDriver())) {
|
||||||
|
// We don't need to dispatch aEvent immediately. However, we need to
|
||||||
|
// dispatch eMouseRawUpdate immediately if there is a `pointerrawupdate`
|
||||||
|
// event listener. Therefore, the cloned event in the queue shouldn't
|
||||||
|
// cause eMouseRawUpdate later when it'll be dispatched.
|
||||||
|
WidgetMouseEvent pendingMouseMoveEvent(aEvent);
|
||||||
|
pendingMouseMoveEvent.convertToPointerRawUpdate = false;
|
||||||
|
data->Coalesce(pendingMouseMoveEvent, aGuid, aInputBlockId);
|
||||||
mCoalescedMouseEventFlusher->StartObserver();
|
mCoalescedMouseEventFlusher->StartObserver();
|
||||||
if (mPointerRawUpdateWindowCount) {
|
HandleMouseRawUpdateEvent(pendingMouseMoveEvent, aGuid, aInputBlockId);
|
||||||
// TODO: Dispatch ePointerRawUpdate here
|
|
||||||
}
|
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't coalesce current mousemove event. Put the coalesced mousemove data
|
// Can't coalesce current mousemove event. Put the coalesced mousemove data
|
||||||
// with the same pointer id to mToBeDispatchedMouseData, coalesce the
|
// with the same pointer id to mToBeDispatchedMouseData, coalesce the
|
||||||
// current one, and process all pending data in mToBeDispatchedMouseData.
|
// current one, and process all pending data in mToBeDispatchedMouseData.
|
||||||
@@ -1578,17 +1591,50 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEvent(
|
|||||||
mCoalescedMouseData
|
mCoalescedMouseData
|
||||||
.InsertOrUpdate(aEvent.pointerId, MakeUnique<CoalescedMouseData>())
|
.InsertOrUpdate(aEvent.pointerId, MakeUnique<CoalescedMouseData>())
|
||||||
.get();
|
.get();
|
||||||
newData->Coalesce(aEvent, aGuid, aInputBlockId);
|
// We don't want to dispatch aEvent immediately. However, we need to
|
||||||
|
// dispatch eMouseRawUpdate immediately if there is a `pointerrawupdate`
|
||||||
|
// event listener. Therefore, the cloned event in the queue shouldn't
|
||||||
|
// cause eMouseRawUpdate later when it'll be dispatched.
|
||||||
|
WidgetMouseEvent pendingMouseMoveEvent(aEvent);
|
||||||
|
pendingMouseMoveEvent.convertToPointerRawUpdate = false;
|
||||||
|
newData->Coalesce(pendingMouseMoveEvent, aGuid, aInputBlockId);
|
||||||
|
|
||||||
// Dispatch all pending mouse events.
|
// Dispatch all pending mouse events which does NOT include aEvent.
|
||||||
ProcessPendingCoalescedMouseDataAndDispatchEvents();
|
ProcessPendingCoalescedMouseDataAndDispatchEvents();
|
||||||
|
|
||||||
mCoalescedMouseEventFlusher->StartObserver();
|
mCoalescedMouseEventFlusher->StartObserver();
|
||||||
} else if (!RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId)) {
|
// Finally, dispatch eMouseRawUpdate for aEvent right now.
|
||||||
|
HandleMouseRawUpdateEvent(pendingMouseMoveEvent, aGuid, aInputBlockId);
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId)) {
|
||||||
return IPC_FAIL_NO_REASON(this);
|
return IPC_FAIL_NO_REASON(this);
|
||||||
}
|
}
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BrowserChild::HandleMouseRawUpdateEvent(
|
||||||
|
const WidgetMouseEvent& aPendingMouseEvent,
|
||||||
|
const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId) {
|
||||||
|
// If there is no window containing pointerrawupdate event listeners or the
|
||||||
|
// event is a synthesized mousemove, we don't need to dispatch eMouseRawUpdate
|
||||||
|
// event.
|
||||||
|
if (!mPointerRawUpdateWindowCount || aPendingMouseEvent.IsSynthesized()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WidgetMouseEvent mouseRawUpdateEvent(aPendingMouseEvent);
|
||||||
|
mouseRawUpdateEvent.mMessage = eMouseRawUpdate;
|
||||||
|
mouseRawUpdateEvent.mCoalescedWidgetEvents = nullptr;
|
||||||
|
mouseRawUpdateEvent.convertToPointer = true;
|
||||||
|
// Nobody checks `convertToPointerRawUpdate` of eMouseRawUpdate event.
|
||||||
|
// However, the name indicates that it would cause ePointerRawUpdate.
|
||||||
|
// For avoiding to make the developers who watch the value with the debugger
|
||||||
|
// confused, here sets it to `true`.
|
||||||
|
mouseRawUpdateEvent.convertToPointerRawUpdate = true;
|
||||||
|
HandleRealMouseButtonEvent(mouseRawUpdateEvent, aGuid, aInputBlockId);
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEventForTests(
|
mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEventForTests(
|
||||||
const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
|
const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
|
||||||
const uint64_t& aInputBlockId) {
|
const uint64_t& aInputBlockId) {
|
||||||
@@ -1640,9 +1686,16 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealMouseButtonEvent(
|
|||||||
UniquePtr<CoalescedMouseData> dispatchData =
|
UniquePtr<CoalescedMouseData> dispatchData =
|
||||||
MakeUnique<CoalescedMouseData>();
|
MakeUnique<CoalescedMouseData>();
|
||||||
|
|
||||||
|
// We'll dispatch aEvent immediately via
|
||||||
|
// ProcessPendingCoalescedMouseDataAndDispatchEvents().
|
||||||
|
// Therefore, PresShell should convert it to eMouseRawUpdate when it starts
|
||||||
|
// handling aEvent if and only if there is a `pointerrawupdate` event
|
||||||
|
// listener. Therefore, let's assert the allowing flag to convert it to
|
||||||
|
// eMouseRawUpdate here.
|
||||||
|
MOZ_ASSERT(aEvent.convertToPointerRawUpdate);
|
||||||
dispatchData->Coalesce(aEvent, aGuid, aInputBlockId);
|
dispatchData->Coalesce(aEvent, aGuid, aInputBlockId);
|
||||||
mToBeDispatchedMouseData.Push(dispatchData.release());
|
|
||||||
|
|
||||||
|
mToBeDispatchedMouseData.Push(dispatchData.release());
|
||||||
ProcessPendingCoalescedMouseDataAndDispatchEvents();
|
ProcessPendingCoalescedMouseDataAndDispatchEvents();
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
@@ -1833,7 +1886,7 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealTouchEvent(
|
|||||||
ProcessPendingCoalescedTouchData();
|
ProcessPendingCoalescedTouchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aEvent.mMessage != eTouchMove) {
|
if (aEvent.mMessage != eTouchMove && aEvent.mMessage != eTouchRawUpdate) {
|
||||||
sConsecutiveTouchMoveCount = 0;
|
sConsecutiveTouchMoveCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1896,36 +1949,88 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealTouchMoveEvent(
|
|||||||
++sConsecutiveTouchMoveCount;
|
++sConsecutiveTouchMoveCount;
|
||||||
if (mCoalescedTouchMoveEventFlusher) {
|
if (mCoalescedTouchMoveEventFlusher) {
|
||||||
MOZ_ASSERT(aEvent.mMessage == eTouchMove);
|
MOZ_ASSERT(aEvent.mMessage == eTouchMove);
|
||||||
|
// NOTE: While dispatching eTouchMove or eTouchRawUpdate,
|
||||||
|
// sConsecutiveTouchMoveCount may be changed by the event loop spun,
|
||||||
|
// e.g., an event listener uses sync XHR or calling window.alert().
|
||||||
|
const auto PostponeDispatchingTouchMove = [&]() {
|
||||||
|
return sConsecutiveTouchMoveCount > 1;
|
||||||
|
};
|
||||||
if (mCoalescedTouchData.IsEmpty() ||
|
if (mCoalescedTouchData.IsEmpty() ||
|
||||||
mCoalescedTouchData.CanCoalesce(aEvent, aGuid, aInputBlockId,
|
mCoalescedTouchData.CanCoalesce(aEvent, aGuid, aInputBlockId,
|
||||||
aApzResponse)) {
|
aApzResponse)) {
|
||||||
|
if (PostponeDispatchingTouchMove()) {
|
||||||
|
WidgetTouchEvent pendingTouchMoveEvent(
|
||||||
|
aEvent, WidgetTouchEvent::CloneTouches::Yes);
|
||||||
|
// We don't dispatch aEvent immediately here. However, we need to
|
||||||
|
// dispatch eTouchRawUpdate immediately if and only if there is a
|
||||||
|
// `pointerrawupdate` event listener. Therefore, the cloned event in
|
||||||
|
// the queue and it shouldn't cause eTouchRawUpdate again.
|
||||||
|
pendingTouchMoveEvent.SetConvertToPointerRawUpdate(false);
|
||||||
|
mCoalescedTouchData.Coalesce(pendingTouchMoveEvent, aGuid,
|
||||||
|
aInputBlockId, aApzResponse);
|
||||||
|
MOZ_ASSERT(PostponeDispatchingTouchMove());
|
||||||
|
mCoalescedTouchMoveEventFlusher->StartObserver();
|
||||||
|
// Let's notify the web app of `pointerrawupdate` immediately if and
|
||||||
|
// only if they listen to it.
|
||||||
|
HandleTouchRawUpdateEvent(pendingTouchMoveEvent, aGuid, aInputBlockId,
|
||||||
|
aApzResponse);
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll dispatch aEvent via ProcessPendingCoalescedTouchData() below.
|
||||||
|
// Therefore, the touches should cause eTouchRawUpdate event.
|
||||||
|
MOZ_ASSERT(aEvent.CanConvertToPointerRawUpdate());
|
||||||
mCoalescedTouchData.Coalesce(aEvent, aGuid, aInputBlockId,
|
mCoalescedTouchData.Coalesce(aEvent, aGuid, aInputBlockId,
|
||||||
aApzResponse);
|
aApzResponse);
|
||||||
|
MOZ_ASSERT(!PostponeDispatchingTouchMove());
|
||||||
} else {
|
} else {
|
||||||
UniquePtr<WidgetTouchEvent> touchMoveEvent =
|
UniquePtr<WidgetTouchEvent> touchMoveEvent =
|
||||||
mCoalescedTouchData.TakeCoalescedEvent();
|
mCoalescedTouchData.TakeCoalescedEvent();
|
||||||
|
MOZ_ASSERT(touchMoveEvent->mMessage == eTouchMove);
|
||||||
|
|
||||||
|
// Before dispatching touchMoveEvent, we need to put aEvent into the
|
||||||
|
// queue for keeping the event order even if an event listener spins the
|
||||||
|
// event loop and we'll receive another touch event. So, aEvent may be
|
||||||
|
// dispatched while we're dispatching touchMoveEvent. Therefore, we need
|
||||||
|
// to make it convertible to eTouchRawUpdate.
|
||||||
|
MOZ_ASSERT(aEvent.CanConvertToPointerRawUpdate());
|
||||||
mCoalescedTouchData.Coalesce(aEvent, aGuid, aInputBlockId,
|
mCoalescedTouchData.Coalesce(aEvent, aGuid, aInputBlockId,
|
||||||
aApzResponse);
|
aApzResponse);
|
||||||
|
MOZ_ASSERT(!PostponeDispatchingTouchMove());
|
||||||
|
|
||||||
|
// touchMoveEvent was stored by mCoalescedTouchData before receiving
|
||||||
|
// aEvent. Therefore, the receiver should've already dispatched
|
||||||
|
// eTouchRawUpdate for dispatching `pointerrawupdate` and let web apps
|
||||||
|
// know the update immediately (with sacrificing the performance).
|
||||||
|
// Therefore, we don't need to dispatch eTouchRawUpdate here before
|
||||||
|
// dispatching the touchMoveEvent.
|
||||||
|
MOZ_ASSERT(!touchMoveEvent->CanConvertToPointerRawUpdate());
|
||||||
|
const uint32_t generation = mCoalescedTouchData.Generation();
|
||||||
if (!RecvRealTouchEvent(*touchMoveEvent,
|
if (!RecvRealTouchEvent(*touchMoveEvent,
|
||||||
mCoalescedTouchData.GetScrollableLayerGuid(),
|
mCoalescedTouchData.GetScrollableLayerGuid(),
|
||||||
mCoalescedTouchData.GetInputBlockId(),
|
mCoalescedTouchData.GetInputBlockId(),
|
||||||
mCoalescedTouchData.GetApzResponse())) {
|
mCoalescedTouchData.GetApzResponse())) {
|
||||||
return IPC_FAIL_NO_REASON(this);
|
return IPC_FAIL_NO_REASON(this);
|
||||||
}
|
}
|
||||||
}
|
// RecvRealTouchEvent() may have caused spinning the event loop and
|
||||||
|
// changed sConsecutiveTouchMoveCount. So, we need to check it now.
|
||||||
if (sConsecutiveTouchMoveCount > 1) {
|
if (PostponeDispatchingTouchMove()) {
|
||||||
mCoalescedTouchMoveEventFlusher->StartObserver();
|
mCoalescedTouchMoveEventFlusher->StartObserver();
|
||||||
if (mPointerRawUpdateWindowCount) {
|
if (generation == mCoalescedTouchData.Generation()) {
|
||||||
// TODO: Dispatch ePointerRawUpdate here
|
// Let's notify the web app of `pointerrawupdate` immediately if and
|
||||||
|
// only if they listen to it. Additionally, we don't want to notify
|
||||||
|
// eTouchRawUpdate when ProcessPendingCoalescedTouchData() is called
|
||||||
|
// later.
|
||||||
|
mCoalescedTouchData.NotifyTouchRawUpdateOfHandled(aEvent);
|
||||||
|
HandleTouchRawUpdateEvent(aEvent, aGuid, aInputBlockId,
|
||||||
|
aApzResponse);
|
||||||
|
}
|
||||||
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Flush the pending coalesced touch in order to avoid the first
|
|
||||||
// touchmove be overridden by the second one.
|
|
||||||
ProcessPendingCoalescedTouchData();
|
|
||||||
}
|
}
|
||||||
|
// Flush the pending coalesced touch in order to avoid the first
|
||||||
|
// touchmove be overridden by the second one, this contains aEvent.
|
||||||
|
ProcessPendingCoalescedTouchData();
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1936,6 +2041,30 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealTouchMoveEvent(
|
|||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BrowserChild::HandleTouchRawUpdateEvent(
|
||||||
|
const WidgetTouchEvent& aPendingTouchEvent,
|
||||||
|
const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId,
|
||||||
|
const nsEventStatus& aApzResponse) {
|
||||||
|
if (!mPointerRawUpdateWindowCount) {
|
||||||
|
return; // There is no window containing pointerrawupdate event listeners
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetTouchEvent touchRawUpdateEvent(aPendingTouchEvent,
|
||||||
|
WidgetTouchEvent::CloneTouches::Yes);
|
||||||
|
touchRawUpdateEvent.mMessage = eTouchRawUpdate;
|
||||||
|
for (Touch* const touch : touchRawUpdateEvent.mTouches) {
|
||||||
|
touch->mMessage = eTouchRawUpdate;
|
||||||
|
touch->mCoalescedWidgetEvents = nullptr;
|
||||||
|
touch->convertToPointer = true;
|
||||||
|
// Although nobody checks `convertToPointerRawUpdate` of eTouchRawUpdate.
|
||||||
|
// However, the name indicates it would cause ePointerRawUpdate or not, so,
|
||||||
|
// for avoiding to make developers confused when they watch the value with
|
||||||
|
// the debugger, we should set this to `true`.
|
||||||
|
touch->convertToPointerRawUpdate = true;
|
||||||
|
}
|
||||||
|
RecvRealTouchEvent(touchRawUpdateEvent, aGuid, aInputBlockId, aApzResponse);
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealTouchMoveEvent(
|
mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealTouchMoveEvent(
|
||||||
const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
|
const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
|
||||||
const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
|
const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
|
||||||
|
|||||||
@@ -641,14 +641,36 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
|
|||||||
// may reentry the event loop and access to the same hashtable. It's
|
// may reentry the event loop and access to the same hashtable. It's
|
||||||
// called when dispatching some mouse events other than mousemove.
|
// called when dispatching some mouse events other than mousemove.
|
||||||
void FlushAllCoalescedMouseData();
|
void FlushAllCoalescedMouseData();
|
||||||
|
|
||||||
void ProcessPendingCoalescedMouseDataAndDispatchEvents();
|
void ProcessPendingCoalescedMouseDataAndDispatchEvents();
|
||||||
|
|
||||||
void ProcessPendingCoalescedTouchData();
|
void ProcessPendingCoalescedTouchData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch an eMouseRawUpdate event for dispatching ePointerRawUpdate event
|
||||||
|
* into the DOM immediately when aPendingEvent will be dispatched later.
|
||||||
|
* This does nothing if there is no window which has at least one
|
||||||
|
* `pointerrawupdate` event listener.
|
||||||
|
*/
|
||||||
|
void HandleMouseRawUpdateEvent(const WidgetMouseEvent& aPendingMouseEvent,
|
||||||
|
const ScrollableLayerGuid& aGuid,
|
||||||
|
const uint64_t& aInputBlockId);
|
||||||
|
|
||||||
void HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
|
void HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
|
||||||
const ScrollableLayerGuid& aGuid,
|
const ScrollableLayerGuid& aGuid,
|
||||||
const uint64_t& aInputBlockId);
|
const uint64_t& aInputBlockId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch an eTouchRawUpdate event for dispatching ePointerRawUpdate event
|
||||||
|
* into the DOM immediately when aPendingEvent will be dispatched later.
|
||||||
|
* This does nothing if there is no window which has at least one
|
||||||
|
* `pointerrawupdate` event listener.
|
||||||
|
*/
|
||||||
|
void HandleTouchRawUpdateEvent(const WidgetTouchEvent& aPendingTouchEvent,
|
||||||
|
const ScrollableLayerGuid& aGuid,
|
||||||
|
const uint64_t& aInputBlockId,
|
||||||
|
const nsEventStatus& aApzResponse);
|
||||||
|
|
||||||
void SetCancelContentJSEpoch(int32_t aEpoch) {
|
void SetCancelContentJSEpoch(int32_t aEpoch) {
|
||||||
mCancelContentJSEpoch = aEpoch;
|
mCancelContentJSEpoch = aEpoch;
|
||||||
}
|
}
|
||||||
@@ -716,7 +738,7 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
|
|||||||
void OnPointerRawUpdateEventListenerAdded(const nsPIDOMWindowInner* aWindow);
|
void OnPointerRawUpdateEventListenerAdded(const nsPIDOMWindowInner* aWindow);
|
||||||
void OnPointerRawUpdateEventListenerRemoved(
|
void OnPointerRawUpdateEventListenerRemoved(
|
||||||
const nsPIDOMWindowInner* aWindow);
|
const nsPIDOMWindowInner* aWindow);
|
||||||
[[nodiscard]] bool HasWindowHavingPointerRawUpdateEventListeners() const {
|
[[nodiscard]] bool HasPointerRawUpdateEventListeners() const {
|
||||||
return !!mPointerRawUpdateWindowCount;
|
return !!mPointerRawUpdateWindowCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,21 @@ class CoalescedInputData {
|
|||||||
|
|
||||||
UniquePtr<InputEventType> mCoalescedInputEvent;
|
UniquePtr<InputEventType> mCoalescedInputEvent;
|
||||||
ScrollableLayerGuid mGuid;
|
ScrollableLayerGuid mGuid;
|
||||||
uint64_t mInputBlockId;
|
uint64_t mInputBlockId = 0;
|
||||||
|
uint32_t mGeneration = 0;
|
||||||
|
|
||||||
|
void AdvanceGeneration() {
|
||||||
|
if (!IsEmpty()) {
|
||||||
|
mGeneration++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CoalescedInputData() : mInputBlockId(0) {}
|
CoalescedInputData() = default;
|
||||||
|
|
||||||
void RetrieveDataFrom(CoalescedInputData& aSource) {
|
void RetrieveDataFrom(CoalescedInputData& aSource) {
|
||||||
|
aSource.AdvanceGeneration();
|
||||||
|
AdvanceGeneration();
|
||||||
mCoalescedInputEvent = std::move(aSource.mCoalescedInputEvent);
|
mCoalescedInputEvent = std::move(aSource.mCoalescedInputEvent);
|
||||||
mGuid = aSource.mGuid;
|
mGuid = aSource.mGuid;
|
||||||
mInputBlockId = aSource.mInputBlockId;
|
mInputBlockId = aSource.mInputBlockId;
|
||||||
@@ -42,12 +51,23 @@ class CoalescedInputData {
|
|||||||
const uint64_t& aInputBlockId);
|
const uint64_t& aInputBlockId);
|
||||||
|
|
||||||
UniquePtr<InputEventType> TakeCoalescedEvent() {
|
UniquePtr<InputEventType> TakeCoalescedEvent() {
|
||||||
|
AdvanceGeneration();
|
||||||
return std::move(mCoalescedInputEvent);
|
return std::move(mCoalescedInputEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollableLayerGuid GetScrollableLayerGuid() { return mGuid; }
|
ScrollableLayerGuid GetScrollableLayerGuid() { return mGuid; }
|
||||||
|
|
||||||
uint64_t GetInputBlockId() { return mInputBlockId; }
|
uint64_t GetInputBlockId() { return mInputBlockId; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The generation number of the latest state stored by the instance.
|
||||||
|
* It'll be incremented when the coalesced event data is retrieved or taken.
|
||||||
|
* So, this is useful to avoid handling same coalesced events twice when
|
||||||
|
* a nested event loop may handle this.
|
||||||
|
* NOTE: You should compare the value only with `==` or `!=`. Do not use
|
||||||
|
* `<` nor `>` because the value may circulate to 0 from UINT32_MAX.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] uint32_t Generation() const { return mGeneration; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class CoalescedInputFlusher : public nsARefreshObserver {
|
class CoalescedInputFlusher : public nsARefreshObserver {
|
||||||
@@ -61,12 +81,17 @@ class CoalescedInputFlusher : public nsARefreshObserver {
|
|||||||
void StartObserver();
|
void StartObserver();
|
||||||
void RemoveObserver();
|
void RemoveObserver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a refresh driver which is proper one for BrowserChild.
|
||||||
|
* Note that this is not a getter of mRefreshDriver.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] nsRefreshDriver* GetRefreshDriver();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~CoalescedInputFlusher();
|
virtual ~CoalescedInputFlusher();
|
||||||
|
|
||||||
nsRefreshDriver* GetRefreshDriver();
|
|
||||||
|
|
||||||
BrowserChild* mBrowserChild;
|
BrowserChild* mBrowserChild;
|
||||||
|
// A refresh driver which this instance waits for the next refresh of.
|
||||||
RefPtr<nsRefreshDriver> mRefreshDriver;
|
RefPtr<nsRefreshDriver> mRefreshDriver;
|
||||||
};
|
};
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|||||||
@@ -58,17 +58,29 @@ void CoalescedMouseData::Coalesce(const WidgetMouseEvent& aEvent,
|
|||||||
|
|
||||||
bool CoalescedMouseData::CanCoalesce(const WidgetMouseEvent& aEvent,
|
bool CoalescedMouseData::CanCoalesce(const WidgetMouseEvent& aEvent,
|
||||||
const ScrollableLayerGuid& aGuid,
|
const ScrollableLayerGuid& aGuid,
|
||||||
const uint64_t& aInputBlockId) {
|
const uint64_t& aInputBlockId,
|
||||||
|
const nsRefreshDriver* aRefreshDriver) {
|
||||||
MOZ_ASSERT(aEvent.mMessage == eMouseMove);
|
MOZ_ASSERT(aEvent.mMessage == eMouseMove);
|
||||||
return !mCoalescedInputEvent ||
|
if (!mCoalescedInputEvent) {
|
||||||
(!mCoalescedInputEvent->mFlags.mIsSynthesizedForTests &&
|
return true;
|
||||||
!aEvent.mFlags.mIsSynthesizedForTests &&
|
}
|
||||||
mCoalescedInputEvent->mModifiers == aEvent.mModifiers &&
|
if (mCoalescedInputEvent->mFlags.mIsSynthesizedForTests !=
|
||||||
mCoalescedInputEvent->mInputSource == aEvent.mInputSource &&
|
aEvent.mFlags.mIsSynthesizedForTests ||
|
||||||
mCoalescedInputEvent->pointerId == aEvent.pointerId &&
|
mCoalescedInputEvent->mModifiers != aEvent.mModifiers ||
|
||||||
mCoalescedInputEvent->mButton == aEvent.mButton &&
|
mCoalescedInputEvent->mInputSource != aEvent.mInputSource ||
|
||||||
mCoalescedInputEvent->mButtons == aEvent.mButtons && mGuid == aGuid &&
|
mCoalescedInputEvent->pointerId != aEvent.pointerId ||
|
||||||
mInputBlockId == aInputBlockId);
|
mCoalescedInputEvent->mButton != aEvent.mButton ||
|
||||||
|
mCoalescedInputEvent->mButtons != aEvent.mButtons || mGuid != aGuid ||
|
||||||
|
mInputBlockId != aInputBlockId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Basically, tests do not want to coalesces the consecutive mouse events.
|
||||||
|
// However, if the test calls nsIDOMWindowUtils::AdvanceTimeAndRefresh(0),
|
||||||
|
// they must try to check coalesced mouse move events.
|
||||||
|
if (!aEvent.mFlags.mIsSynthesizedForTests) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return aRefreshDriver && aRefreshDriver->IsTestControllingRefreshesEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
CoalescedMouseMoveFlusher::CoalescedMouseMoveFlusher(
|
CoalescedMouseMoveFlusher::CoalescedMouseMoveFlusher(
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class CoalescedMouseData final : public CoalescedInputData<WidgetMouseEvent> {
|
|||||||
|
|
||||||
bool CanCoalesce(const WidgetMouseEvent& aEvent,
|
bool CanCoalesce(const WidgetMouseEvent& aEvent,
|
||||||
const ScrollableLayerGuid& aGuid,
|
const ScrollableLayerGuid& aGuid,
|
||||||
const uint64_t& aInputBlockId);
|
const uint64_t& aInputBlockId,
|
||||||
|
const nsRefreshDriver* aRefreshDriver);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CoalescedMouseMoveFlusher final : public CoalescedInputFlusher {
|
class CoalescedMouseMoveFlusher final : public CoalescedInputFlusher {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ void CoalescedTouchData::CreateCoalescedTouchEvent(
|
|||||||
PointerEventHandler::InitPointerEventFromTouch(*event, aEvent, *touch);
|
PointerEventHandler::InitPointerEventFromTouch(*event, aEvent, *touch);
|
||||||
event->mFlags.mBubbles = false;
|
event->mFlags.mBubbles = false;
|
||||||
event->mFlags.mCancelable = false;
|
event->mFlags.mCancelable = false;
|
||||||
|
event->convertToPointerRawUpdate = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,10 +48,9 @@ void CoalescedTouchData::Coalesce(const WidgetTouchEvent& aEvent,
|
|||||||
MOZ_ASSERT(mCoalescedInputEvent->mModifiers == aEvent.mModifiers);
|
MOZ_ASSERT(mCoalescedInputEvent->mModifiers == aEvent.mModifiers);
|
||||||
MOZ_ASSERT(mCoalescedInputEvent->mInputSource == aEvent.mInputSource);
|
MOZ_ASSERT(mCoalescedInputEvent->mInputSource == aEvent.mInputSource);
|
||||||
|
|
||||||
for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
|
for (const RefPtr<Touch>& touch : aEvent.mTouches) {
|
||||||
const RefPtr<Touch>& touch = aEvent.mTouches[i];
|
|
||||||
// Get the same touch in the original event
|
// Get the same touch in the original event
|
||||||
RefPtr<Touch> sameTouch = GetTouch(touch->Identifier());
|
const RefPtr<Touch> sameTouch = GetTouch(touch->Identifier());
|
||||||
// The checks in CoalescedTouchData::CanCoalesce ensure it should never
|
// The checks in CoalescedTouchData::CanCoalesce ensure it should never
|
||||||
// be null.
|
// be null.
|
||||||
MOZ_ASSERT(sameTouch);
|
MOZ_ASSERT(sameTouch);
|
||||||
@@ -58,6 +58,7 @@ void CoalescedTouchData::Coalesce(const WidgetTouchEvent& aEvent,
|
|||||||
MOZ_ASSERT(!sameTouch->mCoalescedWidgetEvents->mEvents.IsEmpty());
|
MOZ_ASSERT(!sameTouch->mCoalescedWidgetEvents->mEvents.IsEmpty());
|
||||||
if (!sameTouch->Equals(touch)) {
|
if (!sameTouch->Equals(touch)) {
|
||||||
sameTouch->SetSameAs(touch);
|
sameTouch->SetSameAs(touch);
|
||||||
|
sameTouch->convertToPointerRawUpdate = touch->convertToPointerRawUpdate;
|
||||||
WidgetPointerEvent* event =
|
WidgetPointerEvent* event =
|
||||||
sameTouch->mCoalescedWidgetEvents->mEvents.AppendElement(
|
sameTouch->mCoalescedWidgetEvents->mEvents.AppendElement(
|
||||||
WidgetPointerEvent(aEvent.IsTrusted(), ePointerMove,
|
WidgetPointerEvent(aEvent.IsTrusted(), ePointerMove,
|
||||||
@@ -72,6 +73,18 @@ void CoalescedTouchData::Coalesce(const WidgetTouchEvent& aEvent,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CoalescedTouchData::NotifyTouchRawUpdateOfHandled(
|
||||||
|
const WidgetTouchEvent& aEvent) {
|
||||||
|
if (IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const RefPtr<Touch>& touch : aEvent.mTouches) {
|
||||||
|
if (const RefPtr<Touch> sameTouch = GetTouch(touch->Identifier())) {
|
||||||
|
sameTouch->convertToPointerRawUpdate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CoalescedTouchData::CanCoalesce(const WidgetTouchEvent& aEvent,
|
bool CoalescedTouchData::CanCoalesce(const WidgetTouchEvent& aEvent,
|
||||||
const ScrollableLayerGuid& aGuid,
|
const ScrollableLayerGuid& aGuid,
|
||||||
const uint64_t& aInputBlockId,
|
const uint64_t& aInputBlockId,
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ class CoalescedTouchData final : public CoalescedInputData<WidgetTouchEvent> {
|
|||||||
|
|
||||||
nsEventStatus GetApzResponse() { return mApzResponse; }
|
nsEventStatus GetApzResponse() { return mApzResponse; }
|
||||||
|
|
||||||
|
void NotifyTouchRawUpdateOfHandled(const WidgetTouchEvent& aEvent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Touch* GetTouch(int32_t aIdentifier);
|
Touch* GetTouch(int32_t aIdentifier);
|
||||||
|
|
||||||
|
|||||||
@@ -336,6 +336,9 @@ void APZEventState::ProcessTouchEvent(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case eTouchRawUpdate:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
MOZ_ASSERT_UNREACHABLE("Unknown touch event type");
|
MOZ_ASSERT_UNREACHABLE("Unknown touch event type");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -7069,6 +7069,8 @@ void PresShell::RecordPointerLocation(WidgetGUIEvent* aEvent) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ePointerMove:
|
case ePointerMove:
|
||||||
|
case ePointerRawUpdate:
|
||||||
|
case eMouseRawUpdate:
|
||||||
if (!aEvent->AsMouseEvent()->IsReal()) {
|
if (!aEvent->AsMouseEvent()->IsReal()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -7153,6 +7155,7 @@ PresShell* PresShell::GetShellForEventTarget(nsIFrame* aFrame,
|
|||||||
PresShell* PresShell::GetShellForTouchEvent(WidgetGUIEvent* aEvent) {
|
PresShell* PresShell::GetShellForTouchEvent(WidgetGUIEvent* aEvent) {
|
||||||
switch (aEvent->mMessage) {
|
switch (aEvent->mMessage) {
|
||||||
case eTouchMove:
|
case eTouchMove:
|
||||||
|
case eTouchRawUpdate:
|
||||||
case eTouchCancel:
|
case eTouchCancel:
|
||||||
case eTouchEnd: {
|
case eTouchEnd: {
|
||||||
// get the correct shell to dispatch to
|
// get the correct shell to dispatch to
|
||||||
@@ -7304,6 +7307,7 @@ nsresult PresShell::HandleEvent(nsIFrame* aFrameForPresShell,
|
|||||||
if (mPresContext) {
|
if (mPresContext) {
|
||||||
switch (aGUIEvent->mMessage) {
|
switch (aGUIEvent->mMessage) {
|
||||||
case eMouseMove:
|
case eMouseMove:
|
||||||
|
case eMouseRawUpdate:
|
||||||
if (!aGUIEvent->AsMouseEvent()->IsReal()) {
|
if (!aGUIEvent->AsMouseEvent()->IsReal()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -7348,11 +7352,124 @@ nsresult PresShell::HandleEvent(nsIFrame* aFrameForPresShell,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the event may cause ePointerMove, we need to dispatch ePointerRawUpdate
|
||||||
|
// before that if and only if there are some `pointerrawupdate` event
|
||||||
|
// listeners. Note that if a `pointerrawupdate` event listener destroys its
|
||||||
|
// document/window, we need to dispatch the following pointer event (e.g.,
|
||||||
|
// ePointerMove) in the parent document/window with the parent PresShell.
|
||||||
|
// Therefore, we need to consider the target PresShell for each event
|
||||||
|
// (ePointerRawUpdate and the following pointer event) in
|
||||||
|
// EventHandler::HandleEvent(). Thus, we need to dispatch the internal event
|
||||||
|
// for ePointerRawUpdate before calling EventHandler::HandleEvent() below.
|
||||||
|
if (!aDontRetargetEvents &&
|
||||||
|
StaticPrefs::dom_event_pointer_rawupdate_enabled()) {
|
||||||
|
nsresult rv = EnsurePrecedingPointerRawUpdate(
|
||||||
|
weakFrameForPresShell, *aGUIEvent, aDontRetargetEvents);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
if (!CanHandleUserInputEvents(aGUIEvent)) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EventHandler eventHandler(*this);
|
EventHandler eventHandler(*this);
|
||||||
return eventHandler.HandleEvent(weakFrameForPresShell, aGUIEvent,
|
return eventHandler.HandleEvent(weakFrameForPresShell, aGUIEvent,
|
||||||
aDontRetargetEvents, aEventStatus);
|
aDontRetargetEvents, aEventStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult PresShell::EnsurePrecedingPointerRawUpdate(
|
||||||
|
AutoWeakFrame& aWeakFrameForPresShell, const WidgetGUIEvent& aSourceEvent,
|
||||||
|
bool aDontRetargetEvents) {
|
||||||
|
MOZ_ASSERT(StaticPrefs::dom_event_pointer_rawupdate_enabled());
|
||||||
|
if (PointerEventHandler::ToPointerEventMessage(&aSourceEvent) !=
|
||||||
|
ePointerMove) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should not dispatch ePointerRawUpdate directly because dispatching
|
||||||
|
// it requires some steps which are defined by "fire a pointer event" section
|
||||||
|
// in the spec. https://w3c.github.io/pointerevents/#dfn-fire-a-pointer-event
|
||||||
|
// We handle the steps when we call DispatchPrecedingPointerEvent().
|
||||||
|
// Therefore, this method dispatches eMouseRawUpdate or eTouchRawUpdate event
|
||||||
|
// if the event should follow a ePointerRawUpdate. Then,
|
||||||
|
// HandleEventUsingCoordinates() will stop handling the internal events after
|
||||||
|
// calling DispatchPrecedingPointerEvent().
|
||||||
|
|
||||||
|
MOZ_ASSERT(aSourceEvent.mMessage != eMouseRawUpdate);
|
||||||
|
MOZ_ASSERT(aSourceEvent.mMessage != eTouchRawUpdate);
|
||||||
|
|
||||||
|
// If no window in the browser child has `pointerrawupdate` event listener,
|
||||||
|
// we should do nothing.
|
||||||
|
if (auto* const browserChild = BrowserChild::GetFrom(this)) {
|
||||||
|
if (!browserChild->HasPointerRawUpdateEventListeners()) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||||
|
static bool sDispatchingRawUpdateEventFromHere = false;
|
||||||
|
MOZ_DIAGNOSTIC_ASSERT(
|
||||||
|
!sDispatchingRawUpdateEventFromHere,
|
||||||
|
"Dispatching ePointerRawUpdate should not be done recursively");
|
||||||
|
AutoRestore<bool> restoreDispathingFlag(sDispatchingRawUpdateEventFromHere);
|
||||||
|
sDispatchingRawUpdateEventFromHere = true;
|
||||||
|
#endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||||||
|
|
||||||
|
if (const WidgetMouseEvent* const mouseEvent = aSourceEvent.AsMouseEvent()) {
|
||||||
|
// If `convertToPointer` is `false`, it means that we've already handled the
|
||||||
|
// event to dispatch a preceding pointer event. Therefore, its preceding
|
||||||
|
// event should've already been handled.
|
||||||
|
// If `convertToPointerRawUpdate` is `false`, it means that the event was in
|
||||||
|
// the queue in BrowserChild and BrowserChild has already dispatched
|
||||||
|
// `eMouseRawUpdate`. Therefore, we don't need to dispatch it again here.
|
||||||
|
if (mouseEvent->IsSynthesized() || !mouseEvent->convertToPointer ||
|
||||||
|
!mouseEvent->convertToPointerRawUpdate) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
WidgetMouseEvent mouseRawUpdateEvent(*mouseEvent);
|
||||||
|
mouseRawUpdateEvent.mMessage = eMouseRawUpdate;
|
||||||
|
mouseRawUpdateEvent.mCoalescedWidgetEvents = nullptr;
|
||||||
|
nsEventStatus rawUpdateStatus = nsEventStatus_eIgnore;
|
||||||
|
EventHandler eventHandler(*this);
|
||||||
|
return eventHandler.HandleEvent(aWeakFrameForPresShell,
|
||||||
|
&mouseRawUpdateEvent, aDontRetargetEvents,
|
||||||
|
&rawUpdateStatus);
|
||||||
|
}
|
||||||
|
if (const WidgetTouchEvent* const touchEvent = aSourceEvent.AsTouchEvent()) {
|
||||||
|
WidgetTouchEvent touchRawUpdate(*touchEvent,
|
||||||
|
WidgetTouchEvent::CloneTouches::No);
|
||||||
|
touchRawUpdate.mMessage = eTouchRawUpdate;
|
||||||
|
touchRawUpdate.mTouches.Clear();
|
||||||
|
for (const RefPtr<Touch>& touch : touchEvent->mTouches) {
|
||||||
|
// If `convertToPointer` is `false`, it means that we've already handled
|
||||||
|
// the event to dispatch a preceding pointer event. Therefore, its
|
||||||
|
// preceding event should've already been handled.
|
||||||
|
// If ShouldConvertTouchToPointer() returns `false`, the touch is not an
|
||||||
|
// active pointer or the touch hasn't been changed from the previous
|
||||||
|
// state. Therefore, we don't need to dispatch ePointerRawUpdate for the
|
||||||
|
// touch.
|
||||||
|
if (!touch->convertToPointerRawUpdate ||
|
||||||
|
!TouchManager::ShouldConvertTouchToPointer(touch, &touchRawUpdate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
RefPtr<Touch> newTouch = new Touch(*touch);
|
||||||
|
newTouch->mMessage = eTouchRawUpdate;
|
||||||
|
newTouch->mCoalescedWidgetEvents = nullptr;
|
||||||
|
touchRawUpdate.mTouches.AppendElement(std::move(newTouch));
|
||||||
|
}
|
||||||
|
nsEventStatus rawUpdateStatus = nsEventStatus_eIgnore;
|
||||||
|
if (touchRawUpdate.mTouches.IsEmpty()) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
EventHandler eventHandler(*this);
|
||||||
|
return eventHandler.HandleEvent(aWeakFrameForPresShell, &touchRawUpdate,
|
||||||
|
aDontRetargetEvents, &rawUpdateStatus);
|
||||||
|
}
|
||||||
|
MOZ_ASSERT_UNREACHABLE("Handle the event to dispatch ePointerRawUpdate");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
bool PresShell::EventHandler::UpdateFocusSequenceNumber(
|
bool PresShell::EventHandler::UpdateFocusSequenceNumber(
|
||||||
nsIFrame* aFrameForPresShell, uint64_t aEventFocusSequenceNumber) {
|
nsIFrame* aFrameForPresShell, uint64_t aEventFocusSequenceNumber) {
|
||||||
uint64_t focusSequenceNumber;
|
uint64_t focusSequenceNumber;
|
||||||
@@ -7485,6 +7602,24 @@ nsresult PresShell::EventHandler::HandleEventUsingCoordinates(
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are trying to dispatch an ePointerRawUpdate but it's not allowed in
|
||||||
|
// the (maybe retargetted) document, we should not flush the capture state
|
||||||
|
// below.
|
||||||
|
if (aGUIEvent->mMessage == eMouseRawUpdate ||
|
||||||
|
aGUIEvent->mMessage == eTouchRawUpdate) {
|
||||||
|
EventTargetDataWithCapture eventTargetData =
|
||||||
|
EventTargetDataWithCapture::QueryEventTargetUsingCoordinates(
|
||||||
|
*this, aWeakFrameForPresShell,
|
||||||
|
EventTargetDataWithCapture::Query::PendingState, aGUIEvent);
|
||||||
|
if (!PointerEventHandler::NeedToDispatchPointerRawUpdate(
|
||||||
|
eventTargetData.GetDocument())) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
// Then, we need to recompute the target with processing the pending pointer
|
||||||
|
// capture. Note that the result may be differnet since `gotpointercapture`
|
||||||
|
// event listener does something tricky things.
|
||||||
|
}
|
||||||
|
|
||||||
EventTargetDataWithCapture eventTargetData =
|
EventTargetDataWithCapture eventTargetData =
|
||||||
EventTargetDataWithCapture::QueryEventTargetUsingCoordinates(
|
EventTargetDataWithCapture::QueryEventTargetUsingCoordinates(
|
||||||
*this, aWeakFrameForPresShell,
|
*this, aWeakFrameForPresShell,
|
||||||
@@ -7609,6 +7744,12 @@ PresShell::EventHandler::EventTargetDataWithCapture::EventTargetDataWithCapture(
|
|||||||
if (GetDocument() && aGUIEvent->mClass == eTouchEventClass) {
|
if (GetDocument() && aGUIEvent->mClass == eTouchEventClass) {
|
||||||
PointerLockManager::Unlock("TouchEvent");
|
PointerLockManager::Unlock("TouchEvent");
|
||||||
}
|
}
|
||||||
|
// XXX If aGUIEvent is eMouseRawUpdate or eTouchRawUpdate and it's
|
||||||
|
// dispatched by BrowserChild, i.e., the event won't cause ePointerMove
|
||||||
|
// immediately after ePointerRawUpdate, should we skip fluhsing pending
|
||||||
|
// animations here? Doing this could cause different animation result while
|
||||||
|
// the user moves mouse cursor during a long animation whether there is a
|
||||||
|
// `pointerrawupdate` event listener or not.
|
||||||
aEventHandler.MaybeFlushThrottledStyles(aWeakFrameForPresShell);
|
aEventHandler.MaybeFlushThrottledStyles(aWeakFrameForPresShell);
|
||||||
// Previously, MaybeFlushThrottledStyles() recomputed the closest ancestor
|
// Previously, MaybeFlushThrottledStyles() recomputed the closest ancestor
|
||||||
// frame for view of mPresShell if it's reframed. Therefore, we should keep
|
// frame for view of mPresShell if it's reframed. Therefore, we should keep
|
||||||
@@ -7914,11 +8055,15 @@ bool PresShell::EventHandler::DispatchPrecedingPointerEvent(
|
|||||||
aPointerCapturingElement, aGUIEvent, aDontRetargetEvents, aEventStatus,
|
aPointerCapturingElement, aGUIEvent, aDontRetargetEvents, aEventStatus,
|
||||||
getter_AddRefs(mouseOrTouchEventTargetContent));
|
getter_AddRefs(mouseOrTouchEventTargetContent));
|
||||||
|
|
||||||
|
const bool maybeCallerCanHandleEvent =
|
||||||
|
aGUIEvent->mMessage != eMouseRawUpdate &&
|
||||||
|
aGUIEvent->mMessage != eTouchRawUpdate;
|
||||||
|
|
||||||
// If the target frame is alive, the caller should keep handling the event
|
// If the target frame is alive, the caller should keep handling the event
|
||||||
// unless event target frame is destroyed.
|
// unless event target frame is destroyed.
|
||||||
if (weakTargetFrame.IsAlive() && weakFrame.IsAlive()) {
|
if (weakTargetFrame.IsAlive() && weakFrame.IsAlive()) {
|
||||||
aEventTargetData->UpdateTouchEventTarget(aGUIEvent);
|
aEventTargetData->UpdateTouchEventTarget(aGUIEvent);
|
||||||
return true;
|
return maybeCallerCanHandleEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
presShell->FlushPendingNotifications(FlushType::Layout);
|
presShell->FlushPendingNotifications(FlushType::Layout);
|
||||||
@@ -7956,7 +8101,7 @@ bool PresShell::EventHandler::DispatchPrecedingPointerEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
aEventTargetData->UpdateTouchEventTarget(aGUIEvent);
|
aEventTargetData->UpdateTouchEventTarget(aGUIEvent);
|
||||||
return true;
|
return maybeCallerCanHandleEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8376,6 +8521,16 @@ bool PresShell::EventHandler::MaybeDiscardOrDelayMouseEvent(
|
|||||||
MOZ_ASSERT(aFrameToHandleEvent);
|
MOZ_ASSERT(aFrameToHandleEvent);
|
||||||
MOZ_ASSERT(aGUIEvent);
|
MOZ_ASSERT(aGUIEvent);
|
||||||
|
|
||||||
|
// We must not need to let suspend listeners know ePointerRawUpdate events.
|
||||||
|
// And also the delayed events will be dispatched via widget. Therefore,
|
||||||
|
// ePointerRawUpdate event will be dispatched by PresShell::HandleEvent()
|
||||||
|
// again.
|
||||||
|
if (aGUIEvent->mMessage == eMouseRawUpdate ||
|
||||||
|
aGUIEvent->mMessage == eTouchRawUpdate ||
|
||||||
|
aGUIEvent->mMessage == ePointerRawUpdate) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!aGUIEvent->IsMouseEventClassOrHasClickRelatedPointerEvent()) {
|
if (!aGUIEvent->IsMouseEventClassOrHasClickRelatedPointerEvent()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -9077,6 +9232,11 @@ bool PresShell::EventHandler::PrepareToDispatchEvent(
|
|||||||
MaybeHandleKeyboardEventBeforeDispatch(keyboardEvent);
|
MaybeHandleKeyboardEventBeforeDispatch(keyboardEvent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case eMouseRawUpdate:
|
||||||
|
MOZ_ASSERT_UNREACHABLE(
|
||||||
|
"eMouseRawUpdate shouldn't be handled as a DOM event");
|
||||||
|
return false;
|
||||||
|
|
||||||
case eMouseMove: {
|
case eMouseMove: {
|
||||||
bool allowCapture = EventStateManager::GetActiveEventStateManager() &&
|
bool allowCapture = EventStateManager::GetActiveEventStateManager() &&
|
||||||
GetPresContext() &&
|
GetPresContext() &&
|
||||||
@@ -9131,6 +9291,10 @@ bool PresShell::EventHandler::PrepareToDispatchEvent(
|
|||||||
return mPresShell->mTouchManager.PreHandleEvent(
|
return mPresShell->mTouchManager.PreHandleEvent(
|
||||||
aEvent, aEventStatus, *aTouchIsNew,
|
aEvent, aEventStatus, *aTouchIsNew,
|
||||||
mPresShell->mCurrentEventTarget.mContent);
|
mPresShell->mCurrentEventTarget.mContent);
|
||||||
|
case eTouchRawUpdate:
|
||||||
|
MOZ_ASSERT_UNREACHABLE(
|
||||||
|
"eTouchRawUpdate shouldn't be handled as a DOM event");
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -9172,6 +9336,10 @@ void PresShell::EventHandler::FinalizeHandlingEvent(
|
|||||||
// reset the capturing content now that the mouse button is up
|
// reset the capturing content now that the mouse button is up
|
||||||
PresShell::ReleaseCapturingContent();
|
PresShell::ReleaseCapturingContent();
|
||||||
break;
|
break;
|
||||||
|
case eMouseRawUpdate:
|
||||||
|
MOZ_ASSERT_UNREACHABLE(
|
||||||
|
"eMouseRawUpdate shouldn't be handled as a DOM event");
|
||||||
|
break;
|
||||||
case eMouseMove:
|
case eMouseMove:
|
||||||
PresShell::AllowMouseCapture(false);
|
PresShell::AllowMouseCapture(false);
|
||||||
break;
|
break;
|
||||||
@@ -9201,6 +9369,10 @@ void PresShell::EventHandler::FinalizeHandlingEvent(
|
|||||||
mPresShell->mTouchManager.PostHandleEvent(aEvent, aStatus);
|
mPresShell->mTouchManager.PostHandleEvent(aEvent, aStatus);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case eTouchRawUpdate:
|
||||||
|
MOZ_ASSERT_UNREACHABLE(
|
||||||
|
"eTouchRawUpdate shouldn't be handled as a DOM event");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -9313,6 +9485,10 @@ void PresShell::EventHandler::RecordEventPreparationPerformance(
|
|||||||
nsPresContext::InteractionType::ClickInteraction, aEvent->mTimeStamp);
|
nsPresContext::InteractionType::ClickInteraction, aEvent->mTimeStamp);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case eMouseRawUpdate:
|
||||||
|
MOZ_ASSERT_UNREACHABLE(
|
||||||
|
"eMouseRawUpdate shouldn't be handled as a DOM event");
|
||||||
|
break;
|
||||||
case eMouseMove:
|
case eMouseMove:
|
||||||
GetPresContext()->RecordInteractionTime(
|
GetPresContext()->RecordInteractionTime(
|
||||||
nsPresContext::InteractionType::MouseMoveInteraction,
|
nsPresContext::InteractionType::MouseMoveInteraction,
|
||||||
@@ -9493,6 +9669,7 @@ nsresult PresShell::EventHandler::DispatchEventToDOM(
|
|||||||
void PresShell::EventHandler::DispatchTouchEventToDOM(
|
void PresShell::EventHandler::DispatchTouchEventToDOM(
|
||||||
WidgetEvent* aEvent, nsEventStatus* aEventStatus,
|
WidgetEvent* aEvent, nsEventStatus* aEventStatus,
|
||||||
nsPresShellEventCB* aEventCB, bool aTouchIsNew) {
|
nsPresShellEventCB* aEventCB, bool aTouchIsNew) {
|
||||||
|
MOZ_ASSERT(aEvent->mMessage != eTouchRawUpdate);
|
||||||
// calling preventDefault on touchstart or the first touchmove for a
|
// calling preventDefault on touchstart or the first touchmove for a
|
||||||
// point prevents mouse events. calling it on the touchend should
|
// point prevents mouse events. calling it on the touchend should
|
||||||
// prevent click dispatching.
|
// prevent click dispatching.
|
||||||
|
|||||||
@@ -1996,6 +1996,15 @@ class PresShell final : public nsStubDocumentObserver,
|
|||||||
// Utility method to restore the root scrollframe state
|
// Utility method to restore the root scrollframe state
|
||||||
void RestoreRootScrollPosition();
|
void RestoreRootScrollPosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch eMouseRawUpdate or eTouchRawUpdate event if aSourceEvent requires
|
||||||
|
* a preceding "pointerrawupdate" event and there are some windows which have
|
||||||
|
* its listener.
|
||||||
|
*/
|
||||||
|
MOZ_CAN_RUN_SCRIPT nsresult EnsurePrecedingPointerRawUpdate(
|
||||||
|
AutoWeakFrame& aWeakFrameForPresShell, const WidgetGUIEvent& aSourceEvent,
|
||||||
|
bool aDontRetargetEvents);
|
||||||
|
|
||||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY void MaybeReleaseCapturingContent();
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY void MaybeReleaseCapturingContent();
|
||||||
|
|
||||||
class DelayedEvent {
|
class DelayedEvent {
|
||||||
|
|||||||
@@ -303,6 +303,9 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case eTouchRawUpdate:
|
||||||
|
MOZ_ASSERT_UNREACHABLE("eTouchRawUpdate shouldn't be handled as a touch");
|
||||||
|
break;
|
||||||
case eTouchMove: {
|
case eTouchMove: {
|
||||||
// Check for touches that changed. Mark them add to queue
|
// Check for touches that changed. Mark them add to queue
|
||||||
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
|
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
|
||||||
@@ -448,6 +451,9 @@ bool TouchManager::PreHandleEvent(WidgetEvent* aEvent, nsEventStatus* aStatus,
|
|||||||
void TouchManager::PostHandleEvent(const WidgetEvent* aEvent,
|
void TouchManager::PostHandleEvent(const WidgetEvent* aEvent,
|
||||||
const nsEventStatus* aStatus) {
|
const nsEventStatus* aStatus) {
|
||||||
switch (aEvent->mMessage) {
|
switch (aEvent->mMessage) {
|
||||||
|
case eTouchRawUpdate:
|
||||||
|
MOZ_ASSERT_UNREACHABLE("eTouchRawUpdate shouldn't be handled as a touch");
|
||||||
|
break;
|
||||||
case eTouchMove: {
|
case eTouchMove: {
|
||||||
if (sSingleTouchStartTimeStamp.IsNull()) {
|
if (sSingleTouchStartTimeStamp.IsNull()) {
|
||||||
break;
|
break;
|
||||||
@@ -559,7 +565,8 @@ bool TouchManager::ShouldConvertTouchToPointer(const Touch* aTouch,
|
|||||||
// We don't want to fire duplicated pointerdown.
|
// We don't want to fire duplicated pointerdown.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case eTouchMove: {
|
case eTouchMove:
|
||||||
|
case eTouchRawUpdate: {
|
||||||
return !aTouch->Equals(info.mTouch);
|
return !aTouch->Equals(info.mTouch);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
[pointerevent_pointerrawupdate_in_pointerlock.https.html]
|
[pointerevent_pointerrawupdate_in_pointerlock.https.html]
|
||||||
|
max-asserts: 1 # in PointerEventHandler::DispatchPointerFromMouseOrTouch
|
||||||
expected:
|
expected:
|
||||||
if (os == "win") and debug and (processor == "x86_64"): [OK, TIMEOUT]
|
if (os == "win") and debug and (processor == "x86_64"): [OK, TIMEOUT]
|
||||||
if (os == "win") and debug and (processor == "x86"): TIMEOUT
|
if (os == "win") and debug and (processor == "x86"): TIMEOUT
|
||||||
|
|||||||
@@ -93,6 +93,14 @@ NS_EVENT_MESSAGE(eMouseEnter)
|
|||||||
NS_EVENT_MESSAGE(eMouseLeave)
|
NS_EVENT_MESSAGE(eMouseLeave)
|
||||||
NS_EVENT_MESSAGE(eMouseTouchDrag)
|
NS_EVENT_MESSAGE(eMouseTouchDrag)
|
||||||
NS_EVENT_MESSAGE(eMouseLongTap)
|
NS_EVENT_MESSAGE(eMouseLongTap)
|
||||||
|
// eMouseRawUpdate is for dispatching ePointerRawUpdate caused by a mouse input.
|
||||||
|
// When we dispatch ePointerRawUpdate, we need to handle the steps defined by
|
||||||
|
// the spec in "fire a pointer event" section like the other pointer event.
|
||||||
|
// The steps are handled by
|
||||||
|
// PresShell::EventHandler::DispatchPrecedingPointerEvent() and for using it, we
|
||||||
|
// should dispatch this internal event instead of dispatching ePointerRawUpdate
|
||||||
|
// directly.
|
||||||
|
NS_EVENT_MESSAGE(eMouseRawUpdate)
|
||||||
NS_EVENT_MESSAGE(eMouseExploreByTouch)
|
NS_EVENT_MESSAGE(eMouseExploreByTouch)
|
||||||
NS_EVENT_MESSAGE_FIRST_LAST(eMouseEvent, eMouseMove, eMouseExploreByTouch)
|
NS_EVENT_MESSAGE_FIRST_LAST(eMouseEvent, eMouseMove, eMouseExploreByTouch)
|
||||||
|
|
||||||
@@ -100,6 +108,13 @@ NS_EVENT_MESSAGE(ePointerClick)
|
|||||||
NS_EVENT_MESSAGE(ePointerAuxClick)
|
NS_EVENT_MESSAGE(ePointerAuxClick)
|
||||||
|
|
||||||
// Pointer spec events
|
// Pointer spec events
|
||||||
|
// NOTE: We handle the steps before dispatching a pointer event which is defined
|
||||||
|
// by the spec in "fire a pointer event" section in
|
||||||
|
// PresShell::EventHandler::DispatchPrecedingPointerEvent(). Therefore,
|
||||||
|
// we should not dispatch ePointer* events with
|
||||||
|
// `PresShell::EventHandler::HandleEvent` directly. Create a new internal
|
||||||
|
// message for implementing a new pointer event if the new event is defined as
|
||||||
|
// dispatched with "fire a pointer event" steps.
|
||||||
NS_EVENT_MESSAGE(ePointerMove)
|
NS_EVENT_MESSAGE(ePointerMove)
|
||||||
NS_EVENT_MESSAGE(ePointerUp)
|
NS_EVENT_MESSAGE(ePointerUp)
|
||||||
NS_EVENT_MESSAGE(ePointerDown)
|
NS_EVENT_MESSAGE(ePointerDown)
|
||||||
@@ -433,6 +448,14 @@ NS_EVENT_MESSAGE(eTouchMove)
|
|||||||
NS_EVENT_MESSAGE(eTouchEnd)
|
NS_EVENT_MESSAGE(eTouchEnd)
|
||||||
NS_EVENT_MESSAGE(eTouchCancel)
|
NS_EVENT_MESSAGE(eTouchCancel)
|
||||||
NS_EVENT_MESSAGE(eTouchPointerCancel)
|
NS_EVENT_MESSAGE(eTouchPointerCancel)
|
||||||
|
// eTouchRawUpdate is for dispatching ePointerRawUpdate caused by a touch.
|
||||||
|
// When we dispatch ePointerRawUpdate, we need to handle the steps defined by
|
||||||
|
// the spec in "fire a pointer event" section like the other pointer event.
|
||||||
|
// The steps are handled by
|
||||||
|
// PresShell::EventHandler::DispatchPrecedingPointerEvent() and for using it, we
|
||||||
|
// should dispatch this internal event instead of dispatching ePointerRawUpdate
|
||||||
|
// directly.
|
||||||
|
NS_EVENT_MESSAGE(eTouchRawUpdate)
|
||||||
|
|
||||||
// Pointerlock DOM API
|
// Pointerlock DOM API
|
||||||
NS_EVENT_MESSAGE(ePointerLockChange)
|
NS_EVENT_MESSAGE(ePointerLockChange)
|
||||||
|
|||||||
@@ -43,22 +43,22 @@ class WidgetPointerEventHolder final {
|
|||||||
|
|
||||||
class WidgetPointerHelper {
|
class WidgetPointerHelper {
|
||||||
public:
|
public:
|
||||||
uint32_t pointerId;
|
uint32_t pointerId = 0;
|
||||||
int32_t tiltX;
|
int32_t tiltX = 0;
|
||||||
int32_t tiltY;
|
int32_t tiltY = 0;
|
||||||
int32_t twist;
|
int32_t twist = 0;
|
||||||
float tangentialPressure;
|
float tangentialPressure = 0.0f;
|
||||||
bool convertToPointer;
|
bool convertToPointer = true;
|
||||||
|
// When convertToPointerRawUpdate is set to true, the event or the touch may
|
||||||
|
// cause ePointerRawUpdate event in PresShell::HandleEvent() if it's requested
|
||||||
|
// by the web app. This is set to false if the source mouse event or the
|
||||||
|
// source touch move event is not dispatched immediately by BrowserChild
|
||||||
|
// because BrowserChild dispatches only eMouseRawUpdate or eTouchRawUpdate to
|
||||||
|
// dispatch ePointerRawUpdate immediately.
|
||||||
|
bool convertToPointerRawUpdate = true;
|
||||||
RefPtr<WidgetPointerEventHolder> mCoalescedWidgetEvents;
|
RefPtr<WidgetPointerEventHolder> mCoalescedWidgetEvents;
|
||||||
|
|
||||||
WidgetPointerHelper()
|
WidgetPointerHelper() = default;
|
||||||
: pointerId(0),
|
|
||||||
tiltX(0),
|
|
||||||
tiltY(0),
|
|
||||||
twist(0),
|
|
||||||
tangentialPressure(0),
|
|
||||||
convertToPointer(true) {}
|
|
||||||
|
|
||||||
WidgetPointerHelper(uint32_t aPointerId, uint32_t aTiltX, uint32_t aTiltY,
|
WidgetPointerHelper(uint32_t aPointerId, uint32_t aTiltX, uint32_t aTiltY,
|
||||||
uint32_t aTwist = 0, float aTangentialPressure = 0)
|
uint32_t aTwist = 0, float aTangentialPressure = 0)
|
||||||
: pointerId(aPointerId),
|
: pointerId(aPointerId),
|
||||||
@@ -108,6 +108,7 @@ class WidgetPointerHelper {
|
|||||||
twist = aEvent.twist;
|
twist = aEvent.twist;
|
||||||
tangentialPressure = aEvent.tangentialPressure;
|
tangentialPressure = aEvent.tangentialPressure;
|
||||||
convertToPointer = aEvent.convertToPointer;
|
convertToPointer = aEvent.convertToPointer;
|
||||||
|
convertToPointerRawUpdate = aEvent.convertToPointerRawUpdate;
|
||||||
if (aCopyCoalescedEvents) {
|
if (aCopyCoalescedEvents) {
|
||||||
mCoalescedWidgetEvents = aEvent.mCoalescedWidgetEvents;
|
mCoalescedWidgetEvents = aEvent.mCoalescedWidgetEvents;
|
||||||
}
|
}
|
||||||
@@ -413,7 +414,13 @@ class WidgetMouseEvent : public WidgetMouseEventBase,
|
|||||||
* Returns true if the event is a real mouse event. Otherwise, i.e., it's
|
* Returns true if the event is a real mouse event. Otherwise, i.e., it's
|
||||||
* a synthesized event by scroll or something, returns false.
|
* a synthesized event by scroll or something, returns false.
|
||||||
*/
|
*/
|
||||||
bool IsReal() const { return mReason == eReal; }
|
[[nodiscard]] bool IsReal() const { return mReason == eReal; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the event is synthesized for scroll or layout change.
|
||||||
|
* Do not confuse this with a synthesized event for tests.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool IsSynthesized() const { return mReason == eSynthesized; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if middle click paste is enabled.
|
* Returns true if middle click paste is enabled.
|
||||||
|
|||||||
@@ -151,13 +151,23 @@ class WidgetTouchEvent final : public WidgetInputEvent {
|
|||||||
|
|
||||||
MOZ_COUNTED_DEFAULT_CTOR(WidgetTouchEvent)
|
MOZ_COUNTED_DEFAULT_CTOR(WidgetTouchEvent)
|
||||||
|
|
||||||
WidgetTouchEvent(const WidgetTouchEvent& aOther)
|
enum class CloneTouches : bool { No, Yes };
|
||||||
|
WidgetTouchEvent(const WidgetTouchEvent& aOther,
|
||||||
|
CloneTouches aCloneTouches = CloneTouches::No)
|
||||||
: WidgetInputEvent(aOther.IsTrusted(), aOther.mMessage, aOther.mWidget,
|
: WidgetInputEvent(aOther.IsTrusted(), aOther.mMessage, aOther.mWidget,
|
||||||
eTouchEventClass) {
|
eTouchEventClass) {
|
||||||
MOZ_COUNT_CTOR(WidgetTouchEvent);
|
MOZ_COUNT_CTOR(WidgetTouchEvent);
|
||||||
mModifiers = aOther.mModifiers;
|
mModifiers = aOther.mModifiers;
|
||||||
mTimeStamp = aOther.mTimeStamp;
|
mTimeStamp = aOther.mTimeStamp;
|
||||||
mTouches.AppendElements(aOther.mTouches);
|
if (static_cast<bool>(aCloneTouches)) {
|
||||||
|
mTouches.SetCapacity(aOther.mTouches.Length());
|
||||||
|
for (const RefPtr<dom::Touch>& touch : aOther.mTouches) {
|
||||||
|
RefPtr<dom::Touch> clonedTouch = new dom::Touch(*touch);
|
||||||
|
mTouches.AppendElement(std::move(clonedTouch));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mTouches.AppendElements(aOther.mTouches);
|
||||||
|
}
|
||||||
mInputSource = aOther.mInputSource;
|
mInputSource = aOther.mInputSource;
|
||||||
mButton = aOther.mButton;
|
mButton = aOther.mButton;
|
||||||
mButtons = aOther.mButtons;
|
mButtons = aOther.mButtons;
|
||||||
@@ -213,6 +223,20 @@ class WidgetTouchEvent final : public WidgetInputEvent {
|
|||||||
mTouches.AppendElements(aEvent.mTouches);
|
mTouches.AppendElements(aEvent.mTouches);
|
||||||
mInputSource = aEvent.mInputSource;
|
mInputSource = aEvent.mInputSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetConvertToPointerRawUpdate(bool aConvert) {
|
||||||
|
for (dom::Touch* const touch : mTouches) {
|
||||||
|
touch->convertToPointerRawUpdate = aConvert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool CanConvertToPointerRawUpdate() const {
|
||||||
|
for (const dom::Touch* const touch : mTouches) {
|
||||||
|
if (touch->convertToPointerRawUpdate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|||||||
@@ -174,6 +174,12 @@ bool IsForbiddenDispatchingToNonElementContent(EventMessage aMessage) {
|
|||||||
case eTouchPointerCancel:
|
case eTouchPointerCancel:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case eMouseRawUpdate:
|
||||||
|
case eTouchRawUpdate:
|
||||||
|
MOZ_ASSERT_UNREACHABLE(
|
||||||
|
"Internal raw update events shouldn't be dispatched to the DOM");
|
||||||
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -384,6 +390,7 @@ bool WidgetEvent::HasMouseEventMessage() const {
|
|||||||
case eMouseOut:
|
case eMouseOut:
|
||||||
case eMouseHitTest:
|
case eMouseHitTest:
|
||||||
case eMouseMove:
|
case eMouseMove:
|
||||||
|
case eMouseRawUpdate:
|
||||||
return true;
|
return true;
|
||||||
// TODO: Perhaps, we should rename this method.
|
// TODO: Perhaps, we should rename this method.
|
||||||
case ePointerClick:
|
case ePointerClick:
|
||||||
@@ -531,7 +538,7 @@ bool WidgetEvent::IsTargetedAtFocusedContent() const {
|
|||||||
bool WidgetEvent::IsAllowedToDispatchDOMEvent() const {
|
bool WidgetEvent::IsAllowedToDispatchDOMEvent() const {
|
||||||
switch (mClass) {
|
switch (mClass) {
|
||||||
case eMouseEventClass:
|
case eMouseEventClass:
|
||||||
if (mMessage == eMouseTouchDrag) {
|
if (mMessage == eMouseRawUpdate || mMessage == eMouseTouchDrag) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
@@ -541,7 +548,7 @@ bool WidgetEvent::IsAllowedToDispatchDOMEvent() const {
|
|||||||
// DOM events.
|
// DOM events.
|
||||||
// Synthesized button up events also do not cause DOM events because they
|
// Synthesized button up events also do not cause DOM events because they
|
||||||
// do not have a reliable mRefPoint.
|
// do not have a reliable mRefPoint.
|
||||||
return AsMouseEvent()->mReason == WidgetMouseEvent::eReal;
|
return AsMouseEvent()->IsReal();
|
||||||
|
|
||||||
case eWheelEventClass: {
|
case eWheelEventClass: {
|
||||||
// wheel event whose all delta values are zero by user pref applied, it
|
// wheel event whose all delta values are zero by user pref applied, it
|
||||||
@@ -551,7 +558,7 @@ bool WidgetEvent::IsAllowedToDispatchDOMEvent() const {
|
|||||||
wheelEvent->mDeltaZ != 0.0;
|
wheelEvent->mDeltaZ != 0.0;
|
||||||
}
|
}
|
||||||
case eTouchEventClass:
|
case eTouchEventClass:
|
||||||
return mMessage != eTouchPointerCancel;
|
return mMessage != eTouchRawUpdate && mMessage != eTouchPointerCancel;
|
||||||
// Following events are handled in EventStateManager, so, we don't need to
|
// Following events are handled in EventStateManager, so, we don't need to
|
||||||
// dispatch DOM event for them into the DOM tree.
|
// dispatch DOM event for them into the DOM tree.
|
||||||
case eQueryContentEventClass:
|
case eQueryContentEventClass:
|
||||||
@@ -908,6 +915,7 @@ float WidgetMouseEventBase::ComputeMouseButtonPressure() const {
|
|||||||
switch (mMessage) {
|
switch (mMessage) {
|
||||||
// This method is designed for mouse events.
|
// This method is designed for mouse events.
|
||||||
case eMouseMove:
|
case eMouseMove:
|
||||||
|
case eMouseRawUpdate:
|
||||||
case eMouseUp:
|
case eMouseUp:
|
||||||
case eMouseDown:
|
case eMouseDown:
|
||||||
case eMouseEnterIntoWidget:
|
case eMouseEnterIntoWidget:
|
||||||
@@ -936,6 +944,7 @@ float WidgetMouseEventBase::ComputeMouseButtonPressure() const {
|
|||||||
case ePointerClick:
|
case ePointerClick:
|
||||||
case ePointerAuxClick:
|
case ePointerAuxClick:
|
||||||
case ePointerMove:
|
case ePointerMove:
|
||||||
|
case ePointerRawUpdate:
|
||||||
case ePointerUp:
|
case ePointerUp:
|
||||||
case ePointerDown:
|
case ePointerDown:
|
||||||
case ePointerCancel:
|
case ePointerCancel:
|
||||||
|
|||||||
Reference in New Issue
Block a user