tor-browser

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

complex-nested-opt-out.html (5579B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <title>HTML Test: focusgroup - Complex opt-out scenarios with nested structures</title>
      4 <link rel="author" title="Microsoft" href="http://www.microsoft.com/">
      5 <link rel="help" href="https://open-ui.org/components/scoped-focusgroup.explainer/">
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 <script src="/resources/testdriver.js"></script>
      9 <script src="/resources/testdriver-vendor.js"></script>
     10 <script src="/resources/testdriver-actions.js"></script>
     11 <script src="../resources/focusgroup-utils.js"></script>
     12 
     13 <div id=root focusgroup="toolbar">
     14  <button id=item1 tabindex=0>Item 1</button>
     15 
     16  <div>
     17    <button id=item2 tabindex=0>Item 2</button>
     18 
     19    <div id=optout1 focusgroup="none">
     20      <button id=optout_item1 tabindex=0>Opted out 1</button>
     21 
     22      <div>
     23        <button id=optout_item2 tabindex=0>Opted out 2 (nested)</button>
     24 
     25        <!-- Nested focusgroup within opt-out should function independently and not participate in ancestor group -->
     26        <div id=nested_in_optout focusgroup="menu">
     27          <button id=nested_optout_item1 tabindex=0>Nested in opt-out 1</button>
     28          <button id=nested_optout_item2 tabindex=0>Nested in opt-out 2</button>
     29        </div>
     30      </div>
     31 
     32      <!-- Another opt-out within opt-out (redundant but valid) -->
     33      <div id=optout2 focusgroup="none">
     34        <button id=double_optout tabindex=0>Double opt-out</button>
     35      </div>
     36    </div>
     37 
     38    <button id=item3 tabindex=0>Item 3</button>
     39  </div>
     40 
     41  <div>
     42    <div>
     43      <button id=item4 tabindex=0>Item 4 (deeply nested)</button>
     44    </div>
     45  </div>
     46 </div>
     47 
     48 <script>
     49  promise_test(async t => {
     50    const item1 = document.getElementById("item1");
     51    const item2 = document.getElementById("item2");
     52    const item3 = document.getElementById("item3");
     53    const item4 = document.getElementById("item4");
     54 
     55    await focusAndKeyPress(item1, kArrowRight);
     56    assert_equals(document.activeElement, item2, "Should navigate to item2");
     57 
     58    await focusAndKeyPress(item2, kArrowRight);
     59    assert_equals(document.activeElement, item3, "Should skip entire opted-out section (including nested scope) and navigate to item3");
     60 
     61    await focusAndKeyPress(item3, kArrowRight);
     62    assert_equals(document.activeElement, item4, "Should navigate to deeply nested item4");
     63  }, "Outer focusgroup navigation skips opted-out subtree");
     64 
     65  promise_test(async t => {
     66    const optout_item1 = document.getElementById("optout_item1");
     67    const optout_item2 = document.getElementById("optout_item2");
     68    const double_optout = document.getElementById("double_optout");
     69 
     70    optout_item1.focus();
     71    await focusAndKeyPress(optout_item1, kArrowRight);
     72    assert_equals(document.activeElement, optout_item1, "Arrow keys should not move within opted-out generic subtree");
     73 
     74    optout_item2.focus();
     75    await focusAndKeyPress(optout_item2, kArrowRight);
     76    assert_equals(document.activeElement, optout_item2, "Arrow keys should not move within opted-out generic subtree (nested)");
     77 
     78    double_optout.focus();
     79    await focusAndKeyPress(double_optout, kArrowRight);
     80    assert_equals(document.activeElement, double_optout, "Double opt-out still blocks navigation");
     81  }, "Opt-out subtree blocks navigation for its own items");
     82 
     83  promise_test(async t => {
     84    const nested_scope = document.getElementById("nested_in_optout");
     85    const nested_item1 = document.getElementById("nested_optout_item1");
     86    const nested_item2 = document.getElementById("nested_optout_item2");
     87 
     88    nested_item1.focus();
     89    await focusAndKeyPress(nested_item1, kArrowRight);
     90    assert_equals(document.activeElement, nested_item2, "Nested focusgroup should allow forward navigation inside opt-out subtree");
     91 
     92    await focusAndKeyPress(nested_item2, kArrowLeft);
     93    assert_equals(document.activeElement, nested_item1, "Nested focusgroup should allow backward navigation inside opt-out subtree");
     94 
     95    await focusAndKeyPress(nested_item1, kArrowLeft);
     96    assert_equals(document.activeElement, nested_item1, "Backward navigation inside nested scope should not jump to outer items");
     97  }, "Nested focusgroup inside opted-out subtree still works internally");
     98 
     99  promise_test(async t => {
    100    const item2 = document.getElementById("item2");
    101    const item3 = document.getElementById("item3");
    102    const item4 = document.getElementById("item4");
    103 
    104    item4.focus();
    105    await focusAndKeyPress(item4, kArrowLeft);
    106    assert_equals(document.activeElement, item3, "Should navigate backward to item3");
    107 
    108    await focusAndKeyPress(item3, kArrowLeft);
    109    assert_equals(document.activeElement, item2, "Should skip opted-out section backward and navigate to item2");
    110  }, "Backward outer navigation skips opted-out subtree");
    111 
    112  promise_test(async t => {
    113    const root = document.getElementById("root");
    114    const allButtons = Array.from(root.querySelectorAll("button"));
    115    const outerGroupButtons = allButtons.filter(btn => {
    116      for (let n = btn.parentElement; n; n = n.parentElement) {
    117        if (n.hasAttribute("focusgroup") && n.getAttribute("focusgroup") === "none")
    118          return false;
    119        if (n.id === "nested_in_optout")
    120          return false;
    121        if (n.id === "root")
    122          break;
    123      }
    124      return true;
    125    });
    126    const ids = outerGroupButtons.map(b => b.id).sort();
    127    assert_array_equals(ids, ["item1","item2","item3","item4"], "Outer scope should only include four non-opted-out items");
    128  }, "Outer focusgroup membership excludes opted-out and nested scope items");
    129 </script>