338 lines
9.2 KiB
HTML
338 lines
9.2 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta name="author" title="Sean Feng" href="mailto:sefeng@mozilla.com">
|
|
<meta name="assert" content="Elements with autofocus should have high precedence over other elements for delegates focus">
|
|
<link rel="help" href="https://github.com/whatwg/html/pull/6990">
|
|
<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/shadow-utils.js"></script>
|
|
</head>
|
|
|
|
<body>
|
|
<script>
|
|
function createShadowDOMTree() {
|
|
// <div #host> (delegatesFocus = true)
|
|
// #shadowRoot
|
|
// <div #firstOuterDiv>
|
|
// <div #innertHost>
|
|
// #shadowRoot
|
|
// <div #firstInnerDiv>
|
|
// <div #secondInnerDiv>
|
|
// <div #secondOuterDiv>
|
|
const host = document.createElement("div");
|
|
host.setAttribute("id", "host");
|
|
const outerRoot = host.attachShadow({mode: "open", delegatesFocus: true});
|
|
|
|
const firstOuterDiv = document.createElement("div");
|
|
|
|
const innerHost = document.createElement("div");
|
|
const innerRoot = innerHost.attachShadow({mode: "open"});
|
|
const firstInnerDiv = document.createElement("div");
|
|
const secondInnerDiv = document.createElement("div");
|
|
innerRoot.appendChild(firstInnerDiv);
|
|
innerRoot.appendChild(secondInnerDiv);
|
|
|
|
const secondOuterDiv = document.createElement("div");
|
|
|
|
outerRoot.appendChild(firstOuterDiv);
|
|
outerRoot.appendChild(innerHost);
|
|
outerRoot.appendChild(secondOuterDiv);
|
|
document.body.appendChild(host);
|
|
return [
|
|
host,
|
|
outerRoot,
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
innerHost,
|
|
innerRoot,
|
|
firstInnerDiv,
|
|
secondInnerDiv
|
|
]
|
|
}
|
|
|
|
function resetShadowDOMTree() {
|
|
const host = document.getElementById("host");
|
|
if (host) {
|
|
host.remove();
|
|
}
|
|
return createShadowDOMTree();
|
|
}
|
|
|
|
function resetTabIndexAndFocus(
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
firstInnerDiv,
|
|
secondInnerDiv,
|
|
outerRoot,
|
|
innerRoot
|
|
) {
|
|
firstOuterDiv.removeAttribute("tabindex");
|
|
firstOuterDiv.removeAttribute("autofocus");
|
|
|
|
secondOuterDiv.removeAttribute("tabindex");
|
|
secondOuterDiv.removeAttribute("autofocus");
|
|
|
|
firstInnerDiv.removeAttribute("tabindex");
|
|
firstInnerDiv.removeAttribute("autofocus");
|
|
|
|
secondInnerDiv.removeAttribute("tabindex");
|
|
secondInnerDiv.removeAttribute("autofocus");
|
|
|
|
resetFocus(document);
|
|
resetFocus(outerRoot);
|
|
resetFocus(innerRoot);
|
|
}
|
|
|
|
function setAllTabIndexTo(
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
firstInnerDiv,
|
|
secondInnerDiv,
|
|
tabIndex
|
|
) {
|
|
firstOuterDiv.tabIndex = tabIndex;
|
|
secondOuterDiv.tabIndex = tabIndex;
|
|
firstInnerDiv.tabIndex = tabIndex;
|
|
secondInnerDiv.tabIndex = tabIndex;
|
|
}
|
|
|
|
test(function() {
|
|
const [
|
|
host,
|
|
outerRoot,
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
innerHost,
|
|
innerRoot,
|
|
firstInnerDiv,
|
|
secondInnerDiv
|
|
] = resetShadowDOMTree();
|
|
|
|
resetTabIndexAndFocus(
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
firstInnerDiv,
|
|
secondInnerDiv,
|
|
outerRoot,
|
|
innerRoot
|
|
);
|
|
|
|
setAllTabIndexTo(
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
firstInnerDiv,
|
|
secondInnerDiv,
|
|
0
|
|
);
|
|
|
|
// <div #host> (delegatesFocus = true)
|
|
// #shadowRoot
|
|
// <div tabIndex=0 #firstOuterDiv>
|
|
// <div #innertHost>
|
|
// #shadowRoot
|
|
// <div tabIndex=0 #firstInnerDiv>
|
|
// <div tabIndex=0 #secondInnerDiv>
|
|
// <div tabIndex=0 autofocus #secondOuterDiv>
|
|
secondOuterDiv.autofocus = true;
|
|
secondOuterDiv.setAttribute("autofocus", true);
|
|
|
|
host.focus();
|
|
|
|
assert_equals(document.activeElement, host);
|
|
assert_equals(outerRoot.activeElement, secondOuterDiv);
|
|
}, "The second input should be focused since it has autofocus");
|
|
|
|
test(function() {
|
|
const [
|
|
host,
|
|
outerRoot,
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
innerHost,
|
|
innerRoot,
|
|
firstInnerDiv,
|
|
secondInnerDiv
|
|
] = resetShadowDOMTree();
|
|
|
|
resetTabIndexAndFocus(
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
firstInnerDiv,
|
|
secondInnerDiv,
|
|
outerRoot,
|
|
innerRoot
|
|
);
|
|
|
|
// <div #host> (delegatesFocus = true)
|
|
// #shadowRoot
|
|
// <div #firstOuterDiv>
|
|
// <div #innertHost>
|
|
// #shadowRoot
|
|
// <div tabIndex=0 #firstInnerDiv>
|
|
// <div tabIndex=0 autofocus #secondInnerDiv>
|
|
// <div #secondOuterDiv>
|
|
firstInnerDiv.tabIndex = 0;
|
|
secondInnerDiv.tabIndex = 0;
|
|
secondInnerDiv.setAttribute("autofocus", true);
|
|
|
|
host.focus();
|
|
assert_equals(document.activeElement, document.body);
|
|
assert_equals(outerRoot.activeElement, null);
|
|
}, "Focus should not be delegated to the autofocus element because the inner host doesn't have delegates focus");
|
|
|
|
test(function() {
|
|
const [
|
|
host,
|
|
outerRoot,
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
innerHost,
|
|
innerRoot,
|
|
firstInnerDiv,
|
|
secondInnerDiv
|
|
] = resetShadowDOMTree();
|
|
|
|
resetTabIndexAndFocus(
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
firstInnerDiv,
|
|
secondInnerDiv,
|
|
outerRoot,
|
|
innerRoot
|
|
);
|
|
|
|
const newInnerHost = document.createElement("div");
|
|
const newInnerRoot = newInnerHost.attachShadow({mode: "open", delegatesFocus: true});
|
|
const newFirstInnerDiv = document.createElement("div");
|
|
const newSecondInnerDiv = document.createElement("div");
|
|
newFirstInnerDiv.setAttribute("tabIndex", 0);
|
|
newSecondInnerDiv.setAttribute("tabIndex", 0);
|
|
|
|
newSecondInnerDiv.setAttribute("autofocus", true);
|
|
newInnerRoot.appendChild(newFirstInnerDiv);
|
|
newInnerRoot.appendChild(newSecondInnerDiv);
|
|
|
|
// <div #host> (delegatesFocus = true)
|
|
// #shadowRoot
|
|
// <div #firstOuterDiv>
|
|
// <div #innertHost> (delegatesFocus = true)
|
|
// #shadowRoot
|
|
// <div tabIndex=0 #newFirstInnerDiv>
|
|
// <div tabIndex=0 autofocus #newSecondInnerDiv>
|
|
// <div #secondOuterDiv>
|
|
outerRoot.replaceChild(newInnerHost, innerHost);
|
|
|
|
host.focus();
|
|
|
|
assert_equals(document.activeElement, host);
|
|
assert_equals(outerRoot.activeElement, newInnerHost);
|
|
assert_equals(newInnerRoot.activeElement, newSecondInnerDiv);
|
|
}, "Focus should be delegated to the autofocus element when the inner host has delegates focus");
|
|
|
|
test(function() {
|
|
const [
|
|
host,
|
|
outerRoot,
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
innerHost,
|
|
innerRoot,
|
|
firstInnerDiv,
|
|
secondInnerDiv
|
|
] = resetShadowDOMTree();
|
|
|
|
resetTabIndexAndFocus(
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
firstInnerDiv,
|
|
secondInnerDiv,
|
|
outerRoot,
|
|
innerRoot
|
|
);
|
|
|
|
// <div #host> (delegatesFocus = true)
|
|
// #shadowRoot
|
|
// <slot>
|
|
// (slotted) <div autofocus tabIndex=0 #slottedAutofocus></div>
|
|
// <div tabIndex=0 #firstOuterDiv>
|
|
// <div #innertHost>
|
|
// #shadowRoot
|
|
// <div tabIndex=0 #firstInnerDiv>
|
|
// <div tabIndex=0 autofocus #secondInnerDiv>
|
|
// <div #secondOuterDiv>
|
|
|
|
const slottedAutofocus = document.createElement("div");
|
|
slottedAutofocus.tabIndex = 0;
|
|
slottedAutofocus.setAttribute("autofocus", true);
|
|
host.appendChild(slottedAutofocus);
|
|
|
|
const slot = document.createElement("slot");
|
|
outerRoot.insertBefore(slot, firstOuterDiv);
|
|
|
|
firstOuterDiv.tabIndex = 0;
|
|
|
|
host.focus();
|
|
assert_equals(document.activeElement, host);
|
|
assert_equals(outerRoot.activeElement, firstOuterDiv);
|
|
}, "Focus should not be delegated to the slotted elements");
|
|
|
|
test(function() {
|
|
const [
|
|
host,
|
|
outerRoot,
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
innerHost,
|
|
innerRoot,
|
|
firstInnerDiv,
|
|
secondInnerDiv
|
|
] = resetShadowDOMTree();
|
|
|
|
resetTabIndexAndFocus(
|
|
firstOuterDiv,
|
|
secondOuterDiv,
|
|
firstInnerDiv,
|
|
secondInnerDiv,
|
|
outerRoot,
|
|
innerRoot
|
|
);
|
|
|
|
// <div #host> (delegatesFocus = true)
|
|
// #shadowRoot
|
|
// <div #firstOuterDiv>
|
|
// <div tabIndex=0 #firstNestedDiv>
|
|
// <div tabIndex=0 #secondNestedDiv>
|
|
// <div tabIndex=0 autofocus #thirdNestedDiv>
|
|
// <div #innertHost>
|
|
// #shadowRoot
|
|
// <div #firstInnerDiv>
|
|
// <div #secondInnerDiv>
|
|
// <div autofocus tabIndex=0 #secondOuterDiv>
|
|
|
|
secondInnerDiv.tabIndex = 0;
|
|
secondInnerDiv.setAttribute("autofocus", true);
|
|
|
|
const firstNestedDiv = document.createElement("div");
|
|
const secondNestedDiv = document.createElement("div");
|
|
const thirdNestedDiv = document.createElement("div");
|
|
|
|
firstNestedDiv.tabIndex = 0;
|
|
secondNestedDiv.tabIndex = 0;
|
|
thirdNestedDiv.tabIndex = 0;
|
|
thirdNestedDiv.setAttribute("autofocus", true);
|
|
|
|
firstOuterDiv.appendChild(firstNestedDiv);
|
|
firstNestedDiv.appendChild(secondNestedDiv);
|
|
secondNestedDiv.appendChild(thirdNestedDiv);
|
|
|
|
host.focus();
|
|
|
|
assert_equals(document.activeElement, host);
|
|
assert_equals(outerRoot.activeElement, thirdNestedDiv);
|
|
}, "Focus should be delegated to the nested div which has autofocus based on the tree order");
|
|
</script>
|
|
</body>
|
|
</html>
|