3 <title>Test for IME state controling and focus moving for iframes
</title>
4 <script src=
"chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
5 <link rel=
"stylesheet" type=
"text/css"
6 href=
"chrome://mochikit/content/tests/SimpleTest/test.css" />
7 <style type=
"text/css">
14 <body onunload=
"onUnload();">
16 <!-- Use input[readonly] because it isn't affected by the partial focus
18 <input id=
"prev" readonly
><br>
19 <iframe id=
"iframe_not_editable"
20 src=
"data:text/html,<html><body><input id='editor'></body></html>"></iframe><br>
22 <!-- Testing IME state and focus movement, the anchor elements cannot get focus -->
23 <iframe id=
"iframe_html"
24 src=
"data:text/html,<html id='editor' contenteditable='true'><body><a href='about:blank'>about:blank;</a></body></html>"></iframe><br>
25 <iframe id=
"iframe_designMode"
26 src=
"data:text/html,<body id='editor' onload='document.designMode="on";'><a href='about:blank'>about:blank;</a></body>"></iframe><br>
27 <iframe id=
"iframe_body"
28 src=
"data:text/html,<body id='editor' contenteditable='true'><a href='about:blank'>about:blank;</a></body>"></iframe><br>
30 src=
"data:text/html,<body><p id='editor' contenteditable='true'><a href='about:blank'>about:blank;</a></p></body>"></iframe><br>
32 <input id=
"next" readonly
><br>
34 <script class=
"testbody" type=
"application/javascript">
36 window.opener.SimpleTest.waitForFocus(runTests, window);
38 function ok(aCondition, aMessage) {
39 window.opener.SimpleTest.ok(aCondition, aMessage);
42 function is(aLeft, aRight, aMessage) {
43 window.opener.SimpleTest.is(aLeft, aRight, aMessage);
47 window.opener.onFinish();
50 var gFocusObservingElement = null;
51 var gBlurObservingElement = null;
53 function onFocus(aEvent) {
54 if (aEvent.target != gFocusObservingElement) {
57 ok(gFocusObservingElement.willFocus,
58 "focus event is fired on unexpected element");
59 gFocusObservingElement.willFocus = false;
62 function onBlur(aEvent) {
63 if (aEvent.target != gBlurObservingElement) {
66 ok(gBlurObservingElement.willBlur,
67 "blur event is fired on unexpected element");
68 gBlurObservingElement.willBlur = false;
71 function observeFocusBlur(aNextFocusedNode, aWillFireFocusEvent,
72 aNextBlurredNode, aWillFireBlurEvent) {
73 if (gFocusObservingElement) {
74 if (gFocusObservingElement.willFocus) {
75 ok(false,
"focus event was never fired on " + gFocusObservingElement);
77 gFocusObservingElement.removeEventListener(
"focus", onFocus, true);
78 gFocusObservingElement.willFocus = NaN;
79 gFocusObservingElement = null;
81 if (gBlurObservingElement) {
82 if (gBlurObservingElement.willBlur) {
83 ok(false,
"blur event was never fired on " + gBlurObservingElement);
85 gBlurObservingElement.removeEventListener(
"blur", onBlur, true);
86 gBlurObservingElement.willBlur = NaN;
87 gBlurObservingElement = null;
89 if (aNextFocusedNode) {
90 gFocusObservingElement = aNextFocusedNode;
91 gFocusObservingElement.willFocus = aWillFireFocusEvent;
92 gFocusObservingElement.addEventListener(
"focus", onFocus, true);
94 if (aNextBlurredNode) {
95 gBlurObservingElement = aNextBlurredNode;
96 gBlurObservingElement.willBlur = aWillFireBlurEvent;
97 gBlurObservingElement.addEventListener(
"blur", onBlur, true);
101 function runTests() {
102 var utils = window.windowUtils;
103 var fm = Services.focus;
105 var iframe, editor, root;
106 var prev = document.getElementById(
"prev");
107 var next = document.getElementById(
"next");
108 var html = document.documentElement;
110 function resetFocusToInput(aDescription) {
111 observeFocusBlur(null, false, null, false);
113 is(fm.focusedElement, prev,
114 "input#prev[readonly] element didn't get focus: " + aDescription);
115 is(utils.IMEStatus, utils.IME_STATUS_DISABLED,
116 "IME enabled on input#prev[readonly]: " + aDescription);
119 function resetFocusToParentHTML(aDescription) {
120 observeFocusBlur(null, false, null, false);
122 is(fm.focusedElement, html,
123 "Parent html element didn't get focus: " + aDescription);
124 is(utils.IMEStatus, utils.IME_STATUS_DISABLED,
125 "IME enabled on parent html element: " + aDescription);
128 function testTabKey(aForward,
129 aNextFocusedNode, aWillFireFocusEvent,
130 aNextBlurredNode, aWillFireBlurEvent,
131 aIMEShouldBeEnabled, aTestingCaseDescription) {
132 observeFocusBlur(aNextFocusedNode, aWillFireFocusEvent,
133 aNextBlurredNode, aWillFireBlurEvent);
134 synthesizeKey(
"VK_TAB", { shiftKey: !aForward });
135 var description =
"Tab key test: ";
137 description =
"Shift-" + description;
139 description += aTestingCaseDescription +
": ";
140 is(fm.focusedElement, aNextFocusedNode,
141 description +
"didn't move focus as expected");
143 aIMEShouldBeEnabled ?
144 utils.IME_STATUS_ENABLED : utils.IME_STATUS_DISABLED,
145 description +
"didn't set IME state as expected");
148 function testMouseClick(aNextFocusedNode, aWillFireFocusEvent,
149 aWillAllNodeLostFocus,
150 aNextBlurredNode, aWillFireBlurEvent,
151 aIMEShouldBeEnabled, aTestingCaseDescription) {
152 observeFocusBlur(aNextFocusedNode, aWillFireFocusEvent,
153 aNextBlurredNode, aWillFireBlurEvent);
154 // We're relying on layout inside the iframe being up to date, so make it so
155 iframe.contentDocument.documentElement.getBoundingClientRect();
156 synthesizeMouse(iframe,
10,
80, { });
157 var description =
"Click test: " + aTestingCaseDescription +
": ";
158 is(fm.focusedElement, !aWillAllNodeLostFocus ? aNextFocusedNode : null,
159 description +
"didn't move focus as expected");
161 aIMEShouldBeEnabled ?
162 utils.IME_STATUS_ENABLED : utils.IME_STATUS_DISABLED,
163 description +
"didn't set IME state as expected");
166 function testOnEditorFlagChange(aDescription, aIsInDesignMode) {
167 const kReadonly = Ci.nsIEditor.eEditorReadonlyMask;
168 var description =
"testOnEditorFlagChange: " + aDescription;
169 resetFocusToParentHTML(description);
170 var htmlEditor = iframe.contentWindow.docShell.editor;
171 var e = aIsInDesignMode ? root : editor;
173 is(fm.focusedElement, e,
174 description +
": focus() of editor didn't move focus as expected");
175 is(utils.IMEStatus, utils.IME_STATUS_ENABLED,
176 description +
": IME isn't enabled when the editor gets focus");
177 var flags = htmlEditor.flags;
178 htmlEditor.flags |= kReadonly;
179 is(fm.focusedElement, e,
180 description +
": when editor becomes readonly, focus moved unexpectedly");
181 is(utils.IMEStatus, utils.IME_STATUS_DISABLED,
182 description +
": when editor becomes readonly, IME is still enabled");
183 htmlEditor.flags = flags;
184 is(fm.focusedElement, e,
185 description +
": when editor becomes read-write, focus moved unexpectedly");
186 is(utils.IMEStatus, utils.IME_STATUS_ENABLED,
187 description +
": when editor becomes read-write, IME is still disabled");
191 document.getElementById(
"iframe_not_editable").style.display =
"none";
192 document.getElementById(
"iframe_html").style.display =
"none";
193 document.getElementById(
"iframe_designMode").style.display =
"none";
194 document.getElementById(
"iframe_body").style.display =
"none";
195 document.getElementById(
"iframe_p").style.display =
"none";
197 // non editable HTML element and input element can get focus.
198 iframe = document.getElementById(
"iframe_not_editable");
199 iframe.style.display =
"inline";
200 editor = iframe.contentDocument.getElementById(
"editor");
201 root = iframe.contentDocument.documentElement;
202 resetFocusToInput(
"initializing for iframe_not_editable");
204 testTabKey(true, root, false, prev, true,
205 false,
"input#prev[readonly] -> html");
206 testTabKey(true, editor, true, root, false,
207 true,
"html -> input in the subdoc");
208 testTabKey(true, next, true, editor, true,
209 false,
"input in the subdoc -> input#next[readonly]");
210 testTabKey(false, editor, true, next, true,
211 true,
"input#next[readonly] -> input in the subdoc");
212 testTabKey(false, root, false, editor, true,
213 false,
"input in the subdoc -> html");
214 testTabKey(false, prev, true, root, false,
215 false,
"html -> input#next[readonly]");
217 iframe.style.display =
"none";
219 // HTML element (of course, it's root) must enables IME.
220 iframe = document.getElementById(
"iframe_html");
221 iframe.style.display =
"inline";
222 editor = iframe.contentDocument.getElementById(
"editor");
223 root = iframe.contentDocument.documentElement;
224 resetFocusToInput(
"initializing for iframe_html");
226 testTabKey(true, editor, true, prev, true,
227 true,
"input#prev[readonly] -> html[contentediable=true]");
228 testTabKey(true, next, true, editor, true,
229 false,
"html[contentediable=true] -> input#next[readonly]");
230 testTabKey(false, editor, true, next, true,
231 true,
"input#next[readonly] -> html[contentediable=true]");
232 testTabKey(false, prev, true, editor, true,
233 false,
"html[contenteditable=true] -> input[readonly]");
235 prev.style.display =
"none";
236 resetFocusToParentHTML(
"testing iframe_html");
237 testTabKey(true, editor, true, html, false,
238 true,
"html of parent -> html[contentediable=true]");
239 testTabKey(false, html, false, editor, true,
240 false,
"html[contenteditable=true] -> html of parent");
241 prev.style.display =
"inline";
242 resetFocusToInput(
"after parent html <-> html[contenteditable=true]");
244 testMouseClick(editor, true, false, prev, true, true,
"iframe_html");
246 testOnEditorFlagChange(
"html[contentediable=true]", false);
248 iframe.style.display =
"none";
250 // designMode should behave like
<html contenteditable=
"true"></html>
251 // but focus/blur events shouldn't be fired on its root element because
252 // any elements shouldn't be focused state in designMode.
253 iframe = document.getElementById(
"iframe_designMode");
254 iframe.style.display =
"inline";
255 iframe.contentDocument.designMode =
"on";
256 editor = iframe.contentDocument.getElementById(
"editor");
257 root = iframe.contentDocument.documentElement;
258 resetFocusToInput(
"initializing for iframe_designMode");
260 testTabKey(true, root, false, prev, true,
261 true,
"input#prev[readonly] -> html in designMode");
262 testTabKey(true, next, true, root, false,
263 false,
"html in designMode -> input#next[readonly]");
264 testTabKey(false, root, false, next, true,
265 true,
"input#next[readonly] -> html in designMode");
266 testTabKey(false, prev, true, root, false,
267 false,
"html in designMode -> input#prev[readonly]");
269 prev.style.display =
"none";
270 resetFocusToParentHTML(
"testing iframe_designMode");
271 testTabKey(true, root, false, html, false,
272 true,
"html of parent -> html in designMode");
273 testTabKey(false, html, false, root, false,
274 false,
"html in designMode -> html of parent");
275 prev.style.display =
"inline";
276 resetFocusToInput(
"after parent html <-> html in designMode");
278 testMouseClick(editor, false, true, prev, true, true,
"iframe_designMode");
280 testOnEditorFlagChange(
"html in designMode", true);
282 iframe.style.display =
"none";
284 // When there is no HTML element but the BODY element is editable,
285 // the body element should get focus and enables IME.
286 iframe = document.getElementById(
"iframe_body");
287 iframe.style.display =
"inline";
288 editor = iframe.contentDocument.getElementById(
"editor");
289 root = iframe.contentDocument.documentElement;
290 resetFocusToInput(
"initializing for iframe_body");
292 testTabKey(true, editor, true, prev, true,
293 true,
"input#prev[readonly] -> body[contentediable=true]");
294 testTabKey(true, next, true, editor, true,
295 false,
"body[contentediable=true] -> input#next[readonly]");
296 testTabKey(false, editor, true, next, true,
297 true,
"input#next[readonly] -> body[contentediable=true]");
298 testTabKey(false, prev, true, editor, true,
299 false,
"body[contenteditable=true] -> input#prev[readonly]");
301 prev.style.display =
"none";
302 resetFocusToParentHTML(
"testing iframe_body");
303 testTabKey(true, editor, true, html, false,
304 true,
"html of parent -> body[contentediable=true]");
305 testTabKey(false, html, false, editor, true,
306 false,
"body[contenteditable=true] -> html of parent");
307 prev.style.display =
"inline";
308 resetFocusToInput(
"after parent html <-> body[contenteditable=true]");
310 testMouseClick(editor, true, false, prev, true, true,
"iframe_body");
312 testOnEditorFlagChange(
"body[contentediable=true]", false);
314 iframe.style.display =
"none";
316 // When HTML/BODY elements are not editable, focus shouldn't be moved to
317 // the editable content directly.
318 iframe = document.getElementById(
"iframe_p");
319 iframe.style.display =
"inline";
320 editor = iframe.contentDocument.getElementById(
"editor");
321 root = iframe.contentDocument.documentElement;
322 resetFocusToInput(
"initializing for iframe_p");
324 testTabKey(true, root, false, prev, true,
325 false,
"input#prev[readonly] -> html (has p[contenteditable=true])");
326 testTabKey(true, editor, true, root, false,
327 true,
"html (has p[contenteditable=true]) -> p[contentediable=true]");
328 testTabKey(true, next, true, editor, true,
329 false,
"p[contentediable=true] -> input#next[readonly]");
330 testTabKey(false, editor, true, next, true,
331 true,
"input#next[readonly] -> p[contentediable=true]");
332 testTabKey(false, root, false, editor, true,
333 false,
"p[contenteditable=true] -> html (has p[contenteditable=true])");
334 testTabKey(false, prev, true, root, false,
335 false,
"html (has p[contenteditable=true]) -> input#prev[readonly]");
336 prev.style.display =
"none";
338 resetFocusToParentHTML(
"testing iframe_p");
339 testTabKey(true, root, false, html, false,
340 false,
"html of parent -> html (has p[contentediable=true])");
341 testTabKey(false, html, false, root, false,
342 false,
"html (has p[contentediable=true]) -> html of parent");
343 prev.style.display =
"inline";
344 resetFocusToInput(
"after parent html <-> html (has p[contentediable=true])");
346 testMouseClick(root, false, true, prev, true, false,
"iframe_p");
348 testOnEditorFlagChange(
"p[contenteditable=true]", false);
350 iframe.style.display =
"none";