diff options
Diffstat (limited to '')
-rw-r--r-- | dom/base/test/file_focus_shadow_dom.html | 999 |
1 files changed, 999 insertions, 0 deletions
diff --git a/dom/base/test/file_focus_shadow_dom.html b/dom/base/test/file_focus_shadow_dom.html new file mode 100644 index 0000000000..6fa9d1b88e --- /dev/null +++ b/dom/base/test/file_focus_shadow_dom.html @@ -0,0 +1,999 @@ +<html> + <head> + <title>Test for Bug 1453693</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <script> + + class TestNode extends HTMLElement { + constructor() { + super(); + const styles = "<style>:focus{background-color:yellow;}</style>"; + this.attachShadow({ mode: 'open' }); + this.shadowRoot.innerHTML = + `${styles}<div tabindex='-1'>test node</div> <slot></slot>`; + }} + + window.customElements.define('test-node', TestNode); + + var lastFocusTarget; + function focusLogger(event) { + lastFocusTarget = event.target; + console.log(event.target + " under " + event.target.parentNode); + event.stopPropagation(); + } + + function testTabbingThroughShadowDOMWithTabIndexes() { + var anchor = document.createElement("a"); + anchor.onfocus = focusLogger; + anchor.href = "#"; + anchor.textContent = "in light DOM"; + document.body.appendChild(anchor); + + var host = document.createElement("div"); + document.body.appendChild(host); + + var sr = host.attachShadow({mode: "open"}); + var shadowAnchor = anchor.cloneNode(false); + shadowAnchor.onfocus = focusLogger; + shadowAnchor.textContent = "in shadow DOM"; + sr.appendChild(shadowAnchor); + var shadowInput = document.createElement("input"); + shadowInput.onfocus = focusLogger; + shadowInput.tabIndex = 1; + sr.appendChild(shadowInput); + + var shadowDate = document.createElement("input"); + shadowDate.type = "date"; + shadowDate.onfocus = focusLogger; + shadowDate.tabIndex = 1; + sr.appendChild(shadowDate); + + var shadowIframe = document.createElement("iframe"); + shadowIframe.tabIndex = 1; + sr.appendChild(shadowIframe); + shadowIframe.contentDocument.body.innerHTML = "<input>"; + + var input = document.createElement("input"); + input.onfocus = focusLogger; + input.tabIndex = 1; + document.body.appendChild(input); + + var input2 = document.createElement("input"); + input2.onfocus = focusLogger; + document.body.appendChild(input2); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input, "Should have focused input element. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowDate, "Should have focused date element with a calendar button in shadow DOM. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(shadowIframe.contentDocument.activeElement, + shadowIframe.contentDocument.documentElement, + "Should have focused document element in shadow iframe. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(shadowIframe.contentDocument.activeElement, + shadowIframe.contentDocument.body.firstChild, + "Should have focused input element in shadow iframe. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (3)"); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input2, "Should have focused input[2] element. (3)"); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (4)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(shadowIframe.contentDocument.activeElement, + shadowIframe.contentDocument.body.firstChild, + "Should have focused input element in shadow iframe. (4)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(shadowIframe.contentDocument.activeElement, + shadowIframe.contentDocument.documentElement, + "Should have focused document element in shadow iframe. (4)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowDate, "Should have focused date element with a calendar button in shadow DOM. (4)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (4)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (4)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input, "Should have focused input element. (4)"); + + document.body.innerHTML = null; + } + + function testTabbingThroughSimpleShadowDOM() { + var anchor = document.createElement("a"); + anchor.onfocus = focusLogger; + anchor.href = "#"; + anchor.textContent = "in light DOM"; + document.body.appendChild(anchor); + anchor.focus(); + + var host = document.createElement("div"); + document.body.appendChild(host); + + var sr = host.attachShadow({mode: "open"}); + var shadowAnchor = anchor.cloneNode(false); + shadowAnchor.onfocus = focusLogger; + shadowAnchor.textContent = "in shadow DOM"; + sr.appendChild(shadowAnchor); + var shadowInput = document.createElement("input"); + shadowInput.onfocus = focusLogger; + sr.appendChild(shadowInput); + + var hiddenShadowButton = document.createElement("button"); + hiddenShadowButton.setAttribute("style", "display: none;"); + sr.appendChild(hiddenShadowButton); + + var input = document.createElement("input"); + input.onfocus = focusLogger; + document.body.appendChild(input); + + var input2 = document.createElement("input"); + input2.onfocus = focusLogger; + document.body.appendChild(input2); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM."); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM."); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input, "Should have focused input element."); + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input2, "Should have focused input[2] element."); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input, "Should have focused input element. (2)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (2)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (2)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (2)"); + + host.remove(); + input.remove(); + input2.remove(); + } + + function testTabbingThroughNestedShadowDOM() { + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)"); + + var host = document.createElement("div"); + host.id = "host"; + document.body.appendChild(host); + + var sr0 = host.attachShadow({mode: "open"}); + sr0.innerHTML = "<button id='button'>X</button><br id='br'><div id='h1'></div><div id='h2'></div>"; + var button = sr0.getElementById("button"); + button.onfocus = focusLogger; + + var h1 = sr0.getElementById("h1"); + var sr1 = h1.attachShadow({mode: "open"}); + sr1.innerHTML = "h1 <input id='h11' placeholder='click me and press tab'><input id='h12' placeholder='and then tab again'>"; + var input11 = sr1.getElementById("h11"); + input11.onfocus = focusLogger; + var input12 = sr1.getElementById("h12"); + input12.onfocus = focusLogger; + + var h2 = sr0.getElementById("h2"); + var sr2 = h2.attachShadow({mode: "open"}); + sr2.innerHTML = "h2 <input id='h21'><input id='h22'>"; + var input21 = sr2.getElementById("h21"); + input21.onfocus = focusLogger; + var input22 = sr2.getElementById("h22"); + input22.onfocus = focusLogger; + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (1)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (1)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (2)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (3)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input22, "[nested shadow] Should have focused input element. (4)"); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (5)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (6)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (7)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (8)"); + + // Back to beginning, outside of Shadow DOM. + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)"); + + host.remove(); + } + + function testTabbingThroughDisplayContentsHost() { + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)"); + + var host = document.createElement("div"); + host.id = "host"; + host.setAttribute("style", "display: contents; border: 1px solid black;"); + document.body.appendChild(host); + + var sr0 = host.attachShadow({mode: "open"}); + sr0.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>"; + var shadowInput1 = sr0.getElementById("shadowInput1"); + shadowInput1.onfocus = focusLogger; + var shadowInput2 = sr0.getElementById("shadowInput2"); + shadowInput2.onfocus = focusLogger; + + var host1 = document.createElement("div"); + host1.id = "host"; + host1.tabIndex = 0; + host1.setAttribute("style", "display: contents; border: 1px solid black;"); + document.body.appendChild(host1); + + var sr1 = host1.attachShadow({mode: "open"}); + sr1.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>"; + var shadowInput3 = sr1.getElementById("shadowInput1"); + shadowInput3.onfocus = focusLogger; + var shadowInput4 = sr1.getElementById("shadowInput2"); + shadowInput4.onfocus = focusLogger; + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (1)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (2)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (3)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowInput4, "Should have focused input element. (4)"); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (5)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (6)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (7)"); + + // Back to beginning, outside of Shadow DOM. + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)"); + + host.remove(); + host1.remove(); + } + + function testTabbingThroughLightDOMShadowDOMLightDOM() { + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + var host = document.createElement("span"); + host.innerHTML = "\n"; + host.id = "host"; + document.body.appendChild(host); + + var sr0 = host.attachShadow({mode: "open"}); + sr0.innerHTML = document.getElementById("template").innerHTML; + var p1 = sr0.getElementById("p1"); + p1.onfocus = focusLogger; + var p2 = sr0.getElementById("p2"); + p2.onfocus = focusLogger; + + var p = document.createElement("p"); + p.innerHTML = " <a href='#p'>link 1</a> "; + var a = p.firstElementChild; + a.onfocus = focusLogger; + document.body.appendChild(p); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, p1, "Should have focused p1."); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, p2, "Should have focused p2."); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, a, "Should have focused a."); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, p2, "Should have focused p2."); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, p1, "Should have focused p1."); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + host.remove(); + p.remove(); + } + + function testFocusableHost() { + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + var host = document.createElement("div"); + host.id = "host"; + host.tabIndex = 0; + host.onfocus = focusLogger; + document.body.appendChild(host); + + var slotted = document.createElement("div"); + slotted.tabIndex = 0; + slotted.onfocus = focusLogger; + host.appendChild(slotted); + + var sr0 = host.attachShadow({mode: "open"}); + sr0.appendChild(document.createElement("slot")); + + var p = document.createElement("p"); + p.innerHTML = " <a href='#p'>link 1</a> "; + var a = p.firstElementChild; + a.onfocus = focusLogger; + document.body.appendChild(p); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, host, "Should have focused host."); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, slotted, "Should have focused slotted."); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, a, "Should have focused a."); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, slotted, "Should have focused slotted."); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, host, "Should have focused host."); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + host.remove(); + p.remove(); + } + + function testShiftTabbingThroughFocusableHost() { + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + var host = document.createElement("div"); + host.id = "host"; + host.tabIndex = 0; + host.onfocus = focusLogger; + document.body.appendChild(host); + + var sr = host.attachShadow({mode: "open"}); + var shadowButton = document.createElement("button"); + shadowButton.innerText = "X"; + shadowButton.onfocus = focusLogger; + sr.appendChild(shadowButton); + + var shadowInput = document.createElement("input"); + shadowInput.onfocus = focusLogger; + sr.appendChild(shadowInput); + sr.appendChild(document.createElement("br")); + + var input = document.createElement("input"); + input.onfocus = focusLogger; + document.body.appendChild(input); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, host, "Should have focused host element. (1)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (2)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input, "Should have focused input element. (4)"); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (5)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (6)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + // focus is already on host + opener.is(sr.activeElement, null, + "Focus should have left button element in shadow DOM. (7)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + host.remove(); + input.remove(); + } + + function testTabbingThroughNestedSlot() { + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus."); + + var host0 = document.createElement("div"); + var sr0 = host0.attachShadow({mode: "open"}); + sr0.innerHTML = "<slot></slot>"; + document.body.appendChild(host0); + + // focusable + var host00 = document.createElement("div"); + var sr00 = host00.attachShadow({mode: "open"}); + var div00 = document.createElement("div"); + div00.tabIndex = 0; + div00.onfocus = focusLogger; + sr00.appendChild(div00); + host0.appendChild(host00); + + // not focusable + var host01 = document.createElement("div"); + var sr01 = host01.attachShadow({mode: "open"}); + sr01.innerHTML = "<div></div>"; + host0.appendChild(host01); + + // focusable + var host02 = document.createElement("div"); + var sr02 = host02.attachShadow({mode: "open"}); + var div02 = document.createElement("div"); + div02.tabIndex = 0; + div02.onfocus = focusLogger; + sr02.appendChild(div02); + host0.appendChild(host02); + + var host1 = document.createElement("div"); + var sr1 = host1.attachShadow({mode: "open"}); + sr1.innerHTML = "<slot></slot>"; + document.body.appendChild(host1); + + var host10 = document.createElement("div"); + var sr10 = host10.attachShadow({mode: "open"}); + sr10.innerHTML = "<slot></slot>"; + host1.appendChild(host10); + + var input10 = document.createElement("input"); + input10.onfocus = focusLogger; + host10.appendChild(input10); + + var host11 = document.createElement("div"); + var sr11 = host11.attachShadow({mode: "open"}); + sr11.innerHTML = "<slot></slot>"; + host1.appendChild(host11); + + var input11 = document.createElement("input"); + input11.onfocus = focusLogger; + host11.appendChild(input11); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, div00, "Should have focused div element in shadow DOM. (1)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, div02, "Should have focused div element in shadow DOM. (2)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (3)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input11, "Should have focused button element in shadow DOM. (4)"); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (5)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, div02, "Should have focused input element in shadow DOM. (6)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, div00, "Should have focused input element in shadow DOM. (7)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + host0.remove(); + host1.remove(); + } + + function testTabbingThroughSlotInLightDOM() { + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus."); + + var input0 = document.createElement("input"); + input0.onfocus = focusLogger; + document.body.appendChild(input0); + + var slot1 = document.createElement("slot"); + document.body.appendChild(slot1); + + var input10 = document.createElement("input"); + input10.onfocus = focusLogger; + slot1.appendChild(input10); + + var input11 = document.createElement("input"); + input11.onfocus = focusLogger; + slot1.appendChild(input11); + + var input2 = document.createElement("input"); + input2.onfocus = focusLogger; + document.body.appendChild(input2); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input0, "Should have focused input element. (1)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (2)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (3)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input2, "Should have focused input element. (4)"); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (5)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (6)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input0, "Should have focused input element. (7)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + input0.remove(); + slot1.remove(); + input2.remove(); + } + + function testTabbingThroughFocusableSlotInLightDOM() { + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus."); + + var slot0 = document.createElement("slot"); + slot0.tabIndex = 0; + slot0.setAttribute("style", "display: inline;"); + slot0.onfocus = focusLogger; + document.body.appendChild(slot0); + + var slot00 = document.createElement("slot"); + slot00.tabIndex = 0; + slot00.setAttribute("style", "display: inline;"); + slot00.onfocus = focusLogger; + slot0.appendChild(slot00); + + var input000 = document.createElement("input"); + input000.onfocus = focusLogger; + slot00.appendChild(input000); + + var input01 = document.createElement("input"); + input01.onfocus = focusLogger; + slot0.appendChild(input01); + + var input1 = document.createElement("input"); + input1.onfocus = focusLogger; + document.body.appendChild(input1); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, slot0, "Should have focused slot element. (1)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, slot00, "Should have focused slot element. (2)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (3)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (4)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input1, "Should have focused input element. (5)"); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (6)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (7)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, slot00, "Should have focused slot element. (8)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, slot0, "Should have focused slot element. (9)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + slot0.remove(); + input1.remove(); + } + + function testTabbingThroughScrollableShadowDOM() { + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus."); + + var host0 = document.createElement("div"); + host0.setAttribute("style", "height: 50px; overflow: auto;"); + host0.onfocus = focusLogger; + document.body.appendChild(host0); + + var sr0 = host0.attachShadow({mode: "open"}); + sr0.innerHTML = ` + <style> + div,slot { + height: 30px; + display: block; + overflow: auto; + } + input { + display: block; + } + </style> + `; + + var input00 = document.createElement("input"); + input00.setAttribute("style", "background-color: red;"); + input00.onfocus = focusLogger; + sr0.appendChild(input00); + + var container01 = document.createElement("div"); + container01.onfocus = focusLogger; + sr0.appendChild(container01); + + var input010 = document.createElement("input"); + input010.onfocus = focusLogger; + container01.appendChild(input010); + + var input011 = document.createElement("input"); + input011.onfocus = focusLogger; + container01.appendChild(input011); + + var slot02 = document.createElement("slot"); + slot02.onfocus = focusLogger; + sr0.appendChild(slot02); + + var input020 = document.createElement("input"); + input020.setAttribute("style", "display: block;"); + input020.onfocus = focusLogger; + host0.appendChild(input020); + + var input021 = document.createElement("input"); + input021.setAttribute("style", "display: block;"); + input021.onfocus = focusLogger; + host0.appendChild(input021); + + var input1 = document.createElement("input"); + input1.onfocus = focusLogger; + document.body.appendChild(input1); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, host0, "Should have focused shadow host element. (1)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (2)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (3)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (4)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (5)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (6)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (7)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (8)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, input1, "Should have focused input element in light dom. (9)"); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (10)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (11)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (12)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (13)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (14)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (15)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (16)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + // focus is already on host + opener.is(sr0.activeElement, null, + "Focus should have left input element in shadow DOM. (7)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + host0.remove(); + input1.remove(); + } + + // Bug 1604140 + function testTabbingThroughScrollableShadowHost() { + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + let aboveFirstHost = document.createElement("div"); + aboveFirstHost.tabIndex = 0; + aboveFirstHost.onfocus = focusLogger; + document.body.appendChild(aboveFirstHost); + + let firstHost = document.createElement("div"); + firstHost.style = "overflow: scroll"; + document.body.appendChild(firstHost); + + let firstShadow = firstHost.attachShadow({mode: "open"}); + let divInFirstShadow = document.createElement("div"); + divInFirstShadow.tabIndex = 0; + divInFirstShadow.onfocus = focusLogger; + firstShadow.appendChild(divInFirstShadow); + + let aboveSecondHost = document.createElement("div"); + aboveSecondHost.tabIndex = 0; + aboveSecondHost.onfocus = focusLogger; + document.body.appendChild(aboveSecondHost); + + let secondHost = document.createElement("div"); + secondHost.style = "overflow: scroll"; + secondHost.tabIndex = 0; + secondHost.onfocus = focusLogger; + document.body.appendChild(secondHost); + + let secondShadow = secondHost.attachShadow({mode: "open"}); + let divInSecondShadow = document.createElement("div"); + divInSecondShadow.tabIndex = 0; + divInSecondShadow.onfocus = focusLogger; + secondShadow.appendChild(divInSecondShadow); + + let belowSecondHost = document.createElement("div"); + belowSecondHost.tabIndex = 0; + belowSecondHost.onfocus = focusLogger; + document.body.appendChild(belowSecondHost); + + document.body.offsetLeft; + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, aboveFirstHost, "Should have focused div above first host element. (1)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, divInFirstShadow, "Should have focused div in first shadow dom. (2)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, aboveSecondHost, "Should have focused div above second host element. (3)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, secondHost, "Should have focused second host element. (4)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, divInSecondShadow, "Should have focused div in second shadow dom. (5)"); + + synthesizeKey("KEY_Tab"); + opener.is(lastFocusTarget, belowSecondHost, "Should have focused div below second host. (6)"); + + // Backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, divInSecondShadow, "Should have focused div in second shadow dom. (7)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + // focus is already on second host, so lastFocusTarget won't get updated. + opener.is(document.activeElement, secondHost, "Should have focused second host element. (8)"); + opener.is(secondShadow.activeElement, null, "Focus should have left div in second shadow dom. (8)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, aboveSecondHost, "Should have focused div above second host element. (9)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, divInFirstShadow, "Should have focused div in first shadow dom. (10)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(lastFocusTarget, aboveFirstHost, "Should have focused div above first host element. (11)"); + + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, + "body's first child should have focus."); + + aboveFirstHost.remove(); + firstHost.remove(); + aboveSecondHost.remove(); + secondHost.remove(); + belowSecondHost.remove(); + } + + function testDeeplyNestedShadowTree() { + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus."); + var host1 = document.createElement("test-node"); + var lastHost = host1; + for (var i = 0; i < 20; ++i) { + lastHost.appendChild(document.createElement("test-node")); + lastHost = lastHost.firstChild; + } + + var input = document.createElement("input"); + document.body.appendChild(host1); + document.body.appendChild(input); + document.body.offsetLeft; + + // Test shadow tree which doesn't have anything tab-focusable. + host1.shadowRoot.querySelector("div").focus(); + synthesizeKey("KEY_Tab"); + is(document.activeElement, input, "Should have focused input element."); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus."); + + // Same test but with focusable elements in the tree... + var input2 = document.createElement("input"); + var host2 = host1.firstChild; + var host3 = host2.firstChild; + host2.insertBefore(input2, host3); + var input3 = document.createElement("input"); + lastHost.appendChild(input3); + document.body.offsetLeft; + host3.shadowRoot.querySelector("div").focus(); + synthesizeKey("KEY_Tab"); + is(document.activeElement, input3, "Should have focused input3 element."); + + // ...and backwards + host3.shadowRoot.querySelector("div").focus(); + synthesizeKey("KEY_Tab", {shiftKey: true}); + is(document.activeElement, input2, "Should have focused input2 element."); + + // Remove elements added to body element. + host1.remove(); + input.remove(); + + // Tests expect body.firstChild to have focus. + document.body.firstChild.focus(); + } + + // Bug 1558393 + function testBackwardsTabbingWithSlotsWithoutFocusableContent() { + let first = document.createElement("div"); + first.tabIndex = 0; + let host = document.createElement("div"); + host.tabIndex = 0; + let second = document.createElement("div"); + second.tabIndex = 0; + host.appendChild(document.createTextNode("foo")); + host.attachShadow({ mode: "open" }).innerHTML = `<slot></slot>`; + + document.body.appendChild(first); + document.body.appendChild(host); + document.body.appendChild(second); + document.body.offsetLeft; + + first.focus(); + opener.is(document.activeElement, first, "First light div should have focus"); + synthesizeKey("KEY_Tab"); + opener.is(document.activeElement, host, "Host should be focused"); + synthesizeKey("KEY_Tab"); + opener.is(document.activeElement, second, "Second light div should be focused"); + + // Now backwards + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, host, "Focus should return to host"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + opener.is(document.activeElement, first, "Focus should return to first light div"); + + second.remove(); + host.remove(); + first.remove(); + } + + function runTest() { + + testTabbingThroughShadowDOMWithTabIndexes(); + testTabbingThroughSimpleShadowDOM(); + testTabbingThroughNestedShadowDOM(); + testTabbingThroughDisplayContentsHost(); + testTabbingThroughLightDOMShadowDOMLightDOM(); + testFocusableHost(); + testShiftTabbingThroughFocusableHost(); + testTabbingThroughNestedSlot(); + testTabbingThroughSlotInLightDOM(); + testTabbingThroughFocusableSlotInLightDOM(); + testTabbingThroughScrollableShadowDOM(); + testTabbingThroughScrollableShadowHost(); + testDeeplyNestedShadowTree(); + testBackwardsTabbingWithSlotsWithoutFocusableContent(); + + opener.didRunTests(); + window.close(); + } + + function init() { + SimpleTest.waitForFocus(runTest); + } + </script> + <style> + </style> + <template id="template"> + <div style="overflow: hidden"> + <p tabindex="0" id="p1">component</p> + <p tabindex="0" id="p2">/component</p> + </div> + </template> + </head> + <body onload="init()"> + </body> +</html> |