| <!DOCTYPE html> |
| <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/shadow-dom.js"></script> |
| <script src="resources/focus-utils.js"></script> |
| |
| <p>This tests TAB focus navigation with delegatesFocus flag on shadow hosts</p> |
| <pre id="console"></pre> |
| <div id="sandbox"></div> |
| <script> |
| |
| function prepareDOMTree(parent, mode, tabindex, delegatesFocus) { |
| parent.innerHTML = ` |
| <div id="testform"> |
| <input id="input-before"> |
| <div id="host-div"> |
| <input id="inner-input"> |
| </div> |
| <input id="input-after"> |
| </div> |
| `; |
| const hostDiv = document.getElementById('host-div'); |
| const shadowRoot = hostDiv.attachShadow({ mode, delegatesFocus }); |
| |
| const inputBefore = document.getElementById('input-before'); |
| const innerInput = document.getElementById('inner-input'); |
| const inputAfter = document.getElementById('input-after'); |
| shadowRoot.appendChild(innerInput); |
| |
| if (tabindex !== null) |
| hostDiv.tabIndex = tabindex; |
| |
| return { |
| hostDiv, |
| shadowRoot, |
| inputBefore, |
| innerInput, |
| inputAfter, |
| }; |
| |
| } |
| |
| promise_test(async () => { |
| const { shadowRoot, hostDiv } = prepareDOMTree(sandbox, 'open', null, false); |
| assert_false(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, -1); |
| |
| const elements = [ |
| 'input-before', |
| 'host-div/inner-input', |
| 'input-after', |
| ]; |
| |
| await assert_focus_navigation_bidirectional(elements); |
| }, 'Testing tab navigation order with mode open, no tabindex and delegatesFocus=false.'); |
| |
| promise_test(async () => { |
| const { shadowRoot, hostDiv } = prepareDOMTree(sandbox, 'open', null, true); |
| assert_true(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, -1); |
| |
| const elements = [ |
| 'input-before', |
| 'host-div/inner-input', |
| 'input-after', |
| ]; |
| |
| await assert_focus_navigation_bidirectional(elements); |
| }, 'Testing tab navigation order with mode open, no tabindex and delegatesFocus=true.'); |
| |
| promise_test(async () => { |
| const { shadowRoot, hostDiv } = prepareDOMTree(sandbox, 'open', 0, false); |
| assert_false(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, 0); |
| |
| const elements = [ |
| 'input-before', |
| 'host-div', |
| 'host-div/inner-input', |
| 'input-after', |
| ]; |
| |
| await assert_focus_navigation_bidirectional(elements); |
| }, 'Testing tab navigation order with mode open, tabindex=0 and delegatesFocus=false.'); |
| |
| promise_test(async () => { |
| const { shadowRoot, hostDiv } = prepareDOMTree(sandbox, 'open', 0, true); |
| assert_true(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, 0); |
| |
| const elements = [ |
| 'input-before', |
| // 'host-div', // should skip host when delegatesFocus=true |
| 'host-div/inner-input', |
| 'input-after', |
| ]; |
| |
| await assert_focus_navigation_bidirectional(elements); |
| }, 'Testing tab navigation order with mode open, tabindex=0 and delegatesFocus=true.'); |
| |
| promise_test(async () => { |
| const { shadowRoot, hostDiv } = prepareDOMTree(sandbox, 'open', -1, false); |
| assert_false(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, -1); |
| |
| const elements = [ |
| 'input-before', |
| 'input-after', |
| ]; |
| |
| await assert_focus_navigation_bidirectional(elements); |
| }, 'Testing tab navigation order with mode open, tabindex=-1 and delegatesFocus=false.'); |
| |
| promise_test(async () => { |
| const { shadowRoot, hostDiv } = prepareDOMTree(sandbox, 'open', -1, true); |
| assert_true(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, -1); |
| |
| const elements = [ |
| 'input-before', |
| // 'host-div/inner-input', // The whole shadow tree should be skipped |
| 'input-after', |
| ]; |
| |
| await assert_focus_navigation_bidirectional(elements); |
| }, 'Testing tab navigation order with mode open, tabindex=-1 and delegatesFocus=true.'); |
| |
| promise_test(async () => { |
| const { shadowRoot, hostDiv } = prepareDOMTree(sandbox, 'open', 1, false); |
| assert_false(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, 1); |
| |
| const elements = [ |
| 'host-div', |
| 'host-div/inner-input', |
| 'input-before', |
| 'input-after', |
| ]; |
| |
| await assert_focus_navigation_bidirectional(elements); |
| }, 'Testing tab navigation order with mode open, tabindex=1 and delegatesFocus=false.'); |
| |
| promise_test(async () => { |
| const { shadowRoot, hostDiv } = prepareDOMTree(sandbox, 'open', 1, true); |
| assert_true(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, 1); |
| |
| const elements = [ |
| // 'host-div', // should skip host when delegatesFocus=true |
| 'host-div/inner-input', |
| 'input-before', |
| 'input-after', |
| ]; |
| |
| await assert_focus_navigation_bidirectional(elements); |
| }, 'Testing tab navigation order with mode open, tabindex=1 and delegatesFocus=true.'); |
| |
| |
| promise_test(async () => { |
| const { |
| hostDiv, |
| shadowRoot, |
| inputBefore, |
| innerInput, |
| inputAfter, |
| } = prepareDOMTree(sandbox, 'closed', null, false); |
| assert_false(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, -1); |
| |
| const elements = [ |
| [inputBefore], |
| [innerInput, shadowRoot], |
| [inputAfter], |
| ]; |
| |
| await assert_focus_navigation_bidirectional_with_shadow_root(elements, false); |
| }, 'Testing tab navigation order with mode closed, no tabindex and delegatesFocus=false.'); |
| |
| promise_test(async () => { |
| const { |
| hostDiv, |
| shadowRoot, |
| inputBefore, |
| innerInput, |
| inputAfter, |
| } = prepareDOMTree(sandbox, 'closed', null, true); |
| assert_true(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, -1); |
| |
| const elements = [ |
| [inputBefore], |
| [innerInput, shadowRoot], |
| [inputAfter], |
| ]; |
| |
| await assert_focus_navigation_bidirectional_with_shadow_root(elements, false); |
| }, 'Testing tab navigation order with mode closed, no tabindex and delegatesFocus=true.'); |
| |
| promise_test(async () => { |
| const { |
| hostDiv, |
| shadowRoot, |
| inputBefore, |
| innerInput, |
| inputAfter, |
| } = prepareDOMTree(sandbox, 'closed', 0, false); |
| assert_false(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, 0); |
| |
| const elements = [ |
| [inputBefore], |
| [hostDiv], |
| [innerInput, shadowRoot], |
| [inputAfter], |
| ]; |
| |
| await assert_focus_navigation_bidirectional_with_shadow_root(elements, false); |
| }, 'Testing tab navigation order with mode closed, tabindex=0 and delegatesFocus=false.'); |
| |
| promise_test(async () => { |
| const { |
| hostDiv, |
| shadowRoot, |
| inputBefore, |
| innerInput, |
| inputAfter, |
| } = prepareDOMTree(sandbox, 'closed', 0, true); |
| assert_true(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, 0); |
| |
| const elements = [ |
| [inputBefore], |
| // [hostDiv], // should skip host when delegatesFocus=true |
| [innerInput, shadowRoot], |
| [inputAfter], |
| ]; |
| |
| await assert_focus_navigation_bidirectional_with_shadow_root(elements, false); |
| }, 'Testing tab navigation order with mode closed, tabindex=0 and delegatesFocus=true.'); |
| |
| promise_test(async () => { |
| const { |
| hostDiv, |
| shadowRoot, |
| inputBefore, |
| innerInput, |
| inputAfter, |
| } = prepareDOMTree(sandbox, 'closed', -1, false); |
| assert_false(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, -1); |
| |
| const elements = [ |
| [inputBefore], |
| [inputAfter], |
| ]; |
| |
| await assert_focus_navigation_bidirectional_with_shadow_root(elements, false); |
| }, 'Testing tab navigation order with mode closed, tabindex=-1 and delegatesFocus=false.'); |
| |
| promise_test(async () => { |
| const { |
| hostDiv, |
| shadowRoot, |
| inputBefore, |
| innerInput, |
| inputAfter, |
| } = prepareDOMTree(sandbox, 'closed', -1, true); |
| assert_true(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, -1); |
| |
| const elements = [ |
| [inputBefore], |
| // [innerInput, shadowRoot], // The whole shadow tree should be skipped |
| [inputAfter], |
| ]; |
| |
| await assert_focus_navigation_bidirectional_with_shadow_root(elements, false); |
| }, 'Testing tab navigation order with mode closed, tabindex=-1 and delegatesFocus=true.'); |
| |
| promise_test(async () => { |
| const { |
| hostDiv, |
| shadowRoot, |
| inputBefore, |
| innerInput, |
| inputAfter, |
| } = prepareDOMTree(sandbox, 'closed', 1, false); |
| assert_false(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, 1); |
| |
| const elements = [ |
| [hostDiv], |
| [innerInput, shadowRoot], |
| [inputBefore], |
| [inputAfter], |
| ]; |
| |
| await assert_focus_navigation_bidirectional_with_shadow_root(elements, false); |
| }, 'Testing tab navigation order with mode closed, tabindex=1 and delegatesFocus=false.'); |
| |
| promise_test(async () => { |
| const { |
| hostDiv, |
| shadowRoot, |
| inputBefore, |
| innerInput, |
| inputAfter, |
| } = prepareDOMTree(sandbox, 'closed', 1, true); |
| assert_true(shadowRoot.delegatesFocus); |
| assert_equals(hostDiv.tabIndex, 1); |
| |
| const elements = [ |
| // [hostDiv], // should skip host when delegatesFocus=true |
| [innerInput, shadowRoot], |
| [inputBefore], |
| [inputAfter], |
| ]; |
| |
| await assert_focus_navigation_bidirectional_with_shadow_root(elements, false); |
| }, 'Testing tab navigation order with mode closed, tabindex=1 and delegatesFocus=true.'); |
| |
| |
| </script> |