Source code
Revision control
Copy as Markdown
Other Tools
<?xml version="1.0"?>
type="text/css"?>
<?xml-stylesheet href="data:text/css,dropmarker{min-width: 12px; min-height: 12px;}" type="text/css"?>
<!--
This test checks focus in various ways
-->
<window id="outer-document" title="Focus Test" width="600" height="550"
<script type="application/javascript"><![CDATA[
const { BrowserTestUtils } = ChromeUtils.importESModule(
);
const { ContentTask } = ChromeUtils.importESModule(
);
var fm = Cc["@mozilla.org/focus-manager;1"].
getService(Ci.nsIFocusManager);
const kChildDocumentRootIndex = 13;
const kBeforeTabboxIndex = 34;
const kTabbableSteps = 38;
const kFocusSteps = 26;
const kNoFocusSteps = 7;
const kOverflowElementIndex = 27;
var gTestStarted = false;
var gPartialTabbing = false;
var gMoveToFocusFrame = false;
var gLastFocus = null;
var gLastFocusWindow = window;
var gLastFocusMethod = -1;
var gEvents = "";
var gExpectedEvents = "";
var gEventMatched = true;
var gShowOutput = false;
var gChildWindow = null;
var gOldExpectedWindow = null;
var gNewExpectedWindow = null;
var gCanTabMoveFocusToRootElement =
!SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element");
function is(l, r, n) { window.arguments[0].SimpleTest.is(l,r,n); }
function ok(v, n) { window.arguments[0].SimpleTest.ok(v,n); }
function initEvents(target)
{
target.addEventListener("focus", eventOccured, true);
target.addEventListener("blur", eventOccured, true);
getTopWindow(target).addEventListener("activate", eventOccured, true);
getTopWindow(target).addEventListener("deactivate", eventOccured, true);
}
function eventOccured(event)
{
// iframes should never receive focus or blur events directly
if (Element.isInstance(event.target) && event.target.localName == "iframe")
ok(false, "iframe " + event.type + "occured");
var id;
if (gOldExpectedWindow && event.type == "blur") {
if (Window.isInstance(event.target))
id = "frame-" + gOldExpectedWindow.document.documentElement.id + "-window";
else if (Document.isInstance(event.target))
id = "frame-" + gOldExpectedWindow.document.documentElement.id + "-document";
else
id = event.originalTarget.id;
}
else if (gNewExpectedWindow && event.type == "focus") {
if (Window.isInstance(event.target))
id = "frame-" + gNewExpectedWindow.document.documentElement.id + "-window";
else if (Document.isInstance(event.target))
id = "frame-" + gNewExpectedWindow.document.documentElement.id + "-document";
else
id = event.originalTarget.id;
}
else if (event.type == "activate" || event.type == "deactivate")
id = event.target.document.documentElement.id + "-window";
else if (Window.isInstance(event.target))
id = (event.target == window) ? "outer-window" : "child-window";
else if (Document.isInstance(event.target))
id = (event.target == document) ? "outer-document" : "child-document";
else
id = event.originalTarget.id;
if (gEvents)
gEvents += " ";
gEvents += event.type + ": " + id;
}
// eslint-disable-next-line complexity
function expectFocusShift(callback, expectedWindow, expectedElement, focusChanged, testid)
{
if (expectedWindow == null)
expectedWindow = expectedElement ?
expectedElement.ownerGlobal :
gLastFocusWindow;
var expectedEvents = "";
if (focusChanged) {
var id;
if (getTopWindow(gLastFocusWindow) != getTopWindow(expectedWindow)) {
id = getTopWindow(gLastFocusWindow).document.documentElement.id;
expectedEvents += "deactivate: " + id + "-window";
}
if (gLastFocus && gLastFocus.id != "t" + kChildDocumentRootIndex &&
(!gOldExpectedWindow || gOldExpectedWindow.document.documentElement != gLastFocus)) {
if (expectedEvents)
expectedEvents += " ";
if (!gOldExpectedWindow)
expectedEvents += "commandupdate: cu ";
expectedEvents += "blur: " + gLastFocus.id;
}
if (gLastFocusWindow && gLastFocusWindow != expectedWindow) {
if (!gMoveToFocusFrame) {
if (gOldExpectedWindow)
id = "frame-" + gOldExpectedWindow.document.documentElement.id;
else
id = (gLastFocusWindow == window) ? "outer" : "child";
if (expectedEvents)
expectedEvents += " ";
expectedEvents += "blur: " + id + "-document " +
"blur: " + id + "-window";
}
}
if (getTopWindow(gLastFocusWindow) != getTopWindow(expectedWindow)) {
id = getTopWindow(expectedWindow).document.documentElement.id;
if (expectedEvents)
expectedEvents += " ";
expectedEvents += "activate: " + id + "-window";
}
if (expectedWindow && gLastFocusWindow != expectedWindow) {
if (gNewExpectedWindow)
id = "frame-" + gNewExpectedWindow.document.documentElement.id;
else
id = (expectedWindow == window) ? "outer" : "child";
if (expectedEvents)
expectedEvents += " ";
expectedEvents += "focus: " + id + "-document " +
"focus: " + id + "-window";
}
// for this test which fires a mouse event on a label, the document will
// be focused first and then the label code will focus the related
// control. This doesn't result in different focus events, but a command
// update will occur for the document and then a second command update will
// occur when the control is focused. However, this will only happen on
// platforms or controls where mouse clicks cause trigger focus.
if (testid == "mouse on html label with content inside" &&
mouseWillTriggerFocus(expectedElement)) {
expectedEvents += " commandupdate: cu";
}
if (expectedElement &&
(!gNewExpectedWindow || gNewExpectedWindow.document.documentElement != expectedElement)) {
if (!gNewExpectedWindow) {
if (expectedEvents)
expectedEvents += " ";
expectedEvents += "commandupdate: cu";
}
if (expectedElement.id != "t" + kChildDocumentRootIndex) {
if (expectedEvents)
expectedEvents += " ";
expectedEvents += "focus: " + expectedElement.id;
}
}
else if (expectedWindow && gLastFocusWindow != expectedWindow &&
!expectedElement) {
if (expectedEvents)
expectedEvents += " ";
expectedEvents += "commandupdate: cu";
}
}
gLastFocus = expectedElement;
gLastFocusWindow = expectedWindow;
callback();
compareEvents(expectedEvents, expectedWindow, expectedElement, testid);
}
function compareEvents(expectedEvents, expectedWindow, expectedElement, testid)
{
if (!gShowOutput) {
gEvents = "";
return;
}
is(gEvents, expectedEvents, testid + " events");
gEvents = "";
var doc;
if (expectedWindow == window)
doc = "outer-document";
else if (expectedWindow == gChildWindow)
doc = "inner-document";
else if (gNewExpectedWindow)
doc = gNewExpectedWindow.document.body ? gNewExpectedWindow.document.body.id :
gNewExpectedWindow.document.documentElement.id;
else
doc = "other-document";
var focusedElement = fm.focusedElement;
is(focusedElement ? focusedElement.id : "none",
expectedElement ? expectedElement.id : "none", testid + " focusedElement");
is(fm.focusedWindow, expectedWindow, testid + " focusedWindow");
var focusedWindow = {};
is(fm.getFocusedElementForWindow(expectedWindow, false, focusedWindow),
expectedElement, testid + " getFocusedElementForWindow");
is(focusedWindow.value, expectedWindow, testid + " getFocusedElementForWindow frame");
is(expectedWindow.document.hasFocus(), true, testid + " hasFocus");
is(expectedWindow.document.activeElement ? expectedWindow.document.activeElement.id : "none",
expectedElement ? expectedElement.id : doc, testid + " activeElement");
var cdwindow = getTopWindow(expectedWindow);
if (cdwindow.document.commandDispatcher) {
is(cdwindow.document.commandDispatcher.focusedWindow, expectedWindow, testid + " commandDispatcher focusedWindow");
is(cdwindow.document.commandDispatcher.focusedElement, focusedElement, testid + " commandDispatcher focusedElement");
}
if (gLastFocusMethod != -1) {
is(fm.getLastFocusMethod(null), gLastFocusMethod, testid + " lastFocusMethod null");
is(fm.getLastFocusMethod(expectedWindow), gLastFocusMethod, testid + " lastFocusMethod window");
}
// the parent should have the iframe focused
if (doc == "inner-document") {
is(document.hasFocus(), true, testid + " hasFocus");
is(fm.getFocusedElementForWindow(window, false, focusedWindow),
$("childframe"), testid + " getFocusedElementForWindow for parent");
is(focusedWindow.value, window, testid + " getFocusedElementForWindow for parent frame");
is(fm.getFocusedElementForWindow(window, true, focusedWindow),
expectedElement, testid + " getFocusedElementForWindow deep for parent");
is(focusedWindow.value, gChildWindow, testid + " getFocusedElementForWindow deep for parent frame");
is(document.activeElement.id, "childframe", testid + " activeElement for parent");
}
// compare the selection for the child window. Skip mouse tests as the caret
// is adjusted by the selection code for mouse clicks, and not the focus code.
if (expectedWindow == window) {
var selection = window.getSelection();
ok(selection.focusNode == null && selection.focusOffset == 0 &&
selection.anchorNode == null && selection.anchorOffset == 0, testid + " selection");
}
else if ((expectedWindow == gChildWindow) && !testid.indexOf("mouse") == -1) {
checkSelection(expectedElement, testid);
}
}
function checkSelection(node, testid)
{
var selection = gChildWindow.getSelection();
var range = gChildWindow.document.createRange();
range.selectNodeContents(node);
if (!node.firstChild || node.localName == "input" ||
node.localName == "select" || node.localName == "button") {
range.setStartBefore(node);
range.setEndBefore(node);
}
if (node.firstChild)
range.setEnd(range.startContainer, range.startOffset);
is(selection.focusNode, range.startContainer, testid + " selection focusNode");
is(selection.focusOffset, range.startOffset, testid + " selection focusOffset");
is(selection.anchorNode, range.endContainer, testid + " selection anchorNode");
is(selection.anchorOffset, range.endOffset, testid + " selection anchorOffset");
}
function getTopWindow(win)
{
return win.browsingContext.topChromeWindow;
}
function mouseWillTriggerFocus(element)
{
if (!element) {
return false;
}
if (SpecialPowers.getIntPref("accessibility.mouse_focuses_formcontrol") == 1) {
// This is a chrome document, so we'll only trigger focus with the mouse if the value of the pref is 1.
return true;
}
// links are special. They can be focused but show no focus ring
if (element.localName == "a" || element.localName == "div" ||
element.localName == "select" ||
element.localName == "input" && (element.type == "text" ||
element.type == "password")) {
return true;
}
} else if (element.localName == "richlistbox") {
return true;
}
return false;
}
function mouseOnElement(element, expectedElement, focusChanged, testid)
{
var expectedWindow = (element.ownerGlobal == gChildWindow) ? gChildWindow : window;
// on Mac, form elements are not focused when clicking, except for lists and inputs.
var noFocusOnMouse = !mouseWillTriggerFocus(element)
if (noFocusOnMouse) {
// no focus so the last focus method will be 0
gLastFocusMethod = 0;
expectFocusShift(() => synthesizeMouse(element, 4, 4, { }, element.ownerGlobal),
expectedWindow, null, true, testid);
gLastFocusMethod = fm.FLAG_BYMOUSE;
}
else {
expectFocusShift(() => synthesizeMouse(element, 4, 4, { }, element.ownerGlobal),
element.ownerGlobal,
expectedElement, focusChanged, testid);
}
}
function done()
{
var opener = window.arguments[0];
window.close();
window.arguments[0].SimpleTest.finish();
}
var pressTab = () => synthesizeKey("KEY_Tab");
function setFocusTo(id, fwindow)
{
gLastFocus = getById(id);
gLastFocusWindow = fwindow;
if (gLastFocus)
gLastFocus.focus();
else
fm.clearFocus(fwindow);
gEvents = "";
}
function getById(id)
{
if (gNewExpectedWindow)
return gNewExpectedWindow.document.getElementById(id);
var element = $(id);
if (!element)
element = $("childframe").contentDocument.getElementById(id);
return element;
}
// eslint-disable-next-line complexity
function startTest()
{
if (gTestStarted)
return;
gTestStarted = true;
gChildWindow = $("childframe").contentWindow;
gShowOutput = true;
// synthesize a mousemove over the image to ensure that the imagemap data is
// created. Otherwise, the special imagemap frames might not exist, and
// won't be focusable.
synthesizeMouse(getById("image"), 4, 4, { type: "mousemove" }, gChildWindow);
initEvents(window);
is(fm.activeWindow, window, "activeWindow");
is(gChildWindow.document.hasFocus(), false, " child document hasFocus");
// test to see if the Mac Full Keyboard Access setting is set. If t3 is
// focused after tab is pressed, then it is set to inputs and lists only.
// Otherwise, all elements are in the tab order.
pressTab();
if (fm.focusedElement.id == "t3")
gPartialTabbing = true;
else
is(fm.focusedElement.id, "t1", "initial tab key");
is(fm.getLastFocusMethod(null), fm.FLAG_BYKEY, "last focus method null start");
is(fm.getLastFocusMethod(window), fm.FLAG_BYKEY, "last focus method window start");
fm.clearFocus(window);
gEvents = "";
gLastFocusMethod = fm.FLAG_BYKEY;
if (gPartialTabbing) {
var partialTabList;
if (gCanTabMoveFocusToRootElement) {
partialTabList = ["t3", "t5", "t9", "t10", "t11", "t12", "t13", "t14", "t15",
"t16", "t19", "t20", "t21", "t22", "t26", "t27", "t28", "t29", "t30"];
} else {
// !gCanTabMoveFocusToRootElement
// t13 is the <html> element in child_focus_frame.html,
// and it's not getting the focus
// when gCanTabMoveFocusToRootElement is false.
partialTabList = ["t3", "t5", "t9", "t10", "t11", "t12", "t14", "t15",
"t16", "t19", "t20", "t21", "t22", "t26", "t27", "t28", "t29", "t30"];
}
for (var idx = 0; idx < partialTabList.length; idx++) {
expectFocusShift(pressTab, null, getById(partialTabList[idx]), true, "partial tab key " + partialTabList[idx]);
}
setFocusTo("last", window);
expectFocusShift(pressTab, null, getById(partialTabList[0]), true, "partial tab key wrap to start");
expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
null, getById("last"), true, "partial shift tab key wrap to end");
for (var idx = partialTabList.length - 1; idx >= 0; idx--) {
expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
null, getById(partialTabList[idx]), true, "partial tab key " + partialTabList[idx]);
}
}
else {
// TAB key
for (var idx = 1; idx <= kTabbableSteps; idx++) {
if (!gCanTabMoveFocusToRootElement) {
if (idx == kChildDocumentRootIndex)
// t13 is the <html> element in child_focus_frame.html,
// and it's not getting the focus
// when gCanTabMoveFocusToRootElement is false.
continue;
}
expectFocusShift(pressTab, null, getById("t" + idx), true, "tab key t" + idx);
}
// wrapping around at end with TAB key
setFocusTo("last", window);
expectFocusShift(pressTab, null, getById("t1"), true, "tab key wrap to start");
expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
null, getById("last"), true, "shift tab key wrap to end");
// Shift+TAB key
setFocusTo("o5", window);
for (idx = kTabbableSteps; idx > 0; idx--) {
if (!gCanTabMoveFocusToRootElement) {
if (idx == kChildDocumentRootIndex) {
// t13 is the <html> element in child_focus_frame.html,
// and it's not getting the focus
// when gCanTabMoveFocusToRootElement is false.
continue;
}
}
expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
null, getById("t" + idx), true, "shift tab key t" + idx);
}
}
var t19 = getById("t19");
is(t19.selectionStart, 0, "input focused from tab key selectionStart");
is(t19.selectionEnd, 5, "input focused from tab key selectionEnd");
t19.setSelectionRange(0, 0);
gLastFocusMethod = 0;
var selectFired = false;
function selectListener() { selectFired = true; }
t19.addEventListener("select", selectListener);
expectFocusShift(() => t19.select(),
null, getById("t" + 19), true, "input.select()");
t19.removeEventListener("select", selectListener);
ok(!selectFired, "select event does not fire asynchronously for input");
// mouse clicking
gLastFocusMethod = fm.FLAG_BYMOUSE;
for (idx = kTabbableSteps; idx >= 1; idx--) {
// skip the document root and the overflow element
if (idx == kChildDocumentRootIndex || idx == kOverflowElementIndex)
continue;
if ((navigator.platform.indexOf("Mac") == 0) && (idx == kBeforeTabboxIndex + 1))
continue;
var element = getById("t" + idx);
// skip area elements, as getBoundingClientRect doesn't return their actual coordinates
if (element.localName == "area")
continue;
mouseOnElement(element, element, true, "mouse on element t" + idx);
var expectedWindow = (element.ownerGlobal == gChildWindow) ? gChildWindow : window;
if (element.localName == "richlistbox" && expectedWindow == window &&
navigator.platform.indexOf("Mac") == 0) {
// after focusing a listbox on Mac, clear the focus before continuing.
setFocusTo(null, window);
}
}
ok(t19.selectionStart == t19.selectionEnd, "input focused from mouse selection");
// mouse clicking on elements that are not tabbable
for (idx = 1; idx <= kFocusSteps; idx++) {
var element = getById("o" + (idx % 2 ? idx : idx - 1));
mouseOnElement(element, element, idx % 2,
"mouse on non-tabbable element o" + idx);
}
// mouse clicking on elements that are not tabbable and have user-focus: none
// or are not focusable for other reasons (for instance, being disabled)
// These elements will clear the focus when clicked.
for (idx = 1; idx <= kNoFocusSteps; idx++) {
var element = getById("n" + idx);
gLastFocusMethod = idx % 2 ? 0 : fm.FLAG_BYMOUSE;
mouseOnElement(element, idx % 2 ? null: element, true, "mouse on unfocusable element n" + idx);
}
if (idx == kOverflowElementIndex) {
gLastFocusMethod = fm.FLAG_BYMOUSE;
var element = getById("t" + idx);
expectFocusShift(() => synthesizeMouse(element, 4, 4, { }, element.ownerGlobal),
window, null, true, "mouse on scrollable element");
}
// focus() method
gLastFocusMethod = fm.FLAG_BYJS;
for (idx = kTabbableSteps; idx >= 1; idx--) {
if ((navigator.platform.indexOf("Mac") == 0) && (idx == kBeforeTabboxIndex + 1))
continue;
expectFocusShift(() => getById("t" + idx).focus(),
null, getById("t" + idx), true, "focus method on element t" + idx);
}
$("t1").focus();
ok(gEvents === "", "focusing element that is already focused");
$("t2").blur();
$("t7").blur();
ok(gEvents === "", "blurring element that is not focused");
is(document.activeElement, $("t1"), "old element still focused after blur() on another element");
// focus() method on elements that are not tabbable
for (idx = 1; idx <= kFocusSteps; idx++) {
var expected = getById("o" + (idx % 2 ? idx : idx - 1));
expectFocusShift(() => getById("o" + idx).focus(),
expected.ownerGlobal,
expected, idx % 2, "focus method on non-tabbable element o" + idx);
}
// focus() method on elements that are not tabbable and have user-focus: none
// or are not focusable for other reasons (for instance, being disabled)
for (idx = 1; idx <= kNoFocusSteps; idx++) {
var expected = getById("o" + (idx % 2 ? idx : idx - 1));
expectFocusShift(() => getById("o" + idx).focus(),
expected.ownerGlobal,
expected, idx % 2, "focus method on unfocusable element n" + idx);
}
// the focus() method on the legend element should focus the legend if it is
// focusable, or the first element after the legend if it is not focusable.
if (!gPartialTabbing) {
gLastFocusMethod = fm.FLAG_BYJS;
var legend = getById("legend");
expectFocusShift(() => legend.focus(),
null, getById("t28"), true, "focus method on unfocusable legend");
legend.tabIndex = "0";
expectFocusShift(() => legend.focus(),
null, getById("legend"), true, "focus method on focusable legend");
legend.tabIndex = "-1";
}
var accessKeyDetails = (navigator.platform.includes("Mac")) ?
{ ctrlKey : true } : { altKey : true };
// test accesskeys
var keys = ["t26", "t19", "t22", "t29", "t15", "t17", "n6",
"t4", "o1", "o9", "n4"];
for (var k = 0; k < keys.length; k++) {
var key = String.fromCharCode(65 + k);
// accesskeys D and G are for labels so get redirected
gLastFocusMethod = (key == "D" || key == "G") ? fm.FLAG_BYMOVEFOCUS : fm.FLAG_BYKEY;
// on Windows and Linux, the shift key must be pressed for content area access keys
// and on Mac, the alt key must be pressed for content area access keys
var isContent = (getById(keys[k]).ownerGlobal == gChildWindow);
if (!navigator.platform.includes("Mac")) {
accessKeyDetails.shiftKey = isContent;
} else {
accessKeyDetails.altKey = isContent;
}
expectFocusShift(() => synthesizeKey(key, accessKeyDetails),
null, getById(keys[k]), true, "accesskey " + key);
}
// clicking on the labels
gLastFocusMethod = fm.FLAG_BYMOVEFOCUS | fm.FLAG_BYMOUSE;
mouseOnElement(getById("ad"), getById("t29"), true, "mouse on html label with content inside");
mouseOnElement(getById("ag"), getById("n6"), true, "mouse on html label with for attribute");
gLastFocusMethod = fm.FLAG_BYJS;
expectFocusShift(() => synthesizeMouse(getById("aj"), 2, 2, { }),
null, getById("o9"), true, "mouse on xul label with content inside");
expectFocusShift(() => synthesizeMouse(getById("ak"), 2, 2, { }),
null, getById("n4"), true, "mouse on xul label with control attribute");
// test accesskeys that shouldn't work
k = "o".charCodeAt(0);
gLastFocusMethod = fm.FLAG_BYJS;
while (k++ < "v".charCodeAt(0)) {
var key = String.fromCharCode(k);
expectFocusShift(() => synthesizeKey(key, accessKeyDetails),
window, getById("n4"), false, "non accesskey " + key);
}
gLastFocusMethod = -1;
// should focus the for element when using the focus method on a label as well
expectFocusShift(() => getById("ad").focus(),
null, getById("t29"), true, "mouse on html label using focus method");
// make sure that the text is selected when clicking a label associated with an input
getById("ag").htmlFor = "t19";
expectFocusShift(() => synthesizeMouse(getById("ag"), 2, 2, { }, gChildWindow),
null, getById("t19"), true, "mouse on html label with for attribute changed");
is(t19.selectionStart, 0, "input focused from label, selectionStart");
is(t19.selectionEnd, 5, "input focused from label, selectionEnd");
// switch to another panel in a tabbox and ensure that tabbing moves between
// elements on the new panel.
$("tabbox").selectedIndex = 1;
expectFocusShift(() => getById("t" + kBeforeTabboxIndex).focus(),
null, getById("t" + kBeforeTabboxIndex), true, "focus method on element before tabbox");
if (!gPartialTabbing) {
expectFocusShift(pressTab, null, getById("tab2"), true, "focus method on tab");
expectFocusShift(pressTab, null, getById("htab1"), true, "tab key switch tabpanel 1");
expectFocusShift(pressTab, null, getById("htab2"), true, "tab key switch tabpanel 2");
expectFocusShift(pressTab, null, getById("t" + (kBeforeTabboxIndex + 4)), true, "tab key switch tabpanel 3");
}
$("tabbox").selectedIndex = 0;
// ---- the following checks when the focus changes during a blur or focus event ----
var o5 = $("o5");
var o9 = $("o9");
var t3 = $("t3");
var t17 = getById("t17");
var t19 = getById("t19");
var shiftFocusParentDocument = () => o9.focus();
var shiftFocusChildDocument = () => t17.focus();
var trapBlur = function (element, eventListener, blurFunction)
{
element.focus();
gEvents = "";
element.addEventListener("blur", eventListener);
blurFunction();
element.removeEventListener("blur", eventListener);
}
var functions = [
element => element.focus(),
element => synthesizeMouse(element, 4, 4, { }, element.ownerGlobal)
];
// first, check cases where the focus is adjusted during the blur event. Iterate twice,
// once with the focus method and then focusing by mouse clicking
for (var l = 0; l < 2; l++) {
var adjustFocus = functions[l];
var mod = (l == 1) ? " with mouse" : "";
// an attempt is made to switch the focus from one element (o5) to another
// element (t3) within the same document, yet the focus is shifted to a
// third element (o9) in the same document during the blur event for the
// first element.
trapBlur(o5, shiftFocusParentDocument, () => adjustFocus(t3));
compareEvents("commandupdate: cu blur: o5 commandupdate: cu focus: o9",
window, o9, "change focus to sibling during element blur, attempted sibling" + mod);
// similar, but the third element (t17) is in a child document
trapBlur(o9, shiftFocusChildDocument, () => adjustFocus(t3));
compareEvents("commandupdate: cu blur: o9 blur: outer-document blur: outer-window " +
"focus: child-document focus: child-window commandupdate: cu focus: t17",
gChildWindow, t17, "change focus to child document during element blur, attempted sibling" + mod);
// similar, but an attempt to switch focus within the same document, but the
// third element (t17) is in a parent document
trapBlur(t17, shiftFocusParentDocument, () => adjustFocus(t19));
compareEvents("commandupdate: cu blur: t17 blur: child-document blur: child-window " +
"focus: outer-document focus: outer-window commandupdate: cu focus: o9",
window, o9, "change focus to parent document during element blur, attempted sibling" + mod);
// similar, but blur is called instead of switching focus
trapBlur(t3, shiftFocusParentDocument, () => t3.blur());
compareEvents("commandupdate: cu blur: t3 commandupdate: cu focus: o9",
window, o9, "change focus to same document during clear focus" + mod);
// check when an element in the same document is focused during the
// element's blur event, but an attempt was made to focus an element in the
// child document. In this case, the focus in the parent document should be
// what was set during the blur event, but the actual focus should still
// move to the child document.
trapBlur(t3, shiftFocusParentDocument, () => adjustFocus(t17));
compareEvents("commandupdate: cu blur: t3 commandupdate: cu focus: o9 " +
"blur: outer-document blur: outer-window " +
"focus: child-document focus: child-window commandupdate: cu focus: t17",
gChildWindow, t17, "change focus to sibling during element blur, attempted child" + mod);
is(fm.getFocusedElementForWindow(window, false, {}), $("childframe"),
"change focus to sibling during element blur, attempted child, focused in parent" + mod);
// similar, but with a parent
trapBlur(t19, shiftFocusChildDocument, () => adjustFocus(t3));
compareEvents("commandupdate: cu blur: t19 commandupdate: cu focus: t17 " +
"blur: child-document blur: child-window " +
"focus: outer-document focus: outer-window commandupdate: cu focus: t3",
window, t3, "change focus to sibling during element blur, attempted parent" + mod);
is(fm.getFocusedElementForWindow(gChildWindow, false, {}), t17,
"change focus to sibling during element blur, attempted child, focused in child" + mod);
// similar, with a child, but the blur event focuses a child element also
trapBlur(t3, shiftFocusChildDocument, () => adjustFocus(t19));
compareEvents("commandupdate: cu blur: t3 blur: outer-document blur: outer-window " +
"focus: child-document focus: child-window commandupdate: cu focus: t17",
gChildWindow, t17, "change focus to child during element blur, attempted child" + mod);
// similar, with a parent, where the blur event focuses a parent element also
trapBlur(t17, shiftFocusParentDocument, () => adjustFocus(t3));
compareEvents("commandupdate: cu blur: t17 blur: child-document blur: child-window " +
"focus: outer-document focus: outer-window commandupdate: cu focus: o9",
window, o9, "change focus to parent during element blur, attempted parent" + mod);
}
var trapFocus = function (element, eventListener)
{
element.addEventListener("focus", eventListener);
element.focus();
element.removeEventListener("focus", eventListener);
}
fm.clearFocus(window);
gEvents = "";
// next, check cases where the focus is adjusted during the focus event
// switch focus to an element in the same document
trapFocus(o5, shiftFocusParentDocument);
compareEvents("commandupdate: cu focus: o5 commandupdate: cu blur: o5 commandupdate: cu focus: o9",
window, o9, "change focus to sibling during element focus");
// similar, but the new element (t17) is in a child document
trapFocus(o5, shiftFocusChildDocument);
compareEvents("commandupdate: cu blur: o9 " +
"commandupdate: cu focus: o5 commandupdate: cu blur: o5 " +
"blur: outer-document blur: outer-window " +
"focus: child-document focus: child-window commandupdate: cu focus: t17",
gChildWindow, t17, "change focus to child document during element focus");
// similar, but the new element (o9) is in a parent document.
trapFocus(t19, shiftFocusParentDocument);
compareEvents("commandupdate: cu blur: t17 " +
"commandupdate: cu focus: t19 commandupdate: cu blur: t19 " +
"blur: child-document blur: child-window " +
"focus: outer-document focus: outer-window commandupdate: cu focus: o9",
window, o9, "change focus to parent document during element focus");
// clear the focus during the focus event
trapFocus(t3, () => fm.clearFocus(window));
compareEvents("commandupdate: cu blur: o9 commandupdate: cu focus: t3 commandupdate: cu blur: t3",
window, null, "clear focus during focus event");
if (!gPartialTabbing)
doCommandDispatcherTests();
testMoveFocus();
doRemoveTests();
// tests various focus manager apis for null checks
var exh = false;
try {
fm.clearFocus(null);
}
catch (ex) { exh = true; }
is(exh, true, "clearFocus with null window causes exception");
var exh = false;
try {
fm.getFocusedElementForWindow(null, false, focusedWindow);
}
catch (ex) { exh = true; }
is(exh, true, "getFocusedElementForWindow with null window causes exception");
// just make sure that this doesn't crash
fm.moveCaretToFocus(null);
// ---- tests for the FLAG_NOSWITCHFRAME flag
getById("o5").focus();
gLastFocusMethod = fm.FLAG_BYJS;
gEvents = "";
// focus is being shifted in a child, so the focus should not change
expectFocusShift(() => fm.setFocus(getById("t20"), fm.FLAG_NOSWITCHFRAME),
window, getById("o5"), false, "no switch frame focus to child");
setFocusTo("t20", gChildWindow);
gLastFocusMethod = 0;
// here, however, focus is being shifted in a parent, which will have to blur
// the child, so the focus will always change
expectFocusShift(() => fm.setFocus(getById("o5"), fm.FLAG_NOSWITCHFRAME),
window, getById("o5"), true, "no switch frame focus to parent");
expectFocusShift(() => fm.setFocus(getById("t1"), fm.FLAG_NOSWITCHFRAME),
window, getById("t1"), true, "no switch frame focus to same window");
// ---- tests for focus and scrolling into view ----
var inscroll = getById("inscroll");
inscroll.tabIndex = 0;
is(inscroll.parentNode.scrollTop, 0, "scroll position before focus");
inscroll.focus();
ok(inscroll.parentNode.scrollTop > 5, "scroll position after focus");
inscroll.parentNode.scrollTop = 0;
fm.setFocus(inscroll, fm.FLAG_NOSCROLL);
is(inscroll.parentNode.scrollTop, 0, "scroll position after noscroll focus");
getById("t9").focus();
getById("inpopup1").focus();
is(fm.focusedElement, getById("t9"), "focus in closed popup");
// ---- tests to check if tabbing out of a input works
setFocusTo("t1", window);
var input1 = document.createElement("input");
$("innerbox").appendChild(input1);
var input2 = document.createElement("input");
$("innerbox").appendChild(input2);
gLastFocusMethod = fm.FLAG_BYJS;
expectFocusShift(() => input2.focus(),
null, input2, true, "focus on input");
gLastFocusMethod = fm.FLAG_BYKEY;
expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
null, input1, true, "shift+tab on input");
input1.tabIndex = 2;
input2.tabIndex = 2;
gLastFocusMethod = fm.FLAG_BYJS;
expectFocusShift(() => input2.focus(),
null, input2, true, "focus on input with tabindex set");
gLastFocusMethod = fm.FLAG_BYKEY;
expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
null, input1, true, "shift+tab on input with tabindex set");
// ---- test to check that refocusing an element during a blur event doesn't succeed
var t1 = getById("t1");
t1.addEventListener("blur", () => t1.focus(), true);
t1.focus();
var t3 = getById("t3");
synthesizeMouse(t3, 2, 2, { });
is(fm.focusedElement, t3, "focus during blur");
setFocusTo("t9", window);
gLastFocusMethod = -1;
window.openDialog("focus_window2.xhtml", "_blank", "chrome", otherWindowFocused);
}
function doCommandDispatcherTests()
{
var t19 = getById("t19");
t19.focus();
gLastFocusWindow = gChildWindow;
gLastFocus = t19;
gEvents = "";
expectFocusShift(() => document.commandDispatcher.focusedElement = getById("o9"),
null, getById("o9"), true, "command dispatcher set focusedElement");
expectFocusShift(() => document.commandDispatcher.advanceFocus(),
null, getById("o13"), true, "command dispatcher advanceFocus");
expectFocusShift(() => document.commandDispatcher.rewindFocus(),
null, getById("o9"), true, "command dispatcher rewindFocus");
expectFocusShift(() => document.commandDispatcher.focusedElement = null,
null, null, true, "command dispatcher set focusedElement to null");
expectFocusShift(() => document.commandDispatcher.focusedWindow = gChildWindow,
null, getById("t19"), true, "command dispatcher set focusedElement to null");
expectFocusShift(() => document.commandDispatcher.focusedElement = null,
gChildWindow, null, true, "command dispatcher set focusedElement to null in child");
expectFocusShift(() => document.commandDispatcher.advanceFocusIntoSubtree(getById("t19")),
null, getById("t20"), true, "command dispatcher advanceFocusIntoSubtree child");
expectFocusShift(() => document.commandDispatcher.advanceFocusIntoSubtree(null),
null, getById("t21"), true, "command dispatcher advanceFocusIntoSubtree null child");
expectFocusShift(() => document.commandDispatcher.advanceFocusIntoSubtree(getById("o9").parentNode),
null, getById("o9"), true, "command dispatcher advanceFocusIntoSubtree parent");
}
function doRemoveTests()
{
// next, some tests which remove elements
var t19 = getById("t19");
t19.focus();
t19.remove();
is(fm.focusedElement, null, "removed element focusedElement");
is(fm.focusedWindow, gChildWindow, "removed element focusedWindow");
is(gChildWindow.document.hasFocus(), true, "removed element hasFocus");
is(gChildWindow.document.activeElement, getById("inner-document"), "removed element activeElement");
getById("t15").focus();
var abs = getById("abs");
abs.remove();
is(fm.focusedElement, null, "removed ancestor focusedElement");
is(fm.focusedWindow, gChildWindow, "removed ancestor focusedWindow");
is(gChildWindow.document.hasFocus(), true, "removed ancestor hasFocus");
is(gChildWindow.document.activeElement, getById("inner-document"), "removed ancestor activeElement");
}
// tests for the FocusManager moveFocus method
function testMoveFocus()
{
setFocusTo("t6", window);
// moving focus while an element is already focused
var newFocus;
gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
var expectedFirst = getById(gPartialTabbing ? "t3" : "t1");
expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FIRST, 0),
window, expectedFirst, true, "moveFocus to first null window null content");
is(newFocus, fm.focusedElement, "moveFocus to first null window null content return value");
expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_LAST, 0),
window, getById("last"), true, "moveFocus to last null window null content");
is(newFocus, fm.focusedElement, "moveFocus to last null window null content return value");
gLastFocusMethod = 0;
newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_ROOT, 0);
is(newFocus, null, "moveFocus to root null window null content return value");
is(fm.focusedWindow, window, "moveFocus to root null window null content focusedWindow");
is(fm.focusedElement, null, "moveFocus to root null window null content focusedElement");
// moving focus while no element is focused
fm.clearFocus(window);
gEvents = "";
gLastFocus = null;
gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FIRST, 0),
window, expectedFirst, true, "moveFocus to first null window null content no focus");
is(newFocus, fm.focusedElement, "moveFocus to first null window null content no focus return value");
fm.clearFocus(window);
gEvents = "";
gLastFocus = null;
expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_LAST, 0),
window, getById("last"), true, "moveFocus to last null window null content no focus");
is(newFocus, fm.focusedElement, "moveFocus to last null window null content no focus return value");
fm.clearFocus(window);
gEvents = "";
gLastFocusMethod = 0;
newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_ROOT, 0);
is(newFocus, null, "moveFocus to root null window null content no focus return value");
is(fm.focusedWindow, window, "moveFocus to root null window null content no focus focusedWindow");
is(fm.focusedElement, null, "moveFocus to root null window null content no focus focusedElement");
// moving focus from a specified element
setFocusTo("t6", window);
gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
expectFocusShift(() => newFocus = fm.moveFocus(null, getById("specialroot"), fm.MOVEFOCUS_FIRST, 0),
window, getById("t3"), true, "moveFocus to first null window with content");
// XXXndeakin P3 this doesn't work
// expectFocusShift(() => newFocus = fm.moveFocus(null, getById("specialroot"), fm.MOVEFOCUS_LAST, 0),
// window, getById("o3"), true, "moveFocus to last null window with content");
// move focus to first in child window
expectFocusShift(() => newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_FIRST, 0),
gChildWindow, getById("t" + (kChildDocumentRootIndex + 1)), true,
"moveFocus to first child window null content");
is(newFocus, getById("t" + (kChildDocumentRootIndex + 1)),
"moveFocus to first child window null content return value");
// move focus to last in child window
setFocusTo("t6", window);
var expectedLast = getById(gPartialTabbing ? "t30" : "t" + (kBeforeTabboxIndex - 1));
expectFocusShift(() => newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_LAST, 0),
gChildWindow, expectedLast, true,
"moveFocus to last child window null content");
is(newFocus, getById(expectedLast),
"moveFocus to last child window null content return value");
// move focus to root in child window
setFocusTo("t6", window);
var childroot = getById("t" + kChildDocumentRootIndex);
gLastFocusMethod = 0;
newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_ROOT, 0);
is(newFocus, childroot, "moveFocus to root child window null content return value");
is(fm.focusedWindow, gChildWindow, "moveFocus to root child window null content focusedWindow");
is(fm.focusedElement, childroot, "moveFocus to root child window null content focusedElement");
// MOVEFOCUS_CARET tests
getById("t20").focus();
gEvents = "";
var selection = gChildWindow.getSelection();
selection.removeAllRanges();
newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_CARET, 0);
is(newFocus, null, "move caret when at document root");
is(fm.focusedElement, null, "move caret when at document root");
var node = getById("t16").firstChild;
var range = gChildWindow.document.createRange();
range.setStart(node, 3);
range.setEnd(node, 3);
selection.addRange(range);
newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_CARET, 0);
is(newFocus, null, "move caret to non-link return value");
is(fm.focusedElement, null, "move caret to non-link");
var t25 = getById("t25");
var node = t25.firstChild;
range.setStart(node, 1);
range.setEnd(node, 1);
newFocus = fm.moveFocus(gChildWindow, null, fm.MOVEFOCUS_CARET, 0);
is(newFocus, t25, "move caret to link return value");
is(fm.focusedElement, t25, "move caret to link focusedElement");
// enable caret browsing temporarily to test caret movement
var prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefs.setBoolPref("accessibility.browsewithcaret", true);
synthesizeKey("KEY_ArrowLeft", {}, gChildWindow);
synthesizeKey("KEY_ArrowLeft", {}, gChildWindow);
is(fm.focusedElement, null, "move caret away from link");
synthesizeKey("KEY_ArrowLeft", {}, gChildWindow);
is(fm.focusedElement, getById("t24"), "move caret away onto link");
prefs.setBoolPref("accessibility.browsewithcaret", false);
// cases where focus in on a content node with no frame
if (!gPartialTabbing) {
getById("t24").blur();
gEvents = "";
gLastFocus = null;
gLastFocusWindow = gChildWindow;
gLastFocusMethod = fm.FLAG_BYKEY;
selection.selectAllChildren(getById("hiddenspan"));
expectFocusShift(() => synthesizeKey("KEY_Tab"),
gChildWindow, getById("t26"), true, "tab with selection on hidden content");
setFocusTo($("o15"), window);
$("o15").hidden = true;
document.documentElement.getBoundingClientRect(); // flush after hiding
expectFocusShift(() => synthesizeKey("KEY_Tab"),
window, $("o17"), true, "tab with focus on hidden content");
$("o17").hidden = true;
document.documentElement.getBoundingClientRect();
expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}),
window, $("o13"), true, "shift+tab with focus on hidden content");
}
// cases with selection in an <input>
var t19 = getById("t19");
t19.setSelectionRange(0, 0);
setFocusTo("t18", gChildWindow);
gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FORWARD, 0),
gChildWindow, t19, true, "moveFocus to next input");
is(t19.selectionStart, 0, "input focused after moveFocus selectionStart");
is(t19.selectionEnd, 5, "input focused after moveFocus selectionEnd");
t19.setSelectionRange(0, 0);
setFocusTo("t18", gChildWindow);
gLastFocusMethod = fm.FLAG_BYKEY;
expectFocusShift(() => newFocus = fm.moveFocus(null, null, fm.MOVEFOCUS_FORWARD, fm.FLAG_BYKEY),
gChildWindow, t19, true, "moveFocus to next input by key");
is(t19.selectionStart, 0, "input focused after moveFocus by key selectionStart");
is(t19.selectionEnd, 5, "input focused after moveFocus by key selectionEnd");
}
function otherWindowFocused(otherWindow)
{
var expectedElement = getById("t9");
is(fm.activeWindow, otherWindow, "other activeWindow");
is(fm.focusedWindow, otherWindow, "other focusedWindow");
is(window.document.hasFocus(), false, "when lowered document hasFocus");
var focusedWindow = {};
is(fm.getFocusedElementForWindow(window, false, focusedWindow),
expectedElement, "when lowered getFocusedElementForWindow");
is(focusedWindow.value, window, "when lowered getFocusedElementForWindow frame");
is(document.activeElement.id, expectedElement.id, "when lowered activeElement");
is(window.document.commandDispatcher.focusedWindow, window, " commandDispatcher in other window focusedWindow");
is(window.document.commandDispatcher.focusedElement, expectedElement, " commandDispatcher in other window focusedElement");
compareEvents("deactivate: outer-document-window blur: t9 blur: outer-document blur: outer-window",
otherWindow, null, "other window opened");
otherWindow.document.getElementById("other").focus();
for (var idx = kTabbableSteps; idx >= 1; idx--) {
expectedElement = getById("t" + idx);
if (!expectedElement) // skip elements that were removed in doRemoveTests()
continue;
if ((navigator.platform.indexOf("Mac") == 0) && (idx == kBeforeTabboxIndex + 1))
continue;
expectedElement.focus();
is(fm.focusedElement.id, "other", "when lowered focusedElement t" + idx);
is(fm.focusedWindow, otherWindow, "when lowered focusedWindow t" + idx);
var checkWindow = expectedElement.ownerGlobal;
is(fm.getFocusedElementForWindow(checkWindow, false, {}).id, expectedElement.id,
"when lowered getFocusedElementForWindow t" + idx);
is(checkWindow.document.activeElement.id, expectedElement.id, "when lowered activeElement t" + idx);
if (checkWindow != window) {
is(fm.getFocusedElementForWindow(window, false, {}), $("childframe"),
"when lowered parent getFocusedElementForWindow t" + idx);
is(document.activeElement.id, "childframe",
"when lowered parent activeElement t" + idx);
}
}
gEvents = gEvents.replace(/commandupdate: cu\s?/g, "");
is(gEvents, "", "when lowered no events fired");
var other = otherWindow.document.getElementById("other");
other.focus();
is(fm.focusedElement, other, "focus method in second window");
otherWindow.close();
getById("n2").focus();
// next, check modal dialogs
window.openDialog("focus_window2.xhtml", "_blank", "chrome,modal", modalWindowOpened);
}
async function modalWindowOpened(modalWindow)
{
var elem = modalWindow.document.getElementById("other");
if (gPartialTabbing)
elem.focus();
else
synthesizeKey("KEY_Tab", {}, modalWindow);
is(fm.activeWindow, modalWindow, "modal activeWindow");
is(fm.focusedElement, elem, "modal focusedElement");
// For silly reasons (see the comments around nsCloseEvent), close() doesn't
// synchronously close the modal window. If we try to focus the window before
// the modal window is fully closed, we fail to focus it (see
// AppWindow::EnableParent).
//
// Wait for the unload event to make sure that we get enabled on time. Another
// alternative would be to use the xul-window-destroyed topic.
await new Promise(r => {
modalWindow.addEventListener("unload", r, { once: true });
modalWindow.close();
});
await SimpleTest.promiseFocus();
modalWindowClosed();
}
function modalWindowClosed()
{
is(fm.activeWindow, window, "modal window closed activeWindow");
is(fm.focusedElement, getById("n2"), "modal window closed focusedElement");
window.arguments[0].framesetWindowLoaded = framesetWindowLoaded;
window.arguments[0].open("focus_frameset.html", "_blank", "width=400,height=400,toolbar=no");
}
function framesetWindowLoaded(framesetWindow)
{
gLastFocus = null;
gLastFocusWindow = framesetWindow;
gEvents = "";
is(fm.activeWindow, getTopWindow(framesetWindow), "frameset window active");
gOldExpectedWindow = getTopWindow(framesetWindow);
gMoveToFocusFrame = true;
let steps = gCanTabMoveFocusToRootElement ? 8 : 4;
for (var idx = 1; idx <= steps; idx++) {
let frameIndex = gCanTabMoveFocusToRootElement ? (idx - 1) >> 1 : idx - 1;
gNewExpectedWindow = framesetWindow.frames[frameIndex];
if (gCanTabMoveFocusToRootElement) {
if (idx % 2) {
initEvents(gNewExpectedWindow);
}
} else {
initEvents(gNewExpectedWindow);
}
expectFocusShift(() => synthesizeKey("KEY_Tab", {}, framesetWindow),
gNewExpectedWindow, getById("f" + (gCanTabMoveFocusToRootElement ? idx : idx * 2)), true, "frameset tab key f" + idx);
gMoveToFocusFrame = false;
gOldExpectedWindow = gNewExpectedWindow;
}
gNewExpectedWindow = framesetWindow.frames[0];
expectFocusShift(() => synthesizeKey("KEY_Tab", {}, framesetWindow),
gNewExpectedWindow, gCanTabMoveFocusToRootElement ? getById("f1") : getById("f2"), true, "frameset tab key wrap to start");
gOldExpectedWindow = gNewExpectedWindow;
gNewExpectedWindow = framesetWindow.frames[3];
expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}, framesetWindow),
gNewExpectedWindow, getById("f8"), true, "frameset shift tab key wrap to end");
steps = gCanTabMoveFocusToRootElement ? 7 : 3;
for (idx = steps; idx >= 1; idx--) {
gOldExpectedWindow = gNewExpectedWindow;
let frameIndex = gCanTabMoveFocusToRootElement ? (idx - 1) >> 1 : idx - 1;
gNewExpectedWindow = framesetWindow.frames[frameIndex];
expectFocusShift(() => synthesizeKey("KEY_Tab", {shiftKey: true}, framesetWindow),
gNewExpectedWindow, getById("f" + (gCanTabMoveFocusToRootElement ? idx : idx * 2)), true, "frameset shift tab key f" + idx);
}
// document shifting
// XXXndeakin P3 ctrl+tab doesn't seem to be testable currently for some reason
gNewExpectedWindow = framesetWindow.frames[1];
expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true}, framesetWindow),
gNewExpectedWindow, getById("f3"), true, "switch document forward with f6");
gOldExpectedWindow = gNewExpectedWindow;
gNewExpectedWindow = framesetWindow.frames[2];
expectFocusShift(() => synthesizeKey("KEY_F6", {}, framesetWindow),
gNewExpectedWindow, getById("f5"), true, "switch document forward with ctrl+tab");
gOldExpectedWindow = gNewExpectedWindow;
gNewExpectedWindow = framesetWindow.frames[3];
expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true}, framesetWindow),
gNewExpectedWindow, getById("f7"), true, "switch document forward with ctrl+f6");
gOldExpectedWindow = gNewExpectedWindow;
gNewExpectedWindow = framesetWindow.frames[0];
expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true}, framesetWindow),
gNewExpectedWindow, getById("f1"), true, "switch document forward and wrap");
// going backwards by document and wrapping doesn't currently work, but didn't work
// before the focus reworking either
/*
gOldExpectedWindow = gNewExpectedWindow;
gNewExpectedWindow = framesetWindow.frames[3];
expectFocusShift(() => synthesizeKey("KEY_F6", { ctrlKey: true, shiftKey: true }, framesetWindow),
gNewExpectedWindow, getById("f7"), true, "switch document backward and wrap");
*/
fm.moveFocus(framesetWindow.frames[3], null, fm.MOVEFOCUS_ROOT, 0);
gEvents = "";
gOldExpectedWindow = gNewExpectedWindow;
gNewExpectedWindow = framesetWindow.frames[2];
expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true, shiftKey: true}, framesetWindow),
gNewExpectedWindow, getById("f5"), true, "switch document backward with f6");
gOldExpectedWindow = gNewExpectedWindow;
gNewExpectedWindow = framesetWindow.frames[1];
expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true, shiftKey: true}, framesetWindow),
gNewExpectedWindow, getById("f3"), true, "switch document backward with ctrl+tab");
gOldExpectedWindow = gNewExpectedWindow;
gNewExpectedWindow = framesetWindow.frames[0];
expectFocusShift(() => synthesizeKey("KEY_F6", {ctrlKey: true, shiftKey: true}, framesetWindow),
gNewExpectedWindow, getById("f1"), true, "switch document backward with ctrl+f6");
// skip the window switching tests for now on Linux, as raising and lowering
// a window is asynchronous there
if (!navigator.platform.includes("Linux")) {
window.openDialog("focus_window2.xhtml", "_blank", "chrome", switchWindowTest, framesetWindow);
}
else {
gOldExpectedWindow = null;
gNewExpectedWindow = null;
framesetWindow.close();
SimpleTest.waitForFocus(doWindowNoRootTest);
}
}
// test switching between two windows
function switchWindowTest(otherWindow, framesetWindow)
{
initEvents(otherWindow);
var otherElement = otherWindow.document.getElementById("other");
otherElement.focus();
framesetWindow.frames[1].document.getElementById("f4").focus();
is(fm.focusedElement, otherElement, "focus after inactive window focus");
gLastFocus = otherElement;
gLastFocusWindow = otherWindow;
gEvents = "";
gOldExpectedWindow = otherWindow;
gNewExpectedWindow = framesetWindow.frames[1];
expectFocusShift(() => gNewExpectedWindow.focus(),
gNewExpectedWindow, getById("f4"), true, "switch to frame in another window");
is(fm.getFocusedElementForWindow(otherWindow, false, {}).id, "other", "inactive window has focused element");
gOldExpectedWindow = framesetWindow.frames[1];
gNewExpectedWindow = otherWindow;
expectFocusShift(() => otherWindow.focus(),
gNewExpectedWindow, getById("other"), true, "switch to another window");
var topWindow = getTopWindow(framesetWindow);
ok(topWindow.document.commandDispatcher.getControllerForCommand("cmd_copy"),
"getControllerForCommand for focused window set");
ok(otherWindow.document.commandDispatcher.getControllerForCommand("cmd_copy"),
"getControllerForCommand for non-focused window set");
ok(topWindow.document.commandDispatcher.getControllerForCommand("cmd_copy") !=
otherWindow.document.commandDispatcher.getControllerForCommand("cmd_copy"),
"getControllerForCommand for two windows different");
ok(topWindow.document.commandDispatcher.getControllers() !=
otherWindow.document.commandDispatcher.getControllers(),
"getControllers for two windows different");
gOldExpectedWindow = otherWindow;
gNewExpectedWindow = framesetWindow.frames[1];
expectFocusShift(() => topWindow.focus(),
gNewExpectedWindow, getById("f4"), true, "switch to frame activeWindow");
fm.clearFocus(otherWindow);
gOldExpectedWindow = gNewExpectedWindow;
gNewExpectedWindow = otherWindow;
expectFocusShift(() => fm.setFocus(otherElement, fm.FLAG_RAISE),
gNewExpectedWindow, getById("other"), true, "switch to window with raise");
getTopWindow(framesetWindow).document.commandDispatcher.focusedWindow = gOldExpectedWindow;
is(fm.activeWindow, gNewExpectedWindow, "setting commandDispatcher focusedWindow doesn't raise window");
fm.moveFocus(otherWindow, null, fm.MOVEFOCUS_FORWARD, 0);
var otherInput = otherWindow.document.getElementById("other-input");
otherInput.setSelectionRange(2, 3);
topWindow.focus();
otherWindow.focus();
is(otherInput.selectionStart, 2, "selectionStart after input focus and window raise");
is(otherInput.selectionEnd, 3, "selectionEnd after input focus and window raise");
is(fm.getLastFocusMethod(null), fm.FLAG_BYMOVEFOCUS, "last focus method after input focus and window raise");
fm.clearFocus(otherWindow);
// test to ensure that a synthetic event won't move focus
var synevent = new FocusEvent("focus", {});
otherInput.dispatchEvent(synevent);
is(synevent.type, "focus", "event.type after synthetic focus event");
is(synevent.target, otherInput, "event.target after synthetic focus event");
is(fm.focusedElement, null, "focusedElement after synthetic focus event");
is(otherWindow.document.activeElement, otherWindow.document.documentElement,
"document.activeElement after synthetic focus event");
// check accessing a focus event after the event has finishing firing
function continueTest(event) {
is(event.type, "focus", "event.type after accessing focus event in timeout");
is(event.target, otherInput, "event.target after accessing focus event in timeout");
gOldExpectedWindow = null;
gNewExpectedWindow = null;
otherWindow.close();
framesetWindow.close();
SimpleTest.waitForFocus(doWindowNoRootTest);
}
function inputFocused(event) {
otherInput.removeEventListener("focus", inputFocused, true);
setTimeout(continueTest, 0, event);
}
otherInput.addEventListener("focus", inputFocused, true);
otherInput.focus();
}
// open a window with no root element
var noRootWindow = null;
function doWindowNoRootTest()
{
addEventListener("focus", doFrameSwitchingTests, true);
noRootWindow = window.open("window_focus_inner.xhtml", "_blank", "chrome,width=100,height=100");
}
// these tests check when focus is moved between a tree of frames to ensure
// that the focus is in the right place at each event step.
function doFrameSwitchingTests()
{
removeEventListener("focus", doFrameSwitchingTests, true);
noRootWindow.close();
var framea = document.getElementById("ifa");
var frameb = document.getElementById("ifb");
framea.style.MozUserFocus = "";
frameb.style.MozUserFocus = "";
window.removeEventListener("focus", eventOccured, true);
window.removeEventListener("blur", eventOccured, true);
var inputa = framea.contentDocument.body.firstChild;
inputa.focus();
addFrameSwitchingListeners(framea);
addFrameSwitchingListeners(frameb);
var framec = framea.contentDocument.body.lastChild;
addFrameSwitchingListeners(framec);
var framed = framec.contentDocument.body.lastChild;
addFrameSwitchingListeners(framed);
var inputc = framec.contentDocument.body.firstChild;
var expectedMainWindowFocus = framea;
// An element in the immediate parent frame is focused. Focus an element in
// the child. The child should be focused and the parent's current focus should
// be the child iframe.
gEventMatched = true;
is(fm.getFocusedElementForWindow(window, false, {}), expectedMainWindowFocus,
"parent of framea has iframe focused");
gExpectedEvents = [[inputa, "blur", null, framea.contentWindow, window, framea],
[framea.contentDocument, "blur", null, null, window, framea],
[framea.contentWindow, "blur", null, null, window, framea],
[framec.contentDocument, "focus", null, framec.contentWindow, window, framea],
[framec.contentWindow, "focus", null, framec.contentWindow, window, framea],
[inputc, "focus", inputc, framec.contentWindow, window, framea]];
inputc.focus();
ok(gEventMatched && !gExpectedEvents.length, "frame switch from parent input to child input" + gExpectedEvents);
// An element in a child is focused. Focus an element in the immediate
// parent.
gEventMatched = true;
gExpectedEvents = [[inputc, "blur", null, framec.contentWindow, window, framea],
[framec.contentDocument, "blur", null, null, window, framea],
[framec.contentWindow, "blur", null, null, window, framea],
[framea.contentDocument, "focus", null, framea.contentWindow, window, framea],
[framea.contentWindow, "focus", null, framea.contentWindow, window, framea],
[inputa, "focus", inputa, framea.contentWindow, window, framea]];
inputa.focus();
ok(gEventMatched && !gExpectedEvents.length, "frame switch from child input to parent input");
// An element in a frame is focused. Focus an element in a sibling frame.
// The common ancestor of the two frames should have its focused node
// cleared after the element is blurred.
var inputb = frameb.contentDocument.body.firstChild;
gEventMatched = true;
gExpectedEvents = [[inputa, "blur", null, framea.contentWindow, window, framea],
[framea.contentDocument, "blur", null, null, window, null],
[framea.contentWindow, "blur", null, null, window, null],
[frameb.contentDocument, "focus", null, frameb.contentWindow, window, frameb],
[frameb.contentWindow, "focus", null, frameb.contentWindow, window, frameb],
[inputb, "focus", inputb, frameb.contentWindow, window, frameb]];
inputb.focus();
ok(gEventMatched && !gExpectedEvents.length, "frame switch from input to sibling frame");
is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), inputa,
"blurred frame still has input as focus");
// focus a descendant in a sibling
var inputd = framed.contentDocument.body.firstChild;
gEventMatched = true;
gExpectedEvents = [[inputb, "blur", null, frameb.contentWindow, window, frameb],
[frameb.contentDocument, "blur", null, null, window, null],
[frameb.contentWindow, "blur", null, null, window, null],
[framed.contentDocument, "focus", null, framed.contentWindow, window, framea],
[framed.contentWindow, "focus", null, framed.contentWindow, window, framea],
[inputd, "focus", inputd, framed.contentWindow, window, framea]];
inputd.focus();
ok(gEventMatched && !gExpectedEvents.length, "frame switch from input to sibling descendant");
is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), framec,
"sibling parent focus has shifted to frame");
// focus an ancestor
gEventMatched = true;
gExpectedEvents = [[inputd, "blur", null, framed.contentWindow, window, framea],
[framed.contentDocument, "blur", null, null, window, framea],
[framed.contentWindow, "blur", null, null, window, framea],
[framea.contentDocument, "focus", null, framea.contentWindow, window, framea],
[framea.contentWindow, "focus", null, framea.contentWindow, window, framea],
[inputa, "focus", inputa, framea.contentWindow, window, framea]];
inputa.focus();
ok(gEventMatched && !gExpectedEvents.length, "frame switch from child input to ancestor");
// focus a descendant
gEventMatched = true;
gExpectedEvents = [[inputa, "blur", null, framea.contentWindow, window, framea],
[framea.contentDocument, "blur", null, null, window, framea],
[framea.contentWindow, "blur", null, null, window, framea],
[framed.contentDocument, "focus", null, framed.contentWindow, window, framea],
[framed.contentWindow, "focus", null, framed.contentWindow, window, framea],
[inputd, "focus", inputd, framed.contentWindow, window, framea]];
inputd.focus();
ok(gEventMatched && !gExpectedEvents.length, "frame switch from child input to ancestor");
is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), framec,
"parent focus has shifted to frame");
// focus a sibling frame by setting focusedWindow
gEventMatched = true;
gExpectedEvents = [[inputd, "blur", null, framed.contentWindow, window, framea],
[framed.contentDocument, "blur", null, null, window, null],
[framed.contentWindow, "blur", null, null, window, null],
[frameb.contentDocument, "focus", null, frameb.contentWindow, window, frameb],
[frameb.contentWindow, "focus", null, frameb.contentWindow, window, frameb]];
fm.focusedWindow = frameb.contentWindow;
ok(gEventMatched && !gExpectedEvents.length, "frame switch using focusedWindow");
// clear the focus in an unfocused frame
gEventMatched = true;
gExpectedEvents = [];
fm.clearFocus(framec.contentWindow);
ok(gEventMatched && !gExpectedEvents.length, "clearFocus in unfocused frame");
// focus a sibling frame by setting focusedWindow when no element is focused in that frame
gEventMatched = true;
gExpectedEvents = [[frameb.contentDocument, "blur", null, null, window, null],
[frameb.contentWindow, "blur", null, null, window, null],
[framec.contentDocument, "focus", null, framec.contentWindow, window, framea],
[framec.contentWindow, "focus", null, framec.contentWindow, window, framea]];
fm.focusedWindow = framec.contentWindow;
ok(gEventMatched && !gExpectedEvents.length, "frame switch using focusedWindow with no element focused");
is(fm.getFocusedElementForWindow(framea.contentWindow, false, {}), framec,
"parent focus has shifted to frame using focusedWindow");
// focus the parent frame by setting focusedWindow. This should have no effect.
gEventMatched = true;
gExpectedEvents = [];
fm.focusedWindow = framea.contentWindow;
ok(gEventMatched && !gExpectedEvents.length, "frame switch to parent using focusedWindow");
// clear the focus in the parent frame
gEventMatched = true;
gExpectedEvents = [[framec.contentDocument, "blur", null, null, window, framea],
[framec.contentWindow, "blur", null, null, window, framea],
[framea.contentDocument, "focus", null, framea.contentWindow, window, framea],
[framea.contentWindow, "focus", null, framea.contentWindow, window, framea]];
fm.clearFocus(framea.contentWindow);
ok(gEventMatched && !gExpectedEvents.length, "clearFocus in parent frame");
// clear the focus in an unfocused child frame
gEventMatched = true;
gExpectedEvents = [];
fm.clearFocus(framed.contentWindow);
ok(gEventMatched && !gExpectedEvents.length, "clearFocus in unfocused child frame");
var exh = false;
try {
fm.focusedWindow = null;
}
catch (ex) { exh = true; }
is(exh, true, "focusedWindow set to null");
is(fm.focusedWindow, framea.contentWindow, "window not changed when focusedWindow set to null");
doFrameHistoryTests()
}
function doFrameHistoryTests()
{
let frame = $("childframe");
frame.setAttribute("maychangeremoteness", "true");
let loaded = BrowserTestUtils.browserLoaded(frame, true);
frame.src = firstLocation;
loaded.then(() => {
return ContentTask.spawn(frame, {}, () => {
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
let t20 = content.document.getElementById("t20");
t20.focus();
let goneBack = new Promise(resolve => {
content.addEventListener("pageshow", ({ persisted }) => {
resolve({ location: content.location.href, persisted });
}, { once: true });
});
// make sure that loading a new page and then going back maintains the focus
content.location = "data:text/html,<script>window.onload=function() {setTimeout(function () { history.back() }, 0);}</script>";
return goneBack;
});
}).then(({ location, persisted }) => {
is(location, firstLocation, "should go back to the right page");
ok(persisted, "test relies on BFCache");
return ContentTask.spawn(frame, {}, () => {
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
let t20 = content.document.getElementById("t20");
return fm.focusedElement === t20;
});
}).then((focusCorrect) => {
if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
todo(focusCorrect, "focus restored after history back");
} else {
ok(focusCorrect, "focus restored after history back");
}
done();
});
}
function addFrameSwitchingListeners(frame)
{
frame.contentWindow.addEventListener("focus", frameSwitchingEventOccured);
frame.contentWindow.addEventListener("blur", frameSwitchingEventOccured);
frame.contentDocument.addEventListener("focus", frameSwitchingEventOccured);
frame.contentDocument.addEventListener("blur", frameSwitchingEventOccured);
var node = frame.contentDocument.body.firstChild;
node.addEventListener("focus", frameSwitchingEventOccured);
node.addEventListener("blur", frameSwitchingEventOccured);
}
function frameSwitchingEventOccured(event)
{
if (!gExpectedEvents.length) {
gEventMatched = false;
return;
}
try {
var events = gExpectedEvents.shift();
is(event.target, events[0], "event target");
is(event.type, events[1], "event type");
is(fm.focusedElement, events[2], "focused element");
is(fm.focusedWindow, events[3], "focused frame");
if (events[4])
is(fm.getFocusedElementForWindow(events[4], false, {}), events[5], "focused element in frame");
if (gEventMatched && event.target == events[0] && event.type == events[1] &&
fm.focusedElement == events[2] && fm.focusedWindow == events[3]) {
if (!events[4] || fm.getFocusedElementForWindow(events[4], false, {}) == events[5])
return;
}
} catch (ex) { ok(ex, "exception"); }
gEventMatched = false;
}
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(startTest);
]]>
</script>
<commandset id="cu"
commandupdater="true"
events="focus"
oncommandupdate="eventOccured(event)"/>
<!--
The elements with ids starting with t are focusable and in the taborder.
The elements with ids starting with o are:
odd numbered ids - focusable but not part of the tab order
even numbered ids - not focusable with -moz-user-focus: ignore or disabled
The elements with ids starting with n are:
odd numbered ids - not focusable with -moz-user-focus: none
even numbered ids - focusable but not part of the tab order
-->
<vbox id="buttonbox">
<hbox id="innerbox">
<button id="t4" accesskey="h" label="no tabindex"/>
<button id="o1" accesskey="i" label="tabindex = -1" tabindex="-1"/>
<richlistbox id="t5" label="tabindex = 0" tabindex="0" style="width: 50px">
<richlistitem height="10"/>
</richlistbox>
<button id="t1" label="tabindex = 2" tabindex="2"/>
</hbox>
<hbox>
<button id="o2" accesskey="o" style="-moz-user-focus: ignore;" label="no tabindex"/>
<button id="o4" style="-moz-user-focus: ignore;" label="no tabindex"/>
<button id="t6" style="-moz-user-focus: ignore;" label="tabindex = 0" tabindex="0"/>
<button id="t2" style="-moz-user-focus: ignore;" label="tabindex = 2" tabindex="2"/>
</hbox>
<hbox id="specialroot">
<button id="t7" style="-moz-user-focus: normal;" label="no tabindex"/>
<button id="o3" style="-moz-user-focus: normal;" label="tabindex = -1" tabindex="-1"/>
<button id="t8" style="-moz-user-focus: normal;" label="tabindex = 0" tabindex="0"/>
<richlistbox id="t3" style="-moz-user-focus: normal; width: 50px" label="tabindex = 2" tabindex="2">
<richlistitem style="height: 10px"/>
</richlistbox>
</hbox>
<hbox>
<button accesskey="p" style="display: none;"/> <button accesskey="q" style="visibility: collapse;"/>
<button style="display: none;" tabindex="2"/> <button style="visibility: collapse;" tabindex="2"/>
</hbox>
<hbox>
<button id="o20" accesskey="s" label="no tabindex" disabled="true"/>
<button id="o22" label="tabindex = -1" tabindex="-1" disabled="true"/>
<button id="o24" label="tabindex = 0" tabindex="0" disabled="true"/>
<button id="o26" label="tabindex = 2" tabindex="2" disabled="true"/>
</hbox>
</vbox>
<vbox>
<hbox>
<dropmarker id="o6" value="no tabindex"/>
<dropmarker id="o8" value="no tabindex"/>
<dropmarker id="o10" value="no tabindx"/>
<dropmarker id="o12" value="no tabindex"/>
<dropmarker id="t9" accesskey="r" style="-moz-user-focus: normal;" value="no tabindex" />
<dropmarker id="t10" style="-moz-user-focus: normal;" value="no tabindex"/>
<dropmarker id="t11" style="-moz-user-focus: normal;" value="tabindex = 0" tabindex="0" />
<dropmarker id="t12" style="-moz-user-focus: normal;" value="no tabindex"/>
<dropmarker id="o14" style="-moz-user-focus: ignore;" value="no tabindex"/>
<dropmarker id="o16" style="-moz-user-focus: ignore;" value="no tabindex"/>
<dropmarker id="n1" style="-moz-user-focus: none;" value="no tabindex"/>
<dropmarker id="n3" style="-moz-user-focus: none;" value="no tabindex"/>
</hbox>
</vbox>
<browser id="childframe" type="content" src="child_focus_frame.html" style="height: 195px"/>
<button id="t34"/>
<tabbox id="tabbox">
<tabs><tab id="t35" label="One"/><tab id="tab2" label="Two"/></tabs>
<tabpanels>
<tabpanel>
<checkbox id="t36"/>
<button id="t37"/>
</tabpanel>
<tabpanel>
<checkbox id="htab1"/>
<button id="nohtab2" tabindex="7"/>
<checkbox id="htab2" tabindex="0"/>
</tabpanel>
</tabpanels>
</tabbox>
<hbox>
<panel>
<button id="inpopup1" label="One"/>
</panel>
<description label="o" accesskey="v"/>
<button id="t38"/>
<!-- The 't' element tests end here so it doesn't matter that these elements are tabbable -->
<label id="aj" value="j" accesskey="j" control="o9"/>
<label id="ak" accesskey="k" control="n4">k</label>
<checkbox id="o5"/><checkbox id="o7"/><hbox><checkbox id="o9"/></hbox>
<checkbox id="o13"/><checkbox id="o15"/><checkbox id="o17"/><checkbox id="o19"/><checkbox id="o21"/><checkbox id="o23"/><checkbox id="o25"/>
<checkbox id="n2"/><checkbox id="n4"/>
<richlistbox id="last" style="width: 20px; height: 20px"/>
<iframe id="ifa" style="-moz-user-focus: ignore; width: 40px; height: 60px" type="content"
src="data:text/html,<input id=fra size='2'><input id='fra-b' size='2'>
<iframe src='data:text/html,<input id=frc><iframe src="data:text/html,<input id=frd>"></iframe>'></iframe>"/>
<iframe id="ifb" style="-moz-user-focus: ignore; width: 20px; height: 20px"
src="data:text/html,<input id=frd></iframe>"/>
</hbox>
</window>