tor-browser

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

window_focus.xhtml (74798B)


      1 <?xml version="1.0"?>
      2 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
      3 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
      4                 type="text/css"?>
      5 <?xml-stylesheet href="data:text/css,dropmarker{min-width: 12px; min-height: 12px;}" type="text/css"?>
      6 <!--
      7 This test checks focus in various ways
      8 -->
      9 <window id="outer-document" title="Focus Test" width="600" height="550"
     10        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
     11  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
     12  <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
     13 
     14 <body xmlns="http://www.w3.org/1999/xhtml"/>
     15 
     16  <script type="application/javascript"><![CDATA[
     17 
     18 const { BrowserTestUtils } = ChromeUtils.importESModule(
     19  "resource://testing-common/BrowserTestUtils.sys.mjs"
     20 );
     21 const { ContentTask } = ChromeUtils.importESModule(
     22  "resource://testing-common/ContentTask.sys.mjs"
     23 );
     24 
     25 var fm = Cc["@mozilla.org/focus-manager;1"].
     26           getService(Ci.nsIFocusManager);
     27 
     28 const kChildDocumentRootIndex = 13;
     29 const kBeforeTabboxIndex = 34;
     30 const kTabbableSteps = 38;
     31 const kFocusSteps = 26;
     32 const kNoFocusSteps = 7;
     33 const kOverflowElementIndex = 27;
     34 
     35 var gTestStarted = false;
     36 var gPartialTabbing = false;
     37 var gMoveToFocusFrame = false;
     38 var gLastFocus = null;
     39 var gLastFocusWindow = window;
     40 var gLastFocusMethod = -1;
     41 var gEvents = "";
     42 var gExpectedEvents = "";
     43 var gEventMatched = true;
     44 var gShowOutput = false;
     45 var gChildWindow = null;
     46 
     47 var gOldExpectedWindow = null;
     48 var gNewExpectedWindow = null;
     49 
     50 function is(l, r, n) { window.arguments[0].SimpleTest.is(l,r,n); }
     51 function ok(v, n) { window.arguments[0].SimpleTest.ok(v,n); }
     52 
     53 function initEvents(target)
     54 {
     55  target.addEventListener("focus", eventOccured, true);
     56  target.addEventListener("blur", eventOccured, true);
     57  getTopWindow(target).addEventListener("activate", eventOccured, true);
     58  getTopWindow(target).addEventListener("deactivate", eventOccured, true);
     59 }
     60 
     61 function eventOccured(event)
     62 {
     63  // iframes should never receive focus or blur events directly
     64  if (Element.isInstance(event.target) && event.target.localName == "iframe")
     65    ok(false, "iframe " + event.type + "occured");
     66 
     67  var id;
     68  if (gOldExpectedWindow && event.type == "blur") {
     69    if (Window.isInstance(event.target))
     70      id = "frame-" + gOldExpectedWindow.document.documentElement.id + "-window";
     71    else if (Document.isInstance(event.target))
     72      id = "frame-" + gOldExpectedWindow.document.documentElement.id + "-document";
     73    else
     74      id = event.originalTarget.id;
     75  }
     76  else if (gNewExpectedWindow && event.type == "focus") {
     77    if (Window.isInstance(event.target))
     78      id = "frame-" + gNewExpectedWindow.document.documentElement.id + "-window";
     79    else if (Document.isInstance(event.target))
     80      id = "frame-" + gNewExpectedWindow.document.documentElement.id + "-document";
     81    else
     82      id = event.originalTarget.id;
     83  }
     84  else if (event.type == "activate" || event.type == "deactivate")
     85    id = event.target.document.documentElement.id + "-window";
     86  else if (Window.isInstance(event.target))
     87    id = (event.target == window) ? "outer-window" : "child-window";
     88  else if (Document.isInstance(event.target))
     89    id = (event.target == document) ? "outer-document" : "child-document";
     90  else
     91    id = event.originalTarget.id;
     92 
     93  if (gEvents)
     94    gEvents += " ";
     95  gEvents += event.type + ": " + id;
     96 }
     97 
     98 // eslint-disable-next-line complexity
     99 function expectFocusShift(callback, expectedWindow, expectedElement, focusChanged, testid)
    100 {
    101  if (expectedWindow == null)
    102    expectedWindow = expectedElement ?
    103                     expectedElement.ownerGlobal :
    104                     gLastFocusWindow;
    105 
    106  var expectedEvents = "";
    107  if (focusChanged) {
    108    var id;
    109    if (getTopWindow(gLastFocusWindow) != getTopWindow(expectedWindow)) {
    110      id = getTopWindow(gLastFocusWindow).document.documentElement.id;
    111      expectedEvents += "deactivate: " + id + "-window";
    112    }
    113 
    114    if (gLastFocus && gLastFocus.id != "t" + kChildDocumentRootIndex &&
    115        (!gOldExpectedWindow || gOldExpectedWindow.document.documentElement != gLastFocus)) {
    116      if (expectedEvents)
    117        expectedEvents += " ";
    118      if (!gOldExpectedWindow)
    119        expectedEvents += "commandupdate: cu ";
    120      expectedEvents += "blur: " + gLastFocus.id;
    121    }
    122 
    123    if (gLastFocusWindow && gLastFocusWindow != expectedWindow) {
    124      if (!gMoveToFocusFrame) {
    125        if (gOldExpectedWindow)
    126          id = "frame-" + gOldExpectedWindow.document.documentElement.id;
    127        else
    128          id = (gLastFocusWindow == window) ? "outer" : "child";
    129        if (expectedEvents)
    130          expectedEvents += " ";
    131        expectedEvents += "blur: " + id + "-document " +
    132                          "blur: " + id + "-window";
    133      }
    134    }
    135 
    136    if (getTopWindow(gLastFocusWindow) != getTopWindow(expectedWindow)) {
    137      id = getTopWindow(expectedWindow).document.documentElement.id;
    138      if (expectedEvents)
    139        expectedEvents += " ";
    140      expectedEvents += "activate: " + id + "-window";
    141    }
    142 
    143    if (expectedWindow && gLastFocusWindow != expectedWindow) {
    144      if (gNewExpectedWindow)
    145        id = "frame-" + gNewExpectedWindow.document.documentElement.id;
    146      else
    147        id = (expectedWindow == window) ? "outer" : "child";
    148      if (expectedEvents)
    149        expectedEvents += " ";
    150      expectedEvents += "focus: " + id + "-document " +
    151                        "focus: " + id + "-window";
    152    }
    153 
    154    // for this test which fires a mouse event on a label, the document will
    155    // be focused first and then the label code will focus the related
    156    // control. This doesn't result in different focus events, but a command
    157    // update will occur for the document and then a second command update will
    158    // occur when the control is focused. However, this will only happen on
    159    // platforms or controls where mouse clicks cause trigger focus.
    160    if (testid == "mouse on html label with content inside" &&
    161        mouseWillTriggerFocus(expectedElement)) {
    162      expectedEvents += " commandupdate: cu";
    163    }
    164 
    165    if (expectedElement &&
    166        (!gNewExpectedWindow || gNewExpectedWindow.document.documentElement != expectedElement)) {
    167      if (!gNewExpectedWindow) {
    168        if (expectedEvents)
    169          expectedEvents += " ";
    170        expectedEvents += "commandupdate: cu";
    171      }
    172      if (expectedElement.id != "t" + kChildDocumentRootIndex) {
    173        if (expectedEvents)
    174          expectedEvents += " ";
    175        expectedEvents += "focus: " + expectedElement.id;
    176      }
    177    }
    178    else if (expectedWindow && gLastFocusWindow != expectedWindow &&
    179             !expectedElement) {
    180      if (expectedEvents)
    181        expectedEvents += " ";
    182      expectedEvents += "commandupdate: cu";
    183    }
    184  }
    185 
    186  gLastFocus = expectedElement;
    187  gLastFocusWindow = expectedWindow;
    188 
    189  callback();
    190 
    191  compareEvents(expectedEvents, expectedWindow, expectedElement, testid);
    192 }
    193 
    194 function compareEvents(expectedEvents, expectedWindow, expectedElement, testid)
    195 {
    196  if (!gShowOutput) {
    197    gEvents = "";
    198    return;
    199  }
    200 
    201  is(gEvents, expectedEvents, testid + " events");
    202  gEvents = "";
    203 
    204  var doc;
    205  if (expectedWindow == window)
    206    doc = "outer-document";
    207  else if (expectedWindow == gChildWindow)
    208    doc = "inner-document";
    209  else if (gNewExpectedWindow)
    210    doc = gNewExpectedWindow.document.body ? gNewExpectedWindow.document.body.id :
    211                                             gNewExpectedWindow.document.documentElement.id;
    212  else
    213    doc = "other-document";
    214 
    215  var focusedElement = fm.focusedElement;
    216  is(focusedElement ? focusedElement.id : "none",
    217     expectedElement ? expectedElement.id : "none", testid + " focusedElement");
    218  is(fm.focusedWindow, expectedWindow, testid + " focusedWindow");
    219  var focusedWindow = {};
    220  is(fm.getFocusedElementForWindow(expectedWindow, false, focusedWindow),
    221     expectedElement, testid + " getFocusedElementForWindow");
    222  is(focusedWindow.value, expectedWindow, testid + " getFocusedElementForWindow frame");
    223  is(expectedWindow.document.hasFocus(), true, testid + " hasFocus");
    224  is(expectedWindow.document.activeElement ? expectedWindow.document.activeElement.id : "none",
    225     expectedElement ? expectedElement.id : doc, testid + " activeElement");
    226  var cdwindow = getTopWindow(expectedWindow);
    227  if (cdwindow.document.commandDispatcher) {
    228    is(cdwindow.document.commandDispatcher.focusedWindow, expectedWindow, testid + " commandDispatcher focusedWindow");
    229    is(cdwindow.document.commandDispatcher.focusedElement, focusedElement, testid + " commandDispatcher focusedElement");
    230  }
    231 
    232  if (gLastFocusMethod != -1) {
    233    is(fm.getLastFocusMethod(null), gLastFocusMethod, testid + " lastFocusMethod null");
    234    is(fm.getLastFocusMethod(expectedWindow), gLastFocusMethod, testid + " lastFocusMethod window");
    235  }
    236 
    237  // the parent should have the iframe focused
    238  if (doc == "inner-document") {
    239    is(document.hasFocus(), true, testid + " hasFocus");
    240    is(fm.getFocusedElementForWindow(window, false, focusedWindow),
    241       $("childframe"), testid + " getFocusedElementForWindow for parent");
    242    is(focusedWindow.value, window, testid + " getFocusedElementForWindow for parent frame");
    243    is(fm.getFocusedElementForWindow(window, true, focusedWindow),
    244       expectedElement, testid + " getFocusedElementForWindow deep for parent");
    245    is(focusedWindow.value, gChildWindow, testid + " getFocusedElementForWindow deep for parent frame");
    246    is(document.activeElement.id, "childframe", testid + " activeElement for parent");
    247  }
    248 
    249  // compare the selection for the child window. Skip mouse tests as the caret
    250  // is adjusted by the selection code for mouse clicks, and not the focus code.
    251  if (expectedWindow == window) {
    252    var selection = window.getSelection();
    253    ok(selection.focusNode == null && selection.focusOffset == 0 &&
    254       selection.anchorNode == null && selection.anchorOffset == 0, testid + " selection");
    255  }
    256  else if ((expectedWindow == gChildWindow) && !testid.indexOf("mouse") == -1) {
    257    checkSelection(expectedElement, testid);
    258  }
    259 }
    260 
    261 function checkSelection(node, testid)
    262 {
    263  var selection = gChildWindow.getSelection();
    264 
    265  var range = gChildWindow.document.createRange();
    266  range.selectNodeContents(node);
    267  if (!node.firstChild || node.localName == "input" ||
    268       node.localName == "select" || node.localName == "button") {
    269    range.setStartBefore(node);
    270    range.setEndBefore(node);
    271  }
    272 
    273  if (node.firstChild)
    274    range.setEnd(range.startContainer, range.startOffset);
    275 
    276  is(selection.focusNode, range.startContainer, testid + " selection focusNode");
    277  is(selection.focusOffset, range.startOffset, testid + " selection focusOffset");
    278  is(selection.anchorNode, range.endContainer, testid + " selection anchorNode");
    279  is(selection.anchorOffset, range.endOffset, testid + " selection anchorOffset");
    280 }
    281 
    282 function getTopWindow(win)
    283 {
    284  return win.browsingContext.topChromeWindow;
    285 }
    286 
    287 function mouseWillTriggerFocus(element)
    288 {
    289  if (!element) {
    290    return false;
    291  }
    292 
    293  if (SpecialPowers.getIntPref("accessibility.mouse_focuses_formcontrol") == 1) {
    294    // This is a chrome document, so we'll only trigger focus with the mouse if the value of the pref is 1.
    295    return true;
    296  }
    297 
    298  if (element.namespaceURI == "http://www.w3.org/1999/xhtml") {
    299    // links are special. They can be focused but show no focus ring
    300    if (element.localName == "a" || element.localName == "div" ||
    301        element.localName == "select" ||
    302        element.localName == "input" && (element.type == "text" ||
    303                                         element.type == "password")) {
    304      return true;
    305    }
    306  } else if (element.localName == "richlistbox") {
    307    return true;
    308  }
    309 
    310  return false;
    311 }
    312 
    313 function mouseOnElement(element, expectedElement, focusChanged, testid)
    314 {
    315  var expectedWindow = (element.ownerGlobal == gChildWindow) ? gChildWindow : window;
    316  // on Mac, form elements are not focused when clicking, except for lists and inputs.
    317  var noFocusOnMouse = !mouseWillTriggerFocus(element)
    318 
    319  if (noFocusOnMouse) {
    320    // no focus so the last focus method will be 0
    321    gLastFocusMethod = 0;
    322    expectFocusShift(() => synthesizeMouse(element, 4, 4, { }, element.ownerGlobal),
    323                     expectedWindow, null, true, testid);
    324    gLastFocusMethod = fm.FLAG_BYMOUSE;
    325  }
    326  else {
    327    expectFocusShift(() => synthesizeMouse(element, 4, 4, { }, element.ownerGlobal),
    328                     element.ownerGlobal,
    329                     expectedElement, focusChanged, testid);
    330  }
    331 }
    332 
    333 function done()
    334 {
    335  var opener = window.arguments[0];
    336  window.close();
    337  window.arguments[0].SimpleTest.finish();
    338 }
    339 
    340 var pressTab = () => synthesizeKey("KEY_Tab");
    341 
    342 function setFocusTo(id, fwindow)
    343 {
    344  gLastFocus = getById(id);
    345  gLastFocusWindow = fwindow;
    346  if (gLastFocus)
    347    gLastFocus.focus();
    348  else
    349    fm.clearFocus(fwindow);
    350  gEvents = "";
    351 }
    352 
    353 function getById(id)
    354 {
    355  if (gNewExpectedWindow)
    356    return gNewExpectedWindow.document.getElementById(id);
    357  var element = $(id);
    358  if (!element)
    359    element = $("childframe").contentDocument.getElementById(id);
    360  return element;
    361 }
    362 
    363 // eslint-disable-next-line complexity
    364 function startTest()
    365 {
    366  if (gTestStarted)
    367    return;
    368  gTestStarted = true;
    369 
    370  gChildWindow = $("childframe").contentWindow;
    371  gShowOutput = true;
    372 
    373  // synthesize a mousemove over the image to ensure that the imagemap data is
    374  // created. Otherwise, the special imagemap frames might not exist, and
    375  // won't be focusable.
    376  synthesizeMouse(getById("image"), 4, 4, { type: "mousemove" }, gChildWindow);
    377 
    378  initEvents(window);
    379 
    380  is(fm.activeWindow, window, "activeWindow");
    381  is(gChildWindow.document.hasFocus(), false, " child document hasFocus");
    382 
    383  // test to see if the Mac Full Keyboard Access setting is set. If t3 is
    384  // focused after tab is pressed, then it is set to inputs and lists only.
    385  // Otherwise, all elements are in the tab order.
    386  pressTab();
    387 
    388  if (fm.focusedElement.id == "t3")
    389    gPartialTabbing = true;
    390  else
    391    is(fm.focusedElement.id, "t1", "initial tab key");
    392 
    393  is(fm.getLastFocusMethod(null), fm.FLAG_BYKEY, "last focus method null start");
    394  is(fm.getLastFocusMethod(window), fm.FLAG_BYKEY, "last focus method window start");
    395 
    396  fm.clearFocus(window);
    397  gEvents = "";
    398 
    399  gLastFocusMethod = fm.FLAG_BYKEY;
    400  if (gPartialTabbing) {
    401    var partialTabList = ["t3", "t5", "t9", "t10", "t11", "t12", "t14", "t15",
    402                          "t16", "t19", "t20", "t21", "t22", "t26", "t27", "t28", "t29", "t30"];
    403    for (var idx = 0; idx < partialTabList.length; idx++) {
    404      expectFocusShift(pressTab, null, getById(partialTabList[idx]), true, "partial tab key " + partialTabList[idx]);
    405    }
    406    setFocusTo("last", window);
    407    expectFocusShift(pressTab, null, getById(partialTabList[0]), true, "partial tab key wrap to start");
    408    expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
    409                     null, getById("last"), true, "partial shift tab key wrap to end");
    410    for (var idx = partialTabList.length - 1; idx >= 0; idx--) {
    411      expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
    412                       null, getById(partialTabList[idx]), true, "partial tab key " + partialTabList[idx]);
    413    }
    414  }
    415  else {
    416    // TAB key
    417    for (var idx = 1; idx <= kTabbableSteps; idx++) {
    418      if (idx == kChildDocumentRootIndex) {
    419        continue;
    420      }
    421      expectFocusShift(pressTab, null, getById("t" + idx), true, "tab key t" + idx);
    422    }
    423 
    424    // wrapping around at end with TAB key
    425    setFocusTo("last", window);
    426    expectFocusShift(pressTab, null, getById("t1"), true, "tab key wrap to start");
    427    expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
    428                     null, getById("last"), true, "shift tab key wrap to end");
    429 
    430    // Shift+TAB key
    431    setFocusTo("o5", window);
    432    for (idx = kTabbableSteps; idx > 0; idx--) {
    433      if (idx == kChildDocumentRootIndex) {
    434        continue;
    435      }
    436      expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
    437                       null, getById("t" + idx), true, "shift tab key t" + idx);
    438    }
    439  }
    440 
    441  var t19 = getById("t19");
    442  is(t19.selectionStart, 0, "input focused from tab key selectionStart");
    443  is(t19.selectionEnd, 5, "input focused from tab key selectionEnd");
    444  t19.setSelectionRange(0, 0);
    445 
    446  gLastFocusMethod = 0;
    447  var selectFired = false;
    448  function selectListener() { selectFired = true; }
    449  t19.addEventListener("select", selectListener);
    450  expectFocusShift(() => t19.select(),
    451                   null, getById("t" + 19), true, "input.select()");
    452  t19.removeEventListener("select", selectListener);
    453  ok(!selectFired, "select event does not fire asynchronously for input");
    454 
    455  // mouse clicking
    456  gLastFocusMethod = fm.FLAG_BYMOUSE;
    457  for (idx = kTabbableSteps; idx >= 1; idx--) {
    458    // skip the document root and the overflow element
    459    if (idx == kChildDocumentRootIndex || idx == kOverflowElementIndex)
    460      continue;
    461    if ((navigator.platform.indexOf("Mac") == 0) && (idx == kBeforeTabboxIndex + 1))
    462      continue;
    463 
    464    var element = getById("t" + idx);
    465    // skip area elements, as getBoundingClientRect doesn't return their actual coordinates
    466    if (element.localName == "area")
    467      continue;
    468 
    469    mouseOnElement(element, element, true, "mouse on element t" + idx);
    470    var expectedWindow = (element.ownerGlobal == gChildWindow) ? gChildWindow : window;
    471    if (element.localName == "richlistbox" && expectedWindow == window &&
    472        navigator.platform.indexOf("Mac") == 0) {
    473      // after focusing a listbox on Mac, clear the focus before continuing.
    474      setFocusTo(null, window);
    475    }
    476  }
    477 
    478  ok(t19.selectionStart == t19.selectionEnd, "input focused from mouse selection");
    479 
    480  // mouse clicking on elements that are not tabbable
    481  for (idx = 1; idx <= kFocusSteps; idx++) {
    482    var element = getById("o" + (idx % 2 ? idx : idx - 1));
    483 
    484    mouseOnElement(element, element, idx % 2,
    485                   "mouse on non-tabbable element o" + idx);
    486  }
    487 
    488  // mouse clicking on elements that are not tabbable and have user-focus: none
    489  // or are not focusable for other reasons (for instance, being disabled)
    490  // These elements will clear the focus when clicked.
    491  for (idx = 1; idx <= kNoFocusSteps; idx++) {
    492    var element = getById("n" + idx);
    493    gLastFocusMethod = idx % 2 ? 0 : fm.FLAG_BYMOUSE;
    494 
    495    mouseOnElement(element, idx % 2 ? null: element, true, "mouse on unfocusable element n" + idx);
    496  }
    497 
    498  if (idx == kOverflowElementIndex) {
    499    gLastFocusMethod = fm.FLAG_BYMOUSE;
    500    var element = getById("t" + idx);
    501    expectFocusShift(() => synthesizeMouse(element, 4, 4, { }, element.ownerGlobal),
    502                     window, null, true, "mouse on scrollable element");
    503  }
    504 
    505  // focus() method
    506  gLastFocusMethod = fm.FLAG_BYJS;
    507  for (idx = kTabbableSteps; idx >= 1; idx--) {
    508    if ((navigator.platform.indexOf("Mac") == 0) && (idx == kBeforeTabboxIndex + 1))
    509      continue;
    510    expectFocusShift(() => getById("t" + idx).focus(),
    511                     null, getById("t" + idx), true, "focus method on element t" + idx);
    512  }
    513 
    514  $("t1").focus();
    515  ok(gEvents === "", "focusing element that is already focused");
    516 
    517  $("t2").blur();
    518  $("t7").blur();
    519  ok(gEvents === "", "blurring element that is not focused");
    520  is(document.activeElement, $("t1"), "old element still focused after blur() on another element");
    521 
    522  // focus() method on elements that are not tabbable
    523  for (idx = 1; idx <= kFocusSteps; idx++) {
    524    var expected = getById("o" + (idx % 2 ? idx : idx - 1));
    525    expectFocusShift(() => getById("o" + idx).focus(),
    526                     expected.ownerGlobal,
    527                     expected, idx % 2, "focus method on non-tabbable element o" + idx);
    528  }
    529 
    530  // focus() method on elements that are not tabbable and have user-focus: none
    531  // or are not focusable for other reasons (for instance, being disabled)
    532  for (idx = 1; idx <= kNoFocusSteps; idx++) {
    533    var expected = getById("o" + (idx % 2 ? idx : idx - 1));
    534    expectFocusShift(() => getById("o" + idx).focus(),
    535                     expected.ownerGlobal,
    536                     expected, idx % 2, "focus method on unfocusable element n" + idx);
    537  }
    538 
    539  // the focus() method on the legend element should focus the legend if it is
    540  // focusable, or the first element after the legend if it is not focusable.
    541  if (!gPartialTabbing) {
    542    gLastFocusMethod = fm.FLAG_BYJS;
    543    var legend = getById("legend");
    544    expectFocusShift(() => legend.focus(),
    545                     null, getById("t28"), true, "focus method on unfocusable legend");
    546    legend.tabIndex = "0";
    547    expectFocusShift(() => legend.focus(),
    548                     null, getById("legend"), true, "focus method on focusable legend");
    549    legend.tabIndex = "-1";
    550  }
    551 
    552  var accessKeyDetails = (navigator.platform.includes("Mac")) ?
    553                         { ctrlKey : true } : { altKey : true };
    554 
    555  // test accesskeys
    556  var keys = ["t26", "t19", "t22", "t29", "t15", "t17", "n6",
    557              "t4", "o1", "o9", "n4"];
    558  for (var k = 0; k < keys.length; k++) {
    559    var key = String.fromCharCode(65 + k);
    560 
    561    // accesskeys D and G are for labels so get redirected
    562    gLastFocusMethod = (key == "D" || key == "G") ? fm.FLAG_BYMOVEFOCUS : fm.FLAG_BYKEY;
    563 
    564    // on Windows and Linux, the shift key must be pressed for content area access keys
    565    // and on Mac, the alt key must be pressed for content area access keys
    566    var isContent = (getById(keys[k]).ownerGlobal == gChildWindow);
    567    if (!navigator.platform.includes("Mac")) {
    568      accessKeyDetails.shiftKey = isContent;
    569    } else {
    570      accessKeyDetails.altKey = isContent;
    571    }
    572 
    573    expectFocusShift(() => synthesizeKey(key, accessKeyDetails),
    574                     null, getById(keys[k]), true, "accesskey " + key);
    575  }
    576 
    577  // clicking on the labels
    578  gLastFocusMethod = fm.FLAG_BYMOVEFOCUS | fm.FLAG_BYMOUSE;
    579  mouseOnElement(getById("ad"), getById("t29"), true, "mouse on html label with content inside");
    580  mouseOnElement(getById("ag"), getById("n6"), true, "mouse on html label with for attribute");
    581  gLastFocusMethod = fm.FLAG_BYJS;
    582  expectFocusShift(() => synthesizeMouse(getById("aj"), 2, 2, { }),
    583                   null, getById("o9"), true, "mouse on xul label with content inside");
    584  expectFocusShift(() => synthesizeMouse(getById("ak"), 2, 2, { }),
    585                   null, getById("n4"), true, "mouse on xul label with control attribute");
    586 
    587  // test accesskeys that shouldn't work
    588  k = "o".charCodeAt(0);
    589  gLastFocusMethod = fm.FLAG_BYJS;
    590  while (k++ < "v".charCodeAt(0)) {
    591    var key = String.fromCharCode(k);
    592    expectFocusShift(() => synthesizeKey(key, accessKeyDetails),
    593                     window, getById("n4"), false, "non accesskey " + key);
    594  }
    595  gLastFocusMethod = -1;
    596 
    597  // should focus the for element when using the focus method on a label as well
    598  expectFocusShift(() => getById("ad").focus(),
    599                   null, getById("t29"), true, "mouse on html label using focus method");
    600 
    601  // make sure that the text is selected when clicking a label associated with an input
    602  getById("ag").htmlFor = "t19";
    603  expectFocusShift(() => synthesizeMouse(getById("ag"), 2, 2, { }, gChildWindow),
    604                   null, getById("t19"), true, "mouse on html label with for attribute changed");
    605  is(t19.selectionStart, 0, "input focused from label, selectionStart");
    606  is(t19.selectionEnd, 5, "input focused from label, selectionEnd");
    607 
    608  // switch to another panel in a tabbox and ensure that tabbing moves between
    609  // elements on the new panel.
    610  $("tabbox").selectedIndex = 1;
    611  expectFocusShift(() => getById("t" + kBeforeTabboxIndex).focus(),
    612                   null, getById("t" + kBeforeTabboxIndex), true, "focus method on element before tabbox");
    613 
    614  if (!gPartialTabbing) {
    615    expectFocusShift(pressTab, null, getById("tab2"), true, "focus method on tab");
    616    expectFocusShift(pressTab, null, getById("htab1"), true, "tab key switch tabpanel 1");
    617    expectFocusShift(pressTab, null, getById("htab2"), true, "tab key switch tabpanel 2");
    618    expectFocusShift(pressTab, null, getById("t" + (kBeforeTabboxIndex + 4)), true, "tab key switch tabpanel 3");
    619  }
    620  $("tabbox").selectedIndex = 0;
    621 
    622  // ---- the following checks when the focus changes during a blur or focus event ----
    623 
    624  var o5 = $("o5");
    625  var o9 = $("o9");
    626  var t3 = $("t3");
    627  var t17 = getById("t17");
    628  var t19 = getById("t19");
    629  var shiftFocusParentDocument = () => o9.focus();
    630  var shiftFocusChildDocument = () => t17.focus();
    631 
    632  var trapBlur = function (element, eventListener, blurFunction)
    633  {
    634    element.focus();
    635    gEvents = "";
    636    element.addEventListener("blur", eventListener);
    637    blurFunction();
    638    element.removeEventListener("blur", eventListener);
    639  }
    640 
    641  var functions = [
    642    element => element.focus(),
    643    element => synthesizeMouse(element, 4, 4, { }, element.ownerGlobal)
    644  ];
    645 
    646  // first, check cases where the focus is adjusted during the blur event. Iterate twice,
    647  // once with the focus method and then focusing by mouse clicking
    648  for  (var l = 0; l < 2; l++) {
    649    var adjustFocus = functions[l];
    650    var mod = (l == 1) ? " with mouse" : "";
    651 
    652    // an attempt is made to switch the focus from one element (o5) to another
    653    // element (t3) within the same document, yet the focus is shifted to a
    654    // third element (o9) in the same document during the blur event for the
    655    // first element.
    656    trapBlur(o5, shiftFocusParentDocument, () => adjustFocus(t3));
    657    compareEvents("commandupdate: cu blur: o5 commandupdate: cu focus: o9",
    658                  window, o9, "change focus to sibling during element blur, attempted sibling" + mod);
    659 
    660    // similar, but the third element (t17) is in a child document
    661    trapBlur(o9, shiftFocusChildDocument, () => adjustFocus(t3));
    662    compareEvents("commandupdate: cu blur: o9 blur: outer-document blur: outer-window " +
    663                  "focus: child-document focus: child-window commandupdate: cu focus: t17",
    664                  gChildWindow, t17, "change focus to child document during element blur, attempted sibling" + mod);
    665 
    666    // similar, but an attempt to switch focus within the same document, but the
    667    // third element (t17) is in a parent document
    668    trapBlur(t17, shiftFocusParentDocument, () => adjustFocus(t19));
    669    compareEvents("commandupdate: cu blur: t17 blur: child-document blur: child-window " +
    670                  "focus: outer-document focus: outer-window commandupdate: cu focus: o9",
    671                  window, o9, "change focus to parent document during element blur, attempted sibling" + mod);
    672 
    673    // similar, but blur is called instead of switching focus
    674    trapBlur(t3, shiftFocusParentDocument, () => t3.blur());
    675    compareEvents("commandupdate: cu blur: t3 commandupdate: cu focus: o9",
    676                  window, o9, "change focus to same document during clear focus" + mod);
    677 
    678    // check when an element in the same document is focused during the
    679    // element's blur event, but an attempt was made to focus an element in the
    680    // child document. In this case, the focus in the parent document should be
    681    // what was set during the blur event, but the actual focus should still
    682    // move to the child document.
    683    trapBlur(t3, shiftFocusParentDocument, () => adjustFocus(t17));
    684    compareEvents("commandupdate: cu blur: t3 commandupdate: cu focus: o9 " +
    685                  "blur: outer-document blur: outer-window " +
    686                  "focus: child-document focus: child-window commandupdate: cu focus: t17",
    687                  gChildWindow, t17, "change focus to sibling during element blur, attempted child" + mod);
    688    is(fm.getFocusedElementForWindow(window, false, {}), $("childframe"),
    689       "change focus to sibling during element blur, attempted child, focused in parent" + mod);
    690 
    691    // similar, but with a parent
    692    trapBlur(t19, shiftFocusChildDocument, () => adjustFocus(t3));
    693    compareEvents("commandupdate: cu blur: t19 commandupdate: cu focus: t17 " +
    694                  "blur: child-document blur: child-window " +
    695                  "focus: outer-document focus: outer-window commandupdate: cu focus: t3",
    696                  window, t3, "change focus to sibling during element blur, attempted parent" + mod);
    697    is(fm.getFocusedElementForWindow(gChildWindow, false, {}), t17,
    698       "change focus to sibling during element blur, attempted child, focused in child" + mod);
    699 
    700    // similar, with a child, but the blur event focuses a child element also
    701    trapBlur(t3, shiftFocusChildDocument, () => adjustFocus(t19));
    702    compareEvents("commandupdate: cu blur: t3 blur: outer-document blur: outer-window " +
    703                  "focus: child-document focus: child-window commandupdate: cu focus: t17",
    704                  gChildWindow, t17, "change focus to child during element blur, attempted child" + mod);
    705 
    706    // similar, with a parent, where the blur event focuses a parent element also
    707    trapBlur(t17, shiftFocusParentDocument, () => adjustFocus(t3));
    708    compareEvents("commandupdate: cu blur: t17 blur: child-document blur: child-window " +
    709                  "focus: outer-document focus: outer-window commandupdate: cu focus: o9",
    710                  window, o9, "change focus to parent during element blur, attempted parent" + mod);
    711  }
    712 
    713  var trapFocus = function (element, eventListener)
    714  {
    715    element.addEventListener("focus", eventListener);
    716    element.focus();
    717    element.removeEventListener("focus", eventListener);
    718  }
    719 
    720  fm.clearFocus(window);
    721  gEvents = "";
    722 
    723  // next, check cases where the focus is adjusted during the focus event
    724 
    725  // switch focus to an element in the same document
    726  trapFocus(o5, shiftFocusParentDocument);
    727  compareEvents("commandupdate: cu focus: o5 commandupdate: cu blur: o5 commandupdate: cu focus: o9",
    728                window, o9, "change focus to sibling during element focus");
    729 
    730  // similar, but the new element (t17) is in a child document
    731  trapFocus(o5, shiftFocusChildDocument);
    732  compareEvents("commandupdate: cu blur: o9 " +
    733                "commandupdate: cu focus: o5 commandupdate: cu blur: o5 " +
    734                "blur: outer-document blur: outer-window " +
    735                "focus: child-document focus: child-window commandupdate: cu focus: t17",
    736                gChildWindow, t17, "change focus to child document during element focus");
    737 
    738  // similar, but the new element (o9) is in a parent document.
    739  trapFocus(t19, shiftFocusParentDocument);
    740  compareEvents("commandupdate: cu blur: t17 " +
    741                "commandupdate: cu focus: t19 commandupdate: cu blur: t19 " +
    742                "blur: child-document blur: child-window " +
    743                "focus: outer-document focus: outer-window commandupdate: cu focus: o9",
    744                window, o9, "change focus to parent document during element focus");
    745 
    746  // clear the focus during the focus event
    747  trapFocus(t3, () => fm.clearFocus(window));
    748  compareEvents("commandupdate: cu blur: o9 commandupdate: cu focus: t3 commandupdate: cu blur: t3",
    749                window, null, "clear focus during focus event");
    750 
    751  if (!gPartialTabbing)
    752    doCommandDispatcherTests();
    753 
    754  testMoveFocus();
    755 
    756  doRemoveTests();
    757 
    758  // tests various focus manager apis for null checks
    759  var exh = false;
    760  try {
    761    fm.clearFocus(null);
    762  }
    763  catch (ex) { exh = true; }
    764  is(exh, true, "clearFocus with null window causes exception");
    765 
    766  var exh = false;
    767  try {
    768    fm.getFocusedElementForWindow(null, false, focusedWindow);
    769  }
    770  catch (ex) { exh = true; }
    771  is(exh, true, "getFocusedElementForWindow with null window causes exception");
    772 
    773  // just make sure that this doesn't crash
    774  fm.moveCaretToFocus(null);
    775 
    776  // ---- tests for the FLAG_NOSWITCHFRAME flag
    777  getById("o5").focus();
    778  gLastFocusMethod = fm.FLAG_BYJS;
    779  gEvents = "";
    780  // focus is being shifted in a child, so the focus should not change
    781  expectFocusShift(() => fm.setFocus(getById("t20"), fm.FLAG_NOSWITCHFRAME),
    782                   window, getById("o5"), false, "no switch frame focus to child");
    783  setFocusTo("t20", gChildWindow);
    784 
    785  gLastFocusMethod = 0;
    786 
    787  // here, however, focus is being shifted in a parent, which will have to blur
    788  // the child, so the focus will always change
    789  expectFocusShift(() => fm.setFocus(getById("o5"), fm.FLAG_NOSWITCHFRAME),
    790                   window, getById("o5"), true, "no switch frame focus to parent");
    791 
    792  expectFocusShift(() => fm.setFocus(getById("t1"), fm.FLAG_NOSWITCHFRAME),
    793                   window, getById("t1"), true, "no switch frame focus to same window");
    794 
    795  // ---- tests for focus and scrolling into view ----
    796  var inscroll = getById("inscroll");
    797  inscroll.tabIndex = 0;
    798  is(inscroll.parentNode.scrollTop, 0, "scroll position before focus");
    799  inscroll.focus();
    800  ok(inscroll.parentNode.scrollTop > 5, "scroll position after focus");
    801  inscroll.parentNode.scrollTop = 0;
    802  fm.setFocus(inscroll, fm.FLAG_NOSCROLL);
    803  is(inscroll.parentNode.scrollTop, 0, "scroll position after noscroll focus");
    804 
    805  getById("t9").focus();
    806  getById("inpopup1").focus();
    807  is(fm.focusedElement, getById("t9"), "focus in closed popup");
    808 
    809  // ---- tests to check if tabbing out of a input works
    810 
    811  setFocusTo("t1", window);
    812 
    813  var input1 = document.createElement("input");
    814  $("innerbox").appendChild(input1);
    815 
    816  var input2 = document.createElement("input");
    817  $("innerbox").appendChild(input2);
    818 
    819  gLastFocusMethod = fm.FLAG_BYJS;
    820  expectFocusShift(() => input2.focus(),
    821                   null, input2, true, "focus on input");
    822  gLastFocusMethod = fm.FLAG_BYKEY;
    823  expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
    824                   null, input1, true, "shift+tab on input");
    825 
    826  input1.tabIndex = 2;
    827  input2.tabIndex = 2;
    828  gLastFocusMethod = fm.FLAG_BYJS;
    829  expectFocusShift(() => input2.focus(),
    830                   null, input2, true, "focus on input with tabindex set");
    831  gLastFocusMethod = fm.FLAG_BYKEY;
    832  expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
    833                   null, input1, true, "shift+tab on input with tabindex set");
    834 
    835  // ---- test to check that refocusing an element during a blur event
    836 
    837  var t1 = getById("t1");
    838  t1.addEventListener("blur", () => t1.focus(), {capture: true, once: true});
    839  t1.focus();
    840  var t3 = getById("t3");
    841  synthesizeMouse(t3, 2, 2, { });
    842  is(fm.focusedElement, t1, "focus during blur");
    843 
    844  setFocusTo("t9", window);
    845  gLastFocusMethod = -1;
    846  window.openDialog("focus_window2.xhtml", "_blank", "chrome", otherWindowFocused);
    847 }
    848 
    849 function doCommandDispatcherTests()
    850 {
    851  var t19 = getById("t19");
    852  t19.focus();
    853  gLastFocusWindow = gChildWindow;
    854  gLastFocus = t19;
    855  gEvents = "";
    856 
    857  expectFocusShift(() => document.commandDispatcher.focusedElement = getById("o9"),
    858                   null, getById("o9"), true, "command dispatcher set focusedElement");
    859  expectFocusShift(() => document.commandDispatcher.advanceFocus(),
    860                   null, getById("o13"), true, "command dispatcher advanceFocus");
    861  expectFocusShift(() => document.commandDispatcher.rewindFocus(),
    862                   null, getById("o9"), true, "command dispatcher rewindFocus");
    863  expectFocusShift(() => document.commandDispatcher.focusedElement = null,
    864                   null, null, true, "command dispatcher set focusedElement to null");
    865  expectFocusShift(() => document.commandDispatcher.focusedWindow = gChildWindow,
    866                   null, getById("t19"), true, "command dispatcher set focusedElement to null");
    867  expectFocusShift(() => document.commandDispatcher.focusedElement = null,
    868                   gChildWindow, null, true, "command dispatcher set focusedElement to null in child");
    869  expectFocusShift(() => document.commandDispatcher.advanceFocusIntoSubtree(getById("t19")),
    870                   null, getById("t20"), true, "command dispatcher advanceFocusIntoSubtree child");
    871  expectFocusShift(() => document.commandDispatcher.advanceFocusIntoSubtree(null),
    872                   null, getById("t21"), true, "command dispatcher advanceFocusIntoSubtree null child");
    873  expectFocusShift(() => document.commandDispatcher.advanceFocusIntoSubtree(getById("o9").parentNode),
    874                   null, getById("o9"), true, "command dispatcher advanceFocusIntoSubtree parent");
    875 }
    876 
    877 function doRemoveTests()
    878 {
    879  // next, some tests which remove elements
    880  var t19 = getById("t19");
    881  t19.focus();
    882  t19.remove();
    883 
    884  is(fm.focusedElement, null, "removed element focusedElement");
    885  is(fm.focusedWindow, gChildWindow, "removed element focusedWindow");
    886  is(gChildWindow.document.hasFocus(), true, "removed element hasFocus");
    887  is(gChildWindow.document.activeElement, getById("inner-document"), "removed element activeElement");
    888 
    889  getById("t15").focus();
    890  var abs = getById("abs");
    891  abs.remove();
    892 
    893  is(fm.focusedElement, null, "removed ancestor focusedElement");
    894  is(fm.focusedWindow, gChildWindow, "removed ancestor focusedWindow");
    895  is(gChildWindow.document.hasFocus(), true, "removed ancestor hasFocus");
    896  is(gChildWindow.document.activeElement, getById("inner-document"), "removed ancestor activeElement");
    897 }
    898 
    899 // tests for the FocusManager moveFocus method
    900 function testMoveFocus()
    901 {
    902  setFocusTo("t6", window);
    903 
    904  // moving focus while an element is already focused
    905  var newFocus;
    906  gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
    907  var expectedFirst = getById(gPartialTabbing ? "t3" : "t1");
    908  expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FIRST, 0),
    909                   window, expectedFirst, true, "moveFocus to first null window null content");
    910  is(newFocus, fm.focusedElement, "moveFocus to first null window null content return value");
    911 
    912  expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_LAST, 0),
    913                   window, getById("last"), true, "moveFocus to last null window null content");
    914  is(newFocus, fm.focusedElement, "moveFocus to last null window null content return value");
    915 
    916  gLastFocusMethod = 0;
    917  newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_ROOT, 0);
    918  is(newFocus, null, "moveFocus to root null window null content return value");
    919  is(fm.focusedWindow, window, "moveFocus to root null window null content focusedWindow");
    920  is(fm.focusedElement, null, "moveFocus to root null window null content focusedElement");
    921 
    922  // moving focus while no element is focused
    923  fm.clearFocus(window);
    924  gEvents = "";
    925  gLastFocus = null;
    926  gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
    927  expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FIRST, 0),
    928                   window, expectedFirst, true, "moveFocus to first null window null content no focus");
    929  is(newFocus, fm.focusedElement, "moveFocus to first null window null content no focus return value");
    930  fm.clearFocus(window);
    931  gEvents = "";
    932  gLastFocus = null;
    933  expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_LAST, 0),
    934                   window, getById("last"), true, "moveFocus to last null window null content no focus");
    935  is(newFocus, fm.focusedElement, "moveFocus to last null window null content no focus return value");
    936  fm.clearFocus(window);
    937  gEvents = "";
    938  gLastFocusMethod = 0;
    939  newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_ROOT, 0);
    940  is(newFocus, null, "moveFocus to root null window null content no focus return value");
    941  is(fm.focusedWindow, window, "moveFocus to root null window null content no focus focusedWindow");
    942  is(fm.focusedElement, null, "moveFocus to root null window null content no focus focusedElement");
    943 
    944  // moving focus from a specified element
    945  setFocusTo("t6", window);
    946  gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
    947  expectFocusShift(() => newFocus = fm.moveFocus(null, getById("specialroot"), fm.MOVEFOCUS_FIRST, 0),
    948                   window, getById("t3"), true, "moveFocus to first null window with content");
    949 // XXXndeakin P3 this doesn't work
    950 //  expectFocusShift(() => newFocus = fm.moveFocus(null, getById("specialroot"), fm.MOVEFOCUS_LAST, 0),
    951 //                   window, getById("o3"), true, "moveFocus to last null window with content");
    952 
    953  // move focus to first in child window
    954  expectFocusShift(() => newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_FIRST, 0),
    955                   gChildWindow, getById("t" + (kChildDocumentRootIndex + 1)), true,
    956                   "moveFocus to first child window null content");
    957  is(newFocus, getById("t" + (kChildDocumentRootIndex + 1)),
    958     "moveFocus to first child window null content return value");
    959 
    960  // move focus to last in child window
    961  setFocusTo("t6", window);
    962  var expectedLast = getById(gPartialTabbing ? "t30" : "t" + (kBeforeTabboxIndex - 1));
    963  expectFocusShift(() => newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_LAST, 0),
    964                   gChildWindow, expectedLast, true,
    965                   "moveFocus to last child window null content");
    966  is(newFocus, getById(expectedLast),
    967     "moveFocus to last child window null content return value");
    968 
    969  // move focus to root in child window
    970  setFocusTo("t6", window);
    971  var childroot = getById("t" + kChildDocumentRootIndex);
    972  gLastFocusMethod = 0;
    973  newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_ROOT, 0);
    974  is(newFocus, childroot, "moveFocus to root child window null content return value");
    975  is(fm.focusedWindow, gChildWindow, "moveFocus to root child window null content focusedWindow");
    976  is(fm.focusedElement, childroot, "moveFocus to root child window null content focusedElement");
    977 
    978  // MOVEFOCUS_CARET tests
    979  getById("t20").focus();
    980  gEvents = "";
    981 
    982  var selection = gChildWindow.getSelection();
    983  selection.removeAllRanges();
    984 
    985  newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_CARET, 0);
    986  is(newFocus, null, "move caret when at document root");
    987  is(fm.focusedElement, null, "move caret when at document root");
    988 
    989  var node = getById("t16").firstChild;
    990  var range = gChildWindow.document.createRange();
    991  range.setStart(node, 3);
    992  range.setEnd(node, 3);
    993  selection.addRange(range);
    994 
    995  newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_CARET, 0);
    996  is(newFocus, null, "move caret to non-link return value");
    997  is(fm.focusedElement, null, "move caret to non-link");
    998 
    999  var t25 = getById("t25");
   1000  var node = t25.firstChild;
   1001  range.setStart(node, 1);
   1002  range.setEnd(node, 1);
   1003  newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_CARET, 0);
   1004 
   1005  is(newFocus, t25, "move caret to link return value");
   1006  is(fm.focusedElement, t25, "move caret to link focusedElement");
   1007 
   1008  // enable caret browsing temporarily to test caret movement
   1009  var prefs = Cc["@mozilla.org/preferences-service;1"].
   1010                getService(Ci.nsIPrefBranch);
   1011  prefs.setBoolPref("accessibility.browsewithcaret", true);
   1012 
   1013  synthesizeKey("KEY_ArrowLeft", {}, gChildWindow);
   1014  synthesizeKey("KEY_ArrowLeft", {}, gChildWindow);
   1015  is(fm.focusedElement, null, "move caret away from link");
   1016 
   1017  synthesizeKey("KEY_ArrowLeft", {}, gChildWindow);
   1018  is(fm.focusedElement, getById("t24"), "move caret away onto link");
   1019 
   1020  prefs.setBoolPref("accessibility.browsewithcaret", false);
   1021 
   1022  // cases where focus in on a content node with no frame
   1023 
   1024  if (!gPartialTabbing) {
   1025    getById("t24").blur();
   1026    gEvents = "";
   1027    gLastFocus = null;
   1028    gLastFocusWindow = gChildWindow;
   1029    gLastFocusMethod = fm.FLAG_BYKEY;
   1030 
   1031    selection.selectAllChildren(getById("hiddenspan"));
   1032    expectFocusShift(() => synthesizeKey("KEY_Tab"),
   1033                     gChildWindow, getById("t26"), true, "tab with selection on hidden content");
   1034 
   1035    setFocusTo($("o15"), window);
   1036    $("o15").hidden = true;
   1037    document.documentElement.getBoundingClientRect(); // flush after hiding
   1038    expectFocusShift(() => synthesizeKey("KEY_Tab"),
   1039                     window, $("o17"), true, "tab with focus on hidden content");
   1040 
   1041    $("o17").hidden = true;
   1042    document.documentElement.getBoundingClientRect();
   1043    expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
   1044                     window, $("o13"), true, "shift+tab with focus on hidden content");
   1045  }
   1046 
   1047  // cases with selection in an <input>
   1048 
   1049  var t19 = getById("t19");
   1050  t19.setSelectionRange(0, 0);
   1051  setFocusTo("t18", gChildWindow);
   1052 
   1053  gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
   1054  expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FORWARD, 0),
   1055                   gChildWindow, t19, true, "moveFocus to next input");
   1056  is(t19.selectionStart, 0, "input focused after moveFocus selectionStart");
   1057  is(t19.selectionEnd, 5, "input focused after moveFocus selectionEnd");
   1058 
   1059  t19.setSelectionRange(0, 0);
   1060  setFocusTo("t18", gChildWindow);
   1061  gLastFocusMethod = fm.FLAG_BYKEY;
   1062  expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FORWARD, fm.FLAG_BYKEY),
   1063                   gChildWindow, t19, true, "moveFocus to next input by key");
   1064  is(t19.selectionStart, 0, "input focused after moveFocus by key selectionStart");
   1065  is(t19.selectionEnd, 5, "input focused after moveFocus by key selectionEnd");
   1066 }
   1067 
   1068 function otherWindowFocused(otherWindow)
   1069 {
   1070  var expectedElement = getById("t9");
   1071 
   1072  is(fm.activeWindow, otherWindow, "other activeWindow");
   1073  is(fm.focusedWindow, otherWindow, "other focusedWindow");
   1074  is(window.document.hasFocus(), false, "when lowered document hasFocus");
   1075  var focusedWindow = {};
   1076  is(fm.getFocusedElementForWindow(window, false, focusedWindow),
   1077     expectedElement, "when lowered getFocusedElementForWindow");
   1078  is(focusedWindow.value, window, "when lowered getFocusedElementForWindow frame");
   1079  is(document.activeElement.id, expectedElement.id, "when lowered activeElement");
   1080  is(window.document.commandDispatcher.focusedWindow, window, " commandDispatcher in other window focusedWindow");
   1081  is(window.document.commandDispatcher.focusedElement, expectedElement, " commandDispatcher in other window focusedElement");
   1082 
   1083  compareEvents("deactivate: outer-document-window blur: t9 blur: outer-document blur: outer-window",
   1084                otherWindow, null, "other window opened");
   1085 
   1086  otherWindow.document.getElementById("other").focus();
   1087 
   1088  for (var idx = kTabbableSteps; idx >= 1; idx--) {
   1089    expectedElement = getById("t" + idx);
   1090    if (!expectedElement) // skip elements that were removed in doRemoveTests()
   1091      continue;
   1092    if ((navigator.platform.indexOf("Mac") == 0) && (idx == kBeforeTabboxIndex + 1))
   1093      continue;
   1094 
   1095    expectedElement.focus();
   1096 
   1097    is(fm.focusedElement.id, "other", "when lowered focusedElement t" + idx);
   1098    is(fm.focusedWindow, otherWindow, "when lowered focusedWindow t" + idx);
   1099 
   1100    var checkWindow = expectedElement.ownerGlobal;
   1101    is(fm.getFocusedElementForWindow(checkWindow, false, {}).id, expectedElement.id,
   1102       "when lowered getFocusedElementForWindow t" + idx);
   1103    is(checkWindow.document.activeElement.id, expectedElement.id, "when lowered activeElement t" + idx);
   1104    if (checkWindow != window) {
   1105      is(fm.getFocusedElementForWindow(window, false, {}), $("childframe"),
   1106         "when lowered parent getFocusedElementForWindow t" + idx);
   1107      is(document.activeElement.id, "childframe",
   1108         "when lowered parent activeElement t" + idx);
   1109    }
   1110  }
   1111 
   1112  gEvents = gEvents.replace(/commandupdate: cu\s?/g, "");
   1113  is(gEvents, "", "when lowered no events fired");
   1114 
   1115  var other = otherWindow.document.getElementById("other");
   1116  other.focus();
   1117  is(fm.focusedElement, other, "focus method in second window");
   1118 
   1119  otherWindow.close();
   1120 
   1121  getById("n2").focus();
   1122 
   1123  // next, check modal dialogs
   1124  window.openDialog("focus_window2.xhtml", "_blank", "chrome,modal", modalWindowOpened);
   1125 }
   1126 
   1127 async function modalWindowOpened(modalWindow)
   1128 {
   1129  var elem = modalWindow.document.getElementById("other");
   1130  if (gPartialTabbing)
   1131    elem.focus();
   1132  else
   1133    synthesizeKey("KEY_Tab", {}, modalWindow);
   1134  is(fm.activeWindow, modalWindow, "modal activeWindow");
   1135  is(fm.focusedElement, elem, "modal focusedElement");
   1136 
   1137  // For silly reasons (see the comments around nsCloseEvent), close() doesn't
   1138  // synchronously close the modal window. If we try to focus the window before
   1139  // the modal window is fully closed, we fail to focus it (see
   1140  // AppWindow::EnableParent).
   1141  //
   1142  // Wait for the unload event to make sure that we get enabled on time. Another
   1143  // alternative would be to use the xul-window-destroyed topic.
   1144  await new Promise(r => {
   1145    modalWindow.addEventListener("unload", r, { once: true });
   1146    modalWindow.close();
   1147  });
   1148  await SimpleTest.promiseFocus();
   1149  modalWindowClosed();
   1150 }
   1151 
   1152 function modalWindowClosed()
   1153 {
   1154  is(fm.activeWindow, window, "modal window closed activeWindow");
   1155  is(fm.focusedElement, getById("n2"), "modal window closed focusedElement");
   1156 
   1157  window.arguments[0].framesetWindowLoaded = framesetWindowLoaded;
   1158  window.arguments[0].open("focus_frameset.html", "_blank", "width=400,height=400,toolbar=no");
   1159 }
   1160 
   1161 function framesetWindowLoaded(framesetWindow)
   1162 {
   1163  gLastFocus = null;
   1164  gLastFocusWindow = framesetWindow;
   1165  gEvents = "";
   1166 
   1167  is(fm.activeWindow, getTopWindow(framesetWindow), "frameset window active");
   1168  gOldExpectedWindow = getTopWindow(framesetWindow);
   1169 
   1170  gMoveToFocusFrame = true;
   1171  let steps = 4;
   1172  for (var idx = 1; idx <= steps; idx++) {
   1173    let frameIndex = idx - 1;
   1174    gNewExpectedWindow = framesetWindow.frames[frameIndex];
   1175    initEvents(gNewExpectedWindow);
   1176    expectFocusShift(() => synthesizeKey("KEY_Tab", {}, framesetWindow),
   1177                     gNewExpectedWindow, getById("f" + idx * 2), true, "frameset tab key f" + idx);
   1178    gMoveToFocusFrame = false;
   1179    gOldExpectedWindow = gNewExpectedWindow;
   1180  }
   1181 
   1182  gNewExpectedWindow = framesetWindow.frames[0];
   1183  expectFocusShift(() => synthesizeKey("KEY_Tab", {}, framesetWindow),
   1184                   gNewExpectedWindow, getById("f2"), true, "frameset tab key wrap to start");
   1185  gOldExpectedWindow = gNewExpectedWindow;
   1186  gNewExpectedWindow = framesetWindow.frames[3];
   1187  expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}, framesetWindow),
   1188                   gNewExpectedWindow, getById("f8"), true, "frameset shift tab key wrap to end");
   1189 
   1190  steps = 3;
   1191  for (idx = steps; idx >= 1; idx--) {
   1192    gOldExpectedWindow = gNewExpectedWindow;
   1193    let frameIndex = idx - 1;
   1194    gNewExpectedWindow = framesetWindow.frames[frameIndex];
   1195    expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}, framesetWindow),
   1196                     gNewExpectedWindow, getById("f" + idx * 2), true, "frameset shift tab key f" + idx);
   1197  }
   1198 
   1199  // document shifting
   1200  // XXXndeakin P3 ctrl+tab doesn't seem to be testable currently for some reason
   1201  gNewExpectedWindow = framesetWindow.frames[1];
   1202  expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true}, framesetWindow),
   1203                   gNewExpectedWindow, getById("f3"), true, "switch document forward with f6");
   1204  gOldExpectedWindow = gNewExpectedWindow;
   1205  gNewExpectedWindow = framesetWindow.frames[2];
   1206  expectFocusShift(() => synthesizeKey("KEY_F6", {}, framesetWindow),
   1207                   gNewExpectedWindow, getById("f5"), true, "switch document forward with ctrl+tab");
   1208  gOldExpectedWindow = gNewExpectedWindow;
   1209  gNewExpectedWindow = framesetWindow.frames[3];
   1210  expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true}, framesetWindow),
   1211                   gNewExpectedWindow, getById("f7"), true, "switch document forward with ctrl+f6");
   1212  gOldExpectedWindow = gNewExpectedWindow;
   1213  gNewExpectedWindow = framesetWindow.frames[0];
   1214  expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true}, framesetWindow),
   1215                   gNewExpectedWindow, getById("f1"), true, "switch document forward and wrap");
   1216 
   1217 // going backwards by document and wrapping doesn't currently work, but didn't work
   1218 // before the focus reworking either
   1219 
   1220 /*
   1221  gOldExpectedWindow = gNewExpectedWindow;
   1222  gNewExpectedWindow = framesetWindow.frames[3];
   1223  expectFocusShift(() => synthesizeKey("KEY_F6", { ctrlKey: true, shiftKey: true }, framesetWindow),
   1224                   gNewExpectedWindow, getById("f7"), true, "switch document backward and wrap");
   1225 */
   1226 
   1227  fm.moveFocus(framesetWindow.frames[3], null, fm.MOVEFOCUS_ROOT, 0);
   1228  gEvents = "";
   1229 
   1230  gOldExpectedWindow = gNewExpectedWindow;
   1231  gNewExpectedWindow = framesetWindow.frames[2];
   1232  expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true, shiftKey: true}, framesetWindow),
   1233                   gNewExpectedWindow, getById("f5"), true, "switch document backward with f6");
   1234  gOldExpectedWindow = gNewExpectedWindow;
   1235  gNewExpectedWindow = framesetWindow.frames[1];
   1236  expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true, shiftKey: true}, framesetWindow),
   1237                   gNewExpectedWindow, getById("f3"), true, "switch document backward with ctrl+tab");
   1238  gOldExpectedWindow = gNewExpectedWindow;
   1239  gNewExpectedWindow = framesetWindow.frames[0];
   1240  expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true, shiftKey: true}, framesetWindow),
   1241                   gNewExpectedWindow, getById("f1"), true, "switch document backward with ctrl+f6");
   1242 
   1243  // skip the window switching tests for now on Linux, as raising and lowering
   1244  // a window is asynchronous there
   1245  if (!navigator.platform.includes("Linux")) {
   1246    window.openDialog("focus_window2.xhtml", "_blank", "chrome", switchWindowTest, framesetWindow);
   1247  }
   1248  else {
   1249    gOldExpectedWindow = null;
   1250    gNewExpectedWindow = null;
   1251    framesetWindow.close();
   1252    SimpleTest.waitForFocus(doWindowNoRootTest);
   1253  }
   1254 }
   1255 
   1256 // test switching between two windows
   1257 function switchWindowTest(otherWindow, framesetWindow)
   1258 {
   1259  initEvents(otherWindow);
   1260  var otherElement = otherWindow.document.getElementById("other");
   1261  otherElement.focus();
   1262 
   1263  framesetWindow.frames[1].document.getElementById("f4").focus();
   1264 
   1265  is(fm.focusedElement, otherElement, "focus after inactive window focus");
   1266 
   1267  gLastFocus = otherElement;
   1268  gLastFocusWindow = otherWindow;
   1269  gEvents = "";
   1270  gOldExpectedWindow = otherWindow;
   1271  gNewExpectedWindow = framesetWindow.frames[1];
   1272 
   1273  expectFocusShift(() => gNewExpectedWindow.focus(),
   1274                   gNewExpectedWindow, getById("f4"), true, "switch to frame in another window");
   1275  is(fm.getFocusedElementForWindow(otherWindow, false, {}).id, "other", "inactive window has focused element");
   1276 
   1277  gOldExpectedWindow = framesetWindow.frames[1];
   1278  gNewExpectedWindow = otherWindow;
   1279  expectFocusShift(() => otherWindow.focus(),
   1280                   gNewExpectedWindow, getById("other"), true, "switch to another window");
   1281 
   1282  var topWindow = getTopWindow(framesetWindow);
   1283 
   1284  ok(topWindow.document.commandDispatcher.getControllerForCommand("cmd_copy"),
   1285     "getControllerForCommand for focused window set");
   1286  ok(otherWindow.document.commandDispatcher.getControllerForCommand("cmd_copy"),
   1287     "getControllerForCommand for non-focused window set");
   1288  ok(topWindow.document.commandDispatcher.getControllerForCommand("cmd_copy") !=
   1289     otherWindow.document.commandDispatcher.getControllerForCommand("cmd_copy"),
   1290     "getControllerForCommand for two windows different");
   1291  ok(topWindow.document.commandDispatcher.getControllers() !=
   1292     otherWindow.document.commandDispatcher.getControllers(),
   1293     "getControllers for two windows different");
   1294 
   1295  gOldExpectedWindow = otherWindow;
   1296  gNewExpectedWindow = framesetWindow.frames[1];
   1297  expectFocusShift(() => topWindow.focus(),
   1298                   gNewExpectedWindow, getById("f4"), true, "switch to frame activeWindow");
   1299 
   1300  fm.clearFocus(otherWindow);
   1301  gOldExpectedWindow = gNewExpectedWindow;
   1302  gNewExpectedWindow = otherWindow;
   1303  expectFocusShift(() => fm.setFocus(otherElement, fm.FLAG_RAISE),
   1304                   gNewExpectedWindow, getById("other"), true, "switch to window with raise");
   1305 
   1306  getTopWindow(framesetWindow).document.commandDispatcher.focusedWindow = gOldExpectedWindow;
   1307  is(fm.activeWindow, gNewExpectedWindow, "setting commandDispatcher focusedWindow doesn't raise window");
   1308 
   1309  fm.moveFocus(otherWindow, null, fm.MOVEFOCUS_FORWARD, 0);
   1310  var otherInput = otherWindow.document.getElementById("other-input");
   1311  otherInput.setSelectionRange(2, 3);
   1312  topWindow.focus();
   1313  otherWindow.focus();
   1314  is(otherInput.selectionStart, 2, "selectionStart after input focus and window raise");
   1315  is(otherInput.selectionEnd, 3, "selectionEnd after input focus and window raise");
   1316  is(fm.getLastFocusMethod(null), fm.FLAG_BYMOVEFOCUS, "last focus method after input focus and window raise");
   1317 
   1318  fm.clearFocus(otherWindow);
   1319 
   1320  // test to ensure that a synthetic event won't move focus
   1321  var synevent = new FocusEvent("focus", {});
   1322  otherInput.dispatchEvent(synevent);
   1323  is(synevent.type, "focus", "event.type after synthetic focus event");
   1324  is(synevent.target, otherInput, "event.target after synthetic focus event");
   1325  is(fm.focusedElement, null, "focusedElement after synthetic focus event");
   1326  is(otherWindow.document.activeElement, otherWindow.document.documentElement,
   1327     "document.activeElement after synthetic focus event");
   1328 
   1329  // check accessing a focus event after the event has finishing firing
   1330  function continueTest(event) {
   1331    is(event.type, "focus", "event.type after accessing focus event in timeout");
   1332    is(event.target, otherInput, "event.target after accessing focus event in timeout");
   1333 
   1334    gOldExpectedWindow = null;
   1335    gNewExpectedWindow = null;
   1336    otherWindow.close();
   1337    framesetWindow.close();
   1338 
   1339    SimpleTest.waitForFocus(doWindowNoRootTest);
   1340  }
   1341 
   1342  function inputFocused(event) {
   1343    otherInput.removeEventListener("focus", inputFocused, true);
   1344    setTimeout(continueTest, 0, event);
   1345  }
   1346 
   1347  otherInput.addEventListener("focus", inputFocused, true);
   1348  otherInput.focus();
   1349 }
   1350 
   1351 // open a window with no root element
   1352 var noRootWindow = null;
   1353 function doWindowNoRootTest()
   1354 {
   1355  addEventListener("focus", doFrameSwitchingTests, true);
   1356  noRootWindow = window.open("window_focus_inner.xhtml", "_blank", "chrome,width=100,height=100");
   1357 }
   1358 
   1359 // these tests check when focus is moved between a tree of frames to ensure
   1360 // that the focus is in the right place at each event step.
   1361 function doFrameSwitchingTests()
   1362 {
   1363  removeEventListener("focus", doFrameSwitchingTests, true);
   1364  noRootWindow.close();
   1365 
   1366  var framea = document.getElementById("ifa");
   1367  var frameb = document.getElementById("ifb");
   1368  framea.style.MozUserFocus = "";
   1369  frameb.style.MozUserFocus = "";
   1370 
   1371  window.removeEventListener("focus", eventOccured, true);
   1372  window.removeEventListener("blur", eventOccured, true);
   1373 
   1374  var inputa = framea.contentDocument.body.firstChild;
   1375  inputa.focus();
   1376 
   1377  addFrameSwitchingListeners(framea);
   1378  addFrameSwitchingListeners(frameb);
   1379  var framec = framea.contentDocument.body.lastChild;
   1380  addFrameSwitchingListeners(framec);
   1381 
   1382  var framed = framec.contentDocument.body.lastChild;
   1383  addFrameSwitchingListeners(framed);
   1384 
   1385  var inputc = framec.contentDocument.body.firstChild;
   1386 
   1387  var expectedMainWindowFocus = framea;
   1388 
   1389  // An element in the immediate parent frame is focused. Focus an element in
   1390  // the child. The child should be focused and the parent's current focus should
   1391  // be the child iframe.
   1392  gEventMatched = true;
   1393  is(fm.getFocusedElementForWindow(window, false, {}), expectedMainWindowFocus,
   1394     "parent of framea has iframe focused");
   1395  gExpectedEvents = [[inputa, "blur", null, framea.contentWindow, window, framea],
   1396                     [framea.contentDocument, "blur", null, null, window, framea],
   1397                     [framea.contentWindow, "blur", null, null, window, framea],
   1398                     [framec.contentDocument, "focus", null, framec.contentWindow, window, framea],
   1399                     [framec.contentWindow, "focus", null, framec.contentWindow, window, framea],
   1400                     [inputc, "focus", inputc, framec.contentWindow, window, framea]];
   1401  inputc.focus();
   1402  ok(gEventMatched && !gExpectedEvents.length, "frame switch from parent input to child input" + gExpectedEvents);
   1403 
   1404  // An element in a child is focused. Focus an element in the immediate
   1405  // parent.
   1406  gEventMatched = true;
   1407  gExpectedEvents = [[inputc, "blur", null, framec.contentWindow, window, framea],
   1408                     [framec.contentDocument, "blur", null, null, window, framea],
   1409                     [framec.contentWindow, "blur", null, null, window, framea],
   1410                     [framea.contentDocument, "focus", null, framea.contentWindow, window, framea],
   1411                     [framea.contentWindow, "focus", null, framea.contentWindow, window, framea],
   1412                     [inputa, "focus", inputa, framea.contentWindow, window, framea]];
   1413  inputa.focus();
   1414  ok(gEventMatched && !gExpectedEvents.length, "frame switch from child input to parent input");
   1415 
   1416  // An element in a frame is focused. Focus an element in a sibling frame.
   1417  // The common ancestor of the two frames should have its focused node
   1418  // cleared after the element is blurred.
   1419  var inputb = frameb.contentDocument.body.firstChild;
   1420 
   1421  gEventMatched = true;
   1422  gExpectedEvents = [[inputa, "blur", null, framea.contentWindow, window, framea],
   1423                     [framea.contentDocument, "blur", null, null, window, null],
   1424                     [framea.contentWindow, "blur", null, null, window, null],
   1425                     [frameb.contentDocument, "focus", null, frameb.contentWindow, window, frameb],
   1426                     [frameb.contentWindow, "focus", null, frameb.contentWindow, window, frameb],
   1427                     [inputb, "focus", inputb, frameb.contentWindow, window, frameb]];
   1428  inputb.focus();
   1429  ok(gEventMatched && !gExpectedEvents.length, "frame switch from input to sibling frame");
   1430  is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), inputa,
   1431     "blurred frame still has input as focus");
   1432 
   1433  // focus a descendant in a sibling
   1434  var inputd = framed.contentDocument.body.firstChild;
   1435  gEventMatched = true;
   1436  gExpectedEvents = [[inputb, "blur", null, frameb.contentWindow, window, frameb],
   1437                     [frameb.contentDocument, "blur", null, null, window, null],
   1438                     [frameb.contentWindow, "blur", null, null, window, null],
   1439                     [framed.contentDocument, "focus", null, framed.contentWindow, window, framea],
   1440                     [framed.contentWindow, "focus", null, framed.contentWindow, window, framea],
   1441                     [inputd, "focus", inputd, framed.contentWindow, window, framea]];
   1442  inputd.focus();
   1443  ok(gEventMatched && !gExpectedEvents.length, "frame switch from input to sibling descendant");
   1444  is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), framec,
   1445     "sibling parent focus has shifted to frame");
   1446 
   1447  // focus an ancestor
   1448  gEventMatched = true;
   1449  gExpectedEvents = [[inputd, "blur", null, framed.contentWindow, window, framea],
   1450                     [framed.contentDocument, "blur", null, null, window, framea],
   1451                     [framed.contentWindow, "blur", null, null, window, framea],
   1452                     [framea.contentDocument, "focus", null, framea.contentWindow, window, framea],
   1453                     [framea.contentWindow, "focus", null, framea.contentWindow, window, framea],
   1454                     [inputa, "focus", inputa, framea.contentWindow, window, framea]];
   1455  inputa.focus();
   1456  ok(gEventMatched && !gExpectedEvents.length, "frame switch from child input to ancestor");
   1457 
   1458  // focus a descendant
   1459  gEventMatched = true;
   1460  gExpectedEvents = [[inputa, "blur", null, framea.contentWindow, window, framea],
   1461                     [framea.contentDocument, "blur", null, null, window, framea],
   1462                     [framea.contentWindow, "blur", null, null, window, framea],
   1463                     [framed.contentDocument, "focus", null, framed.contentWindow, window, framea],
   1464                     [framed.contentWindow, "focus", null, framed.contentWindow, window, framea],
   1465                     [inputd, "focus", inputd, framed.contentWindow, window, framea]];
   1466  inputd.focus();
   1467  ok(gEventMatched && !gExpectedEvents.length, "frame switch from child input to ancestor");
   1468  is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), framec,
   1469     "parent focus has shifted to frame");
   1470 
   1471  // focus a sibling frame by setting focusedWindow
   1472  gEventMatched = true;
   1473  gExpectedEvents = [[inputd, "blur", null, framed.contentWindow, window, framea],
   1474                     [framed.contentDocument, "blur", null, null, window, null],
   1475                     [framed.contentWindow, "blur", null, null, window, null],
   1476                     [frameb.contentDocument, "focus", null, frameb.contentWindow, window, frameb],
   1477                     [frameb.contentWindow, "focus", null, frameb.contentWindow, window, frameb]];
   1478  fm.focusedWindow = frameb.contentWindow;
   1479  ok(gEventMatched && !gExpectedEvents.length, "frame switch using focusedWindow");
   1480 
   1481  // clear the focus in an unfocused frame
   1482  gEventMatched = true;
   1483  gExpectedEvents = [];
   1484  fm.clearFocus(framec.contentWindow);
   1485  ok(gEventMatched && !gExpectedEvents.length, "clearFocus in unfocused frame");
   1486 
   1487  // focus a sibling frame by setting focusedWindow when no element is focused in that frame
   1488  gEventMatched = true;
   1489  gExpectedEvents = [[frameb.contentDocument, "blur", null, null, window, null],
   1490                     [frameb.contentWindow, "blur", null, null, window, null],
   1491                     [framec.contentDocument, "focus", null, framec.contentWindow, window, framea],
   1492                     [framec.contentWindow, "focus", null, framec.contentWindow, window, framea]];
   1493  fm.focusedWindow = framec.contentWindow;
   1494  ok(gEventMatched && !gExpectedEvents.length, "frame switch using focusedWindow with no element focused");
   1495  is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), framec,
   1496     "parent focus has shifted to frame using focusedWindow");
   1497 
   1498  // focus the parent frame by setting focusedWindow. This should have no effect.
   1499  gEventMatched = true;
   1500  gExpectedEvents = [];
   1501  fm.focusedWindow = framea.contentWindow;
   1502  ok(gEventMatched && !gExpectedEvents.length, "frame switch to parent using focusedWindow");
   1503 
   1504  // clear the focus in the parent frame
   1505  gEventMatched = true;
   1506  gExpectedEvents = [[framec.contentDocument, "blur", null, null, window, framea],
   1507                     [framec.contentWindow, "blur", null, null, window, framea],
   1508                     [framea.contentDocument, "focus", null, framea.contentWindow, window, framea],
   1509                     [framea.contentWindow, "focus", null, framea.contentWindow, window, framea]];
   1510  fm.clearFocus(framea.contentWindow);
   1511  ok(gEventMatched && !gExpectedEvents.length, "clearFocus in parent frame");
   1512 
   1513  // clear the focus in an unfocused child frame
   1514  gEventMatched = true;
   1515  gExpectedEvents = [];
   1516  fm.clearFocus(framed.contentWindow);
   1517  ok(gEventMatched && !gExpectedEvents.length, "clearFocus in unfocused child frame");
   1518 
   1519  var exh = false;
   1520  try {
   1521    fm.focusedWindow = null;
   1522  }
   1523  catch (ex) { exh = true; }
   1524  is(exh, true, "focusedWindow set to null");
   1525  is(fm.focusedWindow, framea.contentWindow, "window not changed when focusedWindow set to null");
   1526 
   1527  doFrameHistoryTests()
   1528 }
   1529 
   1530 function doFrameHistoryTests()
   1531 {
   1532  let frame = $("childframe");
   1533  frame.setAttribute("maychangeremoteness", "true");
   1534  let loaded = BrowserTestUtils.browserLoaded(frame, true);
   1535  let firstLocation = "https://example.org/chrome/dom/tests/mochitest/chrome/child_focus_frame.html";
   1536  frame.src = firstLocation;
   1537  loaded.then(() => {
   1538    return ContentTask.spawn(frame, {}, () => {
   1539      let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
   1540      let t20 = content.document.getElementById("t20");
   1541      t20.focus();
   1542      let goneBack = new Promise(resolve => {
   1543        content.addEventListener("pageshow", ({ persisted }) => {
   1544          resolve({ location: content.location.href, persisted });
   1545        }, { once: true });
   1546      });
   1547      // Make sure that loading a new page and then going back maintains the focus,
   1548      // but try to stabilize the page first so that it can enter bfcache.
   1549      content.requestAnimationFrame(() => {
   1550        content.requestAnimationFrame(() => {
   1551          content.location = "data:text/html,<script>window.onload=function() {setTimeout(function () { history.back() }, 0);}</script>";
   1552        });
   1553      });
   1554      return goneBack;
   1555    });
   1556  }).then(({ location, persisted }) => {
   1557    is(location, firstLocation, "should go back to the right page");
   1558    ok(persisted, "test relies on BFCache");
   1559    return ContentTask.spawn(frame, {}, () => {
   1560      let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
   1561      let t20 = content.document.getElementById("t20");
   1562      return fm.focusedElement === t20;
   1563    });
   1564  }).then((focusCorrect) => {
   1565    if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
   1566      todo(focusCorrect, "focus restored after history back");
   1567    } else {
   1568      ok(focusCorrect, "focus restored after history back");
   1569    }
   1570    done();
   1571  });
   1572 }
   1573 
   1574 function addFrameSwitchingListeners(frame)
   1575 {
   1576  frame.contentWindow.addEventListener("focus", frameSwitchingEventOccured);
   1577  frame.contentWindow.addEventListener("blur", frameSwitchingEventOccured);
   1578  frame.contentDocument.addEventListener("focus", frameSwitchingEventOccured);
   1579  frame.contentDocument.addEventListener("blur", frameSwitchingEventOccured);
   1580 
   1581  var node = frame.contentDocument.body.firstChild;
   1582  node.addEventListener("focus", frameSwitchingEventOccured);
   1583  node.addEventListener("blur", frameSwitchingEventOccured);
   1584 }
   1585 
   1586 function frameSwitchingEventOccured(event)
   1587 {
   1588  if (!gExpectedEvents.length) {
   1589    gEventMatched = false;
   1590    return;
   1591  }
   1592 
   1593  try {
   1594    var events = gExpectedEvents.shift();
   1595    is(event.target, events[0], "event target");
   1596    is(event.type, events[1], "event type");
   1597    is(fm.focusedElement, events[2], "focused element");
   1598    is(fm.focusedWindow, events[3], "focused frame");
   1599    if (events[4])
   1600      is(fm.getFocusedElementForWindow(events[4], false, {}), events[5], "focused element in frame");
   1601 
   1602    if (gEventMatched && event.target == events[0] && event.type == events[1] &&
   1603        fm.focusedElement == events[2] && fm.focusedWindow == events[3]) {
   1604      if (!events[4] || fm.getFocusedElementForWindow(events[4], false, {}) == events[5])
   1605        return;
   1606    }
   1607  } catch (ex) { ok(ex, "exception"); }
   1608 
   1609  gEventMatched = false;
   1610 }
   1611 
   1612 SimpleTest.waitForExplicitFinish();
   1613 SimpleTest.waitForFocus(startTest);
   1614 
   1615 ]]>
   1616 </script>
   1617 
   1618 <commandset id="cu"
   1619            commandupdater="true"
   1620            events="focus"
   1621            oncommandupdate="eventOccured(event)"/>
   1622 
   1623 <!--
   1624 The elements with ids starting with t are focusable and in the taborder.
   1625 The elements with ids starting with o are:
   1626   odd numbered ids - focusable but not part of the tab order
   1627   even numbered ids - not focusable with -moz-user-focus: ignore or disabled
   1628 The elements with ids starting with n are:
   1629   odd numbered ids - not focusable with -moz-user-focus: none
   1630   even numbered ids - focusable but not part of the tab order
   1631 -->
   1632 <vbox id="buttonbox">
   1633 <hbox id="innerbox">
   1634  <button id="t4" accesskey="h" label="no tabindex"/>
   1635  <button id="o1" accesskey="i" label="tabindex = -1" tabindex="-1"/>
   1636  <richlistbox id="t5" label="tabindex = 0" tabindex="0" style="width: 50px">
   1637    <richlistitem height="10"/>
   1638  </richlistbox>
   1639  <button id="t1" label="tabindex = 2" tabindex="2"/>
   1640 </hbox>
   1641 <hbox>
   1642  <button id="o2" accesskey="o" style="-moz-user-focus: ignore;" label="no tabindex"/>
   1643  <button id="o4" style="-moz-user-focus: ignore;" label="no tabindex"/>
   1644  <button id="t6" style="-moz-user-focus: ignore;" label="tabindex = 0" tabindex="0"/>
   1645  <button id="t2" style="-moz-user-focus: ignore;" label="tabindex = 2" tabindex="2"/>
   1646 </hbox>
   1647 <hbox id="specialroot">
   1648  <button id="t7" style="-moz-user-focus: normal;" label="no tabindex"/>
   1649  <button id="o3" style="-moz-user-focus: normal;" label="tabindex = -1" tabindex="-1"/>
   1650  <button id="t8" style="-moz-user-focus: normal;" label="tabindex = 0" tabindex="0"/>
   1651  <richlistbox id="t3" style="-moz-user-focus: normal; width: 50px" label="tabindex = 2" tabindex="2">
   1652    <richlistitem style="height: 10px"/>
   1653  </richlistbox>
   1654 </hbox>
   1655 <hbox>
   1656  <button accesskey="p" style="display: none;"/> <button accesskey="q" style="visibility: collapse;"/>
   1657  <button style="display: none;" tabindex="2"/> <button style="visibility: collapse;" tabindex="2"/>
   1658 </hbox>
   1659 <hbox>
   1660  <button id="o20" accesskey="s" label="no tabindex" disabled="true"/>
   1661  <button id="o22" label="tabindex = -1" tabindex="-1" disabled="true"/>
   1662  <button id="o24" label="tabindex = 0" tabindex="0" disabled="true"/>
   1663  <button id="o26" label="tabindex = 2" tabindex="2" disabled="true"/>
   1664 </hbox>
   1665 </vbox>
   1666 <vbox>
   1667 <hbox>
   1668  <dropmarker id="o6" value="no tabindex"/>
   1669  <dropmarker id="o8" value="no tabindex"/>
   1670  <dropmarker id="o10" value="no tabindx"/>
   1671  <dropmarker id="o12" value="no tabindex"/>
   1672  <dropmarker id="t9" accesskey="r" style="-moz-user-focus: normal;" value="no tabindex" />
   1673  <dropmarker id="t10" style="-moz-user-focus: normal;" value="no tabindex"/>
   1674  <dropmarker id="t11" style="-moz-user-focus: normal;" value="tabindex = 0" tabindex="0" />
   1675  <dropmarker id="t12" style="-moz-user-focus: normal;" value="no tabindex"/>
   1676  <dropmarker id="o14" style="-moz-user-focus: ignore;" value="no tabindex"/>
   1677  <dropmarker id="o16" style="-moz-user-focus: ignore;" value="no tabindex"/>
   1678  <dropmarker id="n1" style="-moz-user-focus: none;" value="no tabindex"/>
   1679  <dropmarker id="n3" style="-moz-user-focus: none;" value="no tabindex"/>
   1680 </hbox>
   1681 </vbox>
   1682 <browser id="childframe" type="content" src="child_focus_frame.html" style="height: 195px"/>
   1683 <button id="t34"/>
   1684 <tabbox id="tabbox">
   1685  <tabs><tab id="t35" label="One"/><tab id="tab2" label="Two"/></tabs>
   1686  <tabpanels>
   1687    <tabpanel>
   1688      <checkbox id="t36"/>
   1689      <button id="t37"/>
   1690    </tabpanel>
   1691    <tabpanel>
   1692      <checkbox id="htab1"/>
   1693      <button id="nohtab2" tabindex="7"/>
   1694      <checkbox id="htab2" tabindex="0"/>
   1695    </tabpanel>
   1696  </tabpanels>
   1697 </tabbox>
   1698 <hbox>
   1699 <panel>
   1700  <button id="inpopup1" label="One"/>
   1701  <input xmlns="http://www.w3.org/1999/xhtml" label="Two"/>
   1702 </panel>
   1703 <description label="o" accesskey="v"/>
   1704 <button id="t38"/>
   1705 <!-- The 't' element tests end here so it doesn't matter that these elements are tabbable -->
   1706 <label id="aj" value="j" accesskey="j" control="o9"/>
   1707 <label id="ak" accesskey="k" control="n4">k</label>
   1708 <checkbox id="o5"/><checkbox id="o7"/><hbox><checkbox id="o9"/></hbox>
   1709 <checkbox id="o13"/><checkbox id="o15"/><checkbox id="o17"/><checkbox id="o19"/><checkbox id="o21"/><checkbox id="o23"/><checkbox id="o25"/>
   1710 <checkbox id="n2"/><checkbox id="n4"/>
   1711 <richlistbox id="last" style="width: 20px; height: 20px"/>
   1712 
   1713 <iframe id="ifa" style="-moz-user-focus: ignore; width: 40px; height: 60px" type="content"
   1714        src="data:text/html,&lt;input id=fra size='2'&gt;&lt;input id='fra-b' size='2'&gt;
   1715        &lt;iframe src='data:text/html,&lt;input id=frc&gt;&lt;iframe src=&quot;data:text/html,&lt;input id=frd&gt;&quot;&gt;&lt;/iframe&gt;'&gt;&lt;/iframe&gt;"/>
   1716 <iframe id="ifb" style="-moz-user-focus: ignore; width: 20px; height: 20px"
   1717        src="data:text/html,&lt;input id=frd&gt;&lt;/iframe&gt;"/>
   1718 
   1719 </hbox>
   1720 </window>