Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test has a WPT meta file that expects 1 subtest issues.
- This WPT test may be referenced by the following Test IDs:
- /pointerevents/pointerevent_pointerrawupdate_changes_pointer_capture.https.html - WPT Dashboard Interop Dashboard
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<meta name="viewport" content="width=device-width, initial-scale:1, user-scalable=no">
<title>Test for handling of "fire a pointer event named pointerrawupdate"</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
div#initPosition {
height: 1em;
margin: 50px;
}
div#parent, div#child {
width: 200px;
height: 200px;
margin: 0px;
padding: 0;
}
</style>
<script>
"use strict";
/**
* `pointerrawupdate` is defined as:
* > The user agent MUST fire a pointer event named pointerrawupdate, and only
* > do so within a secure context, when a pointer changes any properties that
* > don't fire pointerdown or pointerup events.
*
* The following `pointermove` event is also defined as:
* > The user agent MUST fire a pointer event named pointermove
*
* So, when a set of `pointerrawupdate` and `pointermove` is dispatched, the
* "fire a pointer event" runs twice.
*
* "fire a pointer event" defines:
* > If the event is not a gotpointercapture, lostpointercapture, click,
* > auxclick or contextmenu event, run the process pending pointer capture
* > steps for this PointerEvent.
*
* And also the section defines:
* > Determine the target at which the event is fired as follows:
* > - If the pointer capture target override has been set for the pointer, set
* > the target to pointer capture target override object.
* > - Otherwise, set the target to the object returned by normal hit test
* > mechanisms (out of scope for this specification).
*
* So, dispatching `pointerrawupdate` should fix the pointer capture override
* and cause dispatching `gotpointercapture` and/or `lostpointercapture` and
* `pointermove` event should be retarget to the new pointer capture override.
*/
addEventListener("load", () => {
const ticksToPreventCoalescedPointerMove = 300;
const initDiv = document.getElementById("initPosition");
const parent = document.getElementById("parent");
const child = document.getElementById("child");
let events;
function logEvent(event) {
events.push({type: event.type, target: event.target});
}
function stringifyEvents(arrayOfEvents) {
function stringifyEvent(event) {
return `${event.type}@${event.target.localName}${
event.target.id ? `#${event.target.id}` : ""
}`;
}
let str = "";
for (const event of arrayOfEvents) {
if (str) {
str += ", ";
}
str += stringifyEvent(event);
}
return str;
}
for (const type of ["pointerdown", "pointerup",
"pointerrawupdate", "pointermove",
"gotpointercapture", "lostpointercapture"]) {
parent.addEventListener(type, logEvent, {capture: true});
}
promise_test(async t => {
events = [];
child.addEventListener("pointerdown", pointerDownEvent => {
parent.setPointerCapture(pointerDownEvent.pointerId);
parent.addEventListener("pointerrawupdate", pointerRawUpdateEvent => {
parent.releasePointerCapture(pointerRawUpdateEvent.pointerId);
}, {once: true});
}, {once: true});
await new test_driver.Actions()
.pointerMove(0, 0, {origin: initDiv})
.pause(ticksToPreventCoalescedPointerMove)
.pointerMove(0, 0, {origin: child})
.pointerDown()
.pointerMove(1, 1, {origin: child})
.pointerUp()
.pointerMove(0, 0, {origin: initDiv})
.send();
assert_equals(
stringifyEvents(events),
stringifyEvents([
{type: "pointerrawupdate", target: child},
{type: "pointermove", target: child},
{type: "pointerdown", target: child}, // set pending pointer capture to parent
// The following `pointerrawupdate` event dispatching runs the
// "process pending pointer capture" steps.
{type: "gotpointercapture", target: parent},
{type: "pointerrawupdate", target: parent}, // set pending pointer capture to null
// The following `pointermove` event dispatching runs the
// "process pending pointer capture" steps again.
{type: "lostpointercapture", target: parent},
{type: "pointermove", target: child},
{type: "pointerup", target: child},
])
);
}, "Setting pointer capture at `pointerdown` and releasing pointer capture at `pointerrawupdate`");
promise_test(async t => {
events = [];
child.addEventListener("pointerdown", () => {
parent.addEventListener("pointerrawupdate", pointerRawUpdateEvent => {
parent.setPointerCapture(pointerRawUpdateEvent.pointerId);
}, {once: true});
}, {once: true});
await new test_driver.Actions()
.pointerMove(0, 0, {origin: initDiv})
.pause(ticksToPreventCoalescedPointerMove)
.pointerMove(0, 0, {origin: child})
.pointerDown()
.pointerMove(1, 1, {origin: child})
.pointerUp()
.pointerMove(0, 0, {origin: initDiv})
.send();
assert_equals(
stringifyEvents(events),
stringifyEvents([
{type: "pointerrawupdate", target: child},
{type: "pointermove", target: child},
{type: "pointerdown", target: child},
{type: "pointerrawupdate", target: child}, // set pending pointer capture to parent
// The following `pointermove` event dispatching runs the
// "process pending pointer capture" steps.
{type: "gotpointercapture", target: parent},
{type: "pointermove", target: parent},
{type: "pointerup", target: parent},
{type: "lostpointercapture", target: parent},
])
);
}, "Setting pointer capture at `pointerrawupdate`");
promise_test(async t => {
events = [];
child.addEventListener("pointerdown", pointerDownEvent => {
parent.setPointerCapture(pointerDownEvent.pointerId);
parent.addEventListener("gotpointercapture", gotPointerCaptureEvent => {
parent.releasePointerCapture(gotPointerCaptureEvent.pointerId);
}, {once: true});
}, {once: true});
await new test_driver.Actions()
.pointerMove(0, 0, {origin: initDiv})
.pause(ticksToPreventCoalescedPointerMove)
.pointerMove(0, 0, {origin: child})
.pointerDown()
.pointerMove(1, 1, {origin: child})
.pointerUp()
.pointerMove(0, 0, {origin: initDiv})
.send();
assert_equals(
stringifyEvents(events),
stringifyEvents([
{type: "pointerrawupdate", target: child},
{type: "pointermove", target: child},
{type: "pointerdown", target: child}, // set pending pointer capture to parent
// The following `pointerrawupdate` event dispatching runs the
// "process pending pointer capture" steps.
{type: "gotpointercapture", target: parent}, // set pending pointer capture to null
{type: "pointerrawupdate", target: parent},
// The following `pointermove` event dispatching runs the
// "process pending pointer capture" steps again.
{type: "lostpointercapture", target: parent},
{type: "pointermove", target: child},
{type: "pointerup", target: child},
])
);
}, "Setting pointer capture at `pointerdown` and releasing pointer capture at `gotpointercapture`");
promise_test(async t => {
events = [];
child.addEventListener("pointerdown", pointerDownEvent => {
parent.setPointerCapture(pointerDownEvent.pointerId);
parent.addEventListener("pointermove", pointerMoveEvent => {
parent.releasePointerCapture(pointerMoveEvent.pointerId);
parent.addEventListener("lostpointercapture", lostPointerCaptureEvent => {
parent.setPointerCapture(lostPointerCaptureEvent.pointerId);
}, {once: true});
}, {once: true});
}, {once: true});
await new test_driver.Actions()
.pointerMove(0, 0, {origin: initDiv})
.pause(ticksToPreventCoalescedPointerMove)
.pointerMove(0, 0, {origin: child})
.pointerDown()
.pointerMove(1, 1, {origin: child})
.pause(ticksToPreventCoalescedPointerMove)
.pointerMove(2, 2, {origin: child})
.pointerUp()
.pointerMove(0, 0, {origin: initDiv})
.send();
assert_equals(
stringifyEvents(events),
stringifyEvents([
{type: "pointerrawupdate", target: child},
{type: "pointermove", target: child},
{type: "pointerdown", target: child}, // set pending pointer capture to parent
// The following `pointerrawupdate` event dispatching runs the
// "process pending pointer capture" steps.
{type: "gotpointercapture", target: parent},
{type: "pointerrawupdate", target: parent},
{type: "pointermove", target: parent}, // set pending pointer capture to null
// The following `pointerrawupdate` event dispatching runs the
// "process pending pointer capture" steps again.
{type: "lostpointercapture", target: parent}, // set pending pointer capture to parent again
{type: "pointerrawupdate", target: child},
// The following `pointermove` event dispatching runs the
// "process pending pointer capture" steps again.
{type: "gotpointercapture", target: parent},
{type: "pointermove", target: parent},
{type: "pointerup", target: parent},
{type: "lostpointercapture", target: parent},
])
);
}, "Setting pointer capture at `lostpointercapture`");
promise_test(async () => {
parent.removeEventListener("pointerrawupdate", logEvent, {capture: true});
// Now, there is no `pointerrawupdate` event listener. So, browsers should
// not dispatch `pointerrawupdate` event and the "fire a pointer event" steps
// including the "process pending pointer capture" steps should not run twice
// per `pointermove`.
assert_true(true, "There is no `pointerrawupdate` event listener anymore");
});
promise_test(async t => {
events = [];
child.addEventListener("pointerdown", pointerDownEvent => {
parent.setPointerCapture(pointerDownEvent.pointerId);
parent.addEventListener("gotpointercapture", gotPointerCaptureEvent => {
parent.releasePointerCapture(gotPointerCaptureEvent.pointerId);
}, {once: true});
}, {once: true});
await new test_driver.Actions()
.pointerMove(0, 0, {origin: initDiv})
.pause(ticksToPreventCoalescedPointerMove)
.pointerMove(0, 0, {origin: child})
.pointerDown()
.pointerMove(1, 1, {origin: child})
.pause(ticksToPreventCoalescedPointerMove)
.pointerMove(2, 2, {origin: child})
.pointerUp()
.pointerMove(0, 0, {origin: initDiv})
.send();
assert_equals(
stringifyEvents(events),
stringifyEvents([
{type: "pointermove", target: child},
{type: "pointerdown", target: child}, // set pending pointer capture to parent
// The following `pointermove` event dispatching runs the
// "process pending pointer capture" steps.
{type: "gotpointercapture", target: parent}, // set pending pointer capture to null
{type: "pointermove", target: parent},
// The following `pointermove` event dispatching runs the
// "process pending pointer capture" steps again.
{type: "lostpointercapture", target: parent},
{type: "pointermove", target: child},
{type: "pointerup", target: child},
])
);
}, "Setting pointer capture at `pointerdown` and releasing pointer capture at `gotpointercapture` when no `pointerrawupdate` event listener");
}, {once: true});
</script>
</head>
<body>
<div id="initPosition"></div>
<div id="parent">
<div id="child">
</div>
</div>
</body>
</html>