| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <title>dialog focus delegation</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <body> |
| |
| <!-- |
| We focus this one between each test, to ensure that for non-modal dialogs, |
| if there is no focus delegate, it stays focused (instead of causing focus to reset to the body). |
| --> |
| <button tabindex="-1" id="focus-between-tests">Focus between tests</button> |
| |
| <dialog data-description="No autofocus, no delegatesFocus, no siblings"> |
| <template class="turn-into-shadow-tree"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="No autofocus, no delegatesFocus, sibling before"> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <template class="turn-into-shadow-tree"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="No autofocus, no delegatesFocus, sibling after"> |
| <template class="turn-into-shadow-tree"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| </dialog> |
| |
| <dialog data-description="No autofocus, yes delegatesFocus, no siblings"> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="No autofocus, yes delegatesFocus, sibling before"> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="No autofocus, yes delegatesFocus, sibling after"> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| <button tabindex="-1">Focusable</button> |
| </dialog> |
| |
| <dialog data-description="Autofocus before, no delegatesFocus"> |
| <button tabindex="-1" autofocus class="focus-me">Focusable</button> |
| <template class="turn-into-shadow-tree"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus before, yes delegatesFocus"> |
| <button tabindex="-1" autofocus class="focus-me">Focusable</button> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus after, no delegatesFocus"> |
| <template class="turn-into-shadow-tree"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| <button tabindex="-1" autofocus class="focus-me">Focusable</button> |
| </dialog> |
| |
| <dialog data-description="Autofocus after, yes delegatesFocus"> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| <button tabindex="-1" autofocus class="focus-me">Focusable</button> |
| </dialog> |
| |
| <dialog data-description="Autofocus on shadow host, yes delegatesFocus, no siblings"> |
| <template class="turn-into-shadow-tree delegates-focus autofocus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus on shadow host, yes delegatesFocus, sibling before"> |
| <button tabindex="-1">Focusable</button> |
| <template class="turn-into-shadow-tree delegates-focus autofocus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus on shadow host, yes delegatesFocus, sibling after"> |
| <template class="turn-into-shadow-tree delegates-focus autofocus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| <button tabindex="-1">Focusable</button> |
| </dialog> |
| |
| <dialog data-description="Autofocus on shadow host, no delegatesFocus, no siblings"> |
| <template class="turn-into-shadow-tree autofocus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus on shadow host, no delegatesFocus, sibling before"> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <template class="turn-into-shadow-tree autofocus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus on shadow host, no delegatesFocus, sibling after"> |
| <template class="turn-into-shadow-tree autofocus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| </dialog> |
| |
| <dialog data-description="Autofocus inside shadow tree, yes delegatesFocus, no siblings"> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button tabindex="-1">Focusable</button> |
| <button tabindex="-1" autofocus class="focus-me">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus inside shadow tree, yes delegatesFocus, sibling before"> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button tabindex="-1">Focusable</button> |
| <button tabindex="-1" autofocus>Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus inside shadow tree, yes delegatesFocus, sibling after"> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button tabindex="-1">Focusable</button> |
| <button tabindex="-1" autofocus class="focus-me">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| <button tabindex="-1">Focusable</button> |
| </dialog> |
| |
| <dialog data-description="Autofocus inside shadow tree, no delegatesFocus, no siblings"> |
| <template class="turn-into-shadow-tree"> |
| <button tabindex="-1">Focusable</button> |
| <button tabindex="-1" autofocus>Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus inside shadow tree, no delegatesFocus, sibling before"> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <template class="turn-into-shadow-tree"> |
| <button tabindex="-1">Focusable</button> |
| <button tabindex="-1" autofocus>Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| </dialog> |
| |
| <dialog data-description="Autofocus inside shadow tree, no delegatesFocus, sibling after"> |
| <template class="turn-into-shadow-tree"> |
| <button tabindex="-1">Focusable</button> |
| <button tabindex="-1" autofocus>Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| </dialog> |
| |
| <dialog data-description="Two shadow trees, both delegatesFocus, first tree doesn't have autofocus element, second does"> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button disabled>Non-focusable</button> |
| <button tabindex="-1" class="focus-me">Focusable</button> |
| <button disabled>Non-focusable</button> |
| </template> |
| <template class="turn-into-shadow-tree delegates-focus"> |
| <button tabindex="-1" autofocus>Focusable</button> |
| </template> |
| </dialog> |
| |
| <script> |
| for (const template of document.querySelectorAll(".turn-into-shadow-tree")) { |
| const div = document.createElement("div"); |
| div.attachShadow({ mode: "open", delegatesFocus: template.classList.contains("delegates-focus") }); |
| |
| if (template.classList.contains("autofocus")) { |
| div.setAttribute("autofocus", true); |
| } |
| div.shadowRoot.append(template.content); |
| template.replaceWith(div); |
| } |
| |
| const focusBetweenTests = document.querySelector("#focus-between-tests"); |
| |
| for (const dialog of document.querySelectorAll("dialog")) { |
| for (const method of ["show", "showModal"]) { |
| test(t => { |
| focusBetweenTests.focus(); |
| |
| dialog[method](); |
| t.add_cleanup(() => dialog.close()); |
| |
| const expectedFocusOutsideShadowTree = dialog.querySelector(".focus-me"); |
| if (expectedFocusOutsideShadowTree) { |
| assert_equals(document.activeElement, expectedFocusOutsideShadowTree); |
| } else { |
| const shadowHost = dialog.querySelector("div"); |
| const expectedFocusInsideShadowTree = shadowHost.shadowRoot.querySelector(".focus-me"); |
| if (expectedFocusInsideShadowTree) { |
| assert_equals(document.activeElement, shadowHost); |
| assert_equals(shadowHost.shadowRoot.activeElement, expectedFocusInsideShadowTree); |
| } else { |
| // There is no focus delegate. Expected result depends on show() vs. showModal(). |
| if (method === "show") { |
| assert_equals(document.activeElement, focusBetweenTests); |
| } else { |
| assert_equals(document.activeElement, document.body); |
| } |
| } |
| } |
| }, `${method}: ${dialog.dataset.description}`); |
| } |
| } |
| </script> |