<?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin" type="text/css"?> <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> <!-- This test checks focus in various ways --> <window id="outer-document" title="Focus Test" width="600" height="550" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> <body xmlns="http://www.w3.org/1999/xhtml"/> <script type="application/javascript"><![CDATA[ var fm = Components.classes["@mozilla.org/focus-manager;1"]. getService(Components.interfaces.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; function is(l, r, n) { window.opener.wrappedJSObject.SimpleTest.is(l,r,n); } function ok(v, n) { window.opener.wrappedJSObject.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 (event.target instanceof Element && event.target.localName == "iframe") ok(false, "iframe " + event.type + "occured"); var id; if (gOldExpectedWindow && event.type == "blur") { if (event.target instanceof Window) id = "frame-" + gOldExpectedWindow.document.documentElement.id + "-window"; else if (event.target instanceof Document) id = "frame-" + gOldExpectedWindow.document.documentElement.id + "-document"; else id = event.originalTarget.id; } else if (gNewExpectedWindow && event.type == "focus") { if (event.target instanceof Window) id = "frame-" + gNewExpectedWindow.document.documentElement.id + "-window"; else if (event.target instanceof Document) 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 (event.target instanceof Window) id = (event.target == window) ? "outer-window" : "child-window"; else if (event.target instanceof Document) id = (event.target == document) ? "outer-document" : "child-document"; else id = event.originalTarget.id; if (gEvents) gEvents += " "; gEvents += event.type + ": " + id; } function expectFocusShift(callback, expectedWindow, expectedElement, focusChanged, testid) { if (expectedWindow == null) expectedWindow = expectedElement ? expectedElement.ownerDocument.defaultView : 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.QueryInterface(Components.interfaces.nsIInterfaceRequestor). getInterface(Components.interfaces.nsIWebNavigation). QueryInterface(Components.interfaces.nsIDocShellTreeItem).rootTreeItem. QueryInterface(Components.interfaces.nsIInterfaceRequestor). getInterface(Components.interfaces.nsIDOMWindow); } function mouseWillTriggerFocus(element) { if (!element) { return false; } if (navigator.platform.indexOf("Mac") != 0) { return true; } if (element.namespaceURI == "http://www.w3.org/1999/xhtml") { // 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 == "listbox") { return true; } return false; } function mouseOnElement(element, expectedElement, focusChanged, testid) { var expectedWindow = (element.ownerDocument.defaultView == gChildWindow) ? gChildWindow : window; // on Mac, form elements are not focused when clicking, except for lists and textboxes. var noFocusOnMouse = !mouseWillTriggerFocus(element) if (noFocusOnMouse) { // no focus so the last focus method will be 0 gLastFocusMethod = 0; expectFocusShift(() => synthesizeMouse(element, 4, 4, { }, element.ownerDocument.defaultView), expectedWindow, null, true, testid); gLastFocusMethod = fm.FLAG_BYMOUSE; } else { expectFocusShift(() => synthesizeMouse(element, 4, 4, { }, element.ownerDocument.defaultView), element.ownerDocument.defaultView, expectedElement, focusChanged, testid); } } function done() { var opener = window.opener; window.close(); opener.wrappedJSObject.SimpleTest.finish(); } var pressTab = () => synthesizeKey("VK_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; } 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 textboxes 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 = ["t3", "t5", "t9", "t10", "t11", "t12", "t13", "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("VK_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("VK_TAB", { shiftKey: true }), null, getById(partialTabList[idx]), true, "partial tab key " + partialTabList[idx]); } } else { // TAB key for (var idx = 1; idx <= kTabbableSteps; idx++) { 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("VK_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--) { expectFocusShift(() => synthesizeKey("VK_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, false); expectFocusShift(() => t19.select(), null, getById("t" + 19), true, "input.select()"); t19.removeEventListener("select", selectListener, false); ok(selectFired, "select event fires 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, getById("t" + idx), true, "mouse on element t" + idx); var expectedWindow = (element.ownerDocument.defaultView == gChildWindow) ? gChildWindow : window; if (element.localName == "listbox" && 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.ownerDocument.defaultView), window, null, true, "mouse on scrollable element"); } // focus() method gLastFocusMethod = 0; 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.ownerDocument.defaultView, 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.ownerDocument.defaultView, 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_BYMOVEFOCUS; var legend = getById("legend"); expectFocusShift(() => legend.focus(), null, getById("t28"), true, "focus method on unfocusable legend"); gLastFocusMethod = 0; legend.tabIndex = "0"; expectFocusShift(() => legend.focus(), null, getById("legend"), true, "focus method on focusable legend"); legend.tabIndex = "-1"; } var accessKeyDetails = (navigator.platform.indexOf("Mac") >= 0) ? { 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]).ownerDocument.defaultView == gChildWindow); if (navigator.platform.indexOf("Mac") == -1) { 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 = 0; 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); 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, false); blurFunction(); element.removeEventListener("blur", eventListener, false); } var functions = [ element => element.focus(), element => synthesizeMouse(element, 4, 4, { }, element.ownerDocument.defaultView) ]; // 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, false); element.focus(); element.removeEventListener("focus", eventListener, false); } 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 = 0; 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); // 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 textbox works setFocusTo("t1", window); var textbox1 = document.createElement("textbox"); $("innerbox").appendChild(textbox1); var textbox2 = document.createElement("textbox"); $("innerbox").appendChild(textbox2); gLastFocusMethod = 0; expectFocusShift(() => textbox2.focus(), null, textbox2.inputField, true, "focus on textbox"); gLastFocusMethod = fm.FLAG_BYKEY; expectFocusShift(() => synthesizeKey("VK_TAB", { shiftKey: true }), null, textbox1.inputField, true, "shift+tab on textbox"); textbox1.tabIndex = 2; textbox2.tabIndex = 2; gLastFocusMethod = 0; expectFocusShift(() => textbox2.focus(), null, textbox2.inputField, true, "focus on textbox with tabindex set"); gLastFocusMethod = fm.FLAG_BYKEY; expectFocusShift(() => synthesizeKey("VK_TAB", { shiftKey: true }), null, textbox1.inputField, true, "shift+tab on textbox with tabindex set"); // ---- test for bug 618907 which ensures that canceling the mousedown event still focuses the // right frame var childContentFrame = document.getElementById("ifa") childContentFrame.style.MozUserFocus = ""; var frab = childContentFrame.contentDocument.getElementById("fra-b"); var mouseDownListener = event => event.preventDefault(); frab.addEventListener("mousedown", mouseDownListener, false); var childElementToFocus = childContentFrame.contentDocument.getElementById("fra"); gLastFocus = childElementToFocus; gLastFocusWindow = childContentFrame.contentWindow; gLastFocus.focus(); gEvents = ""; setFocusTo("t1", window); gLastFocusMethod = -1; expectFocusShift(() => synthesizeMouse(frab, 5, 5, { }, childContentFrame.contentWindow), null, childElementToFocus, true, "mousedown event canceled - chrome to content"); frab.removeEventListener("mousedown", mouseDownListener, false); var t5 = getById("t5"); t5.addEventListener("mousedown", mouseDownListener, false); synthesizeMouse(t5, 10, 10, { }) t5.removeEventListener("mousedown", mouseDownListener, false); is(fm.focusedElement, childElementToFocus, "mousedown event cancelled - content to chrome - element"); is(fm.focusedWindow, childContentFrame.contentWindow, "mousedown event cancelled - content to chrome - window"); // ---- 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.xul", "_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.parentNode.removeChild(t19); 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.parentNode.removeChild(abs); 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 = Components.classes["@mozilla.org/preferences-service;1"]. getService(Components.interfaces.nsIPrefBranch); prefs.setBoolPref("accessibility.browsewithcaret", true); synthesizeKey("VK_LEFT", { }, gChildWindow); synthesizeKey("VK_LEFT", { }, gChildWindow); is(fm.focusedElement, null, "move caret away from link"); synthesizeKey("VK_LEFT", { }, 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("VK_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("VK_TAB", { }), window, $("o17"), true, "tab with focus on hidden content"); $("o17").hidden = true; document.documentElement.getBoundingClientRect(); expectFocusShift(() => synthesizeKey("VK_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 textbox"); 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 textbox 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.ownerDocument.defaultView; 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 // XXXndeakin Bug 621399 - the modal dialog test as well as later tests sometime fail // on Windows 8 so just end the test here. if (navigator.userAgent.indexOf("Windows NT 6.2") >= 0) { done(); } else { window.openDialog("focus_window2.xul", "_blank", "chrome,modal", modalWindowOpened); } } function modalWindowOpened(modalWindow) { var elem = modalWindow.document.getElementById("other"); if (gPartialTabbing) elem.focus(); else synthesizeKey("VK_TAB", { }, modalWindow); is(fm.activeWindow, modalWindow, "modal activeWindow"); is(fm.focusedElement, elem, "modal focusedElement"); modalWindow.close(); SimpleTest.waitForFocus(modalWindowClosed); } function modalWindowClosed() { is(fm.activeWindow, window, "modal window closed activeWindow"); is(fm.focusedElement, getById("n2"), "modal window closed focusedElement"); window.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; for (var idx = 1; idx <= 8; idx++) { gNewExpectedWindow = framesetWindow.frames[(idx - 1) >> 1]; if (idx % 2) initEvents(gNewExpectedWindow); expectFocusShift(() => synthesizeKey("VK_TAB", { }, framesetWindow), gNewExpectedWindow, getById("f" + idx), true, "frameset tab key f" + idx); gMoveToFocusFrame = false; gOldExpectedWindow = gNewExpectedWindow; } gNewExpectedWindow = framesetWindow.frames[0]; expectFocusShift(() => synthesizeKey("VK_TAB", { }, framesetWindow), gNewExpectedWindow, getById("f1"), true, "frameset tab key wrap to start"); gOldExpectedWindow = gNewExpectedWindow; gNewExpectedWindow = framesetWindow.frames[3]; expectFocusShift(() => synthesizeKey("VK_TAB", { shiftKey: true }, framesetWindow), gNewExpectedWindow, getById("f8"), true, "frameset shift tab key wrap to end"); for (idx = 7; idx >= 1; idx--) { gOldExpectedWindow = gNewExpectedWindow; gNewExpectedWindow = framesetWindow.frames[(idx - 1) >> 1]; expectFocusShift(() => synthesizeKey("VK_TAB", { shiftKey: true }, framesetWindow), gNewExpectedWindow, getById("f" + idx), 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("VK_F6", { ctrlKey: true }, framesetWindow), gNewExpectedWindow, getById("f3"), true, "switch document forward with f6"); gOldExpectedWindow = gNewExpectedWindow; gNewExpectedWindow = framesetWindow.frames[2]; expectFocusShift(() => synthesizeKey("VK_F6", { }, framesetWindow), gNewExpectedWindow, getById("f5"), true, "switch document forward with ctrl+tab"); gOldExpectedWindow = gNewExpectedWindow; gNewExpectedWindow = framesetWindow.frames[3]; expectFocusShift(() => synthesizeKey("VK_F6", { ctrlKey: true }, framesetWindow), gNewExpectedWindow, getById("f7"), true, "switch document forward with ctrl+f6"); gOldExpectedWindow = gNewExpectedWindow; gNewExpectedWindow = framesetWindow.frames[0]; expectFocusShift(() => synthesizeKey("VK_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("VK_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("VK_F6", { ctrlKey: true, shiftKey: true }, framesetWindow), gNewExpectedWindow, getById("f5"), true, "switch document backward with f6"); gOldExpectedWindow = gNewExpectedWindow; gNewExpectedWindow = framesetWindow.frames[1]; expectFocusShift(() => synthesizeKey("VK_F6", { ctrlKey: true, shiftKey: true }, framesetWindow), gNewExpectedWindow, getById("f3"), true, "switch document backward with ctrl+tab"); gOldExpectedWindow = gNewExpectedWindow; gNewExpectedWindow = framesetWindow.frames[0]; expectFocusShift(() => synthesizeKey("VK_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.indexOf("Linux") == -1) { window.openDialog("focus_window2.xul", "_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 exh = false; try { fm.activeWindow = framesetWindow.frames[0]; } catch (ex) { exh = true; } is(exh, true, "activeWindow set to non top-level window"); exh = false; try { fm.activeWindow = null; } catch (ex) { exh = true; } is(exh, true, "activeWindow set to null"); is(fm.activeWindow, otherWindow, "window not changed when activeWindow set to null"); 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(() => fm.activeWindow = topWindow, 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 otherTextbox = otherWindow.document.getElementById("other-textbox"); otherTextbox.setSelectionRange(2, 3); fm.activeWindow = topWindow; fm.activeWindow = otherWindow; is(otherTextbox.selectionStart, 2, "selectionStart after textbox focus and window raise"); is(otherTextbox.selectionEnd, 3, "selectionEnd after textbox focus and window raise"); is(fm.getLastFocusMethod(null), fm.FLAG_BYMOVEFOCUS, "last focus method after textbox focus and window raise"); fm.clearFocus(otherWindow); // test to ensure that a synthetic event works var synevent = document.createEvent("Event"); synevent.initEvent("focus", false, false); otherTextbox.inputField.dispatchEvent(synevent); is(synevent.type, "focus", "event.type after synthetic focus event"); is(synevent.target, otherTextbox, "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, otherTextbox, "event.target after accessing focus event in timeout"); gOldExpectedWindow = null; gNewExpectedWindow = null; otherWindow.close(); framesetWindow.close(); SimpleTest.waitForFocus(doWindowNoRootTest); } function textboxFocused(event) { otherTextbox.removeEventListener("focus", textboxFocused, true); setTimeout(continueTest, 0, event); } otherTextbox.addEventListener("focus", textboxFocused, true); otherTextbox.focus(); } // open a window with no root element var noRootWindow = null; function doWindowNoRootTest() { var data = "data:application/vnd.mozilla.xul+xml," + unescape( "<window onfocus='dostuff()' xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" + " style='-moz-user-focus: normal;'>" + "<script>function dostuff() { setTimeout(function() { " + "document.documentElement.focus(); document.removeChild(document.documentElement);" + "window.opener.focus(); }, 100); }</script></window>"); addEventListener("focus", doFrameSwitchingTests, true); noRootWindow = window.open(data, "_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 == 0, "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 == 0, "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 == 0, "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 == 0, "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 == 0, "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 == 0, "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], [inputb, "focus", inputb, frameb.contentWindow, window, frameb]]; fm.focusedWindow = frameb.contentWindow; ok(gEventMatched && gExpectedEvents.length == 0, "frame switch using focusedWindow"); // clear the focus in an unfocused frame gEventMatched = true; gExpectedEvents = []; fm.clearFocus(framec.contentWindow); ok(gEventMatched && gExpectedEvents.length == 0, "clearFocus in unfocused frame"); // focus a sibling frame by setting focusedWindow when no element is focused in that frame gEventMatched = true; gExpectedEvents = [[inputb, "blur", null, frameb.contentWindow, window, frameb], [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 == 0, "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 == 0, "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 == 0, "clearFocus in parent frame"); // clear the focus in an unfocused child frame gEventMatched = true; gExpectedEvents = []; fm.clearFocus(framed.contentWindow); ok(gEventMatched && gExpectedEvents.length == 0, "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() { var t20 = getById("t20"); t20.focus(); gChildWindow.addEventListener("focus", function(event) { if (event.target == t20) { is(fm.focusedElement, t20, "focus restored after history back"); done(); } }, true); // make sure that loading a new page and then going back maintains the focus gChildWindow.location = "data:text/html,<script>window.onload=function() {setTimeout(function () {SpecialPowers.wrap(window).back();}, 0);}</script>"; } function addFrameSwitchingListeners(frame) { frame.contentWindow.addEventListener("focus", frameSwitchingEventOccured, false); frame.contentWindow.addEventListener("blur", frameSwitchingEventOccured, false); frame.contentDocument.addEventListener("focus", frameSwitchingEventOccured, false); frame.contentDocument.addEventListener("blur", frameSwitchingEventOccured, false); var node = frame.contentDocument.body.firstChild; node.addEventListener("focus", frameSwitchingEventOccured, false); node.addEventListener("blur", frameSwitchingEventOccured, false); } 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"/> <listbox id="t5" label="tabindex = 0" tabindex="0" rows="1"> <listitem/> </listbox> <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="tabindex = -1" tabindex="-1"/> <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"/> <listbox id="t3" style="-moz-user-focus: normal;" label="tabindex = 2" tabindex="2" rows="1"> <listitem/> </listbox> </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="tabindex = -1" tabindex="-1"/> <dropmarker id="o10" value="tabindex = 0" tabindex="0"/> <dropmarker id="o12" value="tabindex = 2" tabindex="2"/> <dropmarker id="t9" accesskey="r" style="-moz-user-focus: normal;" value="no tabindex" /> <dropmarker id="t10" style="-moz-user-focus: normal;" value="tabindex = -1" tabindex="-1" /> <dropmarker id="t11" style="-moz-user-focus: normal;" value="tabindex = 0" tabindex="0" /> <dropmarker id="t12" style="-moz-user-focus: normal;" value="tabindex = 2" tabindex="2" /> <dropmarker id="o14" style="-moz-user-focus: ignore;" value="no tabindex"/> <dropmarker id="o16" style="-moz-user-focus: ignore;" value="tabindex = -1" tabindex="-1"/> <dropmarker id="n1" style="-moz-user-focus: none;" value="tabindex = 0" tabindex="0"/> <dropmarker id="n3" style="-moz-user-focus: none;" value="tabindex = 2" tabindex="2"/> </hbox> </vbox> <browser id="childframe" type="content" src="child_focus_frame.html" width="300" height="195"/> <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"/> <textbox label="Two"/> </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"/> <listbox id="last" width="20" rows="1"/> <iframe id="ifa" width="40" height="60" style="-moz-user-focus: ignore;" 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" width="20" height="20" style="-moz-user-focus: ignore;" src="data:text/html,<input id=frd></iframe>"/> </hbox> </window>