| <!doctype html> |
| <html> |
| <head> |
| <title>Lostpointercapture fires on document when target is removed</title> |
| <meta name="viewport" content="width=device-width"> |
| <link rel="stylesheet" type="text/css" href="pointerevent_styles.css"> |
| <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> |
| </head> |
| <body> |
| <h1>Pointer Events - lostpointercapture removes new capture element</h1> |
| <input type="button" id="button" value="Set Capture"><br> |
| <div id="target0"></div> |
| <div id="target1"></div> |
| <script type='text/javascript'> |
| var target0 = document.getElementById('target0'); |
| var target1 = document.getElementById('target1'); |
| var captureButton = document.getElementById('button'); |
| var targets = [target0, target1, captureButton]; |
| const LOG_EVENT_TYPES = ['pointerover', 'pointerenter', 'pointerdown', 'pointermove', 'pointerup', 'pointercancel', 'pointerout', 'pointerleave', 'gotpointercapture', 'lostpointercapture']; |
| promise_test(async (test) => { |
| captureButton.focus(); |
| var events = []; |
| var lastLogMessage = ""; |
| let logEvent = (event) => { |
| let logMessage = `${event.type}@${event.target.id || event.target.tagName || 'document'}`; |
| // Only log a particular event once to avoid coalescing differences. |
| if (logMessage == lastLogMessage) |
| return; |
| lastLogMessage = logMessage; |
| events.push(logMessage); |
| }; |
| document.addEventListener('lostpointercapture', logEvent); |
| for (const target of targets) { |
| for (const eventType of LOG_EVENT_TYPES) { |
| target.addEventListener(eventType, logEvent); |
| } |
| } |
| const nextSibling = target1.nextSibling; |
| test.add_cleanup(() => { |
| document.removeEventListener('lostpointercapture', logEvent); |
| for (const target of targets) { |
| for (const eventType of LOG_EVENT_TYPES) { |
| target.removeEventListener(eventType, logEvent); |
| } |
| } |
| nextSibling.parentNode.insertBefore(target1, nextSibling); |
| }); |
| |
| let finishPromise = Promise.any([ |
| getEvent('pointerup', document.body, test), |
| getEvent('pointerup', target1, test)]); |
| |
| getEvent('pointerdown', captureButton, test).then((event) => { |
| target0.setPointerCapture(event.pointerId); |
| }); |
| // On the first captured move, we'll set capture to target1. |
| getEvent('pointermove', target0, test).then((event) => { |
| target1.setPointerCapture(event.pointerId); |
| }); |
| // But remove the new capture target when we lose capture. |
| getEvent('lostpointercapture', target0, test).then((event) => { |
| target1.remove(); |
| }); |
| getEvent('gotpointercapture', target1, test).then((event) => { |
| assert_unreached("target1 is removed and should never get pointer capture."); |
| }); |
| |
| // Inject mouse inputs. |
| const actions = new test_driver.Actions(); |
| actions_promise = actions |
| .pointerMove(0, 0, {origin: captureButton}) |
| .pointerDown() |
| .pointerMove(10, 0, {origin: captureButton}) |
| .pointerUp() |
| .send(); |
| |
| await finishPromise; |
| |
| assert_equals(events.join(", "), [ |
| // Pointer down on button |
| "pointerover@button", "pointerenter@button", "pointermove@button", "pointerdown@button", |
| // Captured by target0 |
| "pointerout@button", "pointerleave@button", "pointerover@target0", "pointerenter@target0", "gotpointercapture@target0", "pointermove@target0", |
| // Captured by target1, losing capture on target0 which removes target1. |
| "lostpointercapture@target0", "pointerout@target0", "pointerleave@target0", |
| // Uncaptured pointer re-enters button and is lifted. |
| "pointerover@button", "pointerenter@button", "pointermove@button", "pointerup@button" |
| ].join(", ")); |
| }, "setPointerCapture target removed by lostpointercapture"); |
| </script> |
| </body> |
| </html> |