pointerevent_pointercapture_in_frame.html (15251B)
1 <meta name="variant" content="?mouse"> 2 <meta name="variant" content="?touch"> 3 <meta name="variant" content="?pen"> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="/resources/testdriver.js"></script> 7 <script src="/resources/testdriver-vendor.js"></script> 8 <script src="/resources/testdriver-actions.js"></script> 9 <script type="text/javascript" src="pointerevent_support.js"></script> 10 <style> 11 iframe { 12 width: 300px; 13 height: 300px; 14 top: 100px; 15 left: 100px; 16 border: 0; 17 position: absolute; 18 background: green; 19 } 20 #outerFrame { 21 width: 500px; 22 height: 500px; 23 background: blue; 24 } 25 body { 26 touch-action:none; 27 } 28 </style> 29 <body id="outerFrame body" onload="run()"> 30 <div id='outerFrame'> 31 <iframe src="resources/pointerevent_pointercapture-iframe.html"></iframe> 32 </div> 33 </body> 34 <script> 35 var receivedEventList = []; 36 // |start_logging| is set to true in the pointerdown event handler, and as such 37 // also tracks if pointerdown happened already in the test. 38 var start_logging = false; 39 var first_pointermove_happened = false; 40 41 // Add a timeout promise when waiting for lostpointercapture and pointerup. 42 // We add this to make it easy to know which event wasn't fired on the right 43 // element. 44 var eventTimeout = ()=>(()=>new Promise((resolve, reject)=>{ 45 const msToWait = 4000; 46 let start; 47 function wait(timestamp){ 48 if(start === undefined) 49 start = timestamp; 50 if(timestamp - start < msToWait) 51 requestAnimationFrame(wait); 52 else 53 resolve(); 54 } 55 requestAnimationFrame(wait); 56 })); 57 58 59 function handleEvent(event) { 60 if (event.type == 'pointerdown') { 61 start_logging = true; 62 const capture_target = document.captureTargetOverride || event.target; 63 try { 64 capture_target.setPointerCapture(event.pointerId); 65 } catch (error) { 66 // The promise_tests below look into logged events to assert capture 67 // failures without logging the exception here. 68 // 69 // Logging the exception here is not interoperable because the pointer 70 // capture algorithm [1] may fail silently at Step 5 instead of throwing 71 // at Step 1 in certain cases related to optional requirements for 72 // `event.pointerId`. Below are two such cases: 73 // - The user agent has a reserved constant pointerId for pointerType 74 // "mouse". 75 // - The user agent exposes the same pointerId for a pointer dragged 76 // across a same-origin iframe boundary. 77 // 78 // [1] https://w3c.github.io/pointerevents/#setting-pointer-capture 79 } 80 } 81 82 // Only log the first pointermove event after pointerdown. We need to account 83 // for coalesced pointermove events and for the pointermove events that 84 // happen after lostpointercapture. 85 if (start_logging && (event.type !== "pointermove" || !first_pointermove_happened)) 86 receivedEventList.push(event.target.id + ' received ' + event.type); 87 88 if (event.type == "pointermove"){ 89 if (document.releasePointerCaptureOnFirstMove && event.target.hasPointerCapture(event.pointerId)) 90 event.target.releasePointerCapture(event.pointerId); 91 if(start_logging) 92 first_pointermove_happened = true; 93 } 94 }; 95 96 document.testEventList = ['pointerup', 'pointerdown', 'pointermove', 'gotpointercapture', 'lostpointercapture']; 97 document.testEventList.forEach(function(eventName) { 98 document.getElementById('outerFrame').addEventListener(eventName, handleEvent); 99 }); 100 101 function Reset() { 102 document.captureTargetOverride = null; 103 document.releasePointerCaptureOnFirstMove = false; 104 receivedEventList = []; 105 start_logging = false; 106 first_pointermove_happened = false; 107 } 108 109 function run() { 110 Reset(); 111 var pointerType = location.search.substring(1); 112 113 promise_test (async(t) => { 114 t.add_cleanup(Reset); 115 expectedEventList = ["innerFrame received pointerdown", 116 "innerFrame received gotpointercapture", 117 "innerFrame received pointermove", 118 "innerFrame received pointerup", 119 "innerFrame received lostpointercapture"]; 120 var pointerId = pointerType + "Pointer1"; 121 122 var innerFrameDocument = frames[0].document; 123 // We are interested in tracking events only after pointerdown 124 var pointerdown_happened = new Promise((resolve, reject)=>{innerFrameDocument.addEventListener("pointerdown",resolve);}); 125 var watcher_promise = pointerdown_happened.then(()=>{ 126 var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["lostpointercapture"], eventTimeout()); 127 return watch_inner_frame.wait_for(["lostpointercapture"]); 128 }); 129 130 await new test_driver.Actions() 131 .addPointer(pointerId, pointerType) 132 .pointerMove(200, 200) 133 .pointerDown() 134 .pointerMove(150, 150) 135 .pointerMove(50, 50) 136 .pointerUp() 137 .send(); 138 // Wait for lostpointercapture to fire. 139 await watcher_promise; 140 assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList); 141 }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at inner frame and set pointer capture."); 142 143 promise_test (async(t) => { 144 t.add_cleanup(Reset); 145 document.captureTargetOverride = document.getElementById('outerFrame'); 146 expectedEventList = ["innerFrame received pointerdown", 147 "innerFrame received pointermove", 148 "innerFrame received pointerup"]; 149 var pointerId = pointerType + "Pointer1"; 150 151 var innerFrameDocument = frames[0].document; 152 // We are interested in tracking events only after pointerdown 153 var pointerdown_happened = new Promise((resolve, reject)=>{innerFrameDocument.addEventListener("pointerdown",resolve);}); 154 155 var watcher_promise = pointerdown_happened.then(()=>{ 156 var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["pointerup"], eventTimeout()); 157 return watch_inner_frame.wait_for(["pointerup"]); 158 }); 159 160 await new test_driver.Actions() 161 .addPointer(pointerId, pointerType) 162 .pointerMove(200, 200) 163 .pointerDown() 164 .pointerMove(150, 150) 165 .pointerUp() 166 .send(); 167 // Interestingly, a drag out of the iframe behaves consitently between 168 // Chrome, Firefox and Safari even though this is not spec-ed: the 169 // `pointerup` always goes to innerFrameDocument. 170 171 // Wait for pointerup to fire. 172 await watcher_promise; 173 assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList); 174 }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at inner frame and set pointer capture to outer frame should not capture."); 175 176 promise_test (async(t) => { 177 t.add_cleanup(Reset); 178 expectedEventList = ["outerFrame received pointerdown", 179 "outerFrame received gotpointercapture", 180 "outerFrame received pointermove", 181 "outerFrame received pointerup", 182 "outerFrame received lostpointercapture"]; 183 var pointerId = pointerType + "Pointer1"; 184 185 // We are interested in tracking events only after pointerdown 186 var pointerdown_happened = new Promise((resolve, reject)=>{document.getElementById('outerFrame').addEventListener("pointerdown",resolve);}); 187 var watcher_promise = pointerdown_happened.then(()=>{ 188 // For this test we're going to wait for both pointerup and lostpointercapture, 189 // as pointerup fires on innerFrame on Windows (see crbug.com/1186788) 190 var watch_outer_frame_for_pointerup_and_lostpointercapture = 191 new EventWatcher(t, document.getElementById('outerFrame'), ["pointerup", "lostpointercapture"], eventTimeout()); 192 return watch_outer_frame_for_pointerup_and_lostpointercapture.wait_for(["pointerup", "lostpointercapture"]); 193 }); 194 195 await new test_driver.Actions() 196 .addPointer(pointerId, pointerType) 197 .pointerMove(25, 25) 198 .pointerDown() 199 .pointerMove(200, 200) 200 .pointerUp() 201 .send(); 202 // Wait for pointerup or lostpointercapture to fire. 203 await watcher_promise; 204 assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList); 205 }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at outer frame body and set pointer capture."); 206 207 promise_test (async(t) => { 208 t.add_cleanup(Reset); 209 document.captureTargetOverride = frames[0].document.body; 210 expectedEventList = ["outerFrame received pointerdown", 211 "outerFrame received pointermove", 212 "outerFrame received pointerup"]; 213 var pointerId = pointerType + "Pointer1"; 214 215 // We are interested in tracking events only after pointerdown 216 var pointerdown_happened = new Promise((resolve, reject)=>{document.getElementById('outerFrame').addEventListener("pointerdown",resolve);}); 217 var watcher_promise = pointerdown_happened.then(()=>{ 218 var watch_outer_frame = new EventWatcher(t, document.getElementById('outerFrame'), ["pointerup"], eventTimeout()); 219 return watch_outer_frame.wait_for("pointerup"); 220 }); 221 222 await new test_driver.Actions() 223 .addPointer(pointerId, pointerType) 224 .pointerMove(25, 25) 225 .pointerDown() 226 .pointerMove(75, 75) 227 .pointerUp() 228 .send(); 229 // Wait for pointerup to fire. 230 await watcher_promise; 231 assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList); 232 }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at outer frame body and set pointer capture in inner frame should not capture."); 233 234 promise_test (async(t) => { 235 t.add_cleanup(Reset); 236 document.releasePointerCaptureOnFirstMove = true; 237 // Mouse event has the frame capture, so after pointer capture released, events are 238 // dispatched to innerFrameDocument. 239 expectedEventList = ["innerFrame received pointerdown", 240 "innerFrame received gotpointercapture", 241 "innerFrame received pointermove", 242 "innerFrame received lostpointercapture", 243 (pointerType == "touch" ? "outerFrame": "innerFrameDocument") + " received pointerup",]; 244 var pointerId = pointerType + "Pointer1"; 245 246 var innerFrameDocument = frames[0].document; 247 // We are interested in tracking events only after pointerdown 248 var pointerdown_happened = new Promise((resolve, reject)=>{innerFrameDocument.addEventListener("pointerdown",resolve);}); 249 var watcher_promise = pointerdown_happened.then(()=>{ 250 if(pointerType === "touch"){ 251 var watch_outer_frame = new EventWatcher(t, document.getElementById('outerFrame'), ["pointerup"], eventTimeout()); 252 return watch_outer_frame.wait_for("pointerup"); 253 }else{ 254 var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["pointerup"], eventTimeout()); 255 return watch_inner_frame.wait_for("pointerup"); 256 } 257 }); 258 259 await new test_driver.Actions() 260 .addPointer(pointerId, pointerType) 261 .pointerMove(200, 200) 262 .pointerDown() 263 .pointerMove(150, 150) 264 .pointerMove(50, 50) 265 .pointerUp() 266 .send(); 267 // Wait for pointerup to fire. 268 await watcher_promise; 269 assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList); 270 document.releasePointerCaptureOnFirstMove = false; 271 }, "Test " + pointerType + "pointer capture in same-origin frame: Pointerdown with set capture at inner frame, then release on next pointermove."); 272 273 promise_test (async(t) => { 274 t.add_cleanup(Reset); 275 document.releasePointerCaptureOnFirstMove = true; 276 expectedEventList = ["outerFrame received pointerdown", 277 "outerFrame received gotpointercapture", 278 "outerFrame received pointermove", 279 "outerFrame received lostpointercapture", 280 "innerFrame received pointerup"]; 281 var pointerId = pointerType + "Pointer1"; 282 283 var innerFrameDocument = frames[0].document; 284 // We are interested in tracking events only after pointerdown 285 var pointerdown_happened = new Promise((resolve, reject)=>{document.getElementById('outerFrame').addEventListener("pointerdown",resolve);}); 286 var watcher_promise = pointerdown_happened.then(()=>{ 287 var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["pointerup"], eventTimeout()); 288 return watch_inner_frame.wait_for(["pointerup"]); 289 }); 290 291 await new test_driver.Actions() 292 .addPointer(pointerId, pointerType) 293 .pointerMove(50, 50) 294 .pointerDown() 295 .pointerMove(200, 200) 296 // Pause here to make sure that the previous and following pointer moves 297 // are not coalesced. If they are coalesced, we will not see the second 298 // move event which is when the pending lostpointercapture should be fired 299 // (Since the pointerup event is targeted at a different frame, it won't dispatch 300 // the pending lostpointercapture event). 301 .pause(300) 302 .pointerMove(250, 250) 303 .pointerUp() 304 .send(); 305 // Wait for pointerup to fire. 306 await watcher_promise; 307 assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList); 308 document.releasePointerCaptureOnFirstMove = false; 309 }, "Test " + pointerType + "pointer capture in same-origin frame: Pointerdown with set capture at outer frame, then release on next pointermove."); 310 } 311 </script> 312 313 </body>