tor-browser

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

browser_PanelMultiView_keyboard.js (23748B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /**
      7 * Test the keyboard behavior of PanelViews.
      8 */
      9 
     10 const kEmbeddedDocUrl =
     11  'data:text/html,<textarea id="docTextarea">value</textarea><button id="docButton"></button>';
     12 
     13 let gAnchor;
     14 let gPanel;
     15 let gPanelMultiView;
     16 let gMainView;
     17 let gMainContext;
     18 let gMainButton1;
     19 let gMainMenulist;
     20 let gMainRadiogroup;
     21 let gMainTextbox;
     22 let gMainButton2;
     23 let gMainButton3;
     24 let gCheckbox;
     25 let gNamespacedLink;
     26 let gLink;
     27 let gMainTabOrder;
     28 let gMainArrowOrder;
     29 let gSubView;
     30 let gSubButton;
     31 let gSubTextarea;
     32 let gBrowserView;
     33 let gBrowserBrowser;
     34 let gIframeView;
     35 let gIframeIframe;
     36 let gToggle;
     37 let gMozButton;
     38 let gMozButtonSubviewNav;
     39 let gComponentView;
     40 let gShadowRoot;
     41 let gShadowRootButtonA;
     42 let gShadowRootButtonB;
     43 
     44 async function openPopup() {
     45  let shown = BrowserTestUtils.waitForEvent(gMainView, "ViewShown");
     46  PanelMultiView.openPopup(gPanel, gAnchor, "bottomright topright");
     47  await shown;
     48 }
     49 
     50 async function hidePopup() {
     51  let hidden = BrowserTestUtils.waitForEvent(gPanel, "popuphidden");
     52  PanelMultiView.hidePopup(gPanel);
     53  await hidden;
     54 }
     55 
     56 async function showSubView(view = gSubView) {
     57  let shown = BrowserTestUtils.waitForEvent(view, "ViewShown");
     58  // We must show with an anchor so the Back button is generated.
     59  gPanelMultiView.showSubView(view, gMainButton1);
     60  await shown;
     61 }
     62 
     63 async function expectFocusAfterKey(aKey, aFocus) {
     64  let res = aKey.match(/^(Shift\+)?(.+)$/);
     65  let shift = Boolean(res[1]);
     66  let key;
     67  if (res[2].length == 1) {
     68    key = res[2]; // Character.
     69  } else {
     70    key = "KEY_" + res[2]; // Tab, ArrowRight, etc.
     71  }
     72  info("Waiting for focus on " + aFocus.id);
     73  let focused = BrowserTestUtils.waitForEvent(aFocus, "focus");
     74  EventUtils.synthesizeKey(key, { shiftKey: shift });
     75  await focused;
     76  ok(true, aFocus.id + " focused after " + aKey + " pressed");
     77 }
     78 
     79 add_setup(async function () {
     80  // This shouldn't be necessary - but it is, because we use same-process frames.
     81  // https://bugzilla.mozilla.org/show_bug.cgi?id=1565276 covers improving this.
     82  await SpecialPowers.pushPrefEnv({
     83    set: [["security.allow_unsafe_parent_loads", true]],
     84  });
     85  let navBar = document.getElementById("nav-bar");
     86  gAnchor = document.createXULElement("toolbarbutton");
     87  navBar.appendChild(gAnchor);
     88  gPanel = document.createXULElement("panel");
     89  navBar.appendChild(gPanel);
     90  gPanelMultiView = document.createXULElement("panelmultiview");
     91  gPanelMultiView.setAttribute("mainViewId", "testMainView");
     92  gPanel.appendChild(gPanelMultiView);
     93 
     94  gMainView = document.createXULElement("panelview");
     95  gMainView.id = "testMainView";
     96  gPanelMultiView.appendChild(gMainView);
     97  gMainContext = document.createXULElement("menupopup");
     98  gMainContext.id = "gMainContext";
     99  gMainView.appendChild(gMainContext);
    100  gMainContext.appendChild(document.createXULElement("menuitem"));
    101  gMainButton1 = document.createXULElement("button");
    102  gMainButton1.id = "gMainButton1";
    103  gMainView.appendChild(gMainButton1);
    104  // We use this for anchoring subviews, so it must have a label.
    105  gMainButton1.setAttribute("label", "gMainButton1");
    106  gMainButton1.setAttribute("context", "gMainContext");
    107  gMainMenulist = document.createXULElement("menulist");
    108  gMainMenulist.id = "gMainMenulist";
    109  gMainView.appendChild(gMainMenulist);
    110  let menuPopup = document.createXULElement("menupopup");
    111  gMainMenulist.appendChild(menuPopup);
    112  let item = document.createXULElement("menuitem");
    113  item.setAttribute("value", "1");
    114  item.setAttribute("selected", "true");
    115  menuPopup.appendChild(item);
    116  item = document.createXULElement("menuitem");
    117  item.setAttribute("value", "2");
    118  menuPopup.appendChild(item);
    119  gMainRadiogroup = document.createXULElement("radiogroup");
    120  gMainRadiogroup.id = "gMainRadiogroup";
    121  gMainView.appendChild(gMainRadiogroup);
    122  let radio = document.createXULElement("radio");
    123  radio.setAttribute("value", "1");
    124  radio.setAttribute("selected", "true");
    125  gMainRadiogroup.appendChild(radio);
    126  radio = document.createXULElement("radio");
    127  radio.setAttribute("value", "2");
    128  gMainRadiogroup.appendChild(radio);
    129  gMainTextbox = document.createElement("input");
    130  gMainTextbox.id = "gMainTextbox";
    131  gMainView.appendChild(gMainTextbox);
    132  gMainTextbox.setAttribute("value", "value");
    133  gMainButton2 = document.createXULElement("button");
    134  gMainButton2.id = "gMainButton2";
    135  gMainView.appendChild(gMainButton2);
    136  gMainButton3 = document.createXULElement("button");
    137  gMainButton3.id = "gMainButton3";
    138  gMainView.appendChild(gMainButton3);
    139  gCheckbox = document.createXULElement("checkbox");
    140  gCheckbox.id = "gCheckbox";
    141  gMainView.appendChild(gCheckbox);
    142 
    143  // moz-support-links in XUL documents are created with the
    144  // <html:a> tag and so we need to test this separately from
    145  // <a> tags.
    146  gNamespacedLink = document.createElementNS(
    147    "http://www.w3.org/1999/xhtml",
    148    "html:a"
    149  );
    150  gNamespacedLink.href = "www.mozilla.org";
    151  gNamespacedLink.innerText = "gNamespacedLink";
    152  gNamespacedLink.id = "gNamespacedLink";
    153  gMainView.appendChild(gNamespacedLink);
    154  gLink = document.createElement("a");
    155  gLink.href = "www.mozilla.org";
    156  gLink.innerText = "gLink";
    157  gLink.id = "gLink";
    158  gMainView.appendChild(gLink);
    159  gToggle = document.createElement("moz-toggle");
    160  gToggle.label = "Test label";
    161  gMainView.appendChild(gToggle);
    162  gMozButton = document.createElement("moz-button");
    163  gMozButton.label = "gMozButton";
    164  gMozButton.id = "gMozButton";
    165  gMainView.appendChild(gMozButton);
    166  gMozButtonSubviewNav = document.createElement("moz-button");
    167  gMozButtonSubviewNav.label = "gMozButtonSubviewNav";
    168  gMozButtonSubviewNav.id = "gMozButtonSubviewNav";
    169  gMozButtonSubviewNav.classList.add("moz-button-subviewbutton-nav");
    170  gMainView.appendChild(gMozButtonSubviewNav);
    171 
    172  gMainTabOrder = [
    173    gMainButton1,
    174    gMainMenulist,
    175    gMainRadiogroup,
    176    gMainTextbox,
    177    gMainButton2,
    178    gMainButton3,
    179    gCheckbox,
    180    gNamespacedLink,
    181    gLink,
    182    gToggle,
    183    gMozButton,
    184    gMozButtonSubviewNav,
    185  ];
    186  gMainArrowOrder = [
    187    gMainButton1,
    188    gMainButton2,
    189    gMainButton3,
    190    gCheckbox,
    191    gNamespacedLink,
    192    gLink,
    193    gToggle,
    194    gMozButton,
    195    gMozButtonSubviewNav,
    196  ];
    197 
    198  gSubView = document.createXULElement("panelview");
    199  gSubView.id = "testSubView";
    200  gPanelMultiView.appendChild(gSubView);
    201  gSubButton = document.createXULElement("button");
    202  gSubView.appendChild(gSubButton);
    203  gSubTextarea = document.createElementNS(
    204    "http://www.w3.org/1999/xhtml",
    205    "textarea"
    206  );
    207  gSubTextarea.id = "gSubTextarea";
    208  gSubView.appendChild(gSubTextarea);
    209  gSubTextarea.value = "value";
    210 
    211  gBrowserView = document.createXULElement("panelview");
    212  gBrowserView.id = "testBrowserView";
    213  gPanelMultiView.appendChild(gBrowserView);
    214  gBrowserBrowser = document.createXULElement("browser");
    215  gBrowserBrowser.id = "GBrowserBrowser";
    216  gBrowserBrowser.setAttribute("type", "content");
    217  gBrowserBrowser.setAttribute("src", kEmbeddedDocUrl);
    218  gBrowserBrowser.style.minWidth = gBrowserBrowser.style.minHeight = "100px";
    219  gBrowserView.appendChild(gBrowserBrowser);
    220 
    221  gIframeView = document.createXULElement("panelview");
    222  gIframeView.id = "testIframeView";
    223  gPanelMultiView.appendChild(gIframeView);
    224  gIframeIframe = document.createXULElement("iframe");
    225  gIframeIframe.id = "gIframeIframe";
    226  gIframeIframe.setAttribute("src", kEmbeddedDocUrl);
    227  gIframeView.appendChild(gIframeIframe);
    228 
    229  gComponentView = document.createXULElement("panelview");
    230  gComponentView.id = "testComponentView";
    231  gPanelMultiView.appendChild(gComponentView);
    232  // Shadow root that delegates focus with multiple buttons
    233  gShadowRoot = document.createElement("section");
    234  gShadowRoot.id = "gShadowRoot";
    235  gShadowRoot.dataset.navigableWithTabOnly = "true";
    236  gShadowRoot.attachShadow({ mode: "open", delegatesFocus: true });
    237  gShadowRootButtonA = document.createElement("moz-button");
    238  gShadowRootButtonA.id = "gShadowRootButtonA";
    239  gShadowRootButtonA.label = "Button A";
    240  gShadowRootButtonB = document.createElement("moz-button");
    241  gShadowRootButtonB.id = "gShadowRootButtonB";
    242  gShadowRootButtonB.label = "Button B";
    243  gShadowRoot.shadowRoot.appendChild(gShadowRootButtonA);
    244  gShadowRoot.shadowRoot.appendChild(gShadowRootButtonB);
    245  gComponentView.appendChild(gShadowRoot);
    246 
    247  registerCleanupFunction(() => {
    248    gAnchor.remove();
    249    gPanel.remove();
    250  });
    251 });
    252 
    253 // Test that the tab key focuses all expected controls.
    254 add_task(async function testTab() {
    255  await openPopup();
    256  for (let elem of gMainTabOrder) {
    257    await expectFocusAfterKey("Tab", elem);
    258  }
    259  // Wrap around.
    260  await expectFocusAfterKey("Tab", gMainTabOrder[0]);
    261  await hidePopup();
    262 });
    263 
    264 // Test that the shift+tab key focuses all expected controls.
    265 add_task(async function testShiftTab() {
    266  await openPopup();
    267  for (let i = gMainTabOrder.length - 1; i >= 0; --i) {
    268    await expectFocusAfterKey("Shift+Tab", gMainTabOrder[i]);
    269  }
    270  // Wrap around.
    271  await expectFocusAfterKey(
    272    "Shift+Tab",
    273    gMainTabOrder[gMainTabOrder.length - 1]
    274  );
    275  await hidePopup();
    276 });
    277 
    278 // Test that the down arrow key skips menulists and textboxes.
    279 add_task(async function testDownArrow() {
    280  await openPopup();
    281  for (let elem of gMainArrowOrder) {
    282    await expectFocusAfterKey("ArrowDown", elem);
    283  }
    284  // Wrap around.
    285  await expectFocusAfterKey("ArrowDown", gMainArrowOrder[0]);
    286  await hidePopup();
    287 });
    288 
    289 // Test that the up arrow key skips menulists and textboxes.
    290 add_task(async function testUpArrow() {
    291  await openPopup();
    292  for (let i = gMainArrowOrder.length - 1; i >= 0; --i) {
    293    await expectFocusAfterKey("ArrowUp", gMainArrowOrder[i]);
    294  }
    295  // Wrap around.
    296  await expectFocusAfterKey(
    297    "ArrowUp",
    298    gMainArrowOrder[gMainArrowOrder.length - 1]
    299  );
    300  await hidePopup();
    301 });
    302 
    303 // Test that the home/end keys move to the first/last controls.
    304 add_task(async function testHomeEnd() {
    305  await openPopup();
    306  await expectFocusAfterKey("Home", gMainArrowOrder[0]);
    307  await expectFocusAfterKey("End", gMainArrowOrder[gMainArrowOrder.length - 1]);
    308  await hidePopup();
    309 });
    310 
    311 // Test that the up/down arrow keys work as expected in menulists.
    312 add_task(async function testArrowsMenulist() {
    313  await openPopup();
    314  gMainMenulist.focus();
    315  is(document.activeElement, gMainMenulist, "menulist focused");
    316  is(gMainMenulist.value, "1", "menulist initial value 1");
    317  if (AppConstants.platform == "macosx") {
    318    // On Mac, down/up arrows just open the menulist.
    319    let popup = gMainMenulist.menupopup;
    320    for (let key of ["ArrowDown", "ArrowUp"]) {
    321      let shown = BrowserTestUtils.waitForEvent(popup, "popupshown");
    322      EventUtils.synthesizeKey("KEY_" + key);
    323      await shown;
    324      ok(gMainMenulist.open, "menulist open after " + key);
    325      let hidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
    326      EventUtils.synthesizeKey("KEY_Escape");
    327      await hidden;
    328      ok(!gMainMenulist.open, "menulist closed after Escape");
    329    }
    330  } else {
    331    // On other platforms, down/up arrows change the value without opening the
    332    // menulist.
    333    EventUtils.synthesizeKey("KEY_ArrowDown");
    334    is(
    335      document.activeElement,
    336      gMainMenulist,
    337      "menulist still focused after ArrowDown"
    338    );
    339    is(gMainMenulist.value, "2", "menulist value 2 after ArrowDown");
    340    EventUtils.synthesizeKey("KEY_ArrowUp");
    341    is(
    342      document.activeElement,
    343      gMainMenulist,
    344      "menulist still focused after ArrowUp"
    345    );
    346    is(gMainMenulist.value, "1", "menulist value 1 after ArrowUp");
    347  }
    348  await hidePopup();
    349 });
    350 
    351 // Test that the tab key closes an open menu list.
    352 add_task(async function testTabOpenMenulist() {
    353  await openPopup();
    354  gMainMenulist.focus();
    355  is(document.activeElement, gMainMenulist, "menulist focused");
    356  let popup = gMainMenulist.menupopup;
    357  let shown = BrowserTestUtils.waitForEvent(popup, "popupshown");
    358  gMainMenulist.open = true;
    359  await shown;
    360  ok(gMainMenulist.open, "menulist open");
    361  let menuHidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
    362  EventUtils.synthesizeKey("KEY_Tab");
    363  await menuHidden;
    364  ok(!gMainMenulist.open, "menulist closed after Tab");
    365  is(gPanel.state, "open", "Panel should be open");
    366  await hidePopup();
    367 });
    368 
    369 if (AppConstants.platform == "macosx") {
    370  // Test that using the mouse to open a menulist still allows keyboard navigation
    371  // inside it.
    372  add_task(async function testNavigateMouseOpenedMenulist() {
    373    await openPopup();
    374    let popup = gMainMenulist.menupopup;
    375    let shown = BrowserTestUtils.waitForEvent(popup, "popupshown");
    376    gMainMenulist.open = true;
    377    await shown;
    378    ok(gMainMenulist.open, "menulist open");
    379    let oldFocus = document.activeElement;
    380    let oldSelectedItem = gMainMenulist.selectedItem;
    381    ok(
    382      oldSelectedItem.hasAttribute("_moz-menuactive"),
    383      "Selected item should show up as active"
    384    );
    385    EventUtils.synthesizeKey("KEY_ArrowDown");
    386    await TestUtils.waitForCondition(
    387      () => !oldSelectedItem.hasAttribute("_moz-menuactive")
    388    );
    389    is(oldFocus, document.activeElement, "Focus should not move on mac");
    390    ok(
    391      !oldSelectedItem.hasAttribute("_moz-menuactive"),
    392      "Selected item should change"
    393    );
    394 
    395    let menuHidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
    396    EventUtils.synthesizeKey("KEY_Tab");
    397    await menuHidden;
    398    ok(!gMainMenulist.open, "menulist closed after Tab");
    399    is(gPanel.state, "open", "Panel should be open");
    400    await hidePopup();
    401  });
    402 }
    403 
    404 // Test that the up/down arrow keys work as expected in radiogroups.
    405 add_task(async function testArrowsRadiogroup() {
    406  await openPopup();
    407  gMainRadiogroup.focus();
    408  is(document.activeElement, gMainRadiogroup, "radiogroup focused");
    409  is(gMainRadiogroup.value, "1", "radiogroup initial value 1");
    410  EventUtils.synthesizeKey("KEY_ArrowDown");
    411  is(
    412    document.activeElement,
    413    gMainRadiogroup,
    414    "radiogroup still focused after ArrowDown"
    415  );
    416  is(gMainRadiogroup.value, "2", "radiogroup value 2 after ArrowDown");
    417  EventUtils.synthesizeKey("KEY_ArrowUp");
    418  is(
    419    document.activeElement,
    420    gMainRadiogroup,
    421    "radiogroup still focused after ArrowUp"
    422  );
    423  is(gMainRadiogroup.value, "1", "radiogroup value 1 after ArrowUp");
    424  await hidePopup();
    425 });
    426 
    427 // Test that pressing space in a textbox inserts a space (instead of trying to
    428 // activate the control).
    429 add_task(async function testSpaceTextbox() {
    430  await openPopup();
    431  gMainTextbox.focus();
    432  gMainTextbox.selectionStart = gMainTextbox.selectionEnd = 0;
    433  EventUtils.synthesizeKey(" ");
    434  is(gMainTextbox.value, " value", "Space typed into textbox");
    435  gMainTextbox.value = "value";
    436  await hidePopup();
    437 });
    438 
    439 // Tests that the left arrow key normally moves back to the previous view.
    440 add_task(async function testLeftArrow() {
    441  await openPopup();
    442  await showSubView();
    443  let shown = BrowserTestUtils.waitForEvent(gMainView, "ViewShown");
    444  EventUtils.synthesizeKey("KEY_ArrowLeft");
    445  await shown;
    446  ok("Moved to previous view after ArrowLeft");
    447  await hidePopup();
    448 });
    449 
    450 // Tests that the left arrow key moves the caret in a textarea in a subview
    451 // (instead of going back to the previous view).
    452 add_task(async function testLeftArrowTextarea() {
    453  await openPopup();
    454  await showSubView();
    455  gSubTextarea.focus();
    456  is(document.activeElement, gSubTextarea, "textarea focused");
    457  EventUtils.synthesizeKey("KEY_End");
    458  is(gSubTextarea.selectionStart, 5, "selectionStart 5 after End");
    459  EventUtils.synthesizeKey("KEY_ArrowLeft");
    460  is(gSubTextarea.selectionStart, 4, "selectionStart 4 after ArrowLeft");
    461  is(document.activeElement, gSubTextarea, "textarea still focused");
    462  await hidePopup();
    463 });
    464 
    465 add_task(async function testRightArrow() {
    466  await openPopup();
    467 
    468  // Ensure non moz-button-subviewbutton-navs respond to the right arrow
    469  let clicked = false;
    470  let assertNoClick = () => {
    471    clicked = true;
    472  };
    473  gMozButton.addEventListener("click", assertNoClick);
    474  // Focus using the arrow keys so PanelMultiView#selectedElement is set
    475  for (let i = 0; i < gMainTabOrder.length; i++) {
    476    EventUtils.synthesizeKey("KEY_ArrowUp");
    477    if (document.activeElement == gMozButton) {
    478      break;
    479    }
    480  }
    481  is(document.activeElement, gMozButton, "gMozButton focused");
    482  EventUtils.synthesizeKey("KEY_ArrowRight");
    483  ok(!clicked, "click handler should not be triggered for regular button");
    484  EventUtils.synthesizeKey(" ");
    485  ok(clicked, "click handler triggered on space");
    486  gMozButton.removeEventListener("click", assertNoClick);
    487 
    488  // Ensure moz-button-subviewbutton-nav gets click on right arrow
    489  gMozButtonSubviewNav.addEventListener(
    490    "click",
    491    () => gPanelMultiView.showSubView(gSubView, gMozButtonSubviewNav),
    492    { once: true }
    493  );
    494  let shown = BrowserTestUtils.waitForEvent(gSubView, "ViewShown");
    495  EventUtils.synthesizeKey("KEY_ArrowDown");
    496  is(
    497    document.activeElement,
    498    gMozButtonSubviewNav,
    499    "gMozButtonSubviewNav focused"
    500  );
    501  EventUtils.synthesizeKey("KEY_ArrowRight");
    502  await shown;
    503  ok(true, "Triggered moz-button-subviewbutton-nav on right arrow");
    504  await hidePopup();
    505 });
    506 
    507 // Test navigation to a button which is initially disabled and later enabled.
    508 add_task(async function testDynamicButton() {
    509  gMainButton2.disabled = true;
    510  await openPopup();
    511  await expectFocusAfterKey("ArrowDown", gMainButton1);
    512  await expectFocusAfterKey("ArrowDown", gMainButton3);
    513  gMainButton2.disabled = false;
    514  await expectFocusAfterKey("ArrowUp", gMainButton2);
    515  await hidePopup();
    516 });
    517 
    518 add_task(async function testActivation() {
    519  function checkActivated(elem, activationFn, reason) {
    520    let activated = false;
    521    elem.onclick = function () {
    522      activated = true;
    523    };
    524    activationFn();
    525    ok(activated, "Should have activated button after " + reason);
    526    elem.onclick = null;
    527  }
    528  await openPopup();
    529  await expectFocusAfterKey("ArrowDown", gMainButton1);
    530  checkActivated(
    531    gMainButton1,
    532    () => EventUtils.synthesizeKey("KEY_Enter"),
    533    "pressing enter"
    534  );
    535  checkActivated(
    536    gMainButton1,
    537    () => EventUtils.synthesizeKey(" "),
    538    "pressing space"
    539  );
    540  checkActivated(
    541    gMainButton1,
    542    () => EventUtils.synthesizeKey("KEY_Enter", { code: "NumpadEnter" }),
    543    "pressing numpad enter"
    544  );
    545  await hidePopup();
    546 });
    547 
    548 // Test that keyboard activation works for buttons responding to mousedown
    549 // events (instead of command or click). The Library button does this, for
    550 // example.
    551 add_task(async function testActivationMousedown() {
    552  await openPopup();
    553  await expectFocusAfterKey("ArrowDown", gMainButton1);
    554  let activated = false;
    555  gMainButton1.onmousedown = function () {
    556    activated = true;
    557  };
    558  EventUtils.synthesizeKey(" ");
    559  ok(activated, "mousedown activated after space");
    560  gMainButton1.onmousedown = null;
    561  await hidePopup();
    562 });
    563 
    564 // Test that tab and the arrow keys aren't overridden in embedded documents.
    565 async function testTabArrowsEmbeddedDoc(aView, aEmbedder) {
    566  await openPopup();
    567  await showSubView(aView);
    568  let doc = aEmbedder.contentDocument;
    569  if (doc.readyState != "complete" || doc.location.href != kEmbeddedDocUrl) {
    570    info(`Embedded doc readyState ${doc.readyState}, location ${doc.location}`);
    571    info("Waiting for load on embedder");
    572    // Browsers don't fire load events, and iframes don't fire load events in
    573    // typeChrome windows. We can handle both by using a capturing event
    574    // listener to capture the load event from the child document.
    575    await BrowserTestUtils.waitForEvent(aEmbedder, "load", true);
    576    // The original doc might have been a temporary about:blank, so fetch it
    577    // again.
    578    doc = aEmbedder.contentDocument;
    579  }
    580  is(doc.location.href, kEmbeddedDocUrl, "Embedded doc has correct URl");
    581  let backButton = aView.querySelector(".subviewbutton-back");
    582  backButton.id = "docBack";
    583  await expectFocusAfterKey("Tab", backButton);
    584  // Documents don't have an id property, but expectFocusAfterKey wants one.
    585  doc.id = "doc";
    586  await expectFocusAfterKey("Tab", doc);
    587  // Make sure tab/arrows aren't overridden within the embedded document.
    588  let textarea = doc.getElementById("docTextarea");
    589  // Tab should really focus the textarea, but default tab handling seems to
    590  // skip everything inside the embedder element when run in this test. This
    591  // behaves as expected in real panels, though. Force focus to the textarea
    592  // and then test from there.
    593  textarea.focus();
    594  is(doc.activeElement, textarea, "textarea focused");
    595  is(textarea.selectionStart, 0, "selectionStart initially 0");
    596  EventUtils.synthesizeKey("KEY_ArrowRight");
    597  is(textarea.selectionStart, 1, "selectionStart 1 after ArrowRight");
    598  EventUtils.synthesizeKey("KEY_ArrowLeft");
    599  is(textarea.selectionStart, 0, "selectionStart 0 after ArrowLeft");
    600  is(doc.activeElement, textarea, "textarea still focused");
    601  let docButton = doc.getElementById("docButton");
    602  await expectFocusAfterKey("Tab", docButton);
    603  await hidePopup();
    604 }
    605 
    606 // Test that tab and the arrow keys aren't overridden in embedded browsers.
    607 add_task(async function testTabArrowsBrowser() {
    608  await testTabArrowsEmbeddedDoc(gBrowserView, gBrowserBrowser);
    609 });
    610 
    611 // Test that tab and the arrow keys aren't overridden in embedded iframes.
    612 add_task(async function testTabArrowsIframe() {
    613  await testTabArrowsEmbeddedDoc(gIframeView, gIframeIframe);
    614 });
    615 
    616 // Test that the arrow keys aren't overridden in context menus.
    617 add_task(async function testArowsContext() {
    618  await openPopup();
    619  await expectFocusAfterKey("ArrowDown", gMainButton1);
    620  let shown = BrowserTestUtils.waitForEvent(gMainContext, "popupshown");
    621  // There's no cross-platform way to open a context menu from the keyboard.
    622  gMainContext.openPopup(gMainButton1);
    623  await shown;
    624  let item = gMainContext.children[0];
    625  ok(
    626    !item.getAttribute("_moz-menuactive"),
    627    "First context menu item initially inactive"
    628  );
    629  let active = BrowserTestUtils.waitForEvent(item, "DOMMenuItemActive");
    630  EventUtils.synthesizeKey("KEY_ArrowDown");
    631  await active;
    632  ok(
    633    item.getAttribute("_moz-menuactive"),
    634    "First context menu item active after ArrowDown"
    635  );
    636  is(
    637    document.activeElement,
    638    gMainButton1,
    639    "gMainButton1 still focused after ArrowDown"
    640  );
    641  let hidden = BrowserTestUtils.waitForEvent(gMainContext, "popuphidden");
    642  gMainContext.hidePopup();
    643  await hidden;
    644  await hidePopup();
    645 });
    646 
    647 add_task(async function testMozToggle() {
    648  await openPopup();
    649  is(gToggle.pressed, false, "The toggle is not pressed initially.");
    650  // Focus the toggle via keyboard navigation.
    651  while (document.activeElement !== gToggle) {
    652    EventUtils.synthesizeKey("KEY_Tab");
    653  }
    654  EventUtils.synthesizeKey(" ");
    655  await gToggle.updateComplete;
    656  is(gToggle.pressed, true, "Toggle pressed state changes via spacebar.");
    657  EventUtils.synthesizeKey("KEY_Enter");
    658  await gToggle.updateComplete;
    659  is(gToggle.pressed, false, "Toggle pressed state changes via enter.");
    660  await hidePopup();
    661 });
    662 
    663 // Test that tab key is not overridden in elements that capture focus.
    664 add_task(async function testTabCapturesFocus() {
    665  await openPopup();
    666  await showSubView(gComponentView);
    667 
    668  let backButton = gComponentView.querySelector(".subviewbutton-back");
    669  backButton.id = "shadowBack";
    670  await expectFocusAfterKey("Tab", backButton);
    671 
    672  // Only Button A can be navigated to before looping to back button.
    673  await expectFocusAfterKey("Tab", gShadowRootButtonA);
    674 
    675  await expectFocusAfterKey("Tab", backButton);
    676 
    677  gShadowRoot.dataset.capturesFocus = "true";
    678 
    679  // Both buttons can be focused before looping.
    680  await expectFocusAfterKey("Tab", gShadowRootButtonA);
    681  await expectFocusAfterKey("Tab", gShadowRootButtonB);
    682 
    683  await expectFocusAfterKey("Tab", backButton);
    684 
    685  await hidePopup();
    686 });