| <!DOCTYPE html> |
| <html> |
| <head> |
| <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> |
| <style> |
| button#foreground, |
| button#background { |
| position: absolute; |
| top: 75px; |
| left: 50px; |
| width: 100px; |
| height: 20px; |
| } |
| |
| button#background { |
| left: 75px; |
| width: 150px; |
| height: 40px; |
| top: 125px; |
| line-height: 53px; |
| } |
| |
| .clicked { |
| background-color: red; |
| } |
| |
| #ancestorContainer.clicked { |
| background-color: green; |
| } |
| |
| #ancestorContainer { |
| position: relative; |
| width: 300px; |
| height: 300px; |
| background-color: blue; |
| } |
| |
| #displacedAncestor { |
| position: absolute; |
| top: 13px; |
| left: 240px; |
| width: 300px; |
| height: 250px; |
| background-color: #ff89; |
| } |
| |
| #displacedAncestor.clicked { |
| background-color: #f009; |
| } |
| |
| #inertContainer { |
| background-color: #fff9; |
| position: absolute; |
| top: 35px; |
| left: -192px; |
| width: 200px; |
| height: 200px; |
| } |
| |
| fieldset { |
| margin: 0; |
| padding: 0; |
| border: 1px solid black; |
| } |
| |
| legend { |
| background-color: white; |
| border: 1px solid black; |
| margin-left: 5px; |
| } |
| |
| button.clicked::after { |
| content: " (clicked)"; |
| } |
| |
| .clicked > legend::after { |
| content: " (clicked)"; |
| } |
| |
| </style> |
| </head> |
| <body> |
| <p>Click on "foreground".</p> |
| <ul> |
| <li>The blue square ("Non-inert ancestor container") should turn green.</li> |
| <li>The yellow, semi-transparent square ("Non-inert, displaced container") should not turn red.</li> |
| <li>"Non-inert button" should not turn red.</li> |
| </ul> |
| <p>(The full test suite checks a range of events.)</p> |
| <fieldset id="ancestorContainer"> |
| <legend>Non-inert ancestor container</legend> |
| <button id="background">background</button> |
| <fieldset id="displacedAncestor"> |
| <legend>Non-inert, displaced ancestor</legend> |
| <fieldset id="inertContainer" inert> |
| <legend>Inert container</legend> |
| <button id="foreground">foreground</button> |
| </fieldset> |
| </fieldset> |
| </fieldset> |
| |
| <script> |
| document.body.addEventListener('click', (e) => { |
| e.target.classList.add('clicked'); |
| }); |
| |
| function clickOn(element) { |
| return new test_driver.Actions() |
| .pointerMove(0, 0, {origin: element}) |
| .pointerDown({button: test_driver.Actions.prototype.ButtonType.LEFT}) |
| .pointerUp({button: test_driver.Actions.prototype.ButtonType.LEFT}) |
| .send(); |
| } |
| |
| function auxClickOn(element) { |
| return new test_driver.Actions() |
| .pointerMove(0, 0, {origin: element}) |
| .pointerDown({button: test_driver.Actions.prototype.ButtonType.RIGHT}) |
| .pointerUp({button: test_driver.Actions.prototype.ButtonType.RIGHT}) |
| .send(); |
| } |
| |
| function dblClickOn(element) { |
| return new test_driver.Actions() |
| .pointerMove(0, 0, {origin: element}) |
| .pointerDown({button: test_driver.Actions.prototype.ButtonType.LEFT}) |
| .pointerUp({button: test_driver.Actions.prototype.ButtonType.LEFT}) |
| .pointerDown({button: test_driver.Actions.prototype.ButtonType.LEFT}) |
| .pointerUp({button: test_driver.Actions.prototype.ButtonType.LEFT}) |
| .send(); |
| } |
| |
| function movePointerOver(element) { |
| let rect = element.getBoundingClientRect(); |
| return new test_driver.Actions() |
| .pointerMove(0, 0, { origin: element }) |
| .send(); |
| } |
| |
| function movePointerTo(x, y) { |
| return new test_driver.Actions() |
| .pointerMove(x, y, { origin: "viewport" }) |
| .send(); |
| } |
| |
| function expectEventsOn(events, element) { |
| let promises = []; |
| for (event of events) { |
| ((event, element) => { |
| var promise = new Promise((resolve, reject) => { |
| let f = (e) => { |
| assert_equals(e.type, event); |
| assert_equals(e.target, element); |
| resolve(); |
| } |
| element.addEventListener(event, f, { capture: true, once: true }); |
| |
| step_timeout(() => { |
| element.removeEventListener(event, f, { capture: true }); |
| reject("did not get " + event + " on " + element.id); |
| }, 1000); |
| }); |
| promises.push(promise); |
| })(event, element); |
| } |
| return promises; |
| } |
| |
| function unexpectEventsOn(events, element) { |
| let promises = []; |
| for (event of events) { |
| ((event, element) => { |
| var promise = new Promise((resolve, reject) => { |
| let f = (e) => { |
| assert_equals(e.type, event); |
| assert_equals(e.target, element); |
| reject("got " + e.type + " on " + e.target.id); |
| } |
| element.addEventListener(event, f, { capture: true, once: true }); |
| |
| step_timeout(() => { |
| element.removeEventListener(event, f, { capture: true }); |
| resolve(); |
| }, 1000); |
| }); |
| promises.push(promise); |
| })(event, element); |
| } |
| return promises; |
| } |
| |
| test(() => { |
| let rect = foreground.getBoundingClientRect(); |
| let center_x = rect.left + (rect.width / 2); |
| let center_y = rect.top + (rect.height / 2); |
| assert_equals(document.elementsFromPoint(center_x, center_y)[0], foreground); |
| }, "elementsFromPoint returns inert element"); |
| |
| promise_test(async () => { |
| // Test mouse events on non-inert element - events should go to "foreground" |
| inertContainer.inert = false; |
| await movePointerTo(0, 0); |
| let promises = expectEventsOn(["mouseover", "mouseenter", "mousemove", "mousedown", |
| "mouseup", "click", "auxclick", "mouseout", |
| "mouseleave"], |
| foreground); |
| await clickOn(foreground); |
| await auxClickOn(foreground); |
| await dblClickOn(foreground); |
| let ancestorBox = ancestorContainer.getBoundingClientRect(); |
| let inertBox = inertContainer.getBoundingClientRect(); |
| let x = ancestorBox.left + (ancestorBox.width / 2); |
| let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2); |
| await movePointerTo(x, y); |
| await Promise.all(promises); |
| }, "Tests that any mouse event on a non-inert element is correctly targeted to that element"); |
| |
| promise_test(async () => { |
| // Make the containing element inert - now events should go to "container" |
| // which is the non-inert ancestor at the same position |
| inertContainer.inert = true; |
| await movePointerTo(0, 0); |
| |
| let promises = expectEventsOn(["mouseover", "mouseenter", "mousemove", "mousedown", |
| "mouseup", "click", "auxclick"], |
| ancestorContainer); |
| |
| // TODO(aboxhall): We are getting these unexpected events. Why? |
| promises = promises.concat(unexpectEventsOn(["mouseout", "mouseleave"], |
| ancestorContainer)); |
| |
| await clickOn(foreground); |
| await auxClickOn(foreground); |
| await dblClickOn(foreground); |
| let ancestorBox = ancestorContainer.getBoundingClientRect(); |
| let inertBox = inertContainer.getBoundingClientRect(); |
| let x = ancestorBox.left + (ancestorBox.width / 2); |
| let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2); |
| await movePointerTo(x, y); |
| await Promise.all(promises); |
| }, 'Tests that any mouse event on an inert element is targeted to the nearest non-inert ancestor at the same coordinates'); |
| |
| promise_test(async () => { |
| // Test pointer events on non-inert element - events should go to "foreground" |
| inertContainer.inert = false; |
| await movePointerTo(0, 0); |
| |
| let promises = expectEventsOn(["pointerover", "pointerenter", "pointermove", |
| "pointerdown", "pointerup", "pointerout", |
| "pointerleave"], |
| foreground); |
| await clickOn(foreground); |
| let ancestorBox = ancestorContainer.getBoundingClientRect(); |
| let inertBox = inertContainer.getBoundingClientRect(); |
| let x = ancestorBox.left + (ancestorBox.width / 2); |
| let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2); |
| await movePointerTo(x, y); |
| await Promise.all(promises); |
| }, "Tests that any pointer event on a non-inert element is correctly targeted to that element"); |
| |
| promise_test(async () => { |
| // Make the containing element inert - now events should go to "container" |
| // which is the non-inert ancestor at the same position |
| inertContainer.inert = true; |
| await movePointerTo(0, 0); |
| |
| let promises = expectEventsOn(["pointerover", "pointerenter", "pointermove", |
| "pointerdown", "pointerup" ], |
| ancestorContainer); |
| |
| // TODO(aboxhall): We are getting these unexpected events. Why? |
| promises = promises.concat(unexpectEventsOn(["pointerout", "pointerleave"], |
| ancestorContainer)); |
| |
| await clickOn(foreground); |
| let ancestorBox = ancestorContainer.getBoundingClientRect(); |
| let inertBox = inertContainer.getBoundingClientRect(); |
| let x = ancestorBox.left + (ancestorBox.width / 2); |
| let y = inertBox.bottom + ((ancestorBox.bottom - inertBox.bottom) / 2); |
| await movePointerTo(x, y); |
| await Promise.all(promises); |
| }, 'Tests that any pointer event on an inert element is targeted to the nearest non-inert ancestor at the same coordinates'); |
| |
| </script> |
| </body> |
| </html> |