Bug 1966551 - Make PointerEventHandler::DispatchPointerFromMouseOrTouch() dispatch synthesized ePointerMove for synthesized eMouseMove if it's caused by hoverable pointer r=smaug
Currently, we don't dispatch synthesized `ePointerMove` unless it's required for dispatch the boundary events after dispatching `ePointerLostCapture` event [1] since Pointer Events defined that the boundary events should be fired only when before dispatching a pointer event. However, it's changed, Point Events currently defines that the boundary events should be fired if the element under the pointer is changed without a `pointermove` [2] if and only if the pointer supports hover. Therefore, this patch makes `PresShell` store the last input source whose event set the mouse location at last and `PresShell::ProcessSynthMouseMoveEvent()` sets the input source to make `PointerEventHandler::DispatchPointerFromMouseOrTouch()` can consider whether it needs to dispatch pointer boundary events or not for the pointer. Additionally, the mochitests for the manual WPTs under `dom/events/test/pointerevents` checks `pointerId`. Therefore, this patch makes `PresShell` also store the last `pointerId` and set it to the synthesized `eMouseMove` too. I think that this approach is **not** correct approach to fix this bug because there could be multiple hoverable pointers, but we synthesize pointer boundary events only for the last input device. I think it's enough for now because we've not supported pen well (we've not supported the test API yet!), so, we only support only mouse input well as hoverable inputs. I think we should extend `PointerInfo` and make a synthesizer of `ePointerMove` later. Note that this patch changes 2 WPTs which both are in the scope of Interop. The expectation of `pointerevent_pointer_boundary_events_after_removing_last_over_element.html` needs to be changed for conforming to the latest spec. I wrote this test before the spec change and it wasn't updated when the spec is changed. I filed this issue to interop [3]. The changes for `pointerevent_pointerout_no_pointer_movement.html` is required for avoiding the timeout. Gecko does not allow recursive synthesized `eMouseMove` to prevent infinite reflow loops without moving the mouse cursor. However, the test expects that and that causes requiring the hack for Chrome too. Therefore, I split the test to make each step run in different event loop and I removed the hack for Chrome. Note that this patch also removes 2 sets of mochitests for WPT manual tests because they are now tested with the test driver [4][5] and they fail without maintained. 1. https://searchfox.org/mozilla-central/rev/f571db8014431de31d245017e2f5457046aec4ea/dom/events/PointerEventHandler.cpp#494-503 2. https://w3c.github.io/pointerevents/#boundary-events-caused-by-layout-changes 3. https://github.com/web-platform-tests/interop/issues/961 4. https://wpt.fyi/results/pointerevents/pointerevent_boundary_events_in_capturing.html%3Fmouse?label=master&label=experimental&aligned&view=interop 5. https://wpt.fyi/results/pointerevents/pointerevent_releasepointercapture_events_to_original_target.html%3Fmouse?label=master&label=experimental&aligned&view=interop Differential Revision: https://phabricator.services.mozilla.com/D250421
This commit is contained in:
committed by
masayuki@d-toybox.com
parent
9b669b5846
commit
66ee19892a
@@ -894,12 +894,31 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch(
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. If it is not mouse then it is likely will come as touch event
|
||||
// 2. We don't synthesize pointer events for synthesized mouse move
|
||||
if (!mouseEvent->convertToPointer || mouseEvent->IsSynthesized()) {
|
||||
// If it is not mouse then it is likely will come as touch event
|
||||
if (!mouseEvent->convertToPointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's a synthesized eMouseMove and the input source supports hover, we
|
||||
// need to dispatch pointer boundary events if the element underneath the
|
||||
// pointer has already been changed from the last `pointerover` event
|
||||
// target.
|
||||
if (mouseEvent->IsSynthesized()) {
|
||||
if (!StaticPrefs::
|
||||
dom_event_pointer_boundary_dispatch_when_layout_change() ||
|
||||
!mouseEvent->InputSourceSupportsHover()) {
|
||||
return;
|
||||
}
|
||||
// So, if the pointer is captured, we don't need to dispatch pointer
|
||||
// boundary events since pointer boundary events should be fired before
|
||||
// gotpointercapture.
|
||||
PointerCaptureInfo* const captureInfo =
|
||||
GetPointerCaptureInfo(mouseEvent->pointerId);
|
||||
if (captureInfo && captureInfo->mOverrideElement) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pointerMessage = PointerEventHandler::ToPointerEventMessage(mouseEvent);
|
||||
if (pointerMessage == eVoidEvent) {
|
||||
return;
|
||||
|
||||
@@ -144,13 +144,6 @@ skip-if = [
|
||||
"http2",
|
||||
]
|
||||
|
||||
["test_wpt_pointerevent_boundary_events_in_capturing-manual.html"]
|
||||
support-files = ["wpt/pointerevent_boundary_events_in_capturing-manual.html"]
|
||||
skip-if = [
|
||||
"http3",
|
||||
"http2",
|
||||
]
|
||||
|
||||
["test_wpt_pointerevent_drag_interaction-manual.html"]
|
||||
support-files = ["wpt/html/pointerevent_drag_interaction-manual.html"]
|
||||
skip-if = [
|
||||
@@ -207,13 +200,6 @@ skip-if = [
|
||||
"http2",
|
||||
]
|
||||
|
||||
["test_wpt_pointerevent_releasepointercapture_events_to_original_target-manual.html"]
|
||||
support-files = ["wpt/pointerevent_releasepointercapture_events_to_original_target-manual.html"]
|
||||
skip-if = [
|
||||
"http3",
|
||||
"http2",
|
||||
]
|
||||
|
||||
["test_wpt_pointerevent_releasepointercapture_onpointercancel_touch-manual.html"]
|
||||
support-files = ["wpt/pointerevent_releasepointercapture_onpointercancel_touch-manual.html"]
|
||||
skip-if = [
|
||||
|
||||
@@ -24,16 +24,22 @@ function runTests() {
|
||||
let target0 = window.document.getElementById("target0");
|
||||
let pointerEventsList = ["pointerover", "pointerenter", "pointerdown",
|
||||
"pointerup", "pointerleave", "pointerout"];
|
||||
let receivedPointerEvents = false;
|
||||
const pointerEvents = [];
|
||||
pointerEventsList.forEach((elem, index, arr) => {
|
||||
target0.addEventListener(elem, (event) => {
|
||||
ok(false, "receiving event " + event.type);
|
||||
receivedPointerEvents = true;
|
||||
});
|
||||
target0.addEventListener(elem, event => pointerEvents.push(event.type));
|
||||
});
|
||||
|
||||
target1.addEventListener("mouseup", () => {
|
||||
ok(!receivedPointerEvents, "synthesized mousemove should not trigger any pointer events");
|
||||
is(
|
||||
pointerEvents.join(", "),
|
||||
[
|
||||
"pointerover", // Should be caused by the synthesized mousemove
|
||||
"pointerenter",
|
||||
"pointerout", // Should be caused by clicking target1
|
||||
"pointerleave",
|
||||
].join(", "),
|
||||
"Synthesizing mousemove should cause only pointer boundary events"
|
||||
);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>W3C pointerevent_boundary_events_in_capturing-manual.html in Mochitest form</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="mochitest_support_external.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
function startTest() {
|
||||
runTestInNewWindow("wpt/pointerevent_boundary_events_in_capturing-manual.html", true);
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
sendMouseEvent(int_win, "target0", "mousemove");
|
||||
sendMouseEvent(int_win, "target0", "mousedown");
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
|
||||
sendMouseEvent(int_win, "target0", "mouseup");
|
||||
|
||||
window.addEventListener("message", function(aEvent) {
|
||||
if (aEvent.data == "Test Touch") {
|
||||
// Synthesize touch events to run this test.
|
||||
sendTouchEvent(int_win, "target0", "touchstart");
|
||||
sendTouchEvent(int_win, "target0", "touchmove", {offsetX: 10});
|
||||
sendTouchEvent(int_win, "target0", "touchmove", {offsetX: 15});
|
||||
sendTouchEvent(int_win, "target0", "touchmove", {offsetX: 20});
|
||||
sendTouchEvent(int_win, "target0", "touchend");
|
||||
window.postMessage("Test Pen", "*");
|
||||
} else if (aEvent.data == "Test Pen") {
|
||||
// Synthesize pen events to run this test.
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
|
||||
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
}
|
||||
});
|
||||
window.postMessage("Test Touch", "*");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,49 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1000870</title>
|
||||
<meta name="author" content="Maksim Lebedev" />
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="mochitest_support_external.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
function startTest() {
|
||||
runTestInNewWindow("wpt/pointerevent_releasepointercapture_events_to_original_target-manual.html");
|
||||
}
|
||||
function executeTest(int_win) {
|
||||
// Synthesize mouse events to run this test.
|
||||
sendMouseEvent(int_win, "target0", "mousemove");
|
||||
sendMouseEvent(int_win, "target0", "mousedown");
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
|
||||
sendMouseEvent(int_win, "target0", "mouseup");
|
||||
|
||||
window.addEventListener("message", function(aEvent) {
|
||||
if (aEvent.data == "Test Touch") {
|
||||
// Synthesize touch events to run this test.
|
||||
sendTouchEvent(int_win, "target0", "touchstart", {offsetX: 10});
|
||||
sendTouchEvent(int_win, "target0", "touchmove", {offsetX: 11});
|
||||
sendTouchEvent(int_win, "target0", "touchend", {offsetX: 11});
|
||||
window.postMessage("Test Pen", "*");
|
||||
} else if (aEvent.data == "Test Pen") {
|
||||
// Synthesize pen events to run this test.
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
|
||||
sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
|
||||
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
|
||||
}
|
||||
});
|
||||
window.postMessage("Test Touch", "*");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,97 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Pointer Events boundary events in capturing tests</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<!-- Additional helper script for common checks across event types -->
|
||||
<script type="text/javascript" src="pointerevent_support.js"></script>
|
||||
<script>
|
||||
var detected_pointertypes = {};
|
||||
var eventList = All_Pointer_Events;
|
||||
PhaseEnum = {
|
||||
WaitingForDown: "down",
|
||||
WaitingForFirstMove: "firstMove",
|
||||
WaitingForSecondMove: "secondMove",
|
||||
WaitingForUp: "up"
|
||||
}
|
||||
var phase = PhaseEnum.WaitingForDown;
|
||||
var eventsRecieved = [];
|
||||
|
||||
function resetTestState() {
|
||||
eventsRecieved = [];
|
||||
phase = PhaseEnum.WaitingForDown;
|
||||
}
|
||||
function run() {
|
||||
var test_pointerEvent = setup_pointerevent_test("pointerevent boundary events in capturing", ALL_POINTERS);
|
||||
var target = document.getElementById("target0");
|
||||
var listener = document.getElementById("listener");
|
||||
|
||||
eventList.forEach(function(eventName) {
|
||||
on_event(target, eventName, function (event) {
|
||||
if (phase == PhaseEnum.WaitingForDown) {
|
||||
if (eventName == 'pointerdown') {
|
||||
listener.setPointerCapture(event.pointerId);
|
||||
phase = PhaseEnum.WaitingForFirstMove;
|
||||
}
|
||||
} else if (phase == PhaseEnum.WaitingForUp) {
|
||||
if (event.type == 'pointerup')
|
||||
test_pointerEvent.done();
|
||||
} else {
|
||||
eventsRecieved.push(event.type + '@target');
|
||||
if (phase == PhaseEnum.WaitingForSecondMove && event.type == 'pointermove') {
|
||||
test(function () {
|
||||
checkPointerEventType(event);
|
||||
assert_array_equals(eventsRecieved, ['lostpointercapture@listener', 'pointerout@listener', 'pointerleave@listener', 'pointerover@target', 'pointerenter@target', 'pointermove@target'],
|
||||
'lostpointercapture and pointerout/leave should be dispatched to the capturing target and pointerover/enter should be dispatched to the hit-test element before the first pointermove event after releasing pointer capture');
|
||||
}, expectedPointerType + " pointer events boundary events when releasing capture");
|
||||
phase = PhaseEnum.WaitingForUp;
|
||||
}
|
||||
}
|
||||
});
|
||||
on_event(listener, eventName, function (event) {
|
||||
if (phase == PhaseEnum.WaitingForDown)
|
||||
return;
|
||||
eventsRecieved.push(event.type + '@listener');
|
||||
if (phase == PhaseEnum.WaitingForFirstMove && eventName == 'pointermove') {
|
||||
test(function () {
|
||||
checkPointerEventType(event);
|
||||
assert_array_equals(eventsRecieved, ['pointerout@target', 'pointerleave@target', 'pointerover@listener', 'pointerenter@listener', 'gotpointercapture@listener', 'pointermove@listener'],
|
||||
'pointerout/leave should be dispatched to the previous target and pointerover/enter and gotpointercapture should be dispatched to the capturing element before the first captured pointermove event');
|
||||
}, expectedPointerType + " pointer events boundary events when receiving capture");
|
||||
listener.releasePointerCapture(event.pointerId);
|
||||
eventsRecieved = [];
|
||||
phase = PhaseEnum.WaitingForSecondMove;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="run()">
|
||||
<h1>Pointer Events boundary events in capturing</h1>
|
||||
<h2 id="pointerTypeDescription"></h2>
|
||||
<h4>
|
||||
Test Description: This test checks the boundary events of pointer events while the capturing changes. If you are using hoverable pen don't leave the range of digitizer while doing the instructions.
|
||||
<ol>
|
||||
<li>Move your pointer over the black square</li>
|
||||
<li>Press down the pointer (i.e. press left button with mouse or touch the screen with finger or pen).</li>
|
||||
<li>Drag the pointer within the black square.</li>
|
||||
<li>Release the pointer.</li>
|
||||
</ol>
|
||||
|
||||
Test passes if the proper behavior of the events is observed.
|
||||
</h4>
|
||||
<div id="target0" class="touchActionNone">
|
||||
</div>
|
||||
<div id="listener">Do not hover over or touch this element. </div>
|
||||
<div id="complete-notice">
|
||||
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
|
||||
<p>Refresh the page to run the tests again with a different pointer type.</p>
|
||||
</div>
|
||||
<div id="log"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Pointer Event: releasePointerCapture() - subsequent events follow normal hitting testing mechanisms</title>
|
||||
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
|
||||
<link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
|
||||
<meta name="assert" content="After invoking the releasePointerCapture method on an element, subsequent events for the specified pointer must follow normal hit testing mechanisms for determining the event target"/>
|
||||
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<!-- Additional helper script for common checks across event types -->
|
||||
<script type="text/javascript" src="pointerevent_support.js"></script>
|
||||
<script type="text/javascript">
|
||||
var test_pointerEvent;
|
||||
var detected_pointertypes = {};
|
||||
var captured_event = null;
|
||||
var test_done = false;
|
||||
var overEnterEventsFail = false;
|
||||
var outLeaveEventsFail = false;
|
||||
var f_gotPointerCapture = false;
|
||||
var f_lostPointerCapture = false;
|
||||
|
||||
function resetTestState() {
|
||||
captured_event = null;
|
||||
test_done = false;
|
||||
overEnterEventsFail = false;
|
||||
outLeaveEventsFail = false;
|
||||
f_gotPointerCapture = false;
|
||||
f_lostPointerCapture = false;
|
||||
}
|
||||
|
||||
function listenerEventHandler(event) {
|
||||
if (test_done)
|
||||
return;
|
||||
detected_pointertypes[event.pointerType] = true;
|
||||
if (event.type == "gotpointercapture") {
|
||||
f_gotPointerCapture = true;
|
||||
check_PointerEvent(event);
|
||||
}
|
||||
else if (event.type == "lostpointercapture") {
|
||||
f_lostPointerCapture = true;
|
||||
f_gotPointerCapture = false;
|
||||
check_PointerEvent(event);
|
||||
}
|
||||
else if(event.type == "pointerover" || event.type == "pointerenter") {
|
||||
if(captured_event && !overEnterEventsFail) {
|
||||
test(function() {
|
||||
assert_false(f_gotPointerCapture, "pointerover/enter should be received before the target receives gotpointercapture even when the pointer is not over it.");
|
||||
}, expectedPointerType + " pointerover/enter should be received before the target receives gotpointercapture even when the pointer is not over it.");
|
||||
overEnterEventsFail = true;
|
||||
}
|
||||
}
|
||||
else if(event.type == "pointerout" || event.type == "pointerleave") {
|
||||
if(!outLeaveEventsFail) {
|
||||
test(function() {
|
||||
assert_true(f_lostPointerCapture, "pointerout/leave should not be received unless the target just lost the capture.");
|
||||
}, expectedPointerType + " pointerout/leave should not be received unless the target just lost the capture.");
|
||||
outLeaveEventsFail = true;
|
||||
}
|
||||
}
|
||||
else if (event.pointerId == captured_event.pointerId) {
|
||||
if (f_gotPointerCapture && event.type == "pointermove") {
|
||||
// on first event received for capture, release capture
|
||||
listener.releasePointerCapture(event.pointerId);
|
||||
}
|
||||
else {
|
||||
// if any other events are received after releaseCapture, then the test fails
|
||||
test(function () {
|
||||
assert_unreached(event.target.id + "-" + event.type + " should be handled by target element handler");
|
||||
}, expectedPointerType + " No other events should be recieved by capturing node after release");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function targetEventHandler(event) {
|
||||
if (test_done)
|
||||
return;
|
||||
if (f_gotPointerCapture) {
|
||||
if(event.type != "pointerout" && event.type != "pointerleave") {
|
||||
test(function () {
|
||||
assert_unreached("The Target element should not have received any events while capture is active. Event recieved:" + event.type + ". ");
|
||||
}, expectedPointerType + " The target element should not receive any events while capture is active");
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type == "pointerdown") {
|
||||
// pointerdown event received will be used to capture events.
|
||||
listener.setPointerCapture(event.pointerId);
|
||||
captured_event = event;
|
||||
}
|
||||
|
||||
if (f_lostPointerCapture) {
|
||||
test_pointerEvent.step(function () {
|
||||
assert_equals(event.pointerId, captured_event.pointerId, "pointerID is same for event captured and after release");
|
||||
});
|
||||
if (event.type == "pointerup") {
|
||||
test_done = true;
|
||||
test_pointerEvent.done(); // complete test
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function run() {
|
||||
test_pointerEvent = setup_pointerevent_test("got/lost pointercapture: subsequent events to target", ALL_POINTERS); // set up test harness
|
||||
var listener = document.getElementById("listener");
|
||||
var target0 = document.getElementById("target0");
|
||||
target0.style.touchAction = "none";
|
||||
|
||||
// target0 and listener - handle all events
|
||||
for (var i = 0; i < All_Pointer_Events.length; i++) {
|
||||
on_event(target0, All_Pointer_Events[i], targetEventHandler);
|
||||
on_event(listener, All_Pointer_Events[i], listenerEventHandler);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="run()">
|
||||
<h2 id="pointerTypeDescription"></h2>
|
||||
<div id="listener"></div>
|
||||
<h1>Pointer Event: releasePointerCapture() - subsequent events follow normal hitting testing mechanisms</h1>
|
||||
<h4>
|
||||
Test Description:
|
||||
Use your pointer and press down in the black box. Then move around in the box and release your pointer.
|
||||
After invoking the releasePointerCapture method on an element, subsequent events for the specified
|
||||
pointer must follow normal hit testing mechanisms for determining the event target.
|
||||
</h4>
|
||||
<br />
|
||||
<div id="target0">
|
||||
</div>
|
||||
<div id="complete-notice">
|
||||
<p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
|
||||
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
|
||||
<p>Refresh the page to run the tests again with a different pointer type.</p>
|
||||
</div>
|
||||
<div id="log"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -6093,6 +6093,8 @@ void PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll) {
|
||||
event.mRefPoint =
|
||||
LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD);
|
||||
event.mButtons = PresShell::sMouseButtons;
|
||||
event.mInputSource = mMouseLocationInputSource;
|
||||
event.pointerId = mMouseLocationPointerId;
|
||||
// XXX set event.mModifiers ?
|
||||
// XXX mnakano I think that we should get the latest information from widget.
|
||||
|
||||
@@ -6997,10 +6999,6 @@ void PresShell::RecordPointerLocation(WidgetGUIEvent* aEvent) {
|
||||
|
||||
switch (aEvent->mMessage) {
|
||||
case eMouseMove:
|
||||
if (!aEvent->AsMouseEvent()->IsReal()) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case eMouseEnterIntoWidget:
|
||||
case eMouseDown:
|
||||
case eMouseUp:
|
||||
@@ -7008,12 +7006,18 @@ void PresShell::RecordPointerLocation(WidgetGUIEvent* aEvent) {
|
||||
case eDragStart:
|
||||
case eDragOver:
|
||||
case eDrop: {
|
||||
mMouseLocation = GetEventLocation(*aEvent->AsMouseEvent());
|
||||
WidgetMouseEvent* const mouseEvent = aEvent->AsMouseEvent();
|
||||
if (mouseEvent->mMessage == eMouseMove && mouseEvent->IsSynthesized()) {
|
||||
break;
|
||||
}
|
||||
mMouseLocation = GetEventLocation(*mouseEvent);
|
||||
mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
|
||||
// FIXME: Don't trust the synthesized for tests flag of drag events.
|
||||
if (aEvent->mClass != eDragEventClass) {
|
||||
mMouseLocationInputSource = mouseEvent->mInputSource;
|
||||
mMouseLocationPointerId = mouseEvent->pointerId;
|
||||
mMouseLocationWasSetBySynthesizedMouseEventForTests =
|
||||
aEvent->mFlags.mIsSynthesizedForTests;
|
||||
mouseEvent->mFlags.mIsSynthesizedForTests;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (MOZ_LOG_TEST(gLogMouseLocation, LogLevel::Info)) {
|
||||
@@ -7059,6 +7063,8 @@ void PresShell::RecordPointerLocation(WidgetGUIEvent* aEvent) {
|
||||
// into another.
|
||||
mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
||||
mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
|
||||
mMouseLocationInputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
|
||||
mMouseLocationPointerId = 0;
|
||||
mMouseLocationWasSetBySynthesizedMouseEventForTests =
|
||||
aEvent->mFlags.mIsSynthesizedForTests;
|
||||
#ifdef DEBUG
|
||||
|
||||
@@ -3297,6 +3297,12 @@ class PresShell final : public nsStubDocumentObserver,
|
||||
// needed, one hopes, but it is for now.
|
||||
uint16_t mChangeNestCount;
|
||||
|
||||
// This is the input source which set mMouseLocation.
|
||||
uint16_t mMouseLocationInputSource = 0; // MOZ_SOURCE_UNKNOWN by default
|
||||
|
||||
// This is the pointerId which set mMouseLocation.
|
||||
uint32_t mMouseLocationPointerId = 0;
|
||||
|
||||
// Flags controlling how our document is rendered. These persist
|
||||
// between paints and so are tied with retained layer pixels.
|
||||
// PresShell flushes retained layers when the rendering state
|
||||
|
||||
@@ -97,6 +97,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1162990
|
||||
|
||||
function executeTest()
|
||||
{
|
||||
synthesizeMouse(document.querySelector("a"), 0, 0, {type : "mousemove"});
|
||||
logger("executeTest");
|
||||
setEventHandlers();
|
||||
var rectCd = child.getBoundingClientRect();
|
||||
|
||||
@@ -97,6 +97,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1162990
|
||||
|
||||
function executeTest()
|
||||
{
|
||||
synthesizeMouse(document.querySelector("a"), 0, 0, {type : "mousemove"});
|
||||
logger("executeTest");
|
||||
setEventHandlers();
|
||||
var rectTg = target.getBoundingClientRect();
|
||||
|
||||
@@ -2811,6 +2811,13 @@
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# Whether pointer boundary events should be dispatched after the element is
|
||||
# changed by a layout change or something without pointer move.
|
||||
- name: dom.event.pointer.boundary.dispatch_when_layout_change
|
||||
type: bool
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# Whether the result of screenX, screenY, clientX, clientY, offsetX, offsetY,
|
||||
# x and y of PointerEvent may be fractional values (except `click`, `auxclick`
|
||||
# and `contextmenu`)
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
[pointerevent_pointerout_no_pointer_movement.html]
|
||||
expected: TIMEOUT
|
||||
[Layout change under a stationary pointer fires boundary events and no pointermove event]
|
||||
expected: TIMEOUT
|
||||
prefs: [layout.reflow.synthMouseMove:true]
|
||||
|
||||
@@ -79,10 +79,12 @@ addEventListener("load", () => {
|
||||
// initialization, but it's out of scope of this bug. Therefore, we
|
||||
// compare only events after `click`.
|
||||
const expectedEvents = [ // no events should be fired on the child due to disconnected
|
||||
{ type: "pointerleave", target: div2}, // no pointerout because of first pointer move after the mutation
|
||||
{ type: "pointerleave", target: div1},
|
||||
{ type: "pointerover", target: document.body},
|
||||
{ type: "pointermove", target: document.body},
|
||||
{ type: "pointerover", target: div2 }, // no pointerenter because it should've already fired before the `click`
|
||||
{ type: "pointerout", target: div2 },
|
||||
{ type: "pointerleave", target: div2 },
|
||||
{ type: "pointerleave", target: div1 },
|
||||
{ type: "pointerover", target: document.body },
|
||||
{ type: "pointermove", target: document.body },
|
||||
];
|
||||
assert_equals(
|
||||
stringifyEvents(eventsAfterClick(events)),
|
||||
@@ -131,7 +133,9 @@ addEventListener("load", () => {
|
||||
// initialization, but it's out of scope of this bug. Therefore, we
|
||||
// compare only events after `click`.
|
||||
const expectedEvents = [ // no events should be fired on rootElementInShadow due to disconnected
|
||||
{ type: "pointerleave", target: host}, // no pointerout because of first pointer move after the mutation
|
||||
{ type: "pointerover", target: host }, // no pointerenter because it should've already fired before the click
|
||||
{ type: "pointerout", target: host },
|
||||
{ type: "pointerleave", target: host},
|
||||
{ type: "pointerleave", target: hostContainer},
|
||||
{ type: "pointerover", target: document.body},
|
||||
{ type: "pointermove", target: document.body},
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#log {
|
||||
display: none; /* Required to run this test alone */
|
||||
}
|
||||
</style>
|
||||
<div id="target"></div>
|
||||
<div id="overlay"></div>
|
||||
@@ -44,19 +48,23 @@
|
||||
promise_test(async t => {
|
||||
await test_driver.click(target);
|
||||
|
||||
num_pointermoves = 0;
|
||||
let pointerout_promise = getEvent("pointerout", target);
|
||||
overlay.style.display = "block";
|
||||
await pointerout_promise;
|
||||
assert_equals(num_pointermoves, 0, "no pointermove events are expected");
|
||||
}, "Layout change under a stationary pointer (a new element appears) fires boundary events and no pointermove event");
|
||||
|
||||
|
||||
promise_test(async t => {
|
||||
await test_driver.click(overlay);
|
||||
|
||||
num_pointermoves = 0;
|
||||
let pointerover_promise = getEvent("pointerover", target);
|
||||
overlay.style.display = "none";
|
||||
await waitForAnimationFrames(2);
|
||||
// Why does Chrome time out at the next line w/o the redundant wait above?
|
||||
// Does chrome require something to request a main frame in order to detect
|
||||
// the new pointer target after the style update to generate the event?
|
||||
// https://crbug.com/413595088
|
||||
await pointerover_promise;
|
||||
|
||||
assert_equals(num_pointermoves, 0, "no pointermove events are expected");
|
||||
}, "Layout change under a stationary pointer fires boundary events and no pointermove event");
|
||||
|
||||
document.querySelector("style").remove(); // Show the test log if running alone.
|
||||
}, "Layout change under a stationary pointer (the element disappears) fires boundary events and no pointermove event");
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user