diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/inert | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/inert')
19 files changed, 1251 insertions, 0 deletions
diff --git a/testing/web-platform/tests/inert/dynamic-inert-on-focused-element.html b/testing/web-platform/tests/inert/dynamic-inert-on-focused-element.html new file mode 100644 index 0000000000..0ddf5a995a --- /dev/null +++ b/testing/web-platform/tests/inert/dynamic-inert-on-focused-element.html @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title>Dynamic inertness on focused element</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focus-fixup-rule"> +<meta name="assert" content="If a focused element becomes inert, it stops being focused."> +<div id="log"></div> + +<div class="test-wrapper" data-name="<input> that gets 'inert' attribute"> + <input class="becomes-inert check-focus"> +</div> + +<div class="test-wrapper" data-name="<input> whose parent gets 'inert' attribute"> + <div class="becomes-inert"> + <input class="check-focus"> + </div> +</div> + +<div class="test-wrapper" data-name="<button> that gets 'inert' attribute"> + <button class="becomes-inert check-focus">foo</button> +</div> + +<div class="test-wrapper" data-name="<div> that gets 'inert' attribute"> + <div class="becomes-inert check-focus" tabindex="-1"></div> +</div> + +<div class="test-wrapper" data-name="<div> whose parent gets 'inert' attribute"> + <div class="becomes-inert"> + <div class="check-focus" tabindex="-1">bar</div> + </div> +</div> + +<div class="test-wrapper" data-name="<div> whose grandparent gets 'inert' attribute"> + <div class="becomes-inert"> + <p> + <span class="check-focus" tabindex="-1">baz</span> + </p> + </div> +</div> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +function nextRepaint() { + return new Promise(resolve => { + requestAnimationFrame(() => { + requestAnimationFrame(resolve); + }); + }); +} + +const loaded = new Promise(resolve => { + addEventListener("load", resolve, {once: true}); +}); +promise_setup(() => loaded); + +for (const testWrapper of document.querySelectorAll(".test-wrapper")) { + const becomesInert = testWrapper.querySelector(".becomes-inert"); + const checkFocus = testWrapper.querySelector(".check-focus"); + promise_test(async function() { + checkFocus.focus(); + assert_equals(document.activeElement, checkFocus, "The element is focused"); + + becomesInert.inert = true; + + // The focus fixup rule should blur the element since it's no longer focusable. + // In Chromium this happens after a repaint (https://crbug.com/1275097). + await nextRepaint(); + assert_equals(document.activeElement, document.body, "The element stops being focused"); + }, testWrapper.dataset.name); +} +</script> diff --git a/testing/web-platform/tests/inert/inert-and-contenteditable.html b/testing/web-platform/tests/inert/inert-and-contenteditable.html new file mode 100644 index 0000000000..01091d1736 --- /dev/null +++ b/testing/web-platform/tests/inert/inert-and-contenteditable.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Inert and contenteditable</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#attr-contenteditable"> +<meta assert="assert" content=" + Executing an editing command in a node marked as both inert and editable + should have the same result regardless of which ancestors trigger each effect. + Only testing for consistency, the exact result is not interoperable."> + +<div id="log"></div> + +<div class="wrapper" contenteditable inert> + {<p class="target">target</p>} +</div> + +<div class="wrapper" contenteditable> + {<p class="target" inert>target</p>} +</div> + +<div class="wrapper" inert> + {<p class="target" contenteditable>target</p>} +</div> + +<div class="wrapper"> + {<p class="target" contenteditable inert>target</p>} +</div> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +const results = []; +const textContents = []; +setup(function() { + const selection = getSelection(); + for (let wrapper of document.querySelectorAll(".wrapper")) { + const target = wrapper.querySelector(".target"); + selection.collapse(target.firstChild, 3); + results.push(document.execCommand("delete")); + textContents.push(wrapper.textContent.trim()); + } +}); +function testSameValues(array, description) { + test(function() { + assert_greater_than(array.length, 0, "Array shouldn't be empty"); + for (let i = 1; i < array.length; ++i) { + assert_equals(array[i], array[0], `${JSON.stringify(array)} at index ${i}`); + } + }, description); +} +testSameValues(results, "execCommand should return the same value"); +testSameValues(textContents, "The resulting textContent should be the same value"); +</script> diff --git a/testing/web-platform/tests/inert/inert-canvas-fallback-content.html b/testing/web-platform/tests/inert/inert-canvas-fallback-content.html new file mode 100644 index 0000000000..f22549b503 --- /dev/null +++ b/testing/web-platform/tests/inert/inert-canvas-fallback-content.html @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title>Focusability of inert inside canvas</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focusable-area"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/canvas.html#being-used-as-relevant-canvas-fallback-content"> +<link rel="help" href="https://github.com/whatwg/html/issues/7534"> +<meta name="assert" content=" + Checks that inert elements are not focusable + even when being used as relevant canvas fallback content, + even in a display:none subtree"> +<div id="log"></div> +<canvas> + <section> + <div tabindex="-1" data-focusable="true"> + normal `div` + </div> + <span tabindex="-1" data-focusable="true"> + normal `span` + </span> + <a href="#" data-focusable="true"> + normal `a` + </a> + </section> + <section style="display: none"> + <div tabindex="-1" data-focusable="false"> + `div` in `display: none` + </div> + <span tabindex="-1" data-focusable="false"> + `span` in `display: none` + </span> + <a href="#" data-focusable="false"> + `a` in `display: none` + </a> + </section> + <section inert> + <div tabindex="-1" data-focusable="false"> + inert `div` + </div> + <span tabindex="-1" data-focusable="false"> + inert `span` + </span> + <a href="#" data-focusable="false"> + inert `a` + </a> + </section> + <section inert style="display: none"> + <div tabindex="-1" data-focusable="false"> + inert `div` in `display: none` + </div> + <span tabindex="-1" data-focusable="false"> + inert `span` in `display: none` + </span> + <a href="#" data-focusable="false"> + inert `a` in `display: none` + </a> + </section> +</canvas> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +for (let element of document.querySelectorAll("[data-focusable]")) { + test(function() { + assert_not_equals(document.activeElement, element); + element.focus(); + if (JSON.parse(element.dataset.focusable)) { + assert_equals(document.activeElement, element); + } else { + assert_not_equals(document.activeElement, element); + } + }, element.textContent); +} +</script> diff --git a/testing/web-platform/tests/inert/inert-computed-style.html b/testing/web-platform/tests/inert/inert-computed-style.html new file mode 100644 index 0000000000..3c1748de2b --- /dev/null +++ b/testing/web-platform/tests/inert/inert-computed-style.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>inert isn't hit-testable, but that isn't expose in the computed style</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + body { margin: 0 } + div { + width: 100px; + height: 100px; + position: absolute; + top: 0; + left: 0; + background-color: blue; + } + #non-inert { + background-color: red; + } +</style> +<div id="non-inert"></div> +<div id="inert"></div> +<script> + test(function() { + let inert = document.getElementById("inert"); + assert_equals( + document.elementFromPoint(50, 50), + inert, + "not-yet-inert node hit-test before non-inert node", + ); + inert.inert = true; + assert_equals( + document.elementFromPoint(50, 50), + document.getElementById("non-inert"), + "inert node is transparent to events (as pointer-events: none)", + ); + assert_equals( + getComputedStyle(inert).pointerEvents, + "auto", + "inert node doesn't change pointer-events computed value", + ); + assert_equals( + getComputedStyle(inert).userSelect, + "auto", + "inert node doesn't change user-select computed value", + ); + }); +</script> diff --git a/testing/web-platform/tests/inert/inert-does-not-match-disabled-selector.html b/testing/web-platform/tests/inert/inert-does-not-match-disabled-selector.html new file mode 100644 index 0000000000..74b8ac3f7d --- /dev/null +++ b/testing/web-platform/tests/inert/inert-does-not-match-disabled-selector.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<head> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +button { + color: green; +} + +button:disabled { + color: red; +} + +</style> +</head> +<body style="color: green"> +<button inert>The test passes if this is in green.</button> +<script> +test(function() { + button = document.querySelector('button'); + color = document.defaultView.getComputedStyle(button).getPropertyValue('color'); + assert_false(button.matches(':disabled')); +}, 'Tests that inert elements do not match the :disabled selector.'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/inert/inert-iframe-hittest.html b/testing/web-platform/tests/inert/inert-iframe-hittest.html new file mode 100644 index 0000000000..bcc542d35e --- /dev/null +++ b/testing/web-platform/tests/inert/inert-iframe-hittest.html @@ -0,0 +1,101 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Hit-testing with inert iframe</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert"> +<meta assert="assert" content="Contents of an inert iframe can't be reached by hit-testing"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> + +<div id="wrapper" style="width: min-content"> + <iframe id="iframe" inert></iframe> +</div> + +<script> +const events = [ + "mousedown", "mouseenter", "mousemove", "mouseover", + "pointerdown", "pointerenter", "pointermove", "pointerover", +]; +const iframe = document.getElementById("iframe"); +let iframeDoc; +let target; + +promise_setup(async () => { + await new Promise(resolve => { + iframe.addEventListener("load", resolve, {once: true}); + iframe.srcdoc = ` + <style>#target { position: fixed; inset: 0 }</style> + <a id="target" href="#">target</a> + `; + }); + iframeDoc = iframe.contentDocument; + target = iframeDoc.getElementById("target"); + target.addEventListener("click", e => { + e.preventDefault(); + }); +}); + +async function mouseDownAndGetEvents(test) { + const receivedEvents = { + target: [], + wrapper: [], + }; + for (let event of events) { + target.addEventListener(event, () => { + receivedEvents.target.push(event); + }, { once: true, capture: true }); + wrapper.addEventListener(event, () => { + receivedEvents.wrapper.push(event); + }, { once: true, capture: true }); + } + + await new test_driver.Actions() + .pointerMove(0, 0, { origin: wrapper }) + .pointerDown() + .send(); + test.add_cleanup(() => test_driver.click(document.body)); + + // Exact order of events is not interoperable. + receivedEvents.target.sort(); + receivedEvents.wrapper.sort(); + return receivedEvents; +} + +promise_test(async function() { + const receivedEvents = await mouseDownAndGetEvents(this); + assert_array_equals(receivedEvents.target, [], "target got no event"); + assert_array_equals(receivedEvents.wrapper, events, "wrapper got all events"); + + assert_false(target.matches(":focus"), "target is not focused"); + assert_false(target.matches(":active"), "target is not active"); + assert_false(target.matches(":hover"), "target is not hovered"); + assert_true(wrapper.matches(":hover"), "wrapper is hovered"); +}, "Hit-testing doesn't reach contents of an inert iframe"); + +promise_test(async function() { + iframe.inert = false; + + const receivedEvents = await mouseDownAndGetEvents(this); + assert_array_equals(receivedEvents.target, events, "target got all events"); + if (receivedEvents.wrapper.length === 2) { + // Firefox is unstable, sometimes missing the mouse events. + assert_array_equals( + receivedEvents.wrapper, + ["pointerenter", "pointerover"], + "wrapper got enter and over pointer events"); + } else { + assert_array_equals( + receivedEvents.wrapper, + ["mouseenter", "mouseover", "pointerenter", "pointerover"], + "wrapper got enter and over events"); + } + + assert_true(target.matches(":focus"), "target is focused"); + assert_true(target.matches(":active"), "target is active"); + assert_true(target.matches(":hover"), "target is hovered"); + assert_true(wrapper.matches(":hover"), "wrapper is hovered"); +}, "Hit-testing can reach contents of a no longer inert iframe"); +</script> diff --git a/testing/web-platform/tests/inert/inert-iframe-tabbing.html b/testing/web-platform/tests/inert/inert-iframe-tabbing.html new file mode 100644 index 0000000000..a0146b74cc --- /dev/null +++ b/testing/web-platform/tests/inert/inert-iframe-tabbing.html @@ -0,0 +1,123 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Tabbing with inert iframe</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation"> +<meta assert="assert" content="Tabbing can't enter an inert iframe from the outside, but can move within it and can leave it if focus is already there."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<div id="before" tabindex="0">before</div> +<div id="inert" inert> + <iframe id="iframe"></iframe> +</div> +<div id="after" tabindex="0">after</a> + +<script> +const tabKey = "\uE004"; +const before = document.getElementById("before"); +const inert = document.getElementById("inert"); +const after = document.getElementById("after"); +const iframe = document.getElementById("iframe"); +let iframeDoc; +let start; +let end; + +promise_setup(async () => { + await new Promise(resolve => { + iframe.addEventListener("load", resolve, { once: true }); + iframe.srcdoc = ` + <div id="start" tabindex="0">target</div> + <div id="end" tabindex="0">target</div> + `; + }); + iframeDoc = iframe.contentDocument; + start = iframeDoc.getElementById("start"); + end = iframeDoc.getElementById("end"); +}); + +promise_test(async function() { + before.focus(); + assert_equals(document.activeElement, before, "#before got outer focus"); + assert_false(iframeDoc.hasFocus(), "iframeDoc doesn't have focus"); + + await test_driver.send_keys(document.activeElement, tabKey); + assert_equals(document.activeElement, after, "#after got outer focus"); + assert_false(iframeDoc.hasFocus(), "iframeDoc still doesn't have focus"); +}, "Sequential navigation can't enter an inert iframe"); + +promise_test(async function() { + start.focus(); + assert_equals(document.activeElement, iframe, "#iframe got outer focus"); + assert_equals(iframeDoc.activeElement, start, "#start got inner focus"); + assert_true(iframeDoc.hasFocus(), "iframeDoc has focus"); + + await test_driver.send_keys(iframeDoc.activeElement, tabKey); + assert_equals(document.activeElement, iframe, "#iframe still has outer focus"); + assert_equals(iframeDoc.activeElement, end, "#end got inner focus"); + assert_true(iframeDoc.hasFocus(), "iframeDoc still has focus"); +}, "Sequential navigation can move within an inert iframe"); + +promise_test(async function() { + end.focus(); + assert_equals(document.activeElement, iframe, "#iframe got outer focus"); + assert_equals(iframeDoc.activeElement, end, "#end got inner focus"); + assert_true(iframeDoc.hasFocus(), "iframeDoc has focus"); + + await test_driver.send_keys(iframeDoc.activeElement, tabKey); + assert_equals(document.activeElement, after, "#after got outer focus"); + assert_false(iframeDoc.hasFocus(), "iframeDoc doesn't have focus"); +}, "Sequential navigation can leave an inert iframe"); + +// Test again without inertness. + +promise_test(async function() { + inert.inert = false; + + before.focus(); + assert_equals(document.activeElement, before, "#before got outer focus"); + assert_false(iframeDoc.hasFocus(), "iframeDoc doesn't have focus"); + + await test_driver.send_keys(document.activeElement, tabKey); + assert_equals(document.activeElement, iframe, "#iframe got outer focus"); + assert_true(iframeDoc.hasFocus(), "iframeDoc has focus"); + + // The document element is also focusable in Firefox. + if (iframeDoc.activeElement === iframeDoc.documentElement) { + await test_driver.send_keys(document.activeElement, tabKey); + assert_equals(document.activeElement, iframe, "#iframe got outer focus"); + assert_true(iframeDoc.hasFocus(), "iframeDoc has focus"); + } + assert_equals(iframeDoc.activeElement, start, "#start got inner focus"); +}, "Sequential navigation can enter a no longer inert iframe"); + +promise_test(async function() { + inert.inert = false; + + start.focus(); + assert_equals(document.activeElement, iframe, "#iframe got outer focus"); + assert_equals(iframeDoc.activeElement, start, "#start got inner focus"); + assert_true(iframeDoc.hasFocus(), "iframeDoc has focus"); + + await test_driver.send_keys(iframeDoc.activeElement, tabKey); + assert_equals(document.activeElement, iframe, "#iframe still has outer focus"); + assert_equals(iframeDoc.activeElement, end, "#end got inner focus"); + assert_true(iframeDoc.hasFocus(), "iframeDoc still has focus"); +}, "Sequential navigation can move within a no longer inert iframe"); + +promise_test(async function() { + inert.inert = false; + + end.focus(); + assert_equals(document.activeElement, iframe, "#iframe got outer focus"); + assert_equals(iframeDoc.activeElement, end, "#end got inner focus"); + assert_true(iframeDoc.hasFocus(), "iframeDoc has focus"); + + await test_driver.send_keys(iframeDoc.activeElement, tabKey); + assert_equals(document.activeElement, after, "#after got outer focus"); + assert_false(iframeDoc.hasFocus(), "iframeDoc doesn't have focus"); +}, "Sequential navigation can leave a no longer inert iframe"); +</script> diff --git a/testing/web-platform/tests/inert/inert-in-shadow-dom.html b/testing/web-platform/tests/inert/inert-in-shadow-dom.html new file mode 100644 index 0000000000..ec825dbfdc --- /dev/null +++ b/testing/web-platform/tests/inert/inert-in-shadow-dom.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8" /> + <title>inert on Shadow host affects content in shadow</title> + <link rel="author" title="Alice Boxhall" href="aboxhall@chromium.org"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <div>Buttons 1 and 2 should be inert.</div> + <div id="shadow-host" inert> + <button id="button-1">Button 1 (inert)</button> + </div> + <script> + /* + Eventual flattened tree structure: + + <div id="shadow-host" inert> + #shadow-root (open) + | <slot> + : <button id="button-1">Button 1 (inert)</button> <!-- slotted --> + | </slot> + | <button id="button-2">Button 2 (inert)</button> <!-- in shadow --> + </div> + */ + + const shadowHost = document.getElementById("shadow-host"); + const shadowRoot = shadowHost.attachShadow({ mode: "open" }); + + // Button 1 will be slotted + const slot = document.createElement("slot"); + shadowRoot.appendChild(slot); + + const button2 = document.createElement("button"); + button2.id = "button-2"; + button2.textContent = "Button 2 (inert)"; + shadowRoot.appendChild(button2); + + function testCanFocus(selector, canFocus, opt_context) { + let context = opt_context || document; + const element = context.querySelector(selector); + let focusedElement = null; + element.addEventListener("focus", function() { focusedElement = element; }, false); + element.focus(); + assert_equals((focusedElement === element), canFocus); + } + + test(() => { + testCanFocus("#button-1", false); + testCanFocus("#button-2", false, shadowRoot); + }, "inert on Shadow host affects content in shadow"); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/inert/inert-inlines.html b/testing/web-platform/tests/inert/inert-inlines.html new file mode 100644 index 0000000000..b056c6495d --- /dev/null +++ b/testing/web-platform/tests/inert/inert-inlines.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>inert inlines</title> + <link rel="author" title="Alice Boxhall" href="aboxhall@chromium.org"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> +<body> +<a inert id="a" href="javascript:void(0)">Click me</a> +<button inert id="button">Click me</button> +<div inert id="div" style="background-color: blue; width: 50px; height: 50px">Click me</div> +<span inert id="span">Click me</span> +<script> +function eventFiredOnInertElement(e) { + e.target.style.background = 'red'; + inertElementFiredOn = true; +} + +inertElements = ['a', 'button', 'div', 'span'] +inertElements.forEach(function(id) { + element = document.getElementById(id); + element.addEventListener('click', eventFiredOnInertElement); + element.addEventListener('mousemove', eventFiredOnInertElement); +}); + +document.addEventListener('click', function(e) { + document.firedOn = true; +}); + +promise_test(async () => { + for (let id of inertElements) { + var element = document.getElementById(id); + inertElementFiredOn = false; + document.firedOn = false; + try { + await test_driver.click(element); + assert_false(inertElementFiredOn, 'no event should be fired on ' + id); + assert_true(document.firedOn, 'event should be fired on document instead of ' + id); + } catch (e) { + // test driver detects inert elements as unclickable + // and throws an error + assert_false(inertElementFiredOn, 'no event should be fired on ' + id); + } + } +}, 'Tests that inert inlines do not receive mouse events. ' + + 'To test manually, click on all the "Click me"s. The test ' + + 'fails if you see red.'); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/inert/inert-label-focus.html b/testing/web-platform/tests/inert/inert-label-focus.html new file mode 100644 index 0000000000..8bbe1eca15 --- /dev/null +++ b/testing/web-platform/tests/inert/inert-label-focus.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>inert with label/for</title> + <link rel="author" title="Alice Boxhall" href="aboxhall@chromium.org"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <label inert id="for-submit" for="submit">Label for Submit</label> + <input id="text" type="text"> + <input id="submit" type="submit"> + + <label id="for-input-in-inert-subtree" + for="input-in-inert-subtree">Label for input in inert subtree</label> + <div inert> + <input id="input-in-inert-subtree"></input> + </div> + + <script> + test(() => { + label = document.querySelector('#for-submit'); + label.focus(); + assert_equals(document.activeElement, document.querySelector('#submit')); + }, 'Calling focus() on an inert label should still send focus to its target.'); + + promise_test(async () => { + text = document.querySelector('#text'); + text.focus(); + label = document.querySelector('#for-submit'); + try { + await test_driver.click(label); + assert_equals(document.activeElement, document.body); + } catch (e) { + // test driver detects inert elements as unclickable + // and throws an error + } + }, 'Clicking on an inert label should send focus to document.body.'); + + test(() => { + text = document.querySelector('#text'); + text.focus(); + + label = document.querySelector('#for-input-in-inert-subtree'); + label.focus(); + assert_equals(document.activeElement, text); + }, 'Calling focus() on a label for a control which is in an inert subtree ' + + 'should have no effect.'); +</script> +</html> diff --git a/testing/web-platform/tests/inert/inert-node-is-uneditable.html b/testing/web-platform/tests/inert/inert-node-is-uneditable.html new file mode 100644 index 0000000000..23182b937c --- /dev/null +++ b/testing/web-platform/tests/inert/inert-node-is-uneditable.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>inert nodes are uneditable</title> + <link rel="author" title="Alice Boxhall" href="aboxhall@chromium.org"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> +<body> +<span inert id="not-editable" contenteditable>I'm not editable.</span> +<span id="editable" contenteditable>I'm editable.</span> +<script> +var notEditable = document.querySelector('#not-editable'); +var editable = document.querySelector('#editable'); + +promise_test(async function() { + notEditable.focus(); + var oldValue = notEditable.textContent; + assert_equals(oldValue, "I'm not editable."); + await promise_rejects_js( + this, + Error, + test_driver.send_keys(notEditable, 'a'), + "send_keys should reject for non-interactive elements"); + assert_equals(notEditable.textContent, oldValue); +}, "Can't edit inert contenteditable"); + +promise_test(async () => { + editable.focus(); + var oldValue = editable.textContent; + assert_equals(oldValue, "I'm editable."); + await test_driver.send_keys(editable, 'a'); + assert_not_equals(editable.textContent, oldValue); +}, "Can edit non-inert contenteditable"); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/inert/inert-node-is-unfocusable.html b/testing/web-platform/tests/inert/inert-node-is-unfocusable.html new file mode 100644 index 0000000000..8b5de37fdc --- /dev/null +++ b/testing/web-platform/tests/inert/inert-node-is-unfocusable.html @@ -0,0 +1,79 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>inert nodes are unfocusable</title> + <link rel="author" title="Alice Boxhall" href="aboxhall@chromium.org"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> +<body id="body" tabindex="1"> + <button id="focusable">Outside of inert container</button> + <button inert id="inert">Inert button</button> + <div inert id="container"> + <input id="text" type="text"> + <input id="datetime" type="datetime"> + <input id="color" type="color"> + <select id="select"> + <optgroup id="optgroup"> + <option id="option">Option</option> + </optgroup> + </select> + <div id="contenteditable-div" contenteditable>I'm editable</div> + <span id="tabindex-span" tabindex="0">I'm tabindexed.</div> + <embed id="embed" type="application/x-blink-test-plugin" width=100 height=100></embed> + <a id="anchor" href="">Link</a> + </div> +<script> +function testFocus(element, expectFocus) { + focusedElement = null; + element.addEventListener('focus', function() { focusedElement = element; }, false); + element.focus(); + theElement = element; + assert_equals(focusedElement === theElement, expectFocus); +} + +function testTree(element, expectFocus, excludeCurrent) { + if (element.nodeType == Node.ELEMENT_NODE && !excludeCurrent) + testFocus(element, expectFocus); + if (element.tagName === "SELECT") + return; + var childNodes = element.childNodes; + for (var i = 0; i < childNodes.length; i++) + testTree(childNodes[i], expectFocus); +} + + +test(function() { + testFocus(document.getElementById('focusable'), true); +}, "Button outside of inert container is focusable."); + +test(function() { + testFocus(document.getElementById('inert'), false); +}, "Button with inert atribute is unfocusable."); + +test(function() { + testTree(document.getElementById('container'), false); +}, "All focusable elements inside inert subtree are unfocusable"); + +test(function() { + assert_false(document.getElementById("focusable").inert, "Inert not set explicitly is false") + assert_true(document.getElementById("inert").inert, "Inert set explicitly is true"); + assert_true(document.getElementById("container").inert, "Inert set on container is true"); +}, "Can get inert via property"); + +test(function() { + assert_false(document.getElementById("text").inert, "Elements inside of inert subtrees return false when getting inert"); +}, "Elements inside of inert subtrees return false when getting 'inert'"); + +test(function() { + document.getElementById('focusable').inert = true; + testFocus(document.getElementById('focusable'), false); + document.getElementById('inert').inert = false; + testFocus(document.getElementById('inert'), true); + document.getElementById('container').inert = false; + testTree(document.getElementById('container'), true, true); +}, "Setting inert via property correctly modifies inert state"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/inert/inert-node-is-unselectable.html b/testing/web-platform/tests/inert/inert-node-is-unselectable.html new file mode 100644 index 0000000000..b99af0d4cd --- /dev/null +++ b/testing/web-platform/tests/inert/inert-node-is-unselectable.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>inert nodes are unselectable</title> + <link rel="author" title="Alice Boxhall" href="aboxhall@chromium.org"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> +<body> + <div inert>Here is a text node you can't select.</div> + <div>I'm selectable.</div> +<script> +test(function() { + document.execCommand('SelectAll'); + assert_equals(window.getSelection().toString().trim(), "I'm selectable."); +}, "Inert nodes cannot be selected."); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/inert/inert-on-non-html.html b/testing/web-platform/tests/inert/inert-on-non-html.html new file mode 100644 index 0000000000..4d4fdd7059 --- /dev/null +++ b/testing/web-platform/tests/inert/inert-on-non-html.html @@ -0,0 +1,144 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title>'inert' is an HTML attribute and has no effect when used on other elements</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<style> +#tests { + line-height: 1.5em; +} +#tests svg { + height: 1.5em; + vertical-align: middle; +} +#tests svg > text { + transform: translateY(50%); + dominant-baseline: central; +} +#tests foreignObject { + height: 100%; + width: 100%; +} +</style> +<div id="log"></div> +<ul id="tests"> + <!-- The 'inert' attribute only works on HTML elements --> + <li> + <span>non-inert</span> + </li> + <li> + <span inert>inert</span> + </li> + <li> + <foo>non-inert</foo> + </li> + <li> + <foo inert>inert</foo> + </li> + <li> + <foo-bar>non-inert</foo-bar> + </li> + <li> + <foo-bar inert>inert</foo-bar> + </li> + <li> + <math><mi>non-inert</mi></math> + </li> + <li> + <math inert><mi>non-inert</mi></math> + </li> + <li> + <math><mi inert>non-inert</mi></math> + </li> + <li> + <svg><text>non-inert</text></svg> + </li> + <li> + <svg inert><text>non-inert</text></svg> + </li> + <li> + <svg><text inert>non-inert</text></svg> + </li> + + <!-- But non-HTML are inert if an HTML ancestor has the attribute --> + <li> + <span inert><span>inert</span></span> + </li> + <li> + <span inert><foo>inert</foo></span> + </li> + <li> + <span inert><foo-bar>inert</foo-bar></span> + </li> + <li> + <span inert><math><mi>inert</mi></math></span> + </li> + <li> + <span inert><svg><text>inert</text></svg></span> + </li> + + <!-- HTML elements are not inert if an non-HTML ancestor has the attribute --> + <li> + <span data-move>non-inert</span> + <math inert><mi data-destination></mi></math> + </li> + <li> + <span data-move>non-inert</span> + <math><mi inert data-destination></mi></math> + </li> + <li> + <svg inert><foreignObject><span>non-inert</span></foreignObject></svg> + </li> + <li> + <svg><foreignObject inert><span>non-inert</span></foreignObject></svg> + </li> + + <!-- HTML elements with non-HTML ancestors are inert if they have the attribute themselves --> + <li> + <span inert data-move>inert</span> + <math><mi data-destination></mi></math> + </li> + <li> + <foo inert data-move>inert</foo> + <math><mi data-destination></mi></math> + </li> + <li> + <foo-bar inert data-move>inert</foo-bar> + <math><mi data-destination></mi></math> + </li> + <li> + <svg><foreignObject><span inert>inert</span></foreignObject></svg> + </li> + <li> + <svg><foreignObject><foo inert>inert</foo></foreignObject></svg> + </li> + <li> + <svg><foreignObject><foo-bar inert>inert</foo-bar></foreignObject></svg> + </li> +</ul> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +for (let li of document.querySelectorAll("#tests > li")) { + // The HTML parser would mess certain trees, fixup here. + const move = li.querySelector("[data-move]"); + if (move) { + const destination = li.querySelector("[data-destination]"); + destination.append(move); + move.removeAttribute("data-move"); + destination.removeAttribute("data-destination"); + } + test(() => { + assert_equals(li.childElementCount, 1); + const element = li.firstElementChild; + const selection = getSelection(); + selection.selectAllChildren(element); + const selectionText = selection.toString().trim(); + const textContent = element.textContent.trim(); + if (textContent === "inert") { + assert_equals(selectionText, ""); + } else { + assert_equals(selectionText, "non-inert"); + } + }, li.innerHTML.trim()); +} +</script> diff --git a/testing/web-platform/tests/inert/inert-on-slots.html b/testing/web-platform/tests/inert/inert-on-slots.html new file mode 100644 index 0000000000..dd0d7ec6d4 --- /dev/null +++ b/testing/web-platform/tests/inert/inert-on-slots.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8" /> + <title>inert inside ShadowRoot affects slotted content</title> + <link rel="author" title="Alice Boxhall" href="aboxhall@chromium.org"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <div>Button 1 should be inert, and Button 2 should not be inert.</div> + <div id="shadow-host"> + <button slot="slot-1" id="button-1">Button 1 (inert)</button> + <button slot="slot-2" id="button-2">Button 2 (not inert)</button> + </div> + <script> + /* + Eventual flattened tree structure: + + <div id="shadow-host"> + #shadow-root (open) + | <slot id="slot-1" inert> + : <button id="button-1">Button 1</button> <!-- slotted --> + | </slot> + | <slot id="slot-2"> + : <button id="button-2">Button 2</button> <!-- slotted --> + | </slot> + </div> + */ + + const shadowHost = document.getElementById("shadow-host"); + const shadowRoot = shadowHost.attachShadow({ mode: "open" }); + + const slot1 = document.createElement("slot"); + slot1.name = "slot-1"; + slot1.inert = true; + shadowRoot.appendChild(slot1); + + const slot2 = document.createElement("slot"); + slot2.name = "slot-2"; + shadowRoot.appendChild(slot2); + + function testCanFocus(selector, canFocus) { + const element = document.querySelector(selector); + let focusedElement = null; + element.addEventListener("focus", function() { focusedElement = element; }, false); + element.focus(); + assert_equals((focusedElement === element), canFocus); + } + + test(() => { + testCanFocus("#button-1", false); + testCanFocus("#button-2", true); + }, "inert inside ShadowRoot affects slotted content"); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/inert/inert-pseudo-element-hittest.html b/testing/web-platform/tests/inert/inert-pseudo-element-hittest.html new file mode 100644 index 0000000000..bdba769603 --- /dev/null +++ b/testing/web-platform/tests/inert/inert-pseudo-element-hittest.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Hit-testing on pseudo elements of inert nodes</title> +<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<style> +#target::before { + content: ""; + width: 50px; + height: 50px; + background-color: green; + display: inline-block; +} + +#target:hover::before, +#target:active::before { + background-color: red; +} +</style> +<p>Manual test: hover the green square, pass if it does not turn red.</p> +<div id="target" inert></div> +<script> +const events = [ + "mousedown", "mouseenter", "mousemove", "mouseover", + "pointerdown", "pointerenter", "pointermove", "pointerover", +]; +async function mouseDownAndGetEvents(test) { + const receivedEvents = []; + for (let event of events) { + target.addEventListener(event, () => { + receivedEvents.push(event); + }, { once: true, capture: true }); + } + + await new test_driver.Actions() + .pointerMove(0, 0, { origin: target }) + .pointerDown() + .send(); + test.add_cleanup(() => test_driver.click(document.body)); + + // Exact order of events is not interoperable. + receivedEvents.sort(); + return receivedEvents; +} +promise_test(async function() { + const receivedEvents = await mouseDownAndGetEvents(this); + assert_array_equals(receivedEvents, [], "target got no event"); + assert_false(target.matches(":active"), "target is not active"); + assert_false(target.matches(":hover"), "target is not hovered"); + assert_equals(getComputedStyle(target, "::before").backgroundColor, "rgb(0, 128, 0)", "#target::before has no hover style"); +}, "Hit-testing cannot reach pseudo elements of inert nodes"); + +promise_test(async function() { + target.inert = false; + const receivedEvents = await mouseDownAndGetEvents(this); + assert_array_equals(receivedEvents, events, "target got all events"); + assert_true(target.matches(":active"), "target is active"); + assert_true(target.matches(":hover"), "target is hovered"); + assert_equals(getComputedStyle(target, "::before").backgroundColor, "rgb(255, 0, 0)", "#target::before has hover style"); +}, "Hit-testing can reach pseudo elements of non-inert nodes"); +</script> diff --git a/testing/web-platform/tests/inert/inert-svg-hittest.html b/testing/web-platform/tests/inert/inert-svg-hittest.html new file mode 100644 index 0000000000..f743ed25f4 --- /dev/null +++ b/testing/web-platform/tests/inert/inert-svg-hittest.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Hit-testing with inert SVG</title> +<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert"> +<meta assert="assert" content="SVG inside element with inert attribute should be unreachable with hit-testing"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> + +<div id="wrapper"> + <div inert id="svg-container"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"> + <rect width="500" height="500" id="target" fill="red"> + </svg> + </div> +</div> + +<script> +const wrapper = document.getElementById("wrapper"); +const target = document.getElementById("target"); + +promise_test(async function() { + let reachedTarget = false; + target.addEventListener("mousedown", () => { + reachedTarget = true; + }, { once: true }); + + let reachedWrapper = false; + wrapper.addEventListener("mousedown", () => { + reachedWrapper = true; + }, { once: true }); + + await new test_driver.Actions() + .pointerMove(0, 0, { origin: wrapper }) + .pointerDown() + .send(); + this.add_cleanup(() => test_driver.click(document.body)); + + assert_false(target.matches(":active"), "target is not active"); + assert_false(target.matches(":hover"), "target is not hovered"); + assert_false(reachedTarget, "target didn't get event"); + + assert_true(wrapper.matches(":hover"), "wrapper is hovered"); + assert_true(reachedWrapper, "wrapper got event"); +}, "Hit-testing doesn't reach contents of an inert SVG"); + +promise_test(async function() { + document.querySelector("#svg-container").inert = false; + + let reachedTarget = false; + target.addEventListener("mousedown", () => { + reachedTarget = true; + }, { once: true }); + + await new test_driver.Actions() + .pointerMove(0, 0, { origin: wrapper }) + .pointerDown() + .send(); + this.add_cleanup(() => test_driver.click(document.body)); + + assert_true(target.matches(":active"), "target is active"); + assert_true(target.matches(":hover"), "target is hovered"); + assert_true(reachedTarget, "target got event"); + + assert_true(wrapper.matches(":hover"), "wrapper is hovered"); +}, "Hit-testing can reach contents of a no longer inert SVG"); +</script> diff --git a/testing/web-platform/tests/inert/inert-with-modal-dialog-001.html b/testing/web-platform/tests/inert/inert-with-modal-dialog-001.html new file mode 100644 index 0000000000..aa0c29c733 --- /dev/null +++ b/testing/web-platform/tests/inert/inert-with-modal-dialog-001.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title>Interaction of 'inert' attribute with modal dialog</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert"> +<meta name="assert" content="Checks that a modal dialog escapes inertness from ancestors."> +<div id="log"></div> +<div id="wrapper"> + wrapper + <dialog id="dialog"> + dialog + <span id="child"> + child + </span> + </dialog> +</div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup(() => { + dialog.showModal(); + add_completion_callback(() => { + dialog.close(); + getSelection().removeAllRanges(); + }); +}); + +function checkSelection(expectedText) { + const selection = getSelection(); + selection.selectAllChildren(document.documentElement); + const actualText = selection.toString().trim(); + assert_equals(actualText, expectedText); +} + +test(function() { + checkSelection("dialog child"); +}, "Modal dialog only marks outside nodes as inert"); + +test(function() { + child.inert = true; + this.add_cleanup(() => { child.inert = false; }); + checkSelection("dialog"); +}, "Inner nodes with 'inert' attribute become inert anyways"); + +test(function() { + dialog.inert = true; + this.add_cleanup(() => { dialog.inert = false; }); + checkSelection(""); +}, "If the modal dialog has the 'inert' attribute, everything becomes inert"); + +test(function() { + wrapper.inert = true; + this.add_cleanup(() => { wrapper.inert = false; }); + checkSelection("dialog child"); +}, "If an ancestor of the dialog has the 'inert' attribute, the dialog escapes inertness"); +</script> diff --git a/testing/web-platform/tests/inert/inert-with-modal-dialog-002.html b/testing/web-platform/tests/inert/inert-with-modal-dialog-002.html new file mode 100644 index 0000000000..499ac80d09 --- /dev/null +++ b/testing/web-platform/tests/inert/inert-with-modal-dialog-002.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title>Interaction of 'inert' attribute with modal dialog, when the dialog is the root element</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#inert"> +<meta name="assert" content="Checks that being part of a modal dialog does not protect a node from being marked inert by an 'inert' attribute."> +<div id="log"></div> +<dialog id="dialog"> + dialog + <span id="child"> + child + </span> +</dialog> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +const dialog = document.getElementById("dialog"); +const root = document.documentElement; + +setup(() => { + root.remove(); + document.append(dialog); + dialog.showModal(); + add_completion_callback(() => { + getSelection().removeAllRanges(); + }); +}); + +function checkSelection(expectedText) { + const selection = getSelection(); + selection.selectAllChildren(document.documentElement); + const actualText = selection.toString().trim(); + assert_equals(actualText, expectedText); +} + +test(function() { + checkSelection("dialog child"); +}, "Modal dialog only marks outside nodes as inert"); + +test(function() { + child.inert = true; + this.add_cleanup(() => { child.inert = false; }); + checkSelection("dialog"); +}, "Inner nodes with 'inert' attribute become inert anyways"); + +test(function() { + dialog.inert = true; + this.add_cleanup(() => { dialog.inert = false; }); + checkSelection(""); +}, "If the modal dialog has the 'inert' attribute, everything becomes inert"); + +// Ideally this would happen in a completion callback, but then it would +// be too late: the results would have been shown inside the dialog. +dialog.remove(); +document.append(root); +</script> |