| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width,initial-scale=1"> |
| <title>Test focus is moved to the previously focused element when dialog is closed</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/resources/testdriver.js"></script> |
| <script src="/resources/testdriver-actions.js"></script> |
| <script src="/resources/testdriver-vendor.js"></script> |
| |
| <body> |
| <input /> |
| <dialog> |
| <button id="button1">This is a button1</button> |
| <button id="button2">This is a button2</button> |
| <button id="button3">This is a button3</button> |
| </dialog> |
| <script> |
| |
| // Test focus is moved to the previously focused element |
| function test_move_to_previously_focused(showModal) { |
| const input = document.querySelector("input"); |
| input.focus(); |
| const dialog = document.querySelector("dialog"); |
| if (showModal) { |
| dialog.showModal(); |
| } else { |
| dialog.show(); |
| } |
| dialog.close(); |
| |
| assert_equals(document.activeElement, input); |
| } |
| |
| // Test focus is moved to the previously focused element with some complex dialog usage |
| async function test_move_to_previously_focused_with_complex_dialog_usage(showModal) { |
| const input = document.querySelector("input"); |
| input.focus(); |
| const dialog = document.querySelector("dialog"); |
| if (showModal) { |
| dialog.showModal(); |
| } else { |
| dialog.show(); |
| } |
| |
| const button1 = document.getElementById("button1"); |
| const button2 = document.getElementById("button2"); |
| const button3 = document.getElementById("button3"); |
| |
| await test_driver.click(button1); |
| await test_driver.click(button2); |
| await test_driver.click(button3); |
| |
| dialog.close(); |
| |
| assert_equals(document.activeElement, input); |
| } |
| |
| // Test focus is moved to the previously focused element even if that element moved in between |
| function test_element_move_in_between_show_close(showModal) { |
| const input = document.querySelector("input"); |
| input.focus(); |
| const dialog = document.querySelector("dialog"); |
| |
| assert_equals(input.nextElementSibling, dialog, "Element is in correct position"); |
| |
| if (showModal) { |
| dialog.showModal(); |
| } else { |
| dialog.show(); |
| } |
| |
| document.body.appendChild(input); |
| assert_not_equals(input.nextElementSibling, dialog, "Element should have moved"); |
| |
| dialog.close(); |
| assert_equals(document.activeElement, input, "Focus should be restored to previously focused input"); |
| |
| // Clean up |
| document.body.insertBefore(input, dialog); |
| } |
| |
| // Test focus is moved to the previously focused element even if that element moved to shadow root in between |
| function test_element_move_to_shadow_root_in_between_show_close(showModal) { |
| const input = document.querySelector("input"); |
| input.focus(); |
| const dialog = document.querySelector("dialog"); |
| |
| assert_equals(input.nextElementSibling, dialog, "Element is in correct position"); |
| |
| if (showModal) { |
| dialog.showModal(); |
| } else { |
| dialog.show(); |
| } |
| |
| const shadowHost = document.createElement("div"); |
| const shadowRoot = shadowHost.attachShadow({mode: "open"}); |
| shadowRoot.appendChild(input); |
| document.body.appendChild(shadowHost); |
| |
| assert_not_equals(input.nextElementSibling, dialog, "Element should have moved"); |
| |
| dialog.close(); |
| assert_equals(shadowRoot.activeElement, input, "Focus should be restored to previously focused input"); |
| assert_equals(document.activeElement, shadowHost, "document.activeElement should be previously focused input's shadow DOM host"); |
| |
| // Clean up |
| document.body.insertBefore(input, dialog); |
| shadowHost.remove(); |
| } |
| |
| // Test focus is moved to <body> if the previously focused |
| // element can't be focused |
| function test_move_to_body_if_fails(showModal) { |
| const input = document.querySelector("input"); |
| input.focus(); |
| const dialog = document.querySelector("dialog"); |
| if (showModal) { |
| dialog.showModal(); |
| } else { |
| dialog.show(); |
| } |
| dialog.close(); |
| input.remove(); |
| assert_equals(document.activeElement, document.body); |
| |
| // Clean up |
| document.body.insertBefore(input, dialog); |
| } |
| |
| // Test focus is moved to shadow host if the previously |
| // focused element is a shadow node. |
| function test_move_to_shadow_host(showModal) { |
| const shadowHost = document.createElement("div"); |
| |
| const shadowRoot = shadowHost.attachShadow({mode: "open"}); |
| shadowRoot.appendChild(document.createElement("input")); |
| |
| document.body.appendChild(shadowHost); |
| const inputElement = shadowRoot.querySelector("input"); |
| inputElement.focus(); |
| |
| assert_equals(document.activeElement, shadowHost); |
| assert_equals(shadowRoot.activeElement, inputElement); |
| |
| const dialog = document.querySelector("dialog"); |
| if (showModal) { |
| dialog.showModal(); |
| } else { |
| dialog.show(); |
| } |
| dialog.close(); |
| |
| assert_equals(document.activeElement, shadowHost); |
| assert_equals(shadowRoot.activeElement, inputElement); |
| |
| // Clean up |
| shadowHost.remove(); |
| } |
| |
| // Test moving the focus doesn't scroll the viewport |
| async function test_move_focus_dont_scroll_viewport(showModal) { |
| const outViewPortButton = document.createElement("button"); |
| outViewPortButton.style.top = (window.innerHeight + 10).toString() + "px"; |
| outViewPortButton.style.position = "absolute"; |
| document.body.appendChild(outViewPortButton); |
| |
| await new Promise(resolve => { |
| document.addEventListener("scroll", resolve, { once: true }); |
| outViewPortButton.focus(); |
| }); |
| |
| // Since the outViewPortButton is focused, so the viewport should be |
| // scrolled to it |
| assert_true(document.documentElement.scrollTop > 0 ); |
| |
| const dialog = document.querySelector("dialog"); |
| if (showModal) { |
| dialog.showModal(); |
| } else { |
| dialog.show(); |
| } |
| |
| window.scrollTo(0, 0); |
| assert_equals(document.documentElement.scrollTop, 0); |
| |
| dialog.close(); |
| assert_equals(document.documentElement.scrollTop, 0); |
| |
| assert_equals(document.activeElement, outViewPortButton); |
| } |
| |
| test(() => { |
| test_move_to_previously_focused(true); |
| test_move_to_previously_focused(false); |
| }, "Focus should be moved to the previously focused element (Simple dialog usage)"); |
| |
| promise_test(async () => { |
| await test_move_to_previously_focused_with_complex_dialog_usage(true); |
| await test_move_to_previously_focused_with_complex_dialog_usage(false); |
| }, "Focus should be moved to the previously focused element (Complex dialog usage)"); |
| |
| test(() => { |
| test_element_move_in_between_show_close(true); |
| test_element_move_in_between_show_close(false); |
| }, "Focus should be moved to the previously focused element even if it has moved in between show/close"); |
| |
| test(() => { |
| test_element_move_to_shadow_root_in_between_show_close(true); |
| test_element_move_to_shadow_root_in_between_show_close(false); |
| }, "Focus should be moved to the previously focused element even if it has moved to shadow DOM root in between show/close"); |
| |
| test(() => { |
| test_move_to_body_if_fails(true); |
| test_move_to_body_if_fails(false); |
| }, "Focus should be moved to the body if the previously focused element is removed"); |
| |
| test(() => { |
| test_move_to_shadow_host(true); |
| test_move_to_shadow_host(false); |
| }, "Focus should be moved to the shadow DOM host if the previouly focused element is a shadow DOM node"); |
| |
| promise_test(async () => { |
| await test_move_focus_dont_scroll_viewport(true); |
| await test_move_focus_dont_scroll_viewport(false); |
| }, "Focus should not scroll if the previously focused element is outside the viewport"); |
| </script> |
| </body> |