tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit ecd87ae4861db1e50fa51f3adc3e5c29ebe408b1
parent 421581c3b39426ca4e497b3b480b9e6d076e9cc8
Author: Jacques Newman <janewman@microsoft.com>
Date:   Wed, 15 Oct 2025 08:24:27 +0000

Bug 1993646 [wpt PR 55345] - [focusgroup] Allow descendants to be items, remove extending behavior., a=testonly

Automatic update from web-platform-tests
[focusgroup] Allow descendants to be items, remove extending behavior.

This change updates the directional navigation behavior of focusgroup to
match the latest explainer
https://open-ui.org/components/scoped-focusgroup.explainer/
- Removes the ability to extend other focusgroups.
- Allows for arbitrary descendants to participate in focusgroup directional navigation.
- Ensures that opted out elements and their descendants are not considered for directional navigation.

Issue where the removal of extend and the addition of
opt-out(focusgroup='none') was decided on:
https://github.com/openui/open-ui/issues/989

Bug: 40210717
Change-Id: I49e4e6ba78dac63d416d31f9be4e72f82b7121b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7013607
Reviewed-by: Mason Freed <masonf@chromium.org>
Commit-Queue: Jacques Newman <janewman@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1527855}

--

wpt-commits: 582c114f5b163ee3e537577855a4697d0fcdc88b
wpt-pr: 55345

Diffstat:
Mtesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-non-focusgroup-item.html | 6+++---
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-focusgroup.html | 31+++++++++++++++++++++++++++++++
Dtesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-non-extending-focusgroup.html | 31-------------------------------
Mtesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-non-focusgroup-subtree.html | 4++--
Mtesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup.html | 2+-
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-focusgroup.html | 31+++++++++++++++++++++++++++++++
Dtesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-non-extending-focusgroup.html | 31-------------------------------
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/deeply-nested-items.html | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/mixed-content-navigation.html | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/simple-descendant-test.html | 36++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/various-element-types.html | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/wrapping-with-descendants.html | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-non-item.html | 7++++---
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/opt-out-barriers/complex-nested-opt-out.html | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/opt-out-barriers/none-creates-barriers.html | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/resources/focusgroup-utils.js | 4++++
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/shadow/shadow-items-basic.html | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/html/interaction/focus/focusgroup/tentative/shadow/shadow-nested-scope.html | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
18 files changed, 807 insertions(+), 71 deletions(-)

diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-non-focusgroup-item.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/does-not-move-when-on-non-focusgroup-item.html @@ -13,11 +13,11 @@ <div focusgroup="toolbar"> <span id=item1 tabindex=0>item1</span> <span id=item2 tabindex=-1>item2</span> - <div> - <span id=nonitem1 tabindex=0>nonitem1</span> - </div> </div> +<!-- Element outside focusgroup should not participate --> +<span id=nonitem1 tabindex=0>nonitem1</span> + <script> promise_test(async t => { diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-focusgroup.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>HTML Test: focusgroup - Focus does not ascend out of current focusgroup if it does not extend the parent focusgroup.</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/focusgroup.explainer/"> +<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> +<script src="../../resources/focusgroup-utils.js"></script> + +<ul focusgroup="toolbar inline"> + <li id=item1 tabindex="-1"> + <ul focusgroup="toolbar block"> + <li id=item2 tabindex="-1">item2</li> + </ul> + </li> +</ul> + +<script> + + promise_test(async t => { + var item2 = document.getElementById("item2"); + + await focusAndKeyPress(item2, kArrowLeft); + assert_equals(document.activeElement, item2); + }, "When the focus is set on an element in a nested focusgroup that doesn't support the navigation direction, focus should remain on that element and not ascend to the parent focusgroup."); + +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-non-extending-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/horizontal/does-not-ascend-out-of-non-extending-focusgroup.html @@ -1,30 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>HTML Test: focusgroup - Focus does not ascend out of current focusgroup if it does not extend the parent focusgroup.</title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> -<link rel="help" href="https://open-ui.org/components/focusgroup.explainer/"> -<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> -<script src="../../resources/focusgroup-utils.js"></script> - -<ul focusgroup="toolbar inline"> - <li id=item1 tabindex="-1"> - <ul focusgroup="toolbar block"> - <li id=item2 tabindex="-1">item2</li> - </ul> - </li> -</ul> - -<script> - - promise_test(async t => { - var item2 = document.getElementById("item2"); - - await focusAndKeyPress(item2, kArrowLeft); - assert_equals(document.activeElement, item2); - }, "When the focus is set on the first element of a non-extending focusgroup located inside another focusgroup, we should be able to ascend to that other focusgroup."); - -</script> -\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-non-focusgroup-subtree.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-non-focusgroup-subtree.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <meta charset="utf-8"> -<title>HTML Test: focusgroup - Focus moves to previous item, skipping over a subtree that isn't an extending focusgroup.</title> +<title>HTML Test: focusgroup - Focus moves to previous item, skipping over a subtree that isn't in the parent focusgroup.</title> <link rel="author" title="Microsoft" href="http://www.microsoft.com/"> <link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Focusgroup/explainer.md"> <script src="/resources/testharness.js"></script> @@ -12,7 +12,7 @@ <div focusgroup="toolbar wrap"> <span id=item1 tabindex=0>item1</span> - <div> + <div focusgroup="none"> <span id=item2 tabindex=-1>item2</span> <span id=item3 tabindex=-1>item3</span> </div> diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/skips-root-focusgroup.html @@ -32,6 +32,6 @@ await focusAndKeyPress(item4, kArrowLeft); assert_equals(document.activeElement, item1); - }, "When the focus is set on the last item of a focusgroup and the previous item is located past an other (non-extending) focusgroup subtree, a backward arrow key press should move the focus to that previous item without getting stuck in the other focusgroup."); + }, "When the focus is set on the last item of a focusgroup and the previous item is located past an other focusgroup subtree, a backward arrow key press should move the focus to that previous item without getting stuck in the other focusgroup."); </script> \ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-focusgroup.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>HTML Test: focusgroup - Focus does not ascend out of current focusgroup if it does not extend the parent focusgroup.</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/focusgroup.explainer/"> +<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> +<script src="../../resources/focusgroup-utils.js"></script> + +<ul focusgroup="toolbar block"> + <li id=item1 tabindex="-1"> + <ul focusgroup="toolbar inline"> + <li id=item2 tabindex="-1">item2</li> + </ul> + </li> +</ul> + +<script> + + promise_test(async t => { + var item2 = document.getElementById("item2"); + + await focusAndKeyPress(item2, kArrowUp); + assert_equals(document.activeElement, item2); + }, "When the focus is set on an element in a nested focusgroup that doesn't support the navigation direction, focus should remain on that element and not ascend to the parent focusgroup."); + +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-non-extending-focusgroup.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/backward-navigation/vertical/does-not-ascend-out-of-non-extending-focusgroup.html @@ -1,30 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>HTML Test: focusgroup - Focus does not ascend out of current focusgroup if it does not extend the parent focusgroup.</title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> -<link rel="help" href="https://open-ui.org/components/focusgroup.explainer/"> -<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> -<script src="../../resources/focusgroup-utils.js"></script> - -<ul focusgroup="toolbar block"> - <li id=item1 tabindex="-1"> - <ul focusgroup="toolbar inline"> - <li id=item2 tabindex="-1">item2</li> - </ul> - </li> -</ul> - -<script> - - promise_test(async t => { - var item2 = document.getElementById("item2"); - - await focusAndKeyPress(item2, kArrowUp); - assert_equals(document.activeElement, item2); - }, "When the focus is set on the first element of a non-extending focusgroup located inside another focusgroup, we should be able to ascend to that other focusgroup."); - -</script> -\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/deeply-nested-items.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/deeply-nested-items.html @@ -0,0 +1,87 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>HTML Test: focusgroup - Navigation works with deeply nested descendants</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/"> +<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> +<script src="../resources/focusgroup-utils.js"></script> + +<div id=root focusgroup="toolbar"> + <span id=item1 tabindex=0>Item 1</span> + <div class="container"> + <div class="sub-container"> + <div class="deep-container"> + <span id=item2 tabindex=-1>Item 2 (deeply nested)</span> + </div> + </div> + </div> + <span> + <span> + <span id=item3 tabindex=-1>Item 3 (nested in spans)</span> + </span> + </span> + <div> + <p>Some text</p> + <div> + <span id=item4 tabindex=-1>Item 4 (nested)</span> + </div> + </div> + <span id=item5 tabindex=-1>Item 5</span> +</div> + +<script> + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + var item3 = document.getElementById("item3"); + var item4 = document.getElementById("item4"); + var item5 = document.getElementById("item5"); + + await focusAndKeyPress(item1, kArrowRight); + assert_equals(document.activeElement, item2, "Should navigate to deeply nested button"); + + await focusAndKeyPress(item2, kArrowRight); + assert_equals(document.activeElement, item3, "Should navigate to button nested in spans"); + + await focusAndKeyPress(item3, kArrowRight); + assert_equals(document.activeElement, item4, "Should navigate to nested span"); + + await focusAndKeyPress(item4, kArrowRight); + assert_equals(document.activeElement, item5, "Should navigate to final span"); + }, "Forward navigation should work with deeply nested focusgroup descendants"); + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + var item3 = document.getElementById("item3"); + var item4 = document.getElementById("item4"); + var item5 = document.getElementById("item5"); + + item5.focus(); + await focusAndKeyPress(item5, kArrowLeft); + assert_equals(document.activeElement, item4, "Should navigate backward to nested input"); + + await focusAndKeyPress(item4, kArrowLeft); + assert_equals(document.activeElement, item3, "Should navigate backward to span nested in spans"); + + await focusAndKeyPress(item3, kArrowLeft); + assert_equals(document.activeElement, item2, "Should navigate backward to deeply nested span"); + + await focusAndKeyPress(item2, kArrowLeft); + assert_equals(document.activeElement, item1, "Should navigate backward to first span"); + }, "Backward navigation should work with deeply nested focusgroup descendants"); + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + + await focusAndKeyPress(item1, kArrowDown); + assert_equals(document.activeElement, item2, "Vertical navigation should work with nested descendants"); + }, "Vertical navigation should work with nested descendants"); + +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/mixed-content-navigation.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/mixed-content-navigation.html @@ -0,0 +1,96 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>HTML Test: focusgroup - Navigation with mixed focusable and non-focusable descendants</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/"> +<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> +<script src="../resources/focusgroup-utils.js"></script> + +<div id=root focusgroup="toolbar"> + <span id=item1 tabindex=0>Item 1</span> + + <div class="section"> + <h3>Section Title</h3> + <p>Some descriptive text that is not focusable</p> + <span id=item2 tabindex=-1>Item 2</span> + + <div> + <span>More non-focusable content</span> + <div> + <span>Nested non-focusable</span> + <span id=item3 tabindex=-1>Item 3</span> + <span>More text</span> + </div> + </div> + </div> + + <div class="another-section"> + <ul> + <li>List item (not focusable)</li> + <li><span id=item4 tabindex=-1>Item 4</span></li> + <li>Another list item</li> + </ul> + </div> + + <footer> + <p>Footer content</p> + <span id=item5 tabindex=-1>Item 5</span> + </footer> +</div> + +<script> + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + var item3 = document.getElementById("item3"); + var item4 = document.getElementById("item4"); + var item5 = document.getElementById("item5"); + + await focusAndKeyPress(item1, kArrowRight); + assert_equals(document.activeElement, item2, "Should skip non-focusable content and navigate to item2"); + + await focusAndKeyPress(item2, kArrowRight); + assert_equals(document.activeElement, item3, "Should skip non-focusable nested content and navigate to item3"); + + await focusAndKeyPress(item3, kArrowRight); + assert_equals(document.activeElement, item4, "Should skip list content and navigate to focusable span"); + + await focusAndKeyPress(item4, kArrowRight); + assert_equals(document.activeElement, item5, "Should skip footer text and navigate to final span"); + }, "Navigation should skip non-focusable descendants and only move between focusable items"); + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + var item3 = document.getElementById("item3"); + var item4 = document.getElementById("item4"); + var item5 = document.getElementById("item5"); + + item5.focus(); + await focusAndKeyPress(item5, kArrowLeft); + assert_equals(document.activeElement, item4, "Should navigate backward to link"); + + await focusAndKeyPress(item4, kArrowLeft); + assert_equals(document.activeElement, item3, "Should navigate backward to nested span"); + + await focusAndKeyPress(item3, kArrowLeft); + assert_equals(document.activeElement, item2, "Should navigate backward to span"); + + await focusAndKeyPress(item2, kArrowLeft); + assert_equals(document.activeElement, item1, "Should navigate backward to first item"); + }, "Backward navigation should skip non-focusable descendants"); + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + + await focusAndKeyPress(item1, kArrowDown); + assert_equals(document.activeElement, item2, "Vertical navigation should work with mixed content"); + }, "Vertical navigation should work correctly with mixed focusable/non-focusable content"); + +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/simple-descendant-test.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/simple-descendant-test.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>HTML Test: focusgroup - Simple descendant navigation test</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/"> +<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> +<script src="../resources/focusgroup-utils.js"></script> + +<div id=root focusgroup="toolbar"> + <button id=item1 tabindex=0>Item 1</button> + <div> + <button id=item2 tabindex=-1>Item 2 (nested)</button> + </div> + <button id=item3 tabindex=-1>Item 3</button> +</div> + +<script> + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + var item3 = document.getElementById("item3"); + + await focusAndKeyPress(item1, kArrowRight); + assert_equals(document.activeElement, item2, "Should navigate to nested item2"); + + await focusAndKeyPress(item2, kArrowRight); + assert_equals(document.activeElement, item3, "Should navigate to item3"); + }, "Simple descendant navigation should work"); + +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/various-element-types.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/various-element-types.html @@ -0,0 +1,120 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>HTML Test: focusgroup - Various element types work as focusgroup descendants</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/"> +<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> +<script src="../resources/focusgroup-utils.js"></script> + +<div id=root focusgroup="toolbar"> + <span id=item1 tabindex=0>Item 1</span> + <div> + <span id=item2 tabindex=-1>Item 2 (nested)</span> + </div> + <section> + <div> + <span id=item3 tabindex=-1>Item 3 (in section)</span> + </div> + </section> + <article> + <span id=item4 tabindex=-1>Item 4 (in article)</span> + </article> + <div> + <span id=item5 tabindex=-1>Item 5 (deeply nested)</span> + </div> + <nav> + <div> + <span id=item6 tabindex=-1>Item 6 (in nav)</span> + </div> + </nav> + <div> + <div tabindex=-1 id=item7>Item 7 (focusable div)</div> + </div> + <details> + <summary id=item8 tabindex=-1>Item 8 (summary)</summary> + <p>Details content</p> + </details> + <div> + <span id=item9 tabindex=-1>Item 9 (final)</span> + </div> +</div> + +<script> + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + var item3 = document.getElementById("item3"); + var item4 = document.getElementById("item4"); + var item5 = document.getElementById("item5"); + var item6 = document.getElementById("item6"); + var item7 = document.getElementById("item7"); + var item8 = document.getElementById("item8"); + var item9 = document.getElementById("item9"); + + await focusAndKeyPress(item1, kArrowRight); + assert_equals(document.activeElement, item2, "Should navigate to nested item"); + + await focusAndKeyPress(item2, kArrowRight); + assert_equals(document.activeElement, item3, "Should navigate to item in section"); + + await focusAndKeyPress(item3, kArrowRight); + assert_equals(document.activeElement, item4, "Should navigate to item in article"); + + await focusAndKeyPress(item4, kArrowRight); + assert_equals(document.activeElement, item5, "Should navigate to deeply nested item"); + + await focusAndKeyPress(item5, kArrowRight); + assert_equals(document.activeElement, item6, "Should navigate to item in nav"); + + await focusAndKeyPress(item6, kArrowRight); + assert_equals(document.activeElement, item7, "Should navigate to focusable div"); + + await focusAndKeyPress(item7, kArrowRight); + assert_equals(document.activeElement, item8, "Should navigate to summary"); + + await focusAndKeyPress(item8, kArrowRight); + assert_equals(document.activeElement, item9, "Should navigate to final item"); + }, "Navigation should work with items in various container types"); + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item8 = document.getElementById("item8"); + var item9 = document.getElementById("item9"); + + item9.focus(); + await focusAndKeyPress(item9, kArrowLeft); + assert_equals(document.activeElement, item8, "Should navigate backward from final item"); + + var current = document.activeElement; + await focusAndKeyPress(current, kArrowLeft); + assert_equals(document.activeElement, item7, "Should navigate backward to focusable div"); + }, "Backward navigation should work with various container types"); + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + + await focusAndKeyPress(item1, kArrowDown); + assert_equals(document.activeElement, item2, "Vertical navigation should work with different container types"); + }, "Vertical navigation should work with various container types"); + + promise_test(async t => { + var root = document.getElementById("root"); + var expectedElements = [ + "item1", "item2", "item3", "item4", "item5", + "item6", "item7", "item8", "item9" + ]; + + for (let elementId of expectedElements) { + var element = document.getElementById(elementId); + element.focus(); + assert_equals(document.activeElement, element, `${elementId} should be focusable`); + } + }, "All items in different container types should be focusable within focusgroup"); + +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/wrapping-with-descendants.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/descendant-navigation/wrapping-with-descendants.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>HTML Test: focusgroup - Wrapping works correctly with nested descendants</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/"> +<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> +<script src="../resources/focusgroup-utils.js"></script> + +<div id=root focusgroup="toolbar wrap"> + <div class="first-section"> + <button id=first tabindex=0>First Item</button> + </div> + <div class="middle-section"> + <div> + <div> + <button id=middle tabindex=-1>Middle Item (nested)</button> + </div> + </div> + </div> + <div class="last-section"> + <span> + <button id=last tabindex=-1>Last Item</button> + </span> + </div> +</div> + +<script> + + promise_test(async t => { + var first = document.getElementById("first"); + var middle = document.getElementById("middle"); + var last = document.getElementById("last"); + + last.focus(); + await focusAndKeyPress(last, kArrowRight); + assert_equals(document.activeElement, first, "Should wrap from last nested item to first item"); + }, "Forward wrapping should work from nested descendants to first item"); + + promise_test(async t => { + var first = document.getElementById("first"); + var middle = document.getElementById("middle"); + var last = document.getElementById("last"); + + first.focus(); + await focusAndKeyPress(first, kArrowLeft); + assert_equals(document.activeElement, last, "Should wrap from first item to last nested item"); + }, "Backward wrapping should work from first item to nested descendants"); + + promise_test(async t => { + var first = document.getElementById("first"); + var middle = document.getElementById("middle"); + var last = document.getElementById("last"); + + first.focus(); + await focusAndKeyPress(first, kArrowRight); + assert_equals(document.activeElement, middle, "Should navigate normally to middle nested item"); + + await focusAndKeyPress(middle, kArrowRight); + assert_equals(document.activeElement, last, "Should navigate normally to last nested item"); + }, "Normal navigation should still work correctly with nested items"); + + promise_test(async t => { + var first = document.getElementById("first"); + var last = document.getElementById("last"); + + last.focus(); + await focusAndKeyPress(last, kArrowDown); + assert_equals(document.activeElement, first, "Vertical wrapping should work with nested descendants"); + }, "Vertical wrapping should work correctly with nested descendants"); + +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-non-item.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/forward-navigation/does-not-move-when-on-non-item.html @@ -10,14 +10,15 @@ <script src="/resources/testdriver-actions.js"></script> <script src="../resources/focusgroup-utils.js"></script> +<!-- Element outside focusgroup should not participate --> +<span id=nonitem1 tabindex=0>nonitem1</span> + <div tabindex=-1 focusgroup="toolbar"> - <div> - <span id=nonitem1 tabindex=0>nonitem1</span> - </div> <span id=item1 tabindex=0>item1</span> <span id=item2 tabindex=-1>item2</span> </div> + <script> promise_test(async t => { diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/opt-out-barriers/complex-nested-opt-out.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/opt-out-barriers/complex-nested-opt-out.html @@ -0,0 +1,129 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>HTML Test: focusgroup - Complex opt-out scenarios with nested structures</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/"> +<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> +<script src="../resources/focusgroup-utils.js"></script> + +<div id=root focusgroup="toolbar"> + <button id=item1 tabindex=0>Item 1</button> + + <div> + <button id=item2 tabindex=-1>Item 2</button> + + <div id=optout1 focusgroup="none"> + <button id=optout_item1 tabindex=-1>Opted out 1</button> + + <div> + <button id=optout_item2 tabindex=-1>Opted out 2 (nested)</button> + + <!-- Nested focusgroup within opt-out should function independently and not participate in ancestor group --> + <div id=nested_in_optout focusgroup="menu"> + <button id=nested_optout_item1 tabindex=-1>Nested in opt-out 1</button> + <button id=nested_optout_item2 tabindex=-1>Nested in opt-out 2</button> + </div> + </div> + + <!-- Another opt-out within opt-out (redundant but valid) --> + <div id=optout2 focusgroup="none"> + <button id=double_optout tabindex=-1>Double opt-out</button> + </div> + </div> + + <button id=item3 tabindex=-1>Item 3</button> + </div> + + <div> + <div> + <button id=item4 tabindex=-1>Item 4 (deeply nested)</button> + </div> + </div> +</div> + +<script> + promise_test(async t => { + const item1 = document.getElementById("item1"); + const item2 = document.getElementById("item2"); + const item3 = document.getElementById("item3"); + const item4 = document.getElementById("item4"); + + await focusAndKeyPress(item1, kArrowRight); + assert_equals(document.activeElement, item2, "Should navigate to item2"); + + await focusAndKeyPress(item2, kArrowRight); + assert_equals(document.activeElement, item3, "Should skip entire opted-out section (including nested scope) and navigate to item3"); + + await focusAndKeyPress(item3, kArrowRight); + assert_equals(document.activeElement, item4, "Should navigate to deeply nested item4"); + }, "Outer focusgroup navigation skips opted-out subtree"); + + promise_test(async t => { + const optout_item1 = document.getElementById("optout_item1"); + const optout_item2 = document.getElementById("optout_item2"); + const double_optout = document.getElementById("double_optout"); + + optout_item1.focus(); + await focusAndKeyPress(optout_item1, kArrowRight); + assert_equals(document.activeElement, optout_item1, "Arrow keys should not move within opted-out generic subtree"); + + optout_item2.focus(); + await focusAndKeyPress(optout_item2, kArrowRight); + assert_equals(document.activeElement, optout_item2, "Arrow keys should not move within opted-out generic subtree (nested)"); + + double_optout.focus(); + await focusAndKeyPress(double_optout, kArrowRight); + assert_equals(document.activeElement, double_optout, "Double opt-out still blocks navigation"); + }, "Opt-out subtree blocks navigation for its own items"); + + promise_test(async t => { + const nested_scope = document.getElementById("nested_in_optout"); + const nested_item1 = document.getElementById("nested_optout_item1"); + const nested_item2 = document.getElementById("nested_optout_item2"); + + nested_item1.focus(); + await focusAndKeyPress(nested_item1, kArrowRight); + assert_equals(document.activeElement, nested_item2, "Nested focusgroup should allow forward navigation inside opt-out subtree"); + + await focusAndKeyPress(nested_item2, kArrowLeft); + assert_equals(document.activeElement, nested_item1, "Nested focusgroup should allow backward navigation inside opt-out subtree"); + + await focusAndKeyPress(nested_item1, kArrowLeft); + assert_equals(document.activeElement, nested_item1, "Backward navigation inside nested scope should not jump to outer items"); + }, "Nested focusgroup inside opted-out subtree still works internally"); + + promise_test(async t => { + const item2 = document.getElementById("item2"); + const item3 = document.getElementById("item3"); + const item4 = document.getElementById("item4"); + + item4.focus(); + await focusAndKeyPress(item4, kArrowLeft); + assert_equals(document.activeElement, item3, "Should navigate backward to item3"); + + await focusAndKeyPress(item3, kArrowLeft); + assert_equals(document.activeElement, item2, "Should skip opted-out section backward and navigate to item2"); + }, "Backward outer navigation skips opted-out subtree"); + + promise_test(async t => { + const root = document.getElementById("root"); + const allButtons = Array.from(root.querySelectorAll("button")); + const outerGroupButtons = allButtons.filter(btn => { + for (let n = btn.parentElement; n; n = n.parentElement) { + if (n.hasAttribute("focusgroup") && n.getAttribute("focusgroup") === "none") + return false; + if (n.id === "nested_in_optout") + return false; + if (n.id === "root") + break; + } + return true; + }); + const ids = outerGroupButtons.map(b => b.id).sort(); + assert_array_equals(ids, ["item1","item2","item3","item4"], "Outer scope should only include four non-opted-out items"); + }, "Outer focusgroup membership excludes opted-out and nested scope items"); +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/opt-out-barriers/none-creates-barriers.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/opt-out-barriers/none-creates-barriers.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>HTML Test: focusgroup - Opt-out creates navigation barriers</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/"> +<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> +<script src="../resources/focusgroup-utils.js"></script> + +<div id=root focusgroup="toolbar"> + <button id=item1 tabindex=0>Item 1</button> + <button id=item2 tabindex=-1>Item 2</button> + + <div id=optout focusgroup="none"> + <button id=optout_item1 tabindex=-1>Opted out item 1</button> + <div> + <button id=optout_item2 tabindex=-1>Opted out item 2</button> + </div> + </div> + + <button id=item3 tabindex=-1>Item 3</button> + <button id=item4 tabindex=-1>Item 4</button> +</div> + +<script> + + promise_test(async t => { + var item1 = document.getElementById("item1"); + var item2 = document.getElementById("item2"); + var item3 = document.getElementById("item3"); + var optout_item1 = document.getElementById("optout_item1"); + + await focusAndKeyPress(item1, kArrowRight); + assert_equals(document.activeElement, item2, "Should navigate to item2"); + + await focusAndKeyPress(item2, kArrowRight); + assert_equals(document.activeElement, item3, "Should skip opted-out section and navigate to item3"); + + assert_not_equals(document.activeElement, optout_item1, "Should not navigate to opted-out item"); + }, "Navigation should skip elements in opted-out focusgroup subtree"); + + promise_test(async t => { + var item2 = document.getElementById("item2"); + var item3 = document.getElementById("item3"); + var item4 = document.getElementById("item4"); + var optout_item1 = document.getElementById("optout_item1"); + + item4.focus(); + await focusAndKeyPress(item4, kArrowLeft); + assert_equals(document.activeElement, item3, "Should navigate to item3"); + + await focusAndKeyPress(item3, kArrowLeft); + assert_equals(document.activeElement, item2, "Should skip opted-out section and navigate to item2"); + + assert_not_equals(document.activeElement, optout_item1, "Should not navigate to opted-out item from reverse"); + }, "Backward navigation should skip elements in opted-out focusgroup subtree"); + + promise_test(async t => { + var optout_item1 = document.getElementById("optout_item1"); + var optout_item2 = document.getElementById("optout_item2"); + + optout_item1.focus(); + assert_equals(document.activeElement, optout_item1, "Should be able to manually focus opted-out item"); + + await focusAndKeyPress(optout_item1, kArrowRight); + assert_equals(document.activeElement, optout_item1, "Arrow keys should not work within opted-out section"); + + await focusAndKeyPress(optout_item1, kArrowDown); + assert_equals(document.activeElement, optout_item1, "Vertical arrow keys should not work within opted-out section"); + }, "Arrow keys should not work within opted-out focusgroup sections"); + +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/resources/focusgroup-utils.js b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/resources/focusgroup-utils.js @@ -13,3 +13,7 @@ function focusAndKeyPress(target, key) { target.focus(); return test_driver.send_keys(target, key); } + +function sendArrowKey(key) { + return new test_driver.Actions().keyDown(key).keyUp(key).send(); +} diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/shadow/shadow-items-basic.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/shadow/shadow-items-basic.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>focusgroup: shadow DOM items navigation</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/"> +<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> +<script src="../resources/focusgroup-utils.js"></script> + +<!-- In the scoped focusgroup model the behavior token (e.g. toolbar) must be + the first token. The focusgroup scope includes the element with the + attribute and its shadow-inclusive descendants. To exercise cross shadow + boundary behavior, put the focusgroup on the shadow host itself. --> +<div id=host focusgroup="toolbar inline"></div> + +<script> + function deepActiveElement(root = document) { + let a = root.activeElement; + while (a && a.shadowRoot && a.shadowRoot.activeElement) { + a = a.shadowRoot.activeElement; + } + return a; + } + promise_test(async t => { + const host = document.getElementById('host'); + const sr = host.attachShadow({mode: 'open'}); + sr.innerHTML = ` + <button id=item1 tabindex=0>One</button> + <button id=item2 tabindex=-1>Two</button> + <button id=item3 tabindex=-1>Three</button>`; + const item1 = sr.getElementById('item1'); + const item2 = sr.getElementById('item2'); + const item3 = sr.getElementById('item3'); + + item1.focus(); + assert_equals(sr.activeElement, item1, 'Initial focus on item1 (shadow)'); + assert_equals(deepActiveElement(), item1, 'Deep active element is item1'); + await sendArrowKey(kArrowRight); + assert_equals(sr.activeElement, item2, 'Advance to item2'); + assert_equals(deepActiveElement(), item2, 'Deep active element is item2'); + + await sendArrowKey(kArrowRight); + assert_equals(sr.activeElement, item3, 'Advance to item3'); + assert_equals(deepActiveElement(), item3, 'Deep active element is item3'); + + await sendArrowKey(kArrowRight); + assert_equals(sr.activeElement, item3, 'No wrap; remains on item3'); + assert_equals(deepActiveElement(), item3, 'Deep active element remains item3'); + }, 'Shadow host focusgroup scopes shadow root items for navigation'); +</script> +\ No newline at end of file diff --git a/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/shadow/shadow-nested-scope.html b/testing/web-platform/tests/html/interaction/focus/focusgroup/tentative/shadow/shadow-nested-scope.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>focusgroup: nested shadow focusgroup isolation</title> +<link rel="author" title="Microsoft" href="http://www.microsoft.com/"> +<link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/"> +<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> +<script src="../resources/focusgroup-utils.js"></script> + +<div id=outer_fg focusgroup="toolbar inline"> + <button id=outer1 tabindex=0>Outer 1</button> + <div id=shadow_host></div> + <button id=outer2 tabindex=-1>Outer 2</button> +</div> + +<script> + function deepActiveElement(root = document) { + let a = root.activeElement; + while (a && a.shadowRoot && a.shadowRoot.activeElement) { + a = a.shadowRoot.activeElement; + } + return a; + } + promise_test(async t => { + const host = document.getElementById('shadow_host'); + const sr = host.attachShadow({mode: 'open'}); + sr.innerHTML = ` + <div id=inner_fg focusgroup="menu inline"> + <button id=inner1 tabindex=0>Inner 1</button> + <button id=inner2 tabindex=-1>Inner 2</button> + </div>`; + const outer1 = document.getElementById('outer1'); + const outer2 = document.getElementById('outer2'); + const inner1 = sr.getElementById('inner1'); + const inner2 = sr.getElementById('inner2'); + + await focusAndKeyPress(outer1, kArrowRight); + assert_equals(document.activeElement, outer2, 'Outer navigation skips inner shadow scope'); + assert_equals(deepActiveElement(), outer2, 'Deep active element is outer2 after outer navigation'); + + inner1.focus(); + await sendArrowKey(kArrowRight); + assert_equals(sr.activeElement, inner2, 'Inner navigation advances'); + assert_equals(deepActiveElement(), inner2, 'Deep active element is inner2'); + await sendArrowKey(kArrowRight); + assert_equals(sr.activeElement, inner2, 'No wrap inside inner scope'); + assert_equals(deepActiveElement(), inner2, 'Deep active element remains inner2'); + }, 'Nested shadow focusgroup is isolated from outer scope navigation'); +</script> +\ No newline at end of file