diff options
Diffstat (limited to '')
-rw-r--r-- | testing/web-platform/tests/pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html b/testing/web-platform/tests/pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html new file mode 100644 index 0000000000..bf2a405f72 --- /dev/null +++ b/testing/web-platform/tests/pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html @@ -0,0 +1,149 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>Redundant "pointerenter" shouldn't be fired without "pointerleave"s</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> +<script> +"use strict"; + +function stringifyEvents(eventArray) { + if (!eventArray.length) { + return "[]"; + } + let result = ""; + eventArray.forEach(event => { + if (result != "") { + result += ", "; + } + result += `${event.type}@${ + event.target?.nodeType == Node.ELEMENT_NODE + ? `${event.target.localName}${ + event.target.id ? `#${event.target.id}` : "" + }` + : event.target?.localName + }`; + }); + return result; +} + +function eventsAfterClick(eventArray) { + const indexAtClick = eventArray.findIndex(e => e.type == "click"); + if (indexAtClick >= 0) { + return eventArray.slice(indexAtClick + 1); + } + return []; +} + +addEventListener("load", () => { + promise_test(async () => { + const div1 = document.createElement("div"); + div1.setAttribute("id", "grandparent"); + div1.setAttribute("style", "width: 32px; height: 32px"); + const div2 = document.createElement("div"); + div2.setAttribute("id", "parent"); + div2.setAttribute("style", "width: 32px; height: 32px"); + const div3 = document.createElement("div"); + div3.setAttribute("id", "child"); + div3.setAttribute("style", "width: 32px; height: 32px"); + div1.appendChild(div2); + div2.appendChild(div3); + document.body.appendChild(div1); + const bodyRect = document.body.getBoundingClientRect(); + const div3Rect = div3.getBoundingClientRect(); + let events = []; + for (const type of ["pointerenter", "pointerleave", "pointerover", "pointerout", "pointermove"]) { + for (const node of [document.body, div1, div2, div3]) { + node.addEventListener(type, event => { + if (event.target == node) { + events.push({type: event.type, target: event.target}); + } + }, {capture: true}); + } + } + div3.addEventListener("click", event => { + div3.remove(); + events.push({type: event.type, target: event.target}); + }, {once: true}); + await new test_driver.Actions() + .pointerMove(div3Rect.x + 10, div3Rect.y + 10, {}) + .pointerDown() + .pointerUp() // The clicked in the child, then it's removed from the DOM tree + .pointerMove(bodyRect.x + 10, bodyRect.y + 10, {}) // Then, move onto the <body> + .send(); + // FYI: Comparing `pointerenter`s before `click` requires additional + // 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}, + ]; + assert_equals( + stringifyEvents(eventsAfterClick(events)), + stringifyEvents(expectedEvents), + ); + div1.remove(); + }, "After removing the last over element, redundant pointerenter events should not be fired on the ancestors"); + + promise_test(async () => { + const hostContainer = document.createElement("div"); + hostContainer.setAttribute("id", "containerOfShadowHost"); + hostContainer.setAttribute("style", "margin-top: 32px; height: 32px"); + const host = document.createElement("div"); + host.setAttribute("id", "shadowHost"); + host.setAttribute("style", "width: 32px; height: 32px"); + const root = host.attachShadow({mode: "open"}); + const rootElementInShadow = document.createElement("div"); + root.appendChild(rootElementInShadow); + rootElementInShadow.setAttribute("id", "divInShadow"); + rootElementInShadow.setAttribute("style", "width: 32px; height: 32px"); + hostContainer.appendChild(host); + document.body.appendChild(hostContainer); + const bodyRect = document.body.getBoundingClientRect(); + const rootElementInShadowRect = rootElementInShadow.getBoundingClientRect(); + let events = []; + for (const type of ["pointerenter", "pointerleave", "pointerover", "pointerout", "pointermove"]) { + for (const node of [document.body, hostContainer, host, root, rootElementInShadow]) { + node.addEventListener(type, event => { + if (event.target == node) { + events.push({type: event.type, target: event.target}); + } + }, {capture: true}); + } + } + rootElementInShadow.addEventListener("click", event => { + rootElementInShadow.remove(); + events.push({type: event.type, target: event.target}); + }, {once: true}); + await new test_driver.Actions() + .pointerMove(rootElementInShadowRect.x + 10, rootElementInShadowRect.y + 10, {}) + .pointerDown() + .pointerUp() // The clicked root element in the shadow is removed here. + .pointerMove(bodyRect.x + 10, bodyRect.y + 10, {}) // Then, move onto the <body> + .send(); + // FYI: Comparing `pointerenter`s before `click` requires additional + // 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: "pointerleave", target: hostContainer}, + { type: "pointerover", target: document.body}, + { type: "pointermove", target: document.body}, + ]; + assert_equals( + stringifyEvents(eventsAfterClick(events)), + stringifyEvents(expectedEvents), + ); + hostContainer.remove(); + }, "After removing the root element in the shadow under the cursor, pointerleave events should be targeted outside the shadow, but redundant pointerenter events should not be fired"); +}, {once: true}); +</script> +</head> +<body style="padding-top: 32px"></body> +</html> |