pointerevent_pointerrawupdate_changes_pointer_capture.https.html (11938B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="timeout" content="long"> 6 <meta name="viewport" content="width=device-width, initial-scale:1, user-scalable=no"> 7 <title>Test for handling of "fire a pointer event named pointerrawupdate"</title> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script src="/resources/testdriver.js"></script> 11 <script src="/resources/testdriver-actions.js"></script> 12 <script src="/resources/testdriver-vendor.js"></script> 13 <style> 14 div#initPosition { 15 height: 1em; 16 margin: 50px; 17 } 18 div#parent, div#child { 19 width: 200px; 20 height: 200px; 21 margin: 0px; 22 padding: 0; 23 } 24 </style> 25 <script> 26 "use strict"; 27 28 /** 29 * `pointerrawupdate` is defined as: 30 * > The user agent MUST fire a pointer event named pointerrawupdate, and only 31 * > do so within a secure context, when a pointer changes any properties that 32 * > don't fire pointerdown or pointerup events. 33 * 34 * The following `pointermove` event is also defined as: 35 * > The user agent MUST fire a pointer event named pointermove 36 * 37 * So, when a set of `pointerrawupdate` and `pointermove` is dispatched, the 38 * "fire a pointer event" runs twice. 39 * 40 * "fire a pointer event" defines: 41 * > If the event is not a gotpointercapture, lostpointercapture, click, 42 * > auxclick or contextmenu event, run the process pending pointer capture 43 * > steps for this PointerEvent. 44 * 45 * And also the section defines: 46 * > Determine the target at which the event is fired as follows: 47 * > - If the pointer capture target override has been set for the pointer, set 48 * > the target to pointer capture target override object. 49 * > - Otherwise, set the target to the object returned by normal hit test 50 * > mechanisms (out of scope for this specification). 51 * 52 * So, dispatching `pointerrawupdate` should fix the pointer capture override 53 * and cause dispatching `gotpointercapture` and/or `lostpointercapture` and 54 * `pointermove` event should be retarget to the new pointer capture override. 55 */ 56 57 addEventListener("load", () => { 58 const ticksToPreventCoalescedPointerMove = 300; 59 const initDiv = document.getElementById("initPosition"); 60 const parent = document.getElementById("parent"); 61 const child = document.getElementById("child"); 62 63 let events; 64 function logEvent(event) { 65 events.push({type: event.type, target: event.target}); 66 } 67 function stringifyEvents(arrayOfEvents) { 68 function stringifyEvent(event) { 69 return `${event.type}@${event.target.localName}${ 70 event.target.id ? `#${event.target.id}` : "" 71 }`; 72 } 73 let str = ""; 74 for (const event of arrayOfEvents) { 75 if (str) { 76 str += ", "; 77 } 78 str += stringifyEvent(event); 79 } 80 return str; 81 } 82 for (const type of ["pointerdown", "pointerup", 83 "pointerrawupdate", "pointermove", 84 "gotpointercapture", "lostpointercapture"]) { 85 parent.addEventListener(type, logEvent, {capture: true}); 86 } 87 88 promise_test(async t => { 89 events = []; 90 child.addEventListener("pointerdown", pointerDownEvent => { 91 parent.setPointerCapture(pointerDownEvent.pointerId); 92 parent.addEventListener("pointerrawupdate", pointerRawUpdateEvent => { 93 parent.releasePointerCapture(pointerRawUpdateEvent.pointerId); 94 }, {once: true}); 95 }, {once: true}); 96 await new test_driver.Actions() 97 .pointerMove(0, 0, {origin: initDiv}) 98 .pause(ticksToPreventCoalescedPointerMove) 99 .pointerMove(0, 0, {origin: child}) 100 .pointerDown() 101 .pointerMove(1, 1, {origin: child}) 102 .pointerUp() 103 .pointerMove(0, 0, {origin: initDiv}) 104 .send(); 105 assert_equals( 106 stringifyEvents(events), 107 stringifyEvents([ 108 {type: "pointerrawupdate", target: child}, 109 {type: "pointermove", target: child}, 110 {type: "pointerdown", target: child}, // set pending pointer capture to parent 111 // The following `pointerrawupdate` event dispatching runs the 112 // "process pending pointer capture" steps. 113 {type: "gotpointercapture", target: parent}, 114 {type: "pointerrawupdate", target: parent}, // set pending pointer capture to null 115 // The following `pointermove` event dispatching runs the 116 // "process pending pointer capture" steps again. 117 {type: "lostpointercapture", target: parent}, 118 {type: "pointermove", target: child}, 119 {type: "pointerup", target: child}, 120 ]) 121 ); 122 }, "Setting pointer capture at `pointerdown` and releasing pointer capture at `pointerrawupdate`"); 123 124 promise_test(async t => { 125 events = []; 126 child.addEventListener("pointerdown", () => { 127 parent.addEventListener("pointerrawupdate", pointerRawUpdateEvent => { 128 parent.setPointerCapture(pointerRawUpdateEvent.pointerId); 129 }, {once: true}); 130 }, {once: true}); 131 await new test_driver.Actions() 132 .pointerMove(0, 0, {origin: initDiv}) 133 .pause(ticksToPreventCoalescedPointerMove) 134 .pointerMove(0, 0, {origin: child}) 135 .pointerDown() 136 .pointerMove(1, 1, {origin: child}) 137 .pointerUp() 138 .pointerMove(0, 0, {origin: initDiv}) 139 .send(); 140 assert_equals( 141 stringifyEvents(events), 142 stringifyEvents([ 143 {type: "pointerrawupdate", target: child}, 144 {type: "pointermove", target: child}, 145 {type: "pointerdown", target: child}, 146 {type: "pointerrawupdate", target: child}, // set pending pointer capture to parent 147 // The following `pointermove` event dispatching runs the 148 // "process pending pointer capture" steps. 149 {type: "gotpointercapture", target: parent}, 150 {type: "pointermove", target: parent}, 151 {type: "pointerup", target: parent}, 152 {type: "lostpointercapture", target: parent}, 153 ]) 154 ); 155 }, "Setting pointer capture at `pointerrawupdate`"); 156 157 promise_test(async t => { 158 events = []; 159 child.addEventListener("pointerdown", pointerDownEvent => { 160 parent.setPointerCapture(pointerDownEvent.pointerId); 161 parent.addEventListener("gotpointercapture", gotPointerCaptureEvent => { 162 parent.releasePointerCapture(gotPointerCaptureEvent.pointerId); 163 }, {once: true}); 164 }, {once: true}); 165 await new test_driver.Actions() 166 .pointerMove(0, 0, {origin: initDiv}) 167 .pause(ticksToPreventCoalescedPointerMove) 168 .pointerMove(0, 0, {origin: child}) 169 .pointerDown() 170 .pointerMove(1, 1, {origin: child}) 171 .pointerUp() 172 .pointerMove(0, 0, {origin: initDiv}) 173 .send(); 174 assert_equals( 175 stringifyEvents(events), 176 stringifyEvents([ 177 {type: "pointerrawupdate", target: child}, 178 {type: "pointermove", target: child}, 179 {type: "pointerdown", target: child}, // set pending pointer capture to parent 180 // The following `pointerrawupdate` event dispatching runs the 181 // "process pending pointer capture" steps. 182 {type: "gotpointercapture", target: parent}, // set pending pointer capture to null 183 {type: "pointerrawupdate", target: parent}, 184 // The following `pointermove` event dispatching runs the 185 // "process pending pointer capture" steps again. 186 {type: "lostpointercapture", target: parent}, 187 {type: "pointermove", target: child}, 188 {type: "pointerup", target: child}, 189 ]) 190 ); 191 }, "Setting pointer capture at `pointerdown` and releasing pointer capture at `gotpointercapture`"); 192 193 promise_test(async t => { 194 events = []; 195 child.addEventListener("pointerdown", pointerDownEvent => { 196 parent.setPointerCapture(pointerDownEvent.pointerId); 197 parent.addEventListener("pointermove", pointerMoveEvent => { 198 parent.releasePointerCapture(pointerMoveEvent.pointerId); 199 parent.addEventListener("lostpointercapture", lostPointerCaptureEvent => { 200 parent.setPointerCapture(lostPointerCaptureEvent.pointerId); 201 }, {once: true}); 202 }, {once: true}); 203 }, {once: true}); 204 await new test_driver.Actions() 205 .pointerMove(0, 0, {origin: initDiv}) 206 .pause(ticksToPreventCoalescedPointerMove) 207 .pointerMove(0, 0, {origin: child}) 208 .pointerDown() 209 .pointerMove(1, 1, {origin: child}) 210 .pause(ticksToPreventCoalescedPointerMove) 211 .pointerMove(2, 2, {origin: child}) 212 .pointerUp() 213 .pointerMove(0, 0, {origin: initDiv}) 214 .send(); 215 assert_equals( 216 stringifyEvents(events), 217 stringifyEvents([ 218 {type: "pointerrawupdate", target: child}, 219 {type: "pointermove", target: child}, 220 {type: "pointerdown", target: child}, // set pending pointer capture to parent 221 // The following `pointerrawupdate` event dispatching runs the 222 // "process pending pointer capture" steps. 223 {type: "gotpointercapture", target: parent}, 224 {type: "pointerrawupdate", target: parent}, 225 {type: "pointermove", target: parent}, // set pending pointer capture to null 226 // The following `pointerrawupdate` event dispatching runs the 227 // "process pending pointer capture" steps again. 228 {type: "lostpointercapture", target: parent}, // set pending pointer capture to parent again 229 {type: "pointerrawupdate", target: child}, 230 // The following `pointermove` event dispatching runs the 231 // "process pending pointer capture" steps again. 232 {type: "gotpointercapture", target: parent}, 233 {type: "pointermove", target: parent}, 234 {type: "pointerup", target: parent}, 235 {type: "lostpointercapture", target: parent}, 236 ]) 237 ); 238 }, "Setting pointer capture at `lostpointercapture`"); 239 240 promise_test(async () => { 241 parent.removeEventListener("pointerrawupdate", logEvent, {capture: true}); 242 // Now, there is no `pointerrawupdate` event listener. So, browsers should 243 // not dispatch `pointerrawupdate` event and the "fire a pointer event" steps 244 // including the "process pending pointer capture" steps should not run twice 245 // per `pointermove`. 246 assert_true(true, "There is no `pointerrawupdate` event listener anymore"); 247 }); 248 249 promise_test(async t => { 250 events = []; 251 child.addEventListener("pointerdown", pointerDownEvent => { 252 parent.setPointerCapture(pointerDownEvent.pointerId); 253 parent.addEventListener("gotpointercapture", gotPointerCaptureEvent => { 254 parent.releasePointerCapture(gotPointerCaptureEvent.pointerId); 255 }, {once: true}); 256 }, {once: true}); 257 await new test_driver.Actions() 258 .pointerMove(0, 0, {origin: initDiv}) 259 .pause(ticksToPreventCoalescedPointerMove) 260 .pointerMove(0, 0, {origin: child}) 261 .pointerDown() 262 .pointerMove(1, 1, {origin: child}) 263 .pause(ticksToPreventCoalescedPointerMove) 264 .pointerMove(2, 2, {origin: child}) 265 .pointerUp() 266 .pointerMove(0, 0, {origin: initDiv}) 267 .send(); 268 assert_equals( 269 stringifyEvents(events), 270 stringifyEvents([ 271 {type: "pointermove", target: child}, 272 {type: "pointerdown", target: child}, // set pending pointer capture to parent 273 // The following `pointermove` event dispatching runs the 274 // "process pending pointer capture" steps. 275 {type: "gotpointercapture", target: parent}, // set pending pointer capture to null 276 {type: "pointermove", target: parent}, 277 // The following `pointermove` event dispatching runs the 278 // "process pending pointer capture" steps again. 279 {type: "lostpointercapture", target: parent}, 280 {type: "pointermove", target: child}, 281 {type: "pointerup", target: child}, 282 ]) 283 ); 284 }, "Setting pointer capture at `pointerdown` and releasing pointer capture at `gotpointercapture` when no `pointerrawupdate` event listener"); 285 }, {once: true}); 286 </script> 287 </head> 288 <body> 289 <div id="initPosition"></div> 290 <div id="parent"> 291 <div id="child"> 292 </div> 293 </div> 294 </body> 295 </html>