1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
<!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", () => {
if (resolve && document.documentElement.scrollTop) {
resolve();
resolve = null;
}
});
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>
|