pointerevent_after_target_removed.html (5769B)
1 <!DOCTYPE HTML> 2 <link rel="help" href="https://w3c.github.io/pointerevents/#firing-events-using-the-pointerevent-interface"> 3 <title>Enter/leave events fired to parent after child is removed</title> 4 <meta name="variant" content="?mouse"> 5 <meta name="variant" content="?touch"> 6 <meta name="variant" content="?pen"> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="/resources/testdriver.js"></script> 10 <script src="/resources/testdriver-actions.js"></script> 11 <script src="/resources/testdriver-vendor.js"></script> 12 <script src="pointerevent_support.js"></script> 13 14 <style> 15 div.target { 16 width: 100px; 17 height: 100px; 18 } 19 </style> 20 <div class="target" id="parent"> 21 <div class="target" id="child">child</div> 22 </div> 23 <div id="done">done</div> 24 25 <script> 26 'use strict'; 27 const pointer_type = location.search.substring(1); 28 29 const parent = document.getElementById("parent"); 30 const child = document.getElementById("child"); 31 const done = document.getElementById("done"); 32 33 let event_log = []; 34 let logged_event_prefix = ""; 35 let received_compat_mouse_events = false; 36 37 function logEvent(e) { 38 if (e.type.startsWith(logged_event_prefix) && e.eventPhase == e.AT_TARGET) { 39 event_log.push(e.type + "@" + e.target.id); 40 } 41 if (e.type.startsWith("mouse")) { 42 received_compat_mouse_events = true; 43 } 44 } 45 46 function removeChild() { 47 child.remove(); 48 event_log.push("(child-removed)"); 49 } 50 51 function setup() { 52 const logged_event_suffixes = 53 ["over", "out", "enter", "leave", "down", "up"]; 54 let targets = document.getElementsByClassName("target"); 55 for (let i = 0; i < targets.length; i++) { 56 logged_event_suffixes.forEach(suffix => { 57 targets[i].addEventListener("pointer" + suffix, logEvent); 58 targets[i].addEventListener("mouse" + suffix, logEvent); 59 }); 60 } 61 } 62 63 function addPromiseTest(remover_event, tested_event_prefix, expected_events) { 64 const test_name = `${tested_event_prefix} events from ${pointer_type} `+ 65 `received before/after child removal at ${remover_event}`; 66 67 promise_test(async test => { 68 event_log = []; 69 logged_event_prefix = tested_event_prefix; 70 71 child.addEventListener(remover_event, removeChild); 72 test.add_cleanup(() => { 73 child.removeEventListener(remover_event, removeChild); 74 if (!child.parentElement) { 75 parent.appendChild(child); 76 } 77 }); 78 79 let done_click_promise = getEvent("click", done); 80 81 let actions = new test_driver.Actions() 82 .addPointer("TestPointer", pointer_type) 83 .pointerMove(-30, -30, {origin: parent}) 84 .pointerDown() 85 .pointerUp() 86 .pointerMove(30, 30, {origin: parent}) 87 .pointerDown() 88 .pointerUp() 89 .pointerMove(0, 0, {origin: done}) 90 .pointerDown() 91 .pointerUp(); 92 93 await actions.send(); 94 await done_click_promise; 95 96 if (tested_event_prefix == "mouse" && !received_compat_mouse_events) { 97 expected_events = []; 98 } 99 100 assert_equals(event_log.toString(), expected_events.toString(), 101 "events received"); 102 }, test_name); 103 } 104 105 setup(); 106 107 const hoverable = pointer_type != "touch"; 108 109 // Tests for dispatched pointer events. 110 addPromiseTest( 111 "pointerdown", 112 "pointer", 113 hoverable 114 ? ["pointerover@child", "pointerenter@parent", "pointerenter@child", 115 "pointerdown@child", "(child-removed)", "pointerover@parent", "pointerup@parent", 116 "pointerdown@parent", "pointerup@parent", 117 "pointerout@parent", "pointerleave@parent"] 118 : ["pointerover@child", "pointerenter@parent", "pointerenter@child", "pointerdown@child", 119 "(child-removed)", "pointerover@parent", 120 // pointerup should cause pointerout/pointerleave if the input source is not hoverable. 121 "pointerup@parent", "pointerout@parent", "pointerleave@parent", 122 // then, pointerdown should imply a pointermove again. 123 "pointerover@parent", "pointerenter@parent", "pointerdown@parent", 124 "pointerup@parent", "pointerout@parent", "pointerleave@parent"] 125 ); 126 addPromiseTest( 127 "pointerup", 128 "pointer", 129 hoverable 130 ? ["pointerover@child", "pointerenter@parent", "pointerenter@child", 131 "pointerdown@child", "pointerup@child", "(child-removed)", 132 "pointerover@parent", "pointerdown@parent", "pointerup@parent", 133 "pointerout@parent", "pointerleave@parent"] 134 : ["pointerover@child", "pointerenter@parent", "pointerenter@child", 135 "pointerdown@child", "pointerup@child", "(child-removed)", 136 // only pointerleave should be fired if the input source is not hoverable 137 // because pointerup removed the pointerout event target which is not 138 // received pointerover event, but the pointer becomes invalid. 139 "pointerleave@parent", 140 // then, pointerdown should imply a pointermove again. 141 "pointerover@parent", "pointerenter@parent", "pointerdown@parent", "pointerup@parent", 142 "pointerout@parent", "pointerleave@parent"] 143 ); 144 145 // Same tests for dispatched compatibility mouse events. 146 addPromiseTest("mousedown", "mouse", [ 147 "mouseover@child", "mouseenter@parent", "mouseenter@child", 148 "mousedown@child", "(child-removed)", "mouseover@parent", "mouseup@parent", 149 "mousedown@parent", "mouseup@parent", 150 "mouseout@parent", "mouseleave@parent" 151 ]); 152 addPromiseTest("mouseup", "mouse", [ 153 "mouseover@child", "mouseenter@parent", "mouseenter@child", 154 "mousedown@child", "mouseup@child", "(child-removed)", 155 "mouseover@parent", "mousedown@parent", "mouseup@parent", 156 "mouseout@parent", "mouseleave@parent" 157 ]); 158 </script>