<html> <head> <title>Test for IME state controling and focus moving for iframes</title> <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> <style type="text/css"> iframe { border: none; height: 100px; } </style> </head> <body onunload="onUnload();"> <p id="display"> <!-- Use input[readonly] because it isn't affected by the partial focus movement on Mac --> <input id="prev" readonly><br> <iframe id="iframe_not_editable" src="data:text/html,<html><body><input id='editor'></body></html>"></iframe><br> <!-- Testing IME state and focus movement, the anchor elements cannot get focus --> <iframe id="iframe_html" src="data:text/html,<html id='editor' contenteditable='true'><body><a href='about:blank'>about:blank;</a></body></html>"></iframe><br> <iframe id="iframe_designMode" src="data:text/html,<body id='editor' onload='document.designMode="on";'><a href='about:blank'>about:blank;</a></body>"></iframe><br> <iframe id="iframe_body" src="data:text/html,<body id='editor' contenteditable='true'><a href='about:blank'>about:blank;</a></body>"></iframe><br> <iframe id="iframe_p" src="data:text/html,<body><p id='editor' contenteditable='true'><a href='about:blank'>about:blank;</a></p></body>"></iframe><br> <input id="next" readonly><br> </p> <script class="testbody" type="application/javascript"> window.opener.wrappedJSObject.SimpleTest.waitForFocus(runTests, window); function ok(aCondition, aMessage) { window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage); } function is(aLeft, aRight, aMessage) { window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage); } function onUnload() { window.opener.wrappedJSObject.onFinish(); } var gFocusObservingElement = null; var gBlurObservingElement = null; function onFocus(aEvent) { if (aEvent.target != gFocusObservingElement) { return; } ok(gFocusObservingElement.willFocus, "focus event is fired on unexpected element"); gFocusObservingElement.willFocus = false; } function onBlur(aEvent) { if (aEvent.target != gBlurObservingElement) { return; } ok(gBlurObservingElement.willBlur, "blur event is fired on unexpected element"); gBlurObservingElement.willBlur = false; } function observeFocusBlur(aNextFocusedNode, aWillFireFocusEvent, aNextBlurredNode, aWillFireBlurEvent) { if (gFocusObservingElement) { if (gFocusObservingElement.willFocus) { ok(false, "focus event was never fired on " + gFocusObservingElement); } gFocusObservingElement.removeEventListener("focus", onFocus, true); gFocusObservingElement.willFocus = NaN; gFocusObservingElement = null; } if (gBlurObservingElement) { if (gBlurObservingElement.willBlur) { ok(false, "blur event was never fired on " + gBlurObservingElement); } gBlurObservingElement.removeEventListener("blur", onBlur, true); gBlurObservingElement.willBlur = NaN; gBlurObservingElement = null; } if (aNextFocusedNode) { gFocusObservingElement = aNextFocusedNode; gFocusObservingElement.willFocus = aWillFireFocusEvent; gFocusObservingElement.addEventListener("focus", onFocus, true); } if (aNextBlurredNode) { gBlurObservingElement = aNextBlurredNode; gBlurObservingElement.willBlur = aWillFireBlurEvent; gBlurObservingElement.addEventListener("blur", onBlur, true); } } function runTests() { var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); var fm = Components.classes["@mozilla.org/focus-manager;1"] .getService(Components.interfaces.nsIFocusManager); var iframe, editor, root, input; var prev = document.getElementById("prev"); var next = document.getElementById("next"); var html = document.documentElement; function resetFocusToInput(aDescription) { observeFocusBlur(null, false, null, false); prev.focus(); is(fm.focusedElement, prev, "input#prev[readonly] element didn't get focus: " + aDescription); is(utils.IMEStatus, utils.IME_STATUS_DISABLED, "IME enabled on input#prev[readonly]: " + aDescription); } function resetFocusToParentHTML(aDescription) { observeFocusBlur(null, false, null, false); html.focus(); is(fm.focusedElement, html, "Parent html element didn't get focus: " + aDescription); is(utils.IMEStatus, utils.IME_STATUS_DISABLED, "IME enabled on parent html element: " + aDescription); } function testTabKey(aForward, aNextFocusedNode, aWillFireFocusEvent, aNextBlurredNode, aWillFireBlurEvent, aIMEShouldBeEnabled, aTestingCaseDescription) { observeFocusBlur(aNextFocusedNode, aWillFireFocusEvent, aNextBlurredNode, aWillFireBlurEvent); synthesizeKey("VK_TAB", { shiftKey: !aForward }); var description = "Tab key test: "; if (!aForward) { description = "Shift-" + description; } description += aTestingCaseDescription + ": "; is(fm.focusedElement, aNextFocusedNode, description + "didn't move focus as expected"); is(utils.IMEStatus, aIMEShouldBeEnabled ? utils.IME_STATUS_ENABLED : utils.IME_STATUS_DISABLED, description + "didn't set IME state as expected"); } function testMouseClick(aNextFocusedNode, aWillFireFocusEvent, aWillAllNodeLostFocus, aNextBlurredNode, aWillFireBlurEvent, aIMEShouldBeEnabled, aTestingCaseDescription) { observeFocusBlur(aNextFocusedNode, aWillFireFocusEvent, aNextBlurredNode, aWillFireBlurEvent); // We're relying on layout inside the iframe being up to date, so make it so iframe.contentDocument.documentElement.getBoundingClientRect(); synthesizeMouse(iframe, 10, 80, { }); var description = "Click test: " + aTestingCaseDescription + ": "; is(fm.focusedElement, !aWillAllNodeLostFocus ? aNextFocusedNode : null, description + "didn't move focus as expected"); is(utils.IMEStatus, aIMEShouldBeEnabled ? utils.IME_STATUS_ENABLED : utils.IME_STATUS_DISABLED, description + "didn't set IME state as expected"); } function testOnEditorFlagChange(aDescription, aIsInDesignMode) { const kReadonly = Components.interfaces.nsIPlaintextEditor.eEditorReadonlyMask; var description = "testOnEditorFlagChange: " + aDescription; resetFocusToParentHTML(description); var htmlEditor = iframe.contentWindow. QueryInterface(Components.interfaces.nsIInterfaceRequestor). getInterface(Components.interfaces.nsIWebNavigation). QueryInterface(Components.interfaces.nsIDocShell).editor; var e = aIsInDesignMode ? root : editor; e.focus(); is(fm.focusedElement, e, description + ": focus() of editor didn't move focus as expected"); is(utils.IMEStatus, utils.IME_STATUS_ENABLED, description + ": IME isn't enabled when the editor gets focus"); var flags = htmlEditor.flags; htmlEditor.flags |= kReadonly; is(fm.focusedElement, e, description + ": when editor becomes readonly, focus moved unexpectedly"); is(utils.IMEStatus, utils.IME_STATUS_DISABLED, description + ": when editor becomes readonly, IME is still enabled"); htmlEditor.flags = flags; is(fm.focusedElement, e, description + ": when editor becomes read-write, focus moved unexpectedly"); is(utils.IMEStatus, utils.IME_STATUS_ENABLED, description + ": when editor becomes read-write, IME is still disabled"); } // hide all iframes document.getElementById("iframe_not_editable").style.display = "none"; document.getElementById("iframe_html").style.display = "none"; document.getElementById("iframe_designMode").style.display = "none"; document.getElementById("iframe_body").style.display = "none"; document.getElementById("iframe_p").style.display = "none"; // non editable HTML element and input element can get focus. iframe = document.getElementById("iframe_not_editable"); iframe.style.display = "inline"; editor = iframe.contentDocument.getElementById("editor"); root = iframe.contentDocument.documentElement; resetFocusToInput("initializing for iframe_not_editable"); testTabKey(true, root, false, prev, true, false, "input#prev[readonly] -> html"); testTabKey(true, editor, true, root, false, true, "html -> input in the subdoc"); testTabKey(true, next, true, editor, true, false, "input in the subdoc -> input#next[readonly]"); testTabKey(false, editor, true, next, true, true, "input#next[readonly] -> input in the subdoc"); testTabKey(false, root, false, editor, true, false, "input in the subdoc -> html"); testTabKey(false, prev, true, root, false, false, "html -> input#next[readonly]"); iframe.style.display = "none"; // HTML element (of course, it's root) must enables IME. iframe = document.getElementById("iframe_html"); iframe.style.display = "inline"; editor = iframe.contentDocument.getElementById("editor"); root = iframe.contentDocument.documentElement; resetFocusToInput("initializing for iframe_html"); testTabKey(true, editor, true, prev, true, true, "input#prev[readonly] -> html[contentediable=true]"); testTabKey(true, next, true, editor, true, false, "html[contentediable=true] -> input#next[readonly]"); testTabKey(false, editor, true, next, true, true, "input#next[readonly] -> html[contentediable=true]"); testTabKey(false, prev, true, editor, true, false, "html[contenteditable=true] -> input[readonly]"); prev.style.display = "none"; resetFocusToParentHTML("testing iframe_html"); testTabKey(true, editor, true, html, false, true, "html of parent -> html[contentediable=true]"); testTabKey(false, html, false, editor, true, false, "html[contenteditable=true] -> html of parent"); prev.style.display = "inline"; resetFocusToInput("after parent html <-> html[contenteditable=true]"); testMouseClick(editor, true, false, prev, true, true, "iframe_html"); testOnEditorFlagChange("html[contentediable=true]", false); iframe.style.display = "none"; // designMode should behave like <html contenteditable="true"></html> // but focus/blur events shouldn't be fired on its root element because // any elements shouldn't be focused state in designMode. iframe = document.getElementById("iframe_designMode"); iframe.style.display = "inline"; iframe.contentDocument.designMode = "on"; editor = iframe.contentDocument.getElementById("editor"); root = iframe.contentDocument.documentElement; resetFocusToInput("initializing for iframe_designMode"); testTabKey(true, root, false, prev, true, true, "input#prev[readonly] -> html in designMode"); testTabKey(true, next, true, root, false, false, "html in designMode -> input#next[readonly]"); testTabKey(false, root, false, next, true, true, "input#next[readonly] -> html in designMode"); testTabKey(false, prev, true, root, false, false, "html in designMode -> input#prev[readonly]"); prev.style.display = "none"; resetFocusToParentHTML("testing iframe_designMode"); testTabKey(true, root, false, html, false, true, "html of parent -> html in designMode"); testTabKey(false, html, false, root, false, false, "html in designMode -> html of parent"); prev.style.display = "inline"; resetFocusToInput("after parent html <-> html in designMode"); testMouseClick(editor, false, true, prev, true, true, "iframe_designMode"); testOnEditorFlagChange("html in designMode", true); iframe.style.display = "none"; // When there is no HTML element but the BODY element is editable, // the body element should get focus and enables IME. iframe = document.getElementById("iframe_body"); iframe.style.display = "inline"; editor = iframe.contentDocument.getElementById("editor"); root = iframe.contentDocument.documentElement; resetFocusToInput("initializing for iframe_body"); testTabKey(true, editor, true, prev, true, true, "input#prev[readonly] -> body[contentediable=true]"); testTabKey(true, next, true, editor, true, false, "body[contentediable=true] -> input#next[readonly]"); testTabKey(false, editor, true, next, true, true, "input#next[readonly] -> body[contentediable=true]"); testTabKey(false, prev, true, editor, true, false, "body[contenteditable=true] -> input#prev[readonly]"); prev.style.display = "none"; resetFocusToParentHTML("testing iframe_body"); testTabKey(true, editor, true, html, false, true, "html of parent -> body[contentediable=true]"); testTabKey(false, html, false, editor, true, false, "body[contenteditable=true] -> html of parent"); prev.style.display = "inline"; resetFocusToInput("after parent html <-> body[contenteditable=true]"); testMouseClick(editor, true, false, prev, true, true, "iframe_body"); testOnEditorFlagChange("body[contentediable=true]", false); iframe.style.display = "none"; // When HTML/BODY elements are not editable, focus shouldn't be moved to // the editable content directly. iframe = document.getElementById("iframe_p"); iframe.style.display = "inline"; editor = iframe.contentDocument.getElementById("editor"); root = iframe.contentDocument.documentElement; resetFocusToInput("initializing for iframe_p"); testTabKey(true, root, false, prev, true, false, "input#prev[readonly] -> html (has p[contenteditable=true])"); testTabKey(true, editor, true, root, false, true, "html (has p[contenteditable=true]) -> p[contentediable=true]"); testTabKey(true, next, true, editor, true, false, "p[contentediable=true] -> input#next[readonly]"); testTabKey(false, editor, true, next, true, true, "input#next[readonly] -> p[contentediable=true]"); testTabKey(false, root, false, editor, true, false, "p[contenteditable=true] -> html (has p[contenteditable=true])"); testTabKey(false, prev, true, root, false, false, "html (has p[contenteditable=true]) -> input#prev[readonly]"); prev.style.display = "none"; resetFocusToParentHTML("testing iframe_p"); testTabKey(true, root, false, html, false, false, "html of parent -> html (has p[contentediable=true])"); testTabKey(false, html, false, root, false, false, "html (has p[contentediable=true]) -> html of parent"); prev.style.display = "inline"; resetFocusToInput("after parent html <-> html (has p[contentediable=true])"); testMouseClick(root, false, true, prev, true, false, "iframe_p"); testOnEditorFlagChange("p[contenteditable=true]", false); iframe.style.display = "none"; window.close(); } </script> </body> </html>