[Interop] pointerenter/leave events after child element is removed
A removed element shouldn't cause either a `mouseenter` or a
`pointerenter` event to be fired to the parent element. This test
asserts this behavior as implied by the UIEvent spec. Unfortunately
the major browsers don't agree here!
Bug: 1147998
Change-Id: Ia8ede1c121d6f62bda41b7675683e077d20af00f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4064304
Reviewed-by: Kevin Ellis <kevers@chromium.org>
Commit-Queue: Mustaq Ahmed <mustaq@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1078126}
diff --git a/pointerevents/pointerevent_after_target_removed.html b/pointerevents/pointerevent_after_target_removed.html
new file mode 100644
index 0000000..43d5ab3
--- /dev/null
+++ b/pointerevents/pointerevent_after_target_removed.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<title>Enter/leave events fired to parent after child is removed</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 src="pointerevent_support.js"></script>
+
+<style>
+ div.target {
+ width: 100px;
+ height: 50px;
+ }
+</style>
+<div class="target" id="parent">
+ <div class="target" id="child">child</div>
+</div>
+<div id="done">done</div>
+
+<script>
+ 'use strict';
+
+ const parent = document.getElementById("parent");
+ const child = document.getElementById("child");
+ const done = document.getElementById("done");
+
+ let event_log = [];
+ let logged_event_prefix = "";
+
+ function logEvent(e) {
+ if (e.type.startsWith(logged_event_prefix) && e.eventPhase == e.AT_TARGET)
+ event_log.push(e.type);
+ }
+
+ function setup() {
+ const logged_events = [
+ "pointerover", "pointerout", "pointerenter", "pointerleave",
+ "mouseover", "mouseout", "mouseenter", "mouseleave"
+ ];
+ logged_events.forEach(ename => parent.addEventListener(ename, logEvent));
+ }
+
+ function addPromiseTest(remover_event, tested_event_prefix) {
+ const test_name = tested_event_prefix
+ + " events at parent after child is removed at " + remover_event;
+ const expected_events = ["enter", "over", "out", "leave"]
+ .map(suffix => tested_event_prefix + suffix);
+
+ promise_test(async () => {
+ event_log = [];
+ logged_event_prefix = tested_event_prefix;
+
+ // Bring the child back if it was removed in the previous promise_test.
+ if (!child.parentElement)
+ parent.appendChild(child);
+
+ child.addEventListener(remover_event,
+ () => parent.removeChild(child),
+ { once:true });
+
+ let done_click_promise = getEvent("click", done);
+
+ let actions = new test_driver.Actions().addPointer("TestPointer", "mouse")
+ .pointerMove(0, 0, {origin: parent})
+ .pointerDown()
+ .pointerUp()
+ .pointerMove(0, 0, {origin: done})
+ .pointerDown()
+ .pointerUp()
+
+ await actions.send();
+ await done_click_promise;
+
+ assert_equals(event_log.toString(), expected_events.toString(),
+ "received events");
+ }, test_name);
+ }
+
+ setup();
+ addPromiseTest("pointerdown", "pointer");
+ addPromiseTest("pointerdown", "mouse");
+ addPromiseTest("pointerup", "pointer");
+ addPromiseTest("pointerup", "mouse");
+</script>