<!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_forward(elements, false); elements.reverse(); await assert_focus_navigation_backward(elements, false); }, '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_forward(elements, false); elements.reverse(); await assert_focus_navigation_backward(elements, false); }, '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_forward(elements, false); elements.reverse(); await assert_focus_navigation_backward(elements, false); }, '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_forward(elements, false); elements.reverse(); await assert_focus_navigation_backward(elements, false); }, '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_forward(elements, false); elements.reverse(); await assert_focus_navigation_backward(elements, false); }, '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_forward(elements, false); elements.reverse(); await assert_focus_navigation_backward(elements, false); }, '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_forward(elements, false); elements.reverse(); await assert_focus_navigation_backward(elements, false); }, '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_forward(elements, false); elements.reverse(); await assert_focus_navigation_backward(elements, false); }, '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_forward_with_shadow_root(elements, false); elements.reverse(); await assert_focus_navigation_backward_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_forward_with_shadow_root(elements, false); elements.reverse(); await assert_focus_navigation_backward_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_forward_with_shadow_root(elements, false); elements.reverse(); await assert_focus_navigation_backward_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_forward_with_shadow_root(elements, false); elements.reverse(); await assert_focus_navigation_backward_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_forward_with_shadow_root(elements, false); elements.reverse(); await assert_focus_navigation_backward_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_forward_with_shadow_root(elements, false); elements.reverse(); await assert_focus_navigation_backward_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_forward_with_shadow_root(elements, false); elements.reverse(); await assert_focus_navigation_backward_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_forward_with_shadow_root(elements, false); elements.reverse(); await assert_focus_navigation_backward_with_shadow_root(elements, false); }, 'Testing tab navigation order with mode closed, tabindex=1 and delegatesFocus=true.'); </script>