<!DOCTYPE html>
<meta name="timeout" content="long">
<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>