<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>Pointer Events when mouse button up on the parent document while an element in a child document captures the pointer</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
"use strict";
setup({explicit_timeout: true});
addEventListener("load", () => {
promise_test(async t => {
const iframe = document.querySelector("iframe");
const button = iframe.contentDocument.querySelector("button");
const div = iframe.contentDocument.querySelector("div");
const divInParent = document.querySelector("iframe + div");
let pointerEvents = [];
let mouseEvents = [];
function recordPointerEvent(event) {
pointerEvents.push(event);
}
function recordMouseEvent(event) {
mouseEvents.push(event);
}
function stringifyEvent(event) {
function stringifyTarget(target) {
switch (target) {
case button:
return "<button> in child";
case div:
return "<div> in child which captured the pointer";
case divInParent:
return "<div> in parent";
default:
return target?.nodeName;
}
}
return `"${event.type}" on ${stringifyTarget(event.target)}`;
}
const pointerEventTypes = ["pointerup", "lostpointercapture", "pointerover", "pointerout", "pointerenter", "pointerleave", "pointermove"];
const mouseEventTypes = ["mouseup", "mouseover", "mouseout", "mouseenter", "mouseleave", "mousemove"];
const promisePointerUp = new Promise(resolve => {
button.addEventListener("pointerdown", event => {
div.setPointerCapture(event.pointerId);
iframe.contentWindow.addEventListener("pointerup", event => {
recordPointerEvent(event);
[button, div, divInParent].forEach(target => {
pointerEventTypes.forEach(type => {
target.addEventListener(type, recordPointerEvent);
});
mouseEventTypes.forEach(type => {
target.addEventListener(type, recordMouseEvent);
});
});
resolve();
}, {once: true});
}, {once: true});
});
await promisePointerUp;
await new Promise(
resolve => requestAnimationFrame(
() => requestAnimationFrame(resolve)
)
);
const stringifiedPointerEvents = [];
const stringifiedMouseEvents = [];
for (const event of pointerEvents) {
stringifiedPointerEvents.push(stringifyEvent(event));
}
for (const event of mouseEvents) {
stringifiedMouseEvents.push(stringifyEvent(event));
}
const stringifiedExpectedPointerEvents = [
stringifyEvent({ type: "pointerup", target: div }),
stringifyEvent({ type: "lostpointercapture", target: div }),
stringifyEvent({ type: "pointerout", target: div }),
stringifyEvent({ type: "pointerleave", target: div }),
stringifyEvent({ type: "pointerover", target: divInParent }),
stringifyEvent({ type: "pointerenter", target: divInParent }),
];
if (pointerEvents[pointerEvents.length - 1]?.type == "pointermove") {
stringifiedExpectedPointerEvents.push(
stringifyEvent({ type: "pointermove", target: divInParent })
);
}
const stringifiedExpectedMouseEvents = [
stringifyEvent({ type: "mouseup", target: div }),
stringifyEvent({ type: "mouseout", target: div }),
stringifyEvent({ type: "mouseleave", target: div }),
stringifyEvent({ type: "mouseover", target: divInParent }),
stringifyEvent({ type: "mouseenter", target: divInParent }),
];
if (mouseEvents[mouseEvents.length - 1]?.type == "mousemove") {
stringifiedExpectedMouseEvents.push(
stringifyEvent({ type: "mousemove", target: divInParent })
);
}
t.step(() => {
assert_array_equals(stringifiedPointerEvents, stringifiedExpectedPointerEvents)
assert_array_equals(stringifiedMouseEvents, stringifiedExpectedMouseEvents)
});
t.done();
}, "boundary events should be fired for notifying web apps of over the element in parent document");
}, {once: true});
</script>
</head>
<body>
<div>
<p>Test steps:</p>
<ol>
<li>Press the button with primary button of your mouse and start dragging</li>
<li>Move the mouse cursor over the red border box and release the mouse button</li>
</ol>
</div>
<iframe srcdoc="<button>Start dragging from this button</button><div><br></div>"></iframe>
<div style='border: 1px solid red'>And release mouse button over this box</div>
</body>
</html>