| <!DOCTYPE html> |
| <html> |
| <title>View transitions: ensure input targets document root while rendering is suppressed</title> |
| <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> |
| <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7797"> |
| <link rel="author" href="mailto:bokan@chromium.org"> |
| |
| <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> |
| |
| <div id="testContent"> |
| <style> |
| :root { |
| /* Ensure clicks during the transition fall through the pseudo tree root to |
| * real DOM */ |
| view-transition-name: none; |
| } |
| |
| ::view-transition { |
| /* Ensure clicks during the transition fall through the pseudo tree root to |
| * real DOM */ |
| pointer-events: none; |
| width: 0; |
| height: 0; |
| } |
| |
| ::view-transition-group(*) { |
| animation-duration: 30s; |
| } |
| |
| #clicktarget { |
| width: 100px; |
| height: 100px; |
| background: red; |
| } |
| |
| #transition { |
| width: 100px; |
| height: 100px; |
| background: blue; |
| contain: paint; |
| view-transition-name: transitionElement; |
| } |
| </style> |
| |
| <div id="clicktarget"></div> |
| <div id="transition"></div> |
| </div> |
| |
| <iframe id="subframe"></iframe> |
| |
| <script> |
| |
| // Click at (x, y) in top-level window and wait for click in doc |
| async function sendAndWaitForClick(x, y, doc) { |
| return new Promise(async (resolve) => { |
| |
| function eventHandler(ev) { |
| resolve(ev); |
| } |
| |
| doc.documentElement.addEventListener("click", eventHandler); |
| |
| await new test_driver.Actions() |
| .setContext(window) |
| .addPointer("mousePointer1", "mouse") |
| .pointerMove(x, y, {origin: 'viewport', sourceName: "mousePointer1"}) |
| .pointerDown({sourceName: "mousePointer1"}) |
| .pointerUp({sourceName: "mousePointer1"}) |
| .send(); |
| |
| doc.documentElement.removeEventListener("click", eventHandler); |
| }); |
| } |
| |
| // Test that input targets doc root while doc is render blocked due to VT |
| async function test_input_target_during_transition(x, y, doc) { |
| assert_implements(doc.startViewTransition, "Missing document.startViewTransition"); |
| |
| const target = doc.getElementById("clicktarget"); |
| assert_not_equals(target, null, "PRECONDITION: target element is valid"); |
| |
| // Ensure input is initialized before blocking rendering. |
| await new test_driver.Actions() |
| .setContext(window) |
| .addPointer("mousePointer1", "mouse") |
| .pointerMove(0, 0, {origin: "viewport", sourceName: "mousePointer1"}) |
| .send(); |
| |
| let clickEvent = null; |
| |
| const transition = doc.startViewTransition(async () => { |
| clickEvent = await sendAndWaitForClick(x, y, doc); |
| }); |
| |
| await transition.ready; |
| |
| assert_equals(clickEvent.target, doc.documentElement, |
| "Events must target the document element while render blocked"); |
| clickEvent = null; |
| |
| clickEvent = await sendAndWaitForClick(x, y, doc); |
| |
| // This just ensures we're not passing the above check by accident. |
| assert_equals(clickEvent.target, target, |
| "Events must target real DOM during transition"); |
| } |
| |
| promise_test(async t => { |
| await test_input_target_during_transition(10, 10, document); |
| }, "Input in render-blocked document targets root"); |
| |
| promise_test(async t => { |
| const iframeDoc = subframe.contentDocument; |
| iframeDoc.documentElement.innerHTML = testContent.innerHTML; |
| const rect = subframe.getBoundingClientRect(); |
| await test_input_target_during_transition(rect.left + 10, rect.top + 10, iframeDoc); |
| }, "Input in render-blocked subframe targets subframe root"); |
| </script> |