tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

file_focus_shadow_dom.html (39950B)


      1 <html>
      2  <head>
      3    <title>Test for Bug 1453693</title>
      4    <script src="/tests/SimpleTest/SimpleTest.js"></script>
      5    <script src="/tests/SimpleTest/EventUtils.js"></script>
      6    <script>
      7 
      8      class TestNode extends HTMLElement {
      9      constructor() {
     10        super();
     11        const styles = "<style>:focus{background-color:yellow;}</style>";
     12        this.attachShadow({ mode: 'open' });
     13        this.shadowRoot.innerHTML =
     14          `${styles}<div tabindex='-1'>test node</div> <slot></slot>`;
     15      }}
     16 
     17      window.customElements.define('test-node', TestNode);
     18 
     19      var lastFocusTarget;
     20      function focusLogger(event) {
     21        lastFocusTarget = event.target;
     22        console.log(event.target + " under " + event.target.parentNode);
     23        event.stopPropagation();
     24      }
     25 
     26      function flushLayout(...iframes) {
     27        document.body.offsetHeight;
     28        iframes.forEach(ifr => ifr.contentDocument?.body.offsetHeight);
     29      }
     30 
     31      async function testTabbingThroughShadowDOMWithTabIndexes() {
     32        var anchor = document.createElement("a");
     33        anchor.onfocus = focusLogger;
     34        anchor.href = "#";
     35        anchor.textContent = "in light DOM";
     36        document.body.appendChild(anchor);
     37 
     38        var host = document.createElement("div");
     39        document.body.appendChild(host);
     40 
     41        var sr = host.attachShadow({mode: "open"});
     42        var shadowAnchor = anchor.cloneNode(false);
     43        shadowAnchor.onfocus = focusLogger;
     44        shadowAnchor.textContent = "in shadow DOM";
     45        sr.appendChild(shadowAnchor);
     46        var shadowInput = document.createElement("input");
     47        shadowInput.onfocus = focusLogger;
     48        shadowInput.tabIndex = 1;
     49        sr.appendChild(shadowInput);
     50 
     51        var shadowDate = document.createElement("input");
     52        shadowDate.type = "date";
     53        shadowDate.onfocus = focusLogger;
     54        shadowDate.tabIndex = 1;
     55        sr.appendChild(shadowDate);
     56 
     57        var shadowIframe = document.createElement("iframe");
     58        shadowIframe.tabIndex = 1;
     59        sr.appendChild(shadowIframe);
     60        shadowIframe.contentDocument.body.innerHTML = "<input>";
     61 
     62        var input = document.createElement("input");
     63        input.onfocus = focusLogger;
     64        input.tabIndex = 1;
     65        document.body.appendChild(input);
     66 
     67        var input2 = document.createElement("input");
     68        input2.onfocus = focusLogger;
     69        document.body.appendChild(input2);
     70 
     71        flushLayout(shadowIframe);
     72 
     73        synthesizeKey("KEY_Tab");
     74        opener.is(lastFocusTarget, input, "Should have focused input element. (3)");
     75        synthesizeKey("KEY_Tab");
     76        opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (3)");
     77        synthesizeKey("KEY_Tab");
     78        opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)");
     79        synthesizeKey("KEY_Tab");
     80        opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
     81        synthesizeKey("KEY_Tab");
     82        opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
     83        synthesizeKey("KEY_Tab");
     84        opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
     85        synthesizeKey("KEY_Tab");
     86        opener.is(lastFocusTarget, shadowDate, "Should have focused date element with a calendar button in shadow DOM. (3)");
     87 
     88        synthesizeKey("KEY_Tab");
     89        opener.is(shadowIframe.contentDocument.activeElement,
     90                  shadowIframe.contentDocument.body.firstChild,
     91                  "Should have focused input element in shadow iframe. (3)");
     92        synthesizeKey("KEY_Tab");
     93        opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (3)");
     94        synthesizeKey("KEY_Tab");
     95        opener.is(lastFocusTarget, input2, "Should have focused input[2] element. (3)");
     96 
     97        // Backwards
     98        synthesizeKey("KEY_Tab", {shiftKey: true});
     99        opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (4)");
    100        synthesizeKey("KEY_Tab", {shiftKey: true});
    101        opener.is(shadowIframe.contentDocument.activeElement,
    102                  shadowIframe.contentDocument.body.firstChild,
    103                  "Should have focused input element in shadow iframe. (4)");
    104        synthesizeKey("KEY_Tab", {shiftKey: true});
    105        opener.is(lastFocusTarget, shadowDate, "Should have focused date element with a calendar button in shadow DOM. (4)");
    106        synthesizeKey("KEY_Tab", {shiftKey: true});
    107        opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
    108        synthesizeKey("KEY_Tab", {shiftKey: true});
    109        opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
    110        synthesizeKey("KEY_Tab", {shiftKey: true});
    111        opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (4)");
    112        synthesizeKey("KEY_Tab", {shiftKey: true});
    113        opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (4)");
    114        synthesizeKey("KEY_Tab", {shiftKey: true});
    115        opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (4)");
    116        synthesizeKey("KEY_Tab", {shiftKey: true});
    117        opener.is(lastFocusTarget, input, "Should have focused input element. (4)");
    118 
    119        document.body.innerHTML = null;
    120      }
    121 
    122      async function testTabbingThroughSimpleShadowDOM() {
    123        var anchor = document.createElement("a");
    124        anchor.onfocus = focusLogger;
    125        anchor.href = "#";
    126        anchor.textContent = "in light DOM";
    127        document.body.appendChild(anchor);
    128        anchor.focus();
    129 
    130        var host = document.createElement("div");
    131        document.body.appendChild(host);
    132 
    133        var sr = host.attachShadow({mode: "open"});
    134        var shadowAnchor = anchor.cloneNode(false);
    135        shadowAnchor.onfocus = focusLogger;
    136        shadowAnchor.textContent = "in shadow DOM";
    137        sr.appendChild(shadowAnchor);
    138        var shadowInput = document.createElement("input");
    139        shadowInput.onfocus = focusLogger;
    140        sr.appendChild(shadowInput);
    141 
    142        var hiddenShadowButton = document.createElement("button");
    143        hiddenShadowButton.setAttribute("style", "display: none;");
    144        sr.appendChild(hiddenShadowButton);
    145 
    146        var input = document.createElement("input");
    147        input.onfocus = focusLogger;
    148        document.body.appendChild(input);
    149 
    150        var input2 = document.createElement("input");
    151        input2.onfocus = focusLogger;
    152        document.body.appendChild(input2);
    153 
    154        flushLayout();
    155 
    156        synthesizeKey("KEY_Tab");
    157        opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM.");
    158        synthesizeKey("KEY_Tab");
    159        opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM.");
    160        synthesizeKey("KEY_Tab");
    161        opener.is(lastFocusTarget, input, "Should have focused input element.");
    162        synthesizeKey("KEY_Tab");
    163        opener.is(lastFocusTarget, input2, "Should have focused input[2] element.");
    164 
    165        // Backwards
    166        synthesizeKey("KEY_Tab", {shiftKey: true});
    167        opener.is(lastFocusTarget, input, "Should have focused input element. (2)");
    168        synthesizeKey("KEY_Tab", {shiftKey: true});
    169        opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (2)");
    170        synthesizeKey("KEY_Tab", {shiftKey: true});
    171        opener.is(lastFocusTarget, shadowAnchor, "Should have focused anchor element in shadow DOM. (2)");
    172        synthesizeKey("KEY_Tab", {shiftKey: true});
    173        opener.is(lastFocusTarget, anchor, "Should have focused anchor element. (2)");
    174 
    175        host.remove();
    176        input.remove();
    177        input2.remove();
    178      }
    179 
    180      async function testTabbingThroughNestedShadowDOM() {
    181        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)");
    182 
    183        var host = document.createElement("div");
    184        host.id = "host";
    185        document.body.appendChild(host);
    186 
    187        var sr0 = host.attachShadow({mode: "open"});
    188        sr0.innerHTML = "<button id='button'>X</button><br id='br'><div id='h1'></div><div id='h2'></div>";
    189        var button = sr0.getElementById("button");
    190        button.onfocus = focusLogger;
    191 
    192        var h1 = sr0.getElementById("h1");
    193        var sr1 = h1.attachShadow({mode: "open"});
    194        sr1.innerHTML = "h1 <input id='h11' placeholder='click me and press tab'><input id='h12' placeholder='and then tab again'>";
    195        var input11 = sr1.getElementById("h11");
    196        input11.onfocus = focusLogger;
    197        var input12 = sr1.getElementById("h12");
    198        input12.onfocus = focusLogger;
    199 
    200        var h2 = sr0.getElementById("h2");
    201        var sr2 = h2.attachShadow({mode: "open"});
    202        sr2.innerHTML = "h2 <input id='h21'><input id='h22'>";
    203        var input21 = sr2.getElementById("h21");
    204        input21.onfocus = focusLogger;
    205        var input22 = sr2.getElementById("h22");
    206        input22.onfocus = focusLogger;
    207 
    208        flushLayout();
    209 
    210        synthesizeKey("KEY_Tab");
    211        opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (1)");
    212 
    213        synthesizeKey("KEY_Tab");
    214        opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (1)");
    215 
    216        synthesizeKey("KEY_Tab");
    217        opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (2)");
    218 
    219        synthesizeKey("KEY_Tab");
    220        opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (3)");
    221 
    222        synthesizeKey("KEY_Tab");
    223        opener.is(lastFocusTarget, input22, "[nested shadow] Should have focused input element. (4)");
    224 
    225        // Backwards
    226        synthesizeKey("KEY_Tab", {shiftKey: true});
    227        opener.is(lastFocusTarget, input21, "[nested shadow] Should have focused input element. (5)");
    228 
    229        synthesizeKey("KEY_Tab", {shiftKey: true});
    230        opener.is(lastFocusTarget, input12, "[nested shadow] Should have focused input element. (6)");
    231 
    232        synthesizeKey("KEY_Tab", {shiftKey: true});
    233        opener.is(lastFocusTarget, input11, "[nested shadow] Should have focused input element. (7)");
    234 
    235        synthesizeKey("KEY_Tab", {shiftKey: true});
    236        opener.is(lastFocusTarget, button, "[nested shadow] Should have focused button element. (8)");
    237 
    238        // Back to beginning, outside of Shadow DOM.
    239        synthesizeKey("KEY_Tab", {shiftKey: true});
    240        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)");
    241 
    242        host.remove();
    243      }
    244 
    245      async function testTabbingThroughDisplayContentsHost() {
    246        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (1)");
    247 
    248        var host = document.createElement("div");
    249        host.id = "host";
    250        host.setAttribute("style", "display: contents; border: 1px solid black;");
    251        document.body.appendChild(host);
    252 
    253        var sr0 = host.attachShadow({mode: "open"});
    254        sr0.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>";
    255        var shadowInput1 = sr0.getElementById("shadowInput1");
    256        shadowInput1.onfocus = focusLogger;
    257        var shadowInput2 = sr0.getElementById("shadowInput2");
    258        shadowInput2.onfocus = focusLogger;
    259 
    260        var host1 = document.createElement("div");
    261        host1.id = "host";
    262        host1.tabIndex = 0;
    263        host1.setAttribute("style", "display: contents; border: 1px solid black;");
    264        document.body.appendChild(host1);
    265 
    266        var sr1 = host1.attachShadow({mode: "open"});
    267        sr1.innerHTML = "<input id='shadowInput1'><input id='shadowInput2'>";
    268        var shadowInput3 = sr1.getElementById("shadowInput1");
    269        shadowInput3.onfocus = focusLogger;
    270        var shadowInput4 = sr1.getElementById("shadowInput2");
    271        shadowInput4.onfocus = focusLogger;
    272 
    273        flushLayout();
    274 
    275        synthesizeKey("KEY_Tab");
    276        opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (1)");
    277 
    278        synthesizeKey("KEY_Tab");
    279        opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (2)");
    280 
    281        synthesizeKey("KEY_Tab");
    282        opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (3)");
    283 
    284        synthesizeKey("KEY_Tab");
    285        opener.is(lastFocusTarget, shadowInput4, "Should have focused input element. (4)");
    286 
    287        // Backwards
    288        synthesizeKey("KEY_Tab", {shiftKey: true});
    289        opener.is(lastFocusTarget, shadowInput3, "Should have focused input element. (5)");
    290 
    291        synthesizeKey("KEY_Tab", {shiftKey: true});
    292        opener.is(lastFocusTarget, shadowInput2, "Should have focused input element. (6)");
    293 
    294        synthesizeKey("KEY_Tab", {shiftKey: true});
    295        opener.is(lastFocusTarget, shadowInput1, "Should have focused input element. (7)");
    296 
    297        // Back to beginning, outside of Shadow DOM.
    298        synthesizeKey("KEY_Tab", {shiftKey: true});
    299        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus. (2)");
    300 
    301        host.remove();
    302        host1.remove();
    303      }
    304 
    305      async function testTabbingThroughLightDOMShadowDOMLightDOM() {
    306        opener.is(document.activeElement, document.body.firstChild,
    307                  "body's first child should have focus.");
    308 
    309        var host = document.createElement("span");
    310        host.innerHTML = "\n";
    311        host.id = "host";
    312        document.body.appendChild(host);
    313 
    314        var sr0 = host.attachShadow({mode: "open"});
    315        sr0.innerHTML = document.getElementById("template").innerHTML;
    316        var p1 = sr0.getElementById("p1");
    317        p1.onfocus = focusLogger;
    318        var p2 = sr0.getElementById("p2");
    319        p2.onfocus = focusLogger;
    320 
    321        var p = document.createElement("p");
    322        p.innerHTML = " <a href='#p'>link 1</a> ";
    323        var a = p.firstElementChild;
    324        a.onfocus = focusLogger;
    325        document.body.appendChild(p);
    326 
    327        flushLayout();
    328 
    329        synthesizeKey("KEY_Tab");
    330        opener.is(lastFocusTarget, p1, "Should have focused p1.");
    331 
    332        synthesizeKey("KEY_Tab");
    333        opener.is(lastFocusTarget, p2, "Should have focused p2.");
    334 
    335        synthesizeKey("KEY_Tab");
    336        opener.is(lastFocusTarget, a, "Should have focused a.");
    337 
    338        // Backwards
    339        synthesizeKey("KEY_Tab", {shiftKey: true});
    340        opener.is(lastFocusTarget, p2, "Should have focused p2.");
    341 
    342        synthesizeKey("KEY_Tab", {shiftKey: true});
    343        opener.is(lastFocusTarget, p1, "Should have focused p1.");
    344 
    345        synthesizeKey("KEY_Tab", {shiftKey: true});
    346        opener.is(document.activeElement, document.body.firstChild,
    347                  "body's first child should have focus.");
    348 
    349        host.remove();
    350        p.remove();
    351      }
    352 
    353      async function testFocusableHost() {
    354        opener.is(document.activeElement, document.body.firstChild,
    355                  "body's first child should have focus.");
    356 
    357        var host = document.createElement("div");
    358        host.id = "host";
    359        host.tabIndex = 0;
    360        host.onfocus = focusLogger;
    361        document.body.appendChild(host);
    362 
    363        var slotted = document.createElement("div");
    364        slotted.tabIndex = 0;
    365        slotted.onfocus = focusLogger;
    366        host.appendChild(slotted);
    367 
    368        var sr0 = host.attachShadow({mode: "open"});
    369        sr0.appendChild(document.createElement("slot"));
    370 
    371        var p = document.createElement("p");
    372        p.innerHTML = " <a href='#p'>link 1</a> ";
    373        var a = p.firstElementChild;
    374        a.onfocus = focusLogger;
    375        document.body.appendChild(p);
    376 
    377        flushLayout();
    378 
    379        synthesizeKey("KEY_Tab");
    380        opener.is(lastFocusTarget, host, "Should have focused host.");
    381 
    382        synthesizeKey("KEY_Tab");
    383        opener.is(lastFocusTarget, slotted, "Should have focused slotted.");
    384 
    385        synthesizeKey("KEY_Tab");
    386        opener.is(lastFocusTarget, a, "Should have focused a.");
    387 
    388        // Backwards
    389        synthesizeKey("KEY_Tab", {shiftKey: true});
    390        opener.is(lastFocusTarget, slotted, "Should have focused slotted.");
    391 
    392        synthesizeKey("KEY_Tab", {shiftKey: true});
    393        opener.is(lastFocusTarget, host, "Should have focused host.");
    394 
    395        synthesizeKey("KEY_Tab", {shiftKey: true});
    396        opener.is(document.activeElement, document.body.firstChild,
    397                  "body's first child should have focus.");
    398 
    399        host.remove();
    400        p.remove();
    401      }
    402 
    403      async function testShiftTabbingThroughFocusableHost() {
    404        opener.is(document.activeElement, document.body.firstChild,
    405                  "body's first child should have focus.");
    406 
    407        var host = document.createElement("div");
    408        host.id = "host";
    409        host.tabIndex = 0;
    410        host.onfocus = focusLogger;
    411        document.body.appendChild(host);
    412 
    413        var sr = host.attachShadow({mode: "open"});
    414        var shadowButton = document.createElement("button");
    415        shadowButton.innerText = "X";
    416        shadowButton.onfocus = focusLogger;
    417        sr.appendChild(shadowButton);
    418 
    419        var shadowInput = document.createElement("input");
    420        shadowInput.onfocus = focusLogger;
    421        sr.appendChild(shadowInput);
    422        sr.appendChild(document.createElement("br"));
    423 
    424        var input = document.createElement("input");
    425        input.onfocus = focusLogger;
    426        document.body.appendChild(input);
    427 
    428        flushLayout();
    429 
    430        synthesizeKey("KEY_Tab");
    431        opener.is(lastFocusTarget, host, "Should have focused host element. (1)");
    432 
    433        synthesizeKey("KEY_Tab");
    434        opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (2)");
    435 
    436        synthesizeKey("KEY_Tab");
    437        opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (3)");
    438 
    439        synthesizeKey("KEY_Tab");
    440        opener.is(lastFocusTarget, input, "Should have focused input element. (4)");
    441 
    442        // Backwards
    443        synthesizeKey("KEY_Tab", {shiftKey: true});
    444        opener.is(lastFocusTarget, shadowInput, "Should have focused input element in shadow DOM. (5)");
    445 
    446        synthesizeKey("KEY_Tab", {shiftKey: true});
    447        opener.is(lastFocusTarget, shadowButton, "Should have focused button element in shadow DOM. (6)");
    448 
    449        synthesizeKey("KEY_Tab", {shiftKey: true});
    450        // focus is already on host
    451        opener.is(sr.activeElement, null,
    452                  "Focus should have left button element in shadow DOM. (7)");
    453 
    454        synthesizeKey("KEY_Tab", {shiftKey: true});
    455        opener.is(document.activeElement, document.body.firstChild,
    456                  "body's first child should have focus.");
    457 
    458        host.remove();
    459        input.remove();
    460      }
    461 
    462      async function testTabbingThroughNestedSlot() {
    463        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
    464 
    465        var host0 = document.createElement("div");
    466        var sr0 = host0.attachShadow({mode: "open"});
    467        sr0.innerHTML = "<slot></slot>";
    468        document.body.appendChild(host0);
    469 
    470        // focusable
    471        var host00 = document.createElement("div");
    472        var sr00 = host00.attachShadow({mode: "open"});
    473        var div00 = document.createElement("div");
    474        div00.tabIndex = 0;
    475        div00.onfocus = focusLogger;
    476        sr00.appendChild(div00);
    477        host0.appendChild(host00);
    478 
    479        // not focusable
    480        var host01 = document.createElement("div");
    481        var sr01 = host01.attachShadow({mode: "open"});
    482        sr01.innerHTML = "<div></div>";
    483        host0.appendChild(host01);
    484 
    485        // focusable
    486        var host02 = document.createElement("div");
    487        var sr02 = host02.attachShadow({mode: "open"});
    488        var div02 = document.createElement("div");
    489        div02.tabIndex = 0;
    490        div02.onfocus = focusLogger;
    491        sr02.appendChild(div02);
    492        host0.appendChild(host02);
    493 
    494        var host1 = document.createElement("div");
    495        var sr1 = host1.attachShadow({mode: "open"});
    496        sr1.innerHTML = "<slot></slot>";
    497        document.body.appendChild(host1);
    498 
    499        var host10 = document.createElement("div");
    500        var sr10 = host10.attachShadow({mode: "open"});
    501        sr10.innerHTML = "<slot></slot>";
    502        host1.appendChild(host10);
    503 
    504        var input10 = document.createElement("input");
    505        input10.onfocus = focusLogger;
    506        host10.appendChild(input10);
    507 
    508        var host11 = document.createElement("div");
    509        var sr11 = host11.attachShadow({mode: "open"});
    510        sr11.innerHTML = "<slot></slot>";
    511        host1.appendChild(host11);
    512 
    513        var input11 = document.createElement("input");
    514        input11.onfocus = focusLogger;
    515        host11.appendChild(input11);
    516 
    517        flushLayout();
    518 
    519        synthesizeKey("KEY_Tab");
    520        opener.is(lastFocusTarget, div00, "Should have focused div element in shadow DOM. (1)");
    521 
    522        synthesizeKey("KEY_Tab");
    523        opener.is(lastFocusTarget, div02, "Should have focused div element in shadow DOM. (2)");
    524 
    525        synthesizeKey("KEY_Tab");
    526        opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (3)");
    527 
    528        synthesizeKey("KEY_Tab");
    529        opener.is(lastFocusTarget, input11, "Should have focused button element in shadow DOM. (4)");
    530 
    531        // Backwards
    532        synthesizeKey("KEY_Tab", {shiftKey: true});
    533        opener.is(lastFocusTarget, input10, "Should have focused input element in shadow DOM. (5)");
    534 
    535        synthesizeKey("KEY_Tab", {shiftKey: true});
    536        opener.is(lastFocusTarget, div02, "Should have focused input element in shadow DOM. (6)");
    537 
    538        synthesizeKey("KEY_Tab", {shiftKey: true});
    539        opener.is(lastFocusTarget, div00, "Should have focused input element in shadow DOM. (7)");
    540 
    541        synthesizeKey("KEY_Tab", {shiftKey: true});
    542        opener.is(document.activeElement, document.body.firstChild,
    543                  "body's first child should have focus.");
    544 
    545        host0.remove();
    546        host1.remove();
    547      }
    548 
    549      async function testTabbingThroughSlotInLightDOM() {
    550        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
    551 
    552        var input0 = document.createElement("input");
    553        input0.onfocus = focusLogger;
    554        document.body.appendChild(input0);
    555 
    556        var slot1 = document.createElement("slot");
    557        document.body.appendChild(slot1);
    558 
    559        var input10 = document.createElement("input");
    560        input10.onfocus = focusLogger;
    561        slot1.appendChild(input10);
    562 
    563        var input11 = document.createElement("input");
    564        input11.onfocus = focusLogger;
    565        slot1.appendChild(input11);
    566 
    567        var input2 = document.createElement("input");
    568        input2.onfocus = focusLogger;
    569        document.body.appendChild(input2);
    570 
    571        flushLayout();
    572 
    573        synthesizeKey("KEY_Tab");
    574        opener.is(lastFocusTarget, input0, "Should have focused input element. (1)");
    575 
    576        synthesizeKey("KEY_Tab");
    577        opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (2)");
    578 
    579        synthesizeKey("KEY_Tab");
    580        opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (3)");
    581 
    582        synthesizeKey("KEY_Tab");
    583        opener.is(lastFocusTarget, input2, "Should have focused input element. (4)");
    584 
    585        // Backwards
    586        synthesizeKey("KEY_Tab", {shiftKey: true});
    587        opener.is(lastFocusTarget, input11, "Should have focused input element in slot. (5)");
    588 
    589        synthesizeKey("KEY_Tab", {shiftKey: true});
    590        opener.is(lastFocusTarget, input10, "Should have focused input element in slot. (6)");
    591 
    592        synthesizeKey("KEY_Tab", {shiftKey: true});
    593        opener.is(lastFocusTarget, input0, "Should have focused input element. (7)");
    594 
    595        synthesizeKey("KEY_Tab", {shiftKey: true});
    596        opener.is(document.activeElement, document.body.firstChild,
    597                  "body's first child should have focus.");
    598 
    599        input0.remove();
    600        slot1.remove();
    601        input2.remove();
    602      }
    603 
    604      async function testTabbingThroughFocusableSlotInLightDOM() {
    605        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
    606 
    607        var slot0 = document.createElement("slot");
    608        slot0.tabIndex = 0;
    609        slot0.setAttribute("style", "display: inline;");
    610        slot0.onfocus = focusLogger;
    611        document.body.appendChild(slot0);
    612 
    613        var slot00 = document.createElement("slot");
    614        slot00.tabIndex = 0;
    615        slot00.setAttribute("style", "display: inline;");
    616        slot00.onfocus = focusLogger;
    617        slot0.appendChild(slot00);
    618 
    619        var input000 = document.createElement("input");
    620        input000.onfocus = focusLogger;
    621        slot00.appendChild(input000);
    622 
    623        var input01 = document.createElement("input");
    624        input01.onfocus = focusLogger;
    625        slot0.appendChild(input01);
    626 
    627        var input1 = document.createElement("input");
    628        input1.onfocus = focusLogger;
    629        document.body.appendChild(input1);
    630 
    631        flushLayout();
    632 
    633        synthesizeKey("KEY_Tab");
    634        opener.is(lastFocusTarget, slot0, "Should have focused slot element. (1)");
    635 
    636        synthesizeKey("KEY_Tab");
    637        opener.is(lastFocusTarget, slot00, "Should have focused slot element. (2)");
    638 
    639        synthesizeKey("KEY_Tab");
    640        opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (3)");
    641 
    642        synthesizeKey("KEY_Tab");
    643        opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (4)");
    644 
    645        synthesizeKey("KEY_Tab");
    646        opener.is(lastFocusTarget, input1, "Should have focused input element. (5)");
    647 
    648        // Backwards
    649        synthesizeKey("KEY_Tab", {shiftKey: true});
    650        opener.is(lastFocusTarget, input01, "Should have focused input element in slot. (6)");
    651 
    652        synthesizeKey("KEY_Tab", {shiftKey: true});
    653        opener.is(lastFocusTarget, input000, "Should have focused input element in slot. (7)");
    654 
    655        synthesizeKey("KEY_Tab", {shiftKey: true});
    656        opener.is(lastFocusTarget, slot00, "Should have focused slot element. (8)");
    657 
    658        synthesizeKey("KEY_Tab", {shiftKey: true});
    659        opener.is(lastFocusTarget, slot0, "Should have focused slot element. (9)");
    660 
    661        synthesizeKey("KEY_Tab", {shiftKey: true});
    662        opener.is(document.activeElement, document.body.firstChild,
    663                  "body's first child should have focus.");
    664 
    665        slot0.remove();
    666        input1.remove();
    667      }
    668 
    669      async function testTabbingThroughScrollableShadowDOM() {
    670        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
    671 
    672        var host0 = document.createElement("div");
    673        host0.setAttribute("style", "height: 50px; overflow: auto;");
    674        host0.onfocus = focusLogger;
    675        document.body.appendChild(host0);
    676 
    677        var sr0 = host0.attachShadow({mode: "open"});
    678        sr0.innerHTML = `
    679          <style>
    680            div,slot {
    681              height: 30px;
    682              display: block;
    683              overflow: auto;
    684            }
    685            input {
    686              display: block;
    687            }
    688          </style>
    689        `;
    690 
    691        var input00 = document.createElement("input");
    692        input00.setAttribute("style", "background-color: red;");
    693        input00.onfocus = focusLogger;
    694        sr0.appendChild(input00);
    695 
    696        var container01 = document.createElement("div");
    697        container01.onfocus = focusLogger;
    698        sr0.appendChild(container01);
    699 
    700        var input010 = document.createElement("input");
    701        input010.onfocus = focusLogger;
    702        container01.appendChild(input010);
    703 
    704        var input011 = document.createElement("input");
    705        input011.onfocus = focusLogger;
    706        container01.appendChild(input011);
    707 
    708        var slot02 = document.createElement("slot");
    709        slot02.onfocus = focusLogger;
    710        sr0.appendChild(slot02);
    711 
    712        var input020 = document.createElement("input");
    713        input020.setAttribute("style", "display: block;");
    714        input020.onfocus = focusLogger;
    715        host0.appendChild(input020);
    716 
    717        var input021 = document.createElement("input");
    718        input021.setAttribute("style", "display: block;");
    719        input021.onfocus = focusLogger;
    720        host0.appendChild(input021);
    721 
    722        var input1 = document.createElement("input");
    723        input1.onfocus = focusLogger;
    724        document.body.appendChild(input1);
    725 
    726        flushLayout();
    727 
    728        synthesizeKey("KEY_Tab");
    729        opener.is(lastFocusTarget, host0, "Should have focused shadow host element. (1)");
    730 
    731        synthesizeKey("KEY_Tab");
    732        opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (2)");
    733 
    734        synthesizeKey("KEY_Tab");
    735        opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (3)");
    736 
    737        synthesizeKey("KEY_Tab");
    738        opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (4)");
    739 
    740        synthesizeKey("KEY_Tab");
    741        opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (5)");
    742 
    743        synthesizeKey("KEY_Tab");
    744        opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (6)");
    745 
    746        synthesizeKey("KEY_Tab");
    747        opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (7)");
    748 
    749        synthesizeKey("KEY_Tab");
    750        opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (8)");
    751 
    752        synthesizeKey("KEY_Tab");
    753        opener.is(lastFocusTarget, input1, "Should have focused input element in light dom. (9)");
    754 
    755        // Backwards
    756        synthesizeKey("KEY_Tab", {shiftKey: true});
    757        opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (10)");
    758 
    759        synthesizeKey("KEY_Tab", {shiftKey: true});
    760        opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (11)");
    761 
    762        synthesizeKey("KEY_Tab", {shiftKey: true});
    763        opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (12)");
    764 
    765        synthesizeKey("KEY_Tab", {shiftKey: true});
    766        opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (13)");
    767 
    768        synthesizeKey("KEY_Tab", {shiftKey: true});
    769        opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (14)");
    770 
    771        synthesizeKey("KEY_Tab", {shiftKey: true});
    772        opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (15)");
    773 
    774        synthesizeKey("KEY_Tab", {shiftKey: true});
    775        opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (16)");
    776 
    777        synthesizeKey("KEY_Tab", {shiftKey: true});
    778        // focus is already on host
    779        opener.is(sr0.activeElement, null,
    780                  "Focus should have left input element in shadow DOM. (7)");
    781 
    782        synthesizeKey("KEY_Tab", {shiftKey: true});
    783        opener.is(document.activeElement, document.body.firstChild,
    784                  "body's first child should have focus.");
    785 
    786        host0.remove();
    787        input1.remove();
    788      }
    789 
    790      // Bug 1604140
    791      async function testTabbingThroughScrollableShadowHost() {
    792        opener.is(document.activeElement, document.body.firstChild,
    793                  "body's first child should have focus.");
    794 
    795        let aboveFirstHost = document.createElement("div");
    796        aboveFirstHost.tabIndex = 0;
    797        aboveFirstHost.onfocus = focusLogger;
    798        document.body.appendChild(aboveFirstHost);
    799 
    800        let firstHost = document.createElement("div");
    801        firstHost.style = "overflow: scroll";
    802        document.body.appendChild(firstHost);
    803 
    804        let firstShadow = firstHost.attachShadow({mode: "open"});
    805        let divInFirstShadow = document.createElement("div");
    806        divInFirstShadow.tabIndex = 0;
    807        divInFirstShadow.onfocus = focusLogger;
    808        firstShadow.appendChild(divInFirstShadow);
    809 
    810        let aboveSecondHost = document.createElement("div");
    811        aboveSecondHost.tabIndex = 0;
    812        aboveSecondHost.onfocus = focusLogger;
    813        document.body.appendChild(aboveSecondHost);
    814 
    815        let secondHost = document.createElement("div");
    816        secondHost.style = "overflow: scroll";
    817        secondHost.tabIndex = 0;
    818        secondHost.onfocus = focusLogger;
    819        document.body.appendChild(secondHost);
    820 
    821        let secondShadow = secondHost.attachShadow({mode: "open"});
    822        let divInSecondShadow = document.createElement("div");
    823        divInSecondShadow.tabIndex = 0;
    824        divInSecondShadow.onfocus = focusLogger;
    825        secondShadow.appendChild(divInSecondShadow);
    826 
    827        let belowSecondHost = document.createElement("div");
    828        belowSecondHost.tabIndex = 0;
    829        belowSecondHost.onfocus = focusLogger;
    830        document.body.appendChild(belowSecondHost);
    831 
    832        flushLayout();
    833 
    834        synthesizeKey("KEY_Tab");
    835        opener.is(lastFocusTarget, aboveFirstHost, "Should have focused div above first host element. (1)");
    836 
    837        synthesizeKey("KEY_Tab");
    838        opener.is(lastFocusTarget, divInFirstShadow, "Should have focused div in first shadow dom. (2)");
    839 
    840        synthesizeKey("KEY_Tab");
    841        opener.is(lastFocusTarget, aboveSecondHost, "Should have focused div above second host element. (3)");
    842 
    843        synthesizeKey("KEY_Tab");
    844        opener.is(lastFocusTarget, secondHost, "Should have focused second host element. (4)");
    845 
    846        synthesizeKey("KEY_Tab");
    847        opener.is(lastFocusTarget, divInSecondShadow, "Should have focused div in second shadow dom. (5)");
    848 
    849        synthesizeKey("KEY_Tab");
    850        opener.is(lastFocusTarget, belowSecondHost, "Should have focused div below second host. (6)");
    851 
    852        // Backwards
    853        synthesizeKey("KEY_Tab", {shiftKey: true});
    854        opener.is(lastFocusTarget, divInSecondShadow, "Should have focused div in second shadow dom. (7)");
    855 
    856        synthesizeKey("KEY_Tab", {shiftKey: true});
    857        // focus is already on second host, so lastFocusTarget won't get updated.
    858        opener.is(document.activeElement, secondHost, "Should have focused second host element. (8)");
    859        opener.is(secondShadow.activeElement, null, "Focus should have left div in second shadow dom. (8)");
    860 
    861        synthesizeKey("KEY_Tab", {shiftKey: true});
    862        opener.is(lastFocusTarget, aboveSecondHost, "Should have focused div above second host element. (9)");
    863 
    864        synthesizeKey("KEY_Tab", {shiftKey: true});
    865        opener.is(lastFocusTarget, divInFirstShadow, "Should have focused div in first shadow dom. (10)");
    866 
    867        synthesizeKey("KEY_Tab", {shiftKey: true});
    868        opener.is(lastFocusTarget, aboveFirstHost, "Should have focused div above first host element. (11)");
    869 
    870        synthesizeKey("KEY_Tab", {shiftKey: true});
    871        opener.is(document.activeElement, document.body.firstChild,
    872                  "body's first child should have focus.");
    873 
    874        aboveFirstHost.remove();
    875        firstHost.remove();
    876        aboveSecondHost.remove();
    877        secondHost.remove();
    878        belowSecondHost.remove();
    879      }
    880 
    881      async function testDeeplyNestedShadowTree() {
    882        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
    883        var host1 = document.createElement("test-node");
    884        var lastHost = host1;
    885        for (var i = 0; i < 20; ++i) {
    886          lastHost.appendChild(document.createElement("test-node"));
    887          lastHost = lastHost.firstChild;
    888        }
    889 
    890        var input = document.createElement("input");
    891        document.body.appendChild(host1);
    892        document.body.appendChild(input);
    893 
    894        flushLayout();
    895 
    896        // Test shadow tree which doesn't have anything tab-focusable.
    897        host1.shadowRoot.querySelector("div").focus();
    898        synthesizeKey("KEY_Tab");
    899        is(document.activeElement, input, "Should have focused input element.");
    900        synthesizeKey("KEY_Tab", {shiftKey: true});
    901        opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
    902 
    903        // Same test but with focusable elements in the tree...
    904        var input2 = document.createElement("input");
    905        var host2 = host1.firstChild;
    906        var host3 = host2.firstChild;
    907        host2.insertBefore(input2, host3);
    908        var input3 = document.createElement("input");
    909        lastHost.appendChild(input3);
    910 
    911        flushLayout();
    912 
    913        host3.shadowRoot.querySelector("div").focus();
    914        synthesizeKey("KEY_Tab");
    915        is(document.activeElement, input3, "Should have focused input3 element.");
    916 
    917        // ...and backwards
    918        host3.shadowRoot.querySelector("div").focus();
    919        synthesizeKey("KEY_Tab", {shiftKey: true});
    920        is(document.activeElement, input2, "Should have focused input2 element.");
    921 
    922        // Remove elements added to body element.
    923        host1.remove();
    924        input.remove();
    925 
    926        // Tests expect body.firstChild to have focus.
    927        document.body.firstChild.focus();
    928      }
    929 
    930      // Bug 1558393
    931      async function testBackwardsTabbingWithSlotsWithoutFocusableContent() {
    932        let first = document.createElement("div");
    933        first.tabIndex = 0;
    934        let host = document.createElement("div");
    935        host.tabIndex = 0;
    936        let second = document.createElement("div");
    937        second.tabIndex = 0;
    938        host.appendChild(document.createTextNode("foo"));
    939        host.attachShadow({ mode: "open" }).innerHTML = `<slot></slot>`;
    940 
    941        document.body.appendChild(first);
    942        document.body.appendChild(host);
    943        document.body.appendChild(second);
    944 
    945        flushLayout();
    946 
    947        first.focus();
    948        opener.is(document.activeElement, first, "First light div should have focus");
    949        synthesizeKey("KEY_Tab");
    950        opener.is(document.activeElement, host, "Host should be focused");
    951        synthesizeKey("KEY_Tab");
    952        opener.is(document.activeElement, second, "Second light div should be focused");
    953 
    954        // Now backwards
    955        synthesizeKey("KEY_Tab", {shiftKey: true});
    956        opener.is(document.activeElement, host, "Focus should return to host");
    957        synthesizeKey("KEY_Tab", {shiftKey: true});
    958        opener.is(document.activeElement, first, "Focus should return to first light div");
    959 
    960        second.remove();
    961        host.remove();
    962        first.remove();
    963      }
    964 
    965      async function runTest() {
    966 
    967        await testTabbingThroughShadowDOMWithTabIndexes();
    968        await testTabbingThroughSimpleShadowDOM();
    969        await testTabbingThroughNestedShadowDOM();
    970        await testTabbingThroughDisplayContentsHost();
    971        await testTabbingThroughLightDOMShadowDOMLightDOM();
    972        await testFocusableHost();
    973        await testShiftTabbingThroughFocusableHost();
    974        await testTabbingThroughNestedSlot();
    975        await testTabbingThroughSlotInLightDOM();
    976        await testTabbingThroughFocusableSlotInLightDOM();
    977        await testTabbingThroughScrollableShadowDOM();
    978        await testTabbingThroughScrollableShadowHost();
    979        await testDeeplyNestedShadowTree();
    980        await testBackwardsTabbingWithSlotsWithoutFocusableContent();
    981 
    982        opener.didRunTests();
    983        window.close();
    984      }
    985 
    986      function init() {
    987        SimpleTest.waitForFocus(runTest);
    988      }
    989    </script>
    990    <style>
    991    </style>
    992    <template id="template">
    993      <div style="overflow: hidden">
    994        <p tabindex="0" id="p1">component</p>
    995        <p tabindex="0" id="p2">/component</p>
    996      </div>
    997    </template>
    998  </head>
    999  <body onload="init()">
   1000  </body>
   1001 </html>