tor-browser

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

focus-after-close.html (7538B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <meta name="viewport" content="width=device-width,initial-scale=1">
      4 <title>Test focus is moved to the previously focused element when dialog is closed</title>
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="/resources/testdriver.js"></script>
      8 <script src="/resources/testdriver-actions.js"></script>
      9 <script src="/resources/testdriver-vendor.js"></script>
     10 
     11 <body>
     12 <input />
     13 <dialog>
     14    <button id="button1">This is a button1</button>
     15    <button id="button2">This is a button2</button>
     16    <button id="button3">This is a button3</button>
     17 </dialog>
     18 <script>
     19 
     20 // Test focus is moved to the previously focused element
     21 function test_move_to_previously_focused(showModal) {
     22  const input = document.querySelector("input");
     23  input.focus();
     24  const dialog = document.querySelector("dialog");
     25  if (showModal) {
     26    dialog.showModal();
     27  } else {
     28    dialog.show();
     29  }
     30  dialog.close();
     31 
     32  assert_equals(document.activeElement, input);
     33 }
     34 
     35 // Test focus is moved to the previously focused element with some complex dialog usage
     36 async function test_move_to_previously_focused_with_complex_dialog_usage(showModal) {
     37  const input = document.querySelector("input");
     38  input.focus();
     39  const dialog = document.querySelector("dialog");
     40  if (showModal) {
     41    dialog.showModal();
     42  } else {
     43    dialog.show();
     44  }
     45 
     46  const button1 = document.getElementById("button1");
     47  const button2 = document.getElementById("button2");
     48  const button3 = document.getElementById("button3");
     49 
     50  await test_driver.click(button1);
     51  await test_driver.click(button2);
     52  await test_driver.click(button3);
     53 
     54  dialog.close();
     55 
     56  assert_equals(document.activeElement, input);
     57 }
     58 
     59 // Test focus is moved to the previously focused element even if that element moved in between
     60 function test_element_move_in_between_show_close(showModal) {
     61  const input = document.querySelector("input");
     62  input.focus();
     63  const dialog = document.querySelector("dialog");
     64 
     65  assert_equals(input.nextElementSibling, dialog, "Element is in correct position");
     66 
     67  if (showModal) {
     68    dialog.showModal();
     69  } else {
     70    dialog.show();
     71  }
     72 
     73  document.body.appendChild(input);
     74  assert_not_equals(input.nextElementSibling, dialog, "Element should have moved");
     75 
     76  dialog.close();
     77  assert_equals(document.activeElement, input, "Focus should be restored to previously focused input");
     78 
     79  // Clean up
     80  document.body.insertBefore(input, dialog);
     81 }
     82 
     83 // Test focus is moved to the previously focused element even if that element moved to shadow root in between
     84 function test_element_move_to_shadow_root_in_between_show_close(showModal) {
     85  const input = document.querySelector("input");
     86  input.focus();
     87  const dialog = document.querySelector("dialog");
     88 
     89  assert_equals(input.nextElementSibling, dialog, "Element is in correct position");
     90 
     91  if (showModal) {
     92    dialog.showModal();
     93  } else {
     94    dialog.show();
     95  }
     96 
     97  const shadowHost = document.createElement("div");
     98  const shadowRoot = shadowHost.attachShadow({mode: "open"});
     99  shadowRoot.appendChild(input);
    100  document.body.appendChild(shadowHost);
    101 
    102  assert_not_equals(input.nextElementSibling, dialog, "Element should have moved");
    103 
    104  dialog.close();
    105  assert_equals(shadowRoot.activeElement, input, "Focus should be restored to previously focused input");
    106  assert_equals(document.activeElement, shadowHost, "document.activeElement should be previously focused input's shadow DOM host");
    107 
    108  // Clean up
    109  document.body.insertBefore(input, dialog);
    110  shadowHost.remove();
    111 }
    112 
    113 // Test focus is moved to <body> if the previously focused
    114 // element can't be focused
    115 function test_move_to_body_if_fails(showModal) {
    116  const input = document.querySelector("input");
    117  input.focus();
    118  const dialog = document.querySelector("dialog");
    119  if (showModal) {
    120    dialog.showModal();
    121  } else {
    122    dialog.show();
    123  }
    124  dialog.close();
    125  input.remove();
    126  assert_equals(document.activeElement, document.body);
    127 
    128  // Clean up
    129  document.body.insertBefore(input, dialog);
    130 }
    131 
    132 // Test focus is moved to shadow host if the previously
    133 // focused element is a shadow node.
    134 function test_move_to_shadow_host(showModal) {
    135  const shadowHost = document.createElement("div");
    136 
    137  const shadowRoot = shadowHost.attachShadow({mode: "open"});
    138  shadowRoot.appendChild(document.createElement("input"));
    139 
    140  document.body.appendChild(shadowHost);
    141  const inputElement = shadowRoot.querySelector("input");
    142  inputElement.focus();
    143 
    144  assert_equals(document.activeElement, shadowHost);
    145  assert_equals(shadowRoot.activeElement, inputElement);
    146 
    147  const dialog = document.querySelector("dialog");
    148  if (showModal) {
    149    dialog.showModal();
    150  } else {
    151    dialog.show();
    152  }
    153  dialog.close();
    154 
    155  assert_equals(document.activeElement, shadowHost);
    156  assert_equals(shadowRoot.activeElement, inputElement);
    157 
    158  // Clean up
    159  shadowHost.remove();
    160 }
    161 
    162 // Test moving the focus doesn't scroll the viewport
    163 async function test_move_focus_dont_scroll_viewport(showModal) {
    164  const outViewPortButton = document.createElement("button");
    165  outViewPortButton.style.top = (window.innerHeight + 10).toString() + "px";
    166  outViewPortButton.style.position = "absolute";
    167  document.body.appendChild(outViewPortButton);
    168 
    169  await new Promise(resolve => {
    170    document.addEventListener("scroll", () => {
    171      if (resolve && document.documentElement.scrollTop) {
    172        resolve();
    173        resolve = null;
    174      }
    175    });
    176    outViewPortButton.focus();
    177  });
    178 
    179  // Since the outViewPortButton is focused, so the viewport should be
    180  // scrolled to it
    181  assert_true(document.documentElement.scrollTop > 0 );
    182 
    183  const dialog = document.querySelector("dialog");
    184  if (showModal) {
    185    dialog.showModal();
    186  } else {
    187    dialog.show();
    188  }
    189 
    190  window.scrollTo(0, 0);
    191  assert_equals(document.documentElement.scrollTop, 0);
    192 
    193  dialog.close();
    194  assert_equals(document.documentElement.scrollTop, 0);
    195 
    196  assert_equals(document.activeElement, outViewPortButton);
    197 }
    198 
    199 test(() => {
    200  test_move_to_previously_focused(true);
    201  test_move_to_previously_focused(false);
    202 }, "Focus should be moved to the previously focused element (Simple dialog usage)");
    203 
    204 promise_test(async () => {
    205  await test_move_to_previously_focused_with_complex_dialog_usage(true);
    206  await test_move_to_previously_focused_with_complex_dialog_usage(false);
    207 }, "Focus should be moved to the previously focused element (Complex dialog usage)");
    208 
    209 test(() => {
    210  test_element_move_in_between_show_close(true);
    211  test_element_move_in_between_show_close(false);
    212 }, "Focus should be moved to the previously focused element even if it has moved in between show/close");
    213 
    214 test(() => {
    215  test_element_move_to_shadow_root_in_between_show_close(true);
    216  test_element_move_to_shadow_root_in_between_show_close(false);
    217 }, "Focus should be moved to the previously focused element even if it has moved to shadow DOM root in between show/close");
    218 
    219 test(() => {
    220  test_move_to_body_if_fails(true);
    221  test_move_to_body_if_fails(false);
    222 }, "Focus should be moved to the body if the previously focused element is removed");
    223 
    224 test(() => {
    225  test_move_to_shadow_host(true);
    226  test_move_to_shadow_host(false);
    227 }, "Focus should be moved to the shadow DOM host if the previouly focused element is a shadow DOM node");
    228 
    229 promise_test(async () => {
    230  await test_move_focus_dont_scroll_viewport(true);
    231  await test_move_focus_dont_scroll_viewport(false);
    232 }, "Focus should not scroll if the previously focused element is outside the viewport");
    233 </script>
    234 </body>