widget/tests/test_imestate.html

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:0ee1c36909f4
1 <html style="ime-mode: disabled;">
2 <head>
3 <title>Test for IME state controling</title>
4 <script type="text/javascript"
5 src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
6 <script type="text/javascript"
7 src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
8 <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
9 <link rel="stylesheet" type="text/css"
10 href="chrome://mochikit/content/tests/SimpleTest/test.css" />
11 </head>
12 <body onload="setTimeout(runTests, 0);" style="ime-mode: disabled;">
13 <div id="display" style="ime-mode: disabled;">
14 <!-- input elements -->
15 <input type="text" id="text"/><br/>
16 <input type="text" id="text_readonly" readonly="readonly"/><br/>
17 <input type="password" id="password"/><br/>
18 <input type="password" id="password_readonly" readonly="readonly"/><br/>
19 <input type="checkbox" id="checkbox"/><br/>
20 <input type="radio" id="radio"/><br/>
21 <input type="submit" id="submit"/><br/>
22 <input type="reset" id="reset"/><br/>
23 <input type="file" id="file"/><br/>
24 <input type="button" id="ibutton"/><br/>
25 <input type="image" id="image" alt="image"/><br/>
26
27 <!-- html5 input elements -->
28 <input type="url" id="url"/><br/>
29 <input type="email" id="email"/><br/>
30 <input type="search" id="search"/><br/>
31 <input type="tel" id="tel"/><br/>
32 <input type="number" id="number"/><br/>
33
34 <!-- form controls -->
35 <button id="button">button</button><br/>
36 <textarea id="textarea">textarea</textarea><br/>
37 <textarea id="textarea_readonly" readonly="readonly">textarea[readonly]</textarea><br/>
38 <select id="select">
39 <option value="option" selected="selected"/>
40 </select><br/>
41 <select id="select_multiple" multiple="multiple">
42 <option value="option" selected="selected"/>
43 </select><br/>
44 <isindex id="isindex" prompt="isindex"/><br/>
45
46 <!-- a element -->
47 <a id="a_href" href="about:blank">a[href]</a><br/>
48
49 <!-- ime-mode test -->
50 <input type="text" id="ime_mode_auto" style="ime-mode: auto;"/><br/>
51 <input type="text" id="ime_mode_normal" style="ime-mode: normal;"/><br/>
52 <input type="text" id="ime_mode_active" style="ime-mode: active;"/><br/>
53 <input type="text" id="ime_mode_inactive" style="ime-mode: inactive;"/><br/>
54 <input type="text" id="ime_mode_disabled" style="ime-mode: disabled;"/><br/>
55
56 <input type="text" id="ime_mode_auto_url" style="ime-mode: auto;"/><br/>
57 <input type="text" id="ime_mode_normal_url" style="ime-mode: normal;"/><br/>
58 <input type="text" id="ime_mode_active_url" style="ime-mode: active;"/><br/>
59 <input type="text" id="ime_mode_inactive_url" style="ime-mode: inactive;"/><br/>
60 <input type="text" id="ime_mode_disabled_url" style="ime-mode: disabled;"/><br/>
61
62 <input type="text" id="ime_mode_auto_email" style="ime-mode: auto;"/><br/>
63 <input type="text" id="ime_mode_normal_email" style="ime-mode: normal;"/><br/>
64 <input type="text" id="ime_mode_active_email" style="ime-mode: active;"/><br/>
65 <input type="text" id="ime_mode_inactive_email" style="ime-mode: inactive;"/><br/>
66 <input type="text" id="ime_mode_disabled_email" style="ime-mode: disabled;"/><br/>
67
68 <input type="text" id="ime_mode_auto_search" style="ime-mode: auto;"/><br/>
69 <input type="text" id="ime_mode_normal_search" style="ime-mode: normal;"/><br/>
70 <input type="text" id="ime_mode_active_search" style="ime-mode: active;"/><br/>
71 <input type="text" id="ime_mode_inactive_search" style="ime-mode: inactive;"/><br/>
72 <input type="text" id="ime_mode_disabled_search" style="ime-mode: disabled;"/><br/>
73
74 <input type="text" id="ime_mode_auto_tel" style="ime-mode: auto;"/><br/>
75 <input type="text" id="ime_mode_normal_tel" style="ime-mode: normal;"/><br/>
76 <input type="text" id="ime_mode_active_tel" style="ime-mode: active;"/><br/>
77 <input type="text" id="ime_mode_inactive_tel" style="ime-mode: inactive;"/><br/>
78 <input type="text" id="ime_mode_disabled_tel" style="ime-mode: disabled;"/><br/>
79
80 <input type="text" id="ime_mode_auto_number" style="ime-mode: auto;"/><br/>
81 <input type="text" id="ime_mode_normal_number" style="ime-mode: normal;"/><br/>
82 <input type="text" id="ime_mode_active_number" style="ime-mode: active;"/><br/>
83 <input type="text" id="ime_mode_inactive_number" style="ime-mode: inactive;"/><br/>
84 <input type="text" id="ime_mode_disabled_number" style="ime-mode: disabled;"/><br/>
85
86 <input type="password" id="ime_mode_auto_p" style="ime-mode: auto;"/><br/>
87 <input type="password" id="ime_mode_normal_p" style="ime-mode: normal;"/><br/>
88 <input type="password" id="ime_mode_active_p" style="ime-mode: active;"/><br/>
89 <input type="password" id="ime_mode_inactive_p" style="ime-mode: inactive;"/><br/>
90 <input type="password" id="ime_mode_disabled_p" style="ime-mode: disabled;"/><br/>
91 <textarea id="ime_mode_auto_t" style="ime-mode: auto;">textarea</textarea><br/>
92 <textarea id="ime_mode_normal_t" style="ime-mode: normal;">textarea</textarea><br/>
93 <textarea id="ime_mode_active_t" style="ime-mode: active;">textarea</textarea><br/>
94 <textarea id="ime_mode_inactive_t" style="ime-mode: inactive;">textarea</textarea><br/>
95 <textarea id="ime_mode_disabled_t" style="ime-mode: disabled;">textarea</textarea><br/>
96
97 <!-- plugin -->
98 <object type="application/x-test" id="plugin"></object><br/>
99
100 <!-- contenteditable editor -->
101 <div id="contenteditableEditor" contenteditable="true"></div>
102
103 <!-- designMode editor -->
104 <iframe id="designModeEditor"
105 onload="document.getElementById('designModeEditor').contentDocument.designMode = 'on';"
106 src="data:text/html,<html><body></body></html>"></iframe><br/>
107 </div>
108 <div id="content" style="display: none">
109
110 </div>
111 <pre id="test">
112 </pre>
113
114 <script class="testbody" type="application/javascript">
115
116 SimpleTest.waitForExplicitFinish();
117
118 function hitEventLoop(aFunc, aTimes)
119 {
120 if (--aTimes) {
121 setTimeout(hitEventLoop, 0, aFunc, aTimes);
122 } else {
123 setTimeout(aFunc, 20);
124 }
125 }
126
127 var gUtils = window.
128 QueryInterface(Components.interfaces.nsIInterfaceRequestor).
129 getInterface(Components.interfaces.nsIDOMWindowUtils);
130 var gFM = Components.classes["@mozilla.org/focus-manager;1"].
131 getService(Components.interfaces.nsIFocusManager);
132 const kIMEEnabledSupported = navigator.platform.indexOf("Mac") == 0 ||
133 navigator.platform.indexOf("Win") == 0 ||
134 navigator.platform.indexOf("Linux") == 0;
135
136 // We support to control IME open state on Windows and Mac actually. However,
137 // we cannot test it on Mac if the current keyboard layout is not CJK. And also
138 // we cannot test it on Win32 if the system didn't be installed IME. So,
139 // currently we should not run the open state testing.
140 const kIMEOpenSupported = false;
141
142 function runBasicTest(aIsEditable, aInDesignMode, aDescription)
143 {
144 function test(aTest)
145 {
146 function moveFocus(aTest, aFocusEventHandler)
147 {
148 if (aInDesignMode) {
149 if (document.activeElement) {
150 document.activeElement.blur();
151 }
152 } else if (aIsEditable) {
153 document.getElementById("display").focus();
154 } else if (aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED) {
155 document.getElementById("password").focus();
156 } else {
157 document.getElementById("text").focus();
158 }
159 var previousFocusedElement = gFM.focusedElement;
160 var element = document.getElementById(aTest.id);
161 var focusEventTarget = element;
162 var subDocument = null;
163 if (element.contentDocument) {
164 focusEventTarget = element.contentDocument;
165 subDocument = element.contentDocument;
166 element = element.contentDocument.documentElement;
167 }
168
169 focusEventTarget.addEventListener("focus", aFocusEventHandler, true);
170 document.addEventListener("MozIMEFocusIn", aFocusEventHandler, true);
171 document.addEventListener("MozIMEFocusOut", aFocusEventHandler, true);
172 if (subDocument) {
173 subDocument.addEventListener("MozIMEFocusIn", aFocusEventHandler, true);
174 subDocument.addEventListener("MozIMEFocusOut", aFocusEventHandler, true);
175 }
176
177 element.focus();
178
179 focusEventTarget.removeEventListener("focus", aFocusEventHandler, true);
180 document.removeEventListener("MozIMEFocusIn", aFocusEventHandler, true);
181 document.removeEventListener("MozIMEFocusOut", aFocusEventHandler, true);
182 if (element.contentDocument) {
183 subDocument.removeEventListener("MozIMEFocusIn", aFocusEventHandler, true);
184 subDocument.removeEventListener("MozIMEFocusOut", aFocusEventHandler, true);
185 }
186
187 var focusedElement = gFM.focusedElement;
188 if (focusedElement) {
189 var bindingParent = document.getBindingParent(focusedElement);
190 if (bindingParent) {
191 focusedElement = bindingParent;
192 }
193 }
194 if (aTest.focusable) {
195 is(focusedElement, element,
196 aDescription + ": " + aTest.description + ", focus didn't move");
197 return (element == focusedElement);
198 }
199 is(focusedElement, previousFocusedElement,
200 aDescription + ": " + aTest.description + ", focus moved as unexpected");
201 return (previousFocusedElement == focusedElement);
202 }
203
204 function testOpened(aTest, aOpened)
205 {
206 document.getElementById("text").focus();
207 gUtils.IMEIsOpen = aOpened;
208 if (!moveFocus(aTest)) {
209 return;
210 }
211 var message = aDescription + ": " + aTest.description +
212 ", wrong opened state";
213 is(gUtils.IMEIsOpen,
214 aTest.changeOpened ? aTest.expectedOpened : aOpened, message);
215 }
216
217 // IME Enabled state testing
218 var enabled = gUtils.IME_STATUS_ENABLED;
219 if (kIMEEnabledSupported) {
220 var focusEventCount = 0;
221 var mozIMEFocusInCount = 0;
222 var mozIMEFocusOutCount = 0;
223 var IMEHasFocus = false;
224
225 function onFocus(aEvent)
226 {
227 switch (aEvent.type) {
228 case "focus":
229 focusEventCount++;
230 is(gUtils.IMEStatus, aTest.expectedEnabled,
231 aDescription + ": " + aTest.description + ", wrong enabled state at focus event");
232 break;
233 case "MozIMEFocusIn":
234 mozIMEFocusInCount++;
235 IMEHasFocus = true;
236 is(gUtils.IMEStatus, aTest.expectedEnabled,
237 aDescription + ": " + aTest.description +
238 ", MozIMEFocusIn event must be fired after IME state is updated");
239 break;
240 case "MozIMEFocusOut":
241 mozIMEFocusOutCount++;
242 IMEHasFocus = false;
243 var changingStatus = !(aIsEditable && aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED);
244 if (aTest.toDesignModeEditor) {
245 is(gUtils.IME_STATUS_ENABLED, aTest.expectedEnabled,
246 aDescription + ": " + aTest.description +
247 ", MozIMEFocusOut event must be fired after IME state is updated");
248 } else if (changingStatus) {
249 isnot(gUtils.IMEStatus, aTest.expectedEnabled,
250 aDescription + ": " + aTest.description +
251 ", MozIMEFocusOut event must be fired before IME state is updated");
252 } else {
253 is(gUtils.IMEStatus, aTest.expectedEnabled,
254 aDescription + ": " + aTest.description +
255 ", MozIMEFocusOut event must be fired with expected IME state if the state isn't being changed");
256 }
257 break;
258 }
259 }
260
261 if (!moveFocus(aTest, onFocus)) {
262 return;
263 }
264
265 if (aTest.focusable) {
266 if (!aTest.focusEventNotFired) {
267 ok(focusEventCount > 0,
268 aDescription + ": " + aTest.description + ", focus event is never fired");
269 if (aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED || aTest.expectedEnabled == gUtils.IME_STATUS_PASSWORD) {
270 ok(mozIMEFocusInCount > 0,
271 aDescription + ": " + aTest.description + ", MozIMEFocusIn event should be fired");
272 if (aInDesignMode && !aTest.toDesignModeEditor) {
273 is(mozIMEFocusOutCount, 0,
274 aDescription + ": " + aTest.description +
275 ", MozIMEFocusOut event shouldn't be fired in designMode since focus isn't moved from another editor");
276 } else {
277 ok(mozIMEFocusOutCount > 0,
278 aDescription + ": " + aTest.description +
279 ", MozIMEFocusOut event should be fired for the previous focused editor");
280 }
281 ok(IMEHasFocus,
282 aDescription + ": " + aTest.description +
283 ", The latest MozIMEFocus* event must be MozIMEFocusIn");
284 } else {
285 is(mozIMEFocusInCount, 0,
286 aDescription + ": " + aTest.description +
287 ", MozIMEFocusIn event shouldn't be fired");
288 ok(mozIMEFocusOutCount > 0,
289 aDescription + ": " + aTest.description +
290 ", MozIMEFocusOut event should be fired");
291 ok(!IMEHasFocus,
292 aDescription + ": " + aTest.description +
293 ", The latest MozIMEFocus* event must be MozIMEFocusOut");
294 }
295 } else {
296 todo(focusEventCount > 0,
297 aDescription + ": " + aTest.description + ", focus event should be fired");
298 }
299 } else {
300 is(mozIMEFocusInCount, 0,
301 aDescription + ": " + aTest.description +
302 ", MozIMEFocusIn event shouldn't be fired at testing non-focusable element");
303 is(mozIMEFocusOutCount, 0,
304 aDescription + ": " + aTest.description +
305 ", MozIMEFocusOut event shouldn't be fired at testing non-focusable element");
306 }
307
308 enabled = gUtils.IMEStatus;
309 inputtype = gUtils.focusedInputType;
310 is(enabled, aTest.expectedEnabled,
311 aDescription + ": " + aTest.description + ", wrong enabled state");
312 if (aTest.expectedType && !aInDesignMode) {
313 is(inputtype, aTest.expectedType,
314 aDescription + ": " + aTest.description + ", wrong input type");
315 } else if (aInDesignMode) {
316 is(inputtype, "",
317 aDescription + ": " + aTest.description + ", wrong input type")
318 }
319 }
320
321 if (!kIMEOpenSupported || enabled != gUtils.IME_STATUS_ENABLED ||
322 aTest.expectedEnabled != gUtils.IME_STATUS_ENABLED) {
323 return;
324 }
325
326 // IME Open state testing
327 testOpened(aTest, false);
328 testOpened(aTest, true);
329 }
330
331 if (kIMEEnabledSupported) {
332 // make sure there is an active element
333 document.getElementById("text").focus();
334 document.activeElement.blur();
335 is(gUtils.IMEStatus,
336 aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED,
337 aDescription + ": unexpected enabled state when no element has focus");
338 }
339
340 // Form controls except text editable elements are "disable" in normal
341 // condition, however, if they are editable, they are "enabled".
342 // XXX Probably there are some bugs: If the form controls editable, they
343 // shouldn't be focusable.
344 const kEnabledStateOnNonEditableElement =
345 (aInDesignMode || aIsEditable) ? gUtils.IME_STATUS_ENABLED :
346 gUtils.IME_STATUS_DISABLED;
347 const kEnabledStateOnPasswordField =
348 aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_PASSWORD;
349 const kEnabledStateOnReadonlyField =
350 aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED;
351 const kTests = [
352 { id: "text",
353 description: "input[type=text]",
354 focusable: !aInDesignMode,
355 expectedEnabled: gUtils.IME_STATUS_ENABLED,
356 expectedType: "text" },
357 { id: "text_readonly",
358 description: "input[type=text][readonly]",
359 focusable: !aInDesignMode,
360 expectedEnabled: kEnabledStateOnReadonlyField },
361 { id: "password",
362 description: "input[type=password]",
363 focusable: !aInDesignMode,
364 expectedEnabled: kEnabledStateOnPasswordField,
365 expectedType: "password" },
366 { id: "password_readonly",
367 description: "input[type=password][readonly]",
368 focusable: !aInDesignMode,
369 expectedEnabled: kEnabledStateOnReadonlyField },
370 { id: "checkbox",
371 description: "input[type=checkbox]",
372 focusable: !aInDesignMode,
373 focusEventNotFired: aIsEditable && !aInDesignMode,
374 expectedEnabled: kEnabledStateOnNonEditableElement },
375 { id: "radio",
376 description: "input[type=radio]",
377 focusable: !aInDesignMode,
378 focusEventNotFired: aIsEditable && !aInDesignMode,
379 expectedEnabled: kEnabledStateOnNonEditableElement },
380 { id: "submit",
381 description: "input[type=submit]",
382 focusable: !aInDesignMode,
383 expectedEnabled: kEnabledStateOnNonEditableElement },
384 { id: "reset",
385 description: "input[type=reset]",
386 focusable: !aInDesignMode,
387 expectedEnabled: kEnabledStateOnNonEditableElement },
388 { id: "file",
389 description: "input[type=file]",
390 focusable: !aInDesignMode,
391 focusEventNotFired: aIsEditable && !aInDesignMode,
392 expectedEnabled: kEnabledStateOnNonEditableElement },
393 { id: "button",
394 description: "input[type=button]",
395 focusable: !aInDesignMode,
396 expectedEnabled: kEnabledStateOnNonEditableElement },
397 { id: "image",
398 description: "input[type=image]",
399 focusable: !aInDesignMode,
400 expectedEnabled: kEnabledStateOnNonEditableElement },
401 { id: "url",
402 description: "input[type=url]",
403 focusable: !aInDesignMode,
404 expectedEnabled: gUtils.IME_STATUS_ENABLED,
405 expectedType: "url" },
406 { id: "email",
407 description: "input[type=email]",
408 focusable: !aInDesignMode,
409 expectedEnabled: gUtils.IME_STATUS_ENABLED,
410 expectedType: "email" },
411 { id: "search",
412 description: "input[type=search]",
413 focusable: !aInDesignMode,
414 expectedEnabled: gUtils.IME_STATUS_ENABLED,
415 expectedType: "search" },
416 { id: "tel",
417 description: "input[type=tel]",
418 focusable: !aInDesignMode,
419 expectedEnabled: gUtils.IME_STATUS_ENABLED,
420 expectedType: "tel" },
421 { id: "number",
422 description: "input[type=number]",
423 focusable: !aInDesignMode,
424 expectedEnabled: gUtils.IME_STATUS_ENABLED,
425 expectedType: "number" },
426
427 // form controls
428 { id: "button",
429 description: "button",
430 focusable: !aInDesignMode,
431 expectedEnabled: kEnabledStateOnNonEditableElement },
432 { id: "textarea",
433 description: "textarea",
434 focusable: !aInDesignMode,
435 expectedEnabled: gUtils.IME_STATUS_ENABLED },
436 { id: "textarea_readonly",
437 description: "textarea[readonly]",
438 focusable: !aInDesignMode,
439 expectedEnabled: kEnabledStateOnReadonlyField },
440 { id: "select",
441 description: "select (dropdown list)",
442 focusable: !aInDesignMode,
443 focusEventNotFired: aIsEditable && !aInDesignMode,
444 expectedEnabled: kEnabledStateOnNonEditableElement },
445 { id: "select_multiple",
446 description: "select (list box)",
447 focusable: !aInDesignMode,
448 focusEventNotFired: aIsEditable && !aInDesignMode,
449 expectedEnabled: kEnabledStateOnNonEditableElement },
450
451 // a element
452 { id: "a_href",
453 description: "a[href]",
454 focusable: !aIsEditable && !aInDesignMode,
455 expectedEnabled: kEnabledStateOnNonEditableElement },
456
457 // ime-mode
458 { id: "ime_mode_auto",
459 description: "input[type=text][style=\"ime-mode: auto;\"]",
460 focusable: !aInDesignMode,
461 expectedEnabled: gUtils.IME_STATUS_ENABLED },
462 { id: "ime_mode_normal",
463 description: "input[type=text][style=\"ime-mode: normal;\"]",
464 focusable: !aInDesignMode,
465 expectedEnabled: gUtils.IME_STATUS_ENABLED },
466 { id: "ime_mode_active",
467 description: "input[type=text][style=\"ime-mode: active;\"]",
468 expectedEnabled: gUtils.IME_STATUS_ENABLED,
469 focusable: !aInDesignMode,
470 changeOpened: true, expectedOpened: true },
471 { id: "ime_mode_inactive",
472 description: "input[type=text][style=\"ime-mode: inactive;\"]",
473 expectedEnabled: gUtils.IME_STATUS_ENABLED,
474 focusable: !aInDesignMode,
475 changeOpened: true, expectedOpened: false },
476 { id: "ime_mode_disabled",
477 description: "input[type=text][style=\"ime-mode: disabled;\"]",
478 focusable: !aInDesignMode,
479 expectedEnabled: kEnabledStateOnPasswordField },
480
481 { id: "ime_mode_auto_url",
482 description: "input[type=url][style=\"ime-mode: auto;\"]",
483 focusable: !aInDesignMode,
484 expectedEnabled: gUtils.IME_STATUS_ENABLED },
485 { id: "ime_mode_normal_url",
486 description: "input[type=url][style=\"ime-mode: normal;\"]",
487 focusable: !aInDesignMode,
488 expectedEnabled: gUtils.IME_STATUS_ENABLED },
489 { id: "ime_mode_active_url",
490 description: "input[type=url][style=\"ime-mode: active;\"]",
491 expectedEnabled: gUtils.IME_STATUS_ENABLED,
492 focusable: !aInDesignMode,
493 changeOpened: true, expectedOpened: true },
494 { id: "ime_mode_inactive_url",
495 description: "input[type=url][style=\"ime-mode: inactive;\"]",
496 expectedEnabled: gUtils.IME_STATUS_ENABLED,
497 focusable: !aInDesignMode,
498 changeOpened: true, expectedOpened: false },
499 { id: "ime_mode_disabled_url",
500 description: "input[type=url][style=\"ime-mode: disabled;\"]",
501 focusable: !aInDesignMode,
502 expectedEnabled: kEnabledStateOnPasswordField },
503
504 { id: "ime_mode_auto_email",
505 description: "input[type=email][style=\"ime-mode: auto;\"]",
506 focusable: !aInDesignMode,
507 expectedEnabled: gUtils.IME_STATUS_ENABLED },
508 { id: "ime_mode_normal_email",
509 description: "input[type=email][style=\"ime-mode: normal;\"]",
510 focusable: !aInDesignMode,
511 expectedEnabled: gUtils.IME_STATUS_ENABLED },
512 { id: "ime_mode_active_email",
513 description: "input[type=email][style=\"ime-mode: active;\"]",
514 expectedEnabled: gUtils.IME_STATUS_ENABLED,
515 focusable: !aInDesignMode,
516 changeOpened: true, expectedOpened: true },
517 { id: "ime_mode_inactive_email",
518 description: "input[type=email][style=\"ime-mode: inactive;\"]",
519 expectedEnabled: gUtils.IME_STATUS_ENABLED,
520 focusable: !aInDesignMode,
521 changeOpened: true, expectedOpened: false },
522 { id: "ime_mode_disabled_email",
523 description: "input[type=email][style=\"ime-mode: disabled;\"]",
524 focusable: !aInDesignMode,
525 expectedEnabled: kEnabledStateOnPasswordField },
526
527 { id: "ime_mode_auto_search",
528 description: "input[type=search][style=\"ime-mode: auto;\"]",
529 focusable: !aInDesignMode,
530 expectedEnabled: gUtils.IME_STATUS_ENABLED },
531 { id: "ime_mode_normal_search",
532 description: "input[type=search][style=\"ime-mode: normal;\"]",
533 focusable: !aInDesignMode,
534 expectedEnabled: gUtils.IME_STATUS_ENABLED },
535 { id: "ime_mode_active_search",
536 description: "input[type=search][style=\"ime-mode: active;\"]",
537 expectedEnabled: gUtils.IME_STATUS_ENABLED,
538 focusable: !aInDesignMode,
539 changeOpened: true, expectedOpened: true },
540 { id: "ime_mode_inactive_search",
541 description: "input[type=search][style=\"ime-mode: inactive;\"]",
542 expectedEnabled: gUtils.IME_STATUS_ENABLED,
543 focusable: !aInDesignMode,
544 changeOpened: true, expectedOpened: false },
545 { id: "ime_mode_disabled_search",
546 description: "input[type=search][style=\"ime-mode: disabled;\"]",
547 focusable: !aInDesignMode,
548 expectedEnabled: kEnabledStateOnPasswordField },
549
550 { id: "ime_mode_auto_tel",
551 description: "input[type=tel][style=\"ime-mode: auto;\"]",
552 focusable: !aInDesignMode,
553 expectedEnabled: gUtils.IME_STATUS_ENABLED },
554 { id: "ime_mode_normal_tel",
555 description: "input[type=tel][style=\"ime-mode: normal;\"]",
556 focusable: !aInDesignMode,
557 expectedEnabled: gUtils.IME_STATUS_ENABLED },
558 { id: "ime_mode_active_tel",
559 description: "input[type=tel][style=\"ime-mode: active;\"]",
560 expectedEnabled: gUtils.IME_STATUS_ENABLED,
561 focusable: !aInDesignMode,
562 changeOpened: true, expectedOpened: true },
563 { id: "ime_mode_inactive_tel",
564 description: "input[type=tel][style=\"ime-mode: inactive;\"]",
565 expectedEnabled: gUtils.IME_STATUS_ENABLED,
566 focusable: !aInDesignMode,
567 changeOpened: true, expectedOpened: false },
568 { id: "ime_mode_disabled_tel",
569 description: "input[type=tel][style=\"ime-mode: disabled;\"]",
570 focusable: !aInDesignMode,
571 expectedEnabled: kEnabledStateOnPasswordField },
572
573 { id: "ime_mode_auto_number",
574 description: "input[type=number][style=\"ime-mode: auto;\"]",
575 focusable: !aInDesignMode,
576 expectedEnabled: gUtils.IME_STATUS_ENABLED },
577 { id: "ime_mode_normal_number",
578 description: "input[type=number][style=\"ime-mode: normal;\"]",
579 focusable: !aInDesignMode,
580 expectedEnabled: gUtils.IME_STATUS_ENABLED },
581 { id: "ime_mode_active_number",
582 description: "input[type=number][style=\"ime-mode: active;\"]",
583 expectedEnabled: gUtils.IME_STATUS_ENABLED,
584 focusable: !aInDesignMode,
585 changeOpened: true, expectedOpened: true },
586 { id: "ime_mode_inactive_number",
587 description: "input[type=number][style=\"ime-mode: inactive;\"]",
588 expectedEnabled: gUtils.IME_STATUS_ENABLED,
589 focusable: !aInDesignMode,
590 changeOpened: true, expectedOpened: false },
591 { id: "ime_mode_disabled_number",
592 description: "input[type=number][style=\"ime-mode: disabled;\"]",
593 focusable: !aInDesignMode,
594 expectedEnabled: kEnabledStateOnPasswordField },
595
596 { id: "ime_mode_auto_p",
597 description: "input[type=password][style=\"ime-mode: auto;\"]",
598 focusable: !aInDesignMode,
599 expectedEnabled: kEnabledStateOnPasswordField },
600 { id: "ime_mode_normal_p",
601 description: "input[type=password][style=\"ime-mode: normal;\"]",
602 focusable: !aInDesignMode,
603 expectedEnabled: gUtils.IME_STATUS_ENABLED },
604 { id: "ime_mode_active_p",
605 description: "input[type=password][style=\"ime-mode: active;\"]",
606 expectedEnabled: gUtils.IME_STATUS_ENABLED,
607 focusable: !aInDesignMode,
608 changeOpened: true, expectedOpened: true },
609 { id: "ime_mode_inactive_p",
610 description: "input[type=password][style=\"ime-mode: inactive;\"]",
611 expectedEnabled: gUtils.IME_STATUS_ENABLED,
612 focusable: !aInDesignMode,
613 changeOpened: true, expectedOpened: false },
614 { id: "ime_mode_disabled_p",
615 description: "input[type=password][style=\"ime-mode: disabled;\"]",
616 focusable: !aInDesignMode,
617 expectedEnabled: kEnabledStateOnPasswordField },
618 { id: "ime_mode_auto",
619 description: "textarea[style=\"ime-mode: auto;\"]",
620 focusable: !aInDesignMode,
621 expectedEnabled: gUtils.IME_STATUS_ENABLED },
622 { id: "ime_mode_normal",
623 description: "textarea[style=\"ime-mode: normal;\"]",
624 focusable: !aInDesignMode,
625 expectedEnabled: gUtils.IME_STATUS_ENABLED },
626 { id: "ime_mode_active",
627 description: "textarea[style=\"ime-mode: active;\"]",
628 focusable: !aInDesignMode,
629 expectedEnabled: gUtils.IME_STATUS_ENABLED,
630 changeOpened: true, expectedOpened: true },
631 { id: "ime_mode_inactive",
632 description: "textarea[style=\"ime-mode: inactive;\"]",
633 focusable: !aInDesignMode,
634 expectedEnabled: gUtils.IME_STATUS_ENABLED,
635 changeOpened: true, expectedOpened: false },
636 { id: "ime_mode_disabled",
637 description: "textarea[style=\"ime-mode: disabled;\"]",
638 focusable: !aInDesignMode,
639 expectedEnabled: kEnabledStateOnPasswordField },
640
641 // HTML editors
642 { id: "contenteditableEditor",
643 description: "div[contenteditable=\"true\"]",
644 focusable: !aIsEditable && !aInDesignMode,
645 expectedEnabled: gUtils.IME_STATUS_ENABLED },
646 { id: "designModeEditor",
647 description: "designMode editor",
648 focusable: true,
649 toDesignModeEditor: true,
650 expectedEnabled: gUtils.IME_STATUS_ENABLED },
651 ];
652
653 for (var i = 0; i < kTests.length; i++) {
654 test(kTests[i]);
655 }
656 }
657
658 function runPluginTest()
659 {
660 if (!kIMEEnabledSupported) {
661 return;
662 }
663
664 if (navigator.platform.indexOf("Mac") == 0) {
665 // XXX on mac, currently, this test isn't passed because it doesn't return
666 // IME_STATUS_PLUGIN by its bug.
667 return;
668 }
669
670 var plugin = document.getElementById("plugin");
671
672 document.activeElement.blur();
673 is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
674 "runPluginTest: unexpected enabled state when no element has focus");
675
676 plugin.focus();
677 is(gUtils.IMEStatus, gUtils.IME_STATUS_PLUGIN,
678 "runPluginTest: unexpected enabled state when plugin has focus");
679
680 plugin.blur();
681 is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
682 "runPluginTest: unexpected enabled state when plugin has focus");
683
684 plugin.focus();
685 is(gUtils.IMEStatus, gUtils.IME_STATUS_PLUGIN,
686 "runPluginTest: unexpected enabled state when plugin has focus #2");
687
688 var parent = plugin.parentNode;
689 parent.removeChild(plugin);
690 is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
691 "runPluginTest: unexpected enabled state when plugin is removed from tree");
692
693 document.getElementById("text").focus();
694 is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
695 "runPluginTest: unexpected enabled state when input[type=text] has focus");
696 }
697
698 function runTypeChangingTest()
699 {
700 if (!kIMEEnabledSupported)
701 return;
702
703 const kInputControls = [
704 { id: "text",
705 type: "text", expected: gUtils.IME_STATUS_ENABLED,
706 description: "[type=\"text\"]" },
707 { id: "text_readonly",
708 type: "text", expected: gUtils.IME_STATUS_DISABLED, isReadonly: true,
709 description: "[type=\"text\"][readonly]" },
710 { id: "password",
711 type: "password", expected: gUtils.IME_STATUS_PASSWORD,
712 description: "[type=\"password\"]" },
713 { id: "password_readonly",
714 type: "password", expected: gUtils.IME_STATUS_DISABLED, isReadonly: true,
715 description: "[type=\"password\"][readonly]" },
716 { id: "checkbox",
717 type: "checkbox", expected: gUtils.IME_STATUS_DISABLED,
718 description: "[type=\"checkbox\"]" },
719 { id: "radio",
720 type: "radio", expected: gUtils.IME_STATUS_DISABLED,
721 description: "[type=\"radio\"]" },
722 { id: "submit",
723 type: "submit", expected: gUtils.IME_STATUS_DISABLED,
724 description: "[type=\"submit\"]" },
725 { id: "reset",
726 type: "reset", expected: gUtils.IME_STATUS_DISABLED,
727 description: "[type=\"reset\"]" },
728 { id: "file",
729 type: "file", expected: gUtils.IME_STATUS_DISABLED,
730 description: "[type=\"file\"]" },
731 { id: "ibutton",
732 type: "button", expected: gUtils.IME_STATUS_DISABLED,
733 description: "[type=\"button\"]" },
734 { id: "image",
735 type: "image", expected: gUtils.IME_STATUS_DISABLED,
736 description: "[type=\"image\"]" },
737 { id: "url",
738 type: "url", expected: gUtils.IME_STATUS_ENABLED,
739 description: "[type=\"url\"]" },
740 { id: "email",
741 type: "email", expected: gUtils.IME_STATUS_ENABLED,
742 description: "[type=\"email\"]" },
743 { id: "search",
744 type: "search", expected: gUtils.IME_STATUS_ENABLED,
745 description: "[type=\"search\"]" },
746 { id: "tel",
747 type: "tel", expected: gUtils.IME_STATUS_ENABLED,
748 description: "[type=\"tel\"]" },
749 { id: "number",
750 type: "number", expected: gUtils.IME_STATUS_ENABLED,
751 description: "[type=\"number\"]" },
752 { id: "ime_mode_auto",
753 type: "text", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
754 description: "[type=\"text\"][ime-mode: auto;]" },
755 { id: "ime_mode_normal",
756 type: "text", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
757 description: "[type=\"text\"][ime-mode: normal;]" },
758 { id: "ime_mode_active",
759 type: "text", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
760 description: "[type=\"text\"][ime-mode: active;]" },
761 { id: "ime_mode_inactive",
762 type: "text", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
763 description: "[type=\"text\"][ime-mode: inactive;]" },
764 { id: "ime_mode_disabled",
765 type: "text", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
766 description: "[type=\"text\"][ime-mode: disabled;]" },
767
768 { id: "ime_mode_auto_url",
769 type: "url", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
770 description: "[type=\"url\"][ime-mode: auto;]" },
771 { id: "ime_mode_normal_url",
772 type: "url", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
773 description: "[type=\"url\"][ime-mode: normal;]" },
774 { id: "ime_mode_active_url",
775 type: "url", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
776 description: "[type=\"url\"][ime-mode: active;]" },
777 { id: "ime_mode_inactive_url",
778 type: "url", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
779 description: "[type=\"url\"][ime-mode: inactive;]" },
780 { id: "ime_mode_disabled_url",
781 type: "url", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
782 description: "[type=\"url\"][ime-mode: disabled;]" },
783
784 { id: "ime_mode_auto_email",
785 type: "email", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
786 description: "[type=\"email\"][ime-mode: auto;]" },
787 { id: "ime_mode_normal_email",
788 type: "email", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
789 description: "[type=\"email\"][ime-mode: normal;]" },
790 { id: "ime_mode_active_email",
791 type: "email", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
792 description: "[type=\"email\"][ime-mode: active;]" },
793 { id: "ime_mode_inactive_email",
794 type: "email", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
795 description: "[type=\"email\"][ime-mode: inactive;]" },
796 { id: "ime_mode_disabled_email",
797 type: "email", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
798 description: "[type=\"email\"][ime-mode: disabled;]" },
799
800 { id: "ime_mode_auto_search",
801 type: "search", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
802 description: "[type=\"search\"][ime-mode: auto;]" },
803 { id: "ime_mode_normal_search",
804 type: "search", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
805 description: "[type=\"search\"][ime-mode: normal;]" },
806 { id: "ime_mode_active_search",
807 type: "search", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
808 description: "[type=\"search\"][ime-mode: active;]" },
809 { id: "ime_mode_inactive_search",
810 type: "search", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
811 description: "[type=\"search\"][ime-mode: inactive;]" },
812 { id: "ime_mode_disabled_search",
813 type: "search", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
814 description: "[type=\"search\"][ime-mode: disabled;]" },
815
816 { id: "ime_mode_auto_tel",
817 type: "tel", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
818 description: "[type=\"tel\"][ime-mode: auto;]" },
819 { id: "ime_mode_normal_tel",
820 type: "tel", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
821 description: "[type=\"tel\"][ime-mode: normal;]" },
822 { id: "ime_mode_active_tel",
823 type: "tel", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
824 description: "[type=\"tel\"][ime-mode: active;]" },
825 { id: "ime_mode_inactive_tel",
826 type: "tel", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
827 description: "[type=\"tel\"][ime-mode: inactive;]" },
828 { id: "ime_mode_disabled_tel",
829 type: "tel", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
830 description: "[type=\"tel\"][ime-mode: disabled;]" },
831
832 { id: "ime_mode_auto_number",
833 type: "number", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
834 description: "[type=\"number\"][ime-mode: auto;]" },
835 { id: "ime_mode_normal_number",
836 type: "number", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
837 description: "[type=\"number\"][ime-mode: normal;]" },
838 { id: "ime_mode_active_number",
839 type: "number", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
840 description: "[type=\"number\"][ime-mode: active;]" },
841 { id: "ime_mode_inactive_number",
842 type: "number", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
843 description: "[type=\"number\"][ime-mode: inactive;]" },
844 { id: "ime_mode_disabled_number",
845 type: "number", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
846 description: "[type=\"number\"][ime-mode: disabled;]" },
847
848 { id: "ime_mode_auto_p",
849 type: "password", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
850 description: "[type=\"password\"][ime-mode: auto;]" },
851 { id: "ime_mode_normal_p",
852 type: "password", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
853 description: "[type=\"password\"][ime-mode: normal;]" },
854 { id: "ime_mode_active_p",
855 type: "password", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
856 description: "[type=\"password\"][ime-mode: active;]" },
857 { id: "ime_mode_inactive_p",
858 type: "password", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
859 description: "[type=\"password\"][ime-mode: inactive;]" },
860 { id: "ime_mode_disabled_p",
861 type: "password", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
862 description: "[type=\"password\"][ime-mode: disabled;]" }
863 ];
864
865 const kInputTypes = [
866 { type: "", expected: gUtils.IME_STATUS_ENABLED },
867 { type: "text", expected: gUtils.IME_STATUS_ENABLED },
868 { type: "password", expected: gUtils.IME_STATUS_PASSWORD },
869 { type: "checkbox", expected: gUtils.IME_STATUS_DISABLED },
870 { type: "radio", expected: gUtils.IME_STATUS_DISABLED },
871 { type: "submit", expected: gUtils.IME_STATUS_DISABLED },
872 { type: "reset", expected: gUtils.IME_STATUS_DISABLED },
873 { type: "file", expected: gUtils.IME_STATUS_DISABLED },
874 { type: "button", expected: gUtils.IME_STATUS_DISABLED },
875 { type: "image", expected: gUtils.IME_STATUS_DISABLED },
876 { type: "url", expected: gUtils.IME_STATUS_ENABLED },
877 { type: "email", expected: gUtils.IME_STATUS_ENABLED },
878 { type: "search", expected: gUtils.IME_STATUS_ENABLED },
879 { type: "tel", expected: gUtils.IME_STATUS_ENABLED },
880 { type: "number", expected: gUtils.IME_STATUS_ENABLED }
881 ];
882
883 function getExpectedIMEEnabled(aNewType, aInputControl)
884 {
885 if (aNewType.expected == gUtils.IME_STATUS_DISABLED ||
886 aInputControl.isReadonly)
887 return gUtils.IME_STATUS_DISABLED;
888 return aInputControl.imeMode ? aInputControl.expected : aNewType.expected;
889 }
890
891 const kOpenedState = [ true, false ];
892
893 for (var i = 0; i < kOpenedState.length; i++) {
894 const kOpened = kOpenedState[i];
895 for (var j = 0; j < kInputControls.length; j++) {
896 const kInput = kInputControls[j];
897 var e = document.getElementById(kInput.id);
898 e.focus();
899 for (var k = 0; k < kInputTypes.length; k++) {
900 const kType = kInputTypes[k];
901 var typeChangingDescription =
902 "\"" + e.getAttribute("type") + "\" to \"" + kInput.type + "\"";
903 e.setAttribute("type", kInput.type);
904 is(gUtils.IMEStatus, kInput.expected,
905 "type attr changing test (IMEStatus): " + typeChangingDescription +
906 " (" + kInput.description + ")");
907 is(gUtils.focusedInputType, kInput.type,
908 "type attr changing test (type): " + typeChangingDescription +
909 " (" + kInput.description + ")");
910
911 const kTestOpenState = kIMEOpenSupported &&
912 gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED &&
913 getExpectedIMEEnabled(kType, kInput) == gUtils.IME_STATUS_ENABLED;
914 if (kTestOpenState) {
915 gUtils.IMEIsOpen = kOpened;
916 }
917
918 typeChangingDescription =
919 "\"" + e.getAttribute("type") + "\" to \"" + kType.type + "\"";
920 if (kType.type != "")
921 e.setAttribute("type", kType.type);
922 else
923 e.removeAttribute("type");
924
925 is(gUtils.IMEStatus, getExpectedIMEEnabled(kType, kInput),
926 "type attr changing test (IMEStatus): " + typeChangingDescription +
927 " (" + kInput.description + ")");
928 is(gUtils.focusedInputType, kType.type,
929 "type attr changing test (type): " + typeChangingDescription +
930 " (" + kInput.description + ")");
931 if (kTestOpenState && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
932 is(gUtils.IMEIsOpen, kOpened,
933 "type attr changing test (open state is changed): " +
934 typeChangingDescription + " (" + kInput.description + ")");
935 }
936 }
937 // reset the type to default
938 e.setAttribute("type", kInput.type);
939 }
940 if (!kIMEOpenSupported)
941 break;
942 }
943 }
944
945 function runReadonlyChangingTest()
946 {
947 if (!kIMEEnabledSupported)
948 return;
949
950 const kInputControls = [
951 { id: "text",
952 type: "text", expected: gUtils.IME_STATUS_ENABLED },
953 { id: "password",
954 type: "password", expected: gUtils.IME_STATUS_PASSWORD },
955 { id: "url",
956 type: "url", expected: gUtils.IME_STATUS_ENABLED },
957 { id: "email",
958 type: "email", expected: gUtils.IME_STATUS_ENABLED },
959 { id: "search",
960 type: "search", expected: gUtils.IME_STATUS_ENABLED },
961 { id: "tel",
962 type: "tel", expected: gUtils.IME_STATUS_ENABLED },
963 { id: "number",
964 type: "number", expected: gUtils.IME_STATUS_ENABLED },
965 { id: "textarea",
966 type: "textarea", expected: gUtils.IME_STATUS_ENABLED }
967 ];
968 const kOpenedState = [ true, false ];
969
970 for (var i = 0; i < kOpenedState.length; i++) {
971 const kOpened = kOpenedState[i];
972 for (var j = 0; j < kInputControls.length; j++) {
973 const kInput = kInputControls[j];
974 var e = document.getElementById(kInput.id);
975 e.focus();
976 if (kIMEOpenSupported && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
977 gUtils.IMEIsOpen = kOpened;
978 }
979 e.setAttribute("readonly", "readonly");
980 is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
981 "readonly attr setting test: type=" + kInput.type);
982 e.removeAttribute("readonly");
983 is(gUtils.IMEStatus, kInput.expected,
984 "readonly attr removing test: type=" + kInput.type);
985 if (kIMEOpenSupported && gUtils.IMEStatus == gUtils.IME_STATUS_ENABLED) {
986 is(gUtils.IMEIsOpen, kOpened,
987 "readonly attr removing test (open state is changed): type=" +
988 kInput.type);
989 }
990 }
991 if (!kIMEOpenSupported)
992 break;
993 }
994 }
995
996 function runComplexContenteditableTests()
997 {
998 if (!kIMEEnabledSupported) {
999 return;
1000 }
1001
1002 var description = "runReadonlyChangingOnContenteditable: ";
1003
1004 var container = document.getElementById("display");
1005 var button = document.getElementById("button");
1006
1007 // the editor has focus directly.
1008 container.setAttribute("contenteditable", "true");
1009 container.focus();
1010
1011 is(gFM.focusedElement, container,
1012 description + "The editor doesn't get focus");
1013 is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
1014 description + "IME isn't enabled on HTML editor");
1015 const kReadonly =
1016 Components.interfaces.nsIPlaintextEditor.eEditorReadonlyMask;
1017 var editor =
1018 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
1019 getInterface(Components.interfaces.nsIWebNavigation).
1020 QueryInterface(Components.interfaces.nsIDocShell).editor;
1021 var flags = editor.flags;
1022 editor.flags = flags | kReadonly;
1023 is(gFM.focusedElement, container,
1024 description + "The editor loses focus by flag change");
1025 is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
1026 description + "IME is still enabled on readonly HTML editor");
1027 editor.flags = flags;
1028 is(gFM.focusedElement, container,
1029 description + "The editor loses focus by flag change #2");
1030 is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
1031 description + "IME is still disabled, the editor isn't readonly now");
1032 container.removeAttribute("contenteditable");
1033 todo_is(gFM.focusedElement, null,
1034 description + "The container still has focus, the editor has been no editable");
1035 todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
1036 description + "IME is still enabled on the editor, the editor has been no editable");
1037
1038 // a button which is in the editor has focus
1039 button.focus();
1040 is(gFM.focusedElement, button,
1041 description + "The button doesn't get focus");
1042 is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
1043 description + "IME is enabled on the button");
1044 container.setAttribute("contenteditable", "true");
1045 is(gFM.focusedElement, button,
1046 description + "The button loses focus, the container is editable now");
1047 todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
1048 description + "IME is still disabled on the button, the container is editable now");
1049 editor =
1050 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
1051 getInterface(Components.interfaces.nsIWebNavigation).
1052 QueryInterface(Components.interfaces.nsIDocShell).editor;
1053 flags = editor.flags;
1054 editor.flags = flags | kReadonly;
1055 is(gFM.focusedElement, button,
1056 description + "The button loses focus by changing editor flags");
1057 is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
1058 description + "IME is still enabled on the button, the container is readonly now");
1059 editor.flags = flags;
1060 is(gFM.focusedElement, button,
1061 description + "The button loses focus by changing editor flags #2");
1062 is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
1063 description + "IME is still disabled on the button, the container isn't readonly now");
1064 container.removeAttribute("contenteditable");
1065 is(gFM.focusedElement, button,
1066 description + "The button loses focus, the container has been no editable");
1067 todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
1068 description + "IME is still enabled on the button, the container has been no editable");
1069
1070 description = "testOnIndependentEditor: ";
1071 function testOnIndependentEditor(aEditor, aEditorDescription)
1072 {
1073 var isReadonly = aEditor.readOnly;
1074 var expectedState =
1075 aEditor.readOnly ? gUtils.IME_STATUS_DISABLED : gUtils.IME_STATUS_ENABLED;
1076 var unexpectedStateDescription =
1077 expectedState != gUtils.IME_STATUS_ENABLED ? "enabled" : "disabled";
1078 aEditor.focus();
1079 is(gFM.focusedElement, aEditor,
1080 description + "The " + aEditorDescription + " doesn't get focus");
1081 is(gUtils.IMEStatus, expectedState,
1082 description + "IME is " + unexpectedStateDescription +
1083 " on the " + aEditorDescription);
1084 container.setAttribute("contenteditable", "true");
1085 is(gFM.focusedElement, aEditor,
1086 description + "The " + aEditorDescription +
1087 " loses focus, the container is editable now");
1088 is(gUtils.IMEStatus, expectedState,
1089 description + "IME becomes " + unexpectedStateDescription +
1090 " on the " + aEditorDescription + ", the container is editable now");
1091 editor =
1092 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
1093 getInterface(Components.interfaces.nsIWebNavigation).
1094 QueryInterface(Components.interfaces.nsIDocShell).editor;
1095 flags = editor.flags;
1096 editor.flags = flags | kReadonly;
1097 is(gFM.focusedElement, aEditor,
1098 description + "The " + aEditorDescription +
1099 " loses focus by changing editor flags");
1100 is(gUtils.IMEStatus, expectedState,
1101 description + "IME becomes " + unexpectedStateDescription + " on the " +
1102 aEditorDescription + ", the container is readonly now");
1103 editor.flags = flags;
1104 is(gFM.focusedElement, aEditor,
1105 description + "The " + aEditorDescription +
1106 " loses focus by changing editor flags #2");
1107 is(gUtils.IMEStatus, expectedState,
1108 description + "IME becomes " + unexpectedStateDescription + " on the " +
1109 aEditorDescription + ", the container isn't readonly now");
1110 container.removeAttribute("contenteditable");
1111 is(gFM.focusedElement, aEditor,
1112 description + "The " + aEditorDescription +
1113 " loses focus, the container has been no editable");
1114 is(gUtils.IMEStatus, expectedState,
1115 description + "IME becomes " + unexpectedStateDescription + " on the " +
1116 aEditorDescription + ", the container has been no editable");
1117 }
1118
1119 // a textarea which is in the editor has focus
1120 testOnIndependentEditor(document.getElementById("textarea"),
1121 "textarea");
1122 // a readonly textarea which is in the editor has focus
1123 testOnIndependentEditor(document.getElementById("textarea_readonly"),
1124 "textarea[readonly]");
1125 // an input field which is in the editor has focus
1126 testOnIndependentEditor(document.getElementById("text"),
1127 "input[type=\"text\"]");
1128 // a readonly input field which is in the editor has focus
1129 testOnIndependentEditor(document.getElementById("text_readonly"),
1130 "input[type=\"text\"][readonly]");
1131
1132 description = "testOnOutsideOfEditor: ";
1133 function testOnOutsideOfEditor(aFocusNode, aFocusNodeDescription, aEditor)
1134 {
1135 if (aFocusNode) {
1136 aFocusNode.focus();
1137 is(gFM.focusedElement, aFocusNode,
1138 description + "The " + aFocusNodeDescription + " doesn't get focus");
1139 } else {
1140 if (document.activeElement) {
1141 document.activeElement.blur();
1142 }
1143 is(gFM.focusedElement, null,
1144 description + "Unexpected element has focus");
1145 }
1146 var expectedState =
1147 aFocusNode ? gUtils.IMEStatus : gUtils.IME_STATUS_DISABLED;
1148 var unexpectedStateDescription =
1149 expectedState != gUtils.IME_STATUS_ENABLED ? "enabled" : "disabled";
1150
1151 aEditor.setAttribute("contenteditable", "true");
1152 is(gFM.focusedElement, aFocusNode,
1153 description + "The " + aFocusNodeDescription +
1154 " loses focus, a HTML editor is editable now");
1155 is(gUtils.IMEStatus, expectedState,
1156 description + "IME becomes " + unexpectedStateDescription +
1157 " on the " + aFocusNodeDescription +
1158 ", the HTML editor is editable now");
1159 editor =
1160 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
1161 getInterface(Components.interfaces.nsIWebNavigation).
1162 QueryInterface(Components.interfaces.nsIDocShell).editor;
1163 flags = editor.flags;
1164 editor.flags = flags | kReadonly;
1165 is(gFM.focusedElement, aFocusNode,
1166 description + aFocusNodeDescription +
1167 " loses focus by changing HTML editor flags");
1168 is(gUtils.IMEStatus, expectedState,
1169 description + "IME becomes " + unexpectedStateDescription + " on " +
1170 aFocusNodeDescription + ", the HTML editor is readonly now");
1171 editor.flags = flags;
1172 is(gFM.focusedElement, aFocusNode,
1173 description + aFocusNodeDescription +
1174 " loses focus by changing HTML editor flags #2");
1175 is(gUtils.IMEStatus, expectedState,
1176 description + "IME becomes " + unexpectedStateDescription + " on " +
1177 aFocusNodeDescription + ", the HTML editor isn't readonly now");
1178 container.removeAttribute("contenteditable");
1179 is(gFM.focusedElement, aFocusNode,
1180 description + aFocusNodeDescription +
1181 " loses focus, the HTML editor has been no editable");
1182 is(gUtils.IMEStatus, expectedState,
1183 description + "IME becomes " + unexpectedStateDescription + " on " +
1184 aFocusNodeDescription + ", the HTML editor has been no editable");
1185 }
1186
1187 var div = document.getElementById("contenteditableEditor");
1188 // a textarea which is outside of the editor has focus
1189 testOnOutsideOfEditor(document.getElementById("textarea"), "textarea", div);
1190 // a readonly textarea which is outside of the editor has focus
1191 testOnOutsideOfEditor(document.getElementById("textarea_readonly"),
1192 "textarea[readonly]", div);
1193 // an input field which is outside of the editor has focus
1194 testOnOutsideOfEditor(document.getElementById("text"),
1195 "input[type=\"text\"]", div);
1196 // a readonly input field which outside of the editor has focus
1197 testOnOutsideOfEditor(document.getElementById("text_readonly"),
1198 "input[type=\"text\"][readonly]", div);
1199 // a readonly input field which outside of the editor has focus
1200 testOnOutsideOfEditor(document.getElementById("button"), "button", div);
1201 // nobody has focus.
1202 testOnOutsideOfEditor(null, "nobody", div);
1203 }
1204
1205 function runEditorFlagChangeTests()
1206 {
1207 if (!kIMEEnabledSupported) {
1208 return;
1209 }
1210
1211 var description = "runEditorFlagChangeTests: ";
1212
1213 var container = document.getElementById("display");
1214
1215 // the editor has focus directly.
1216 container.setAttribute("contenteditable", "true");
1217 container.focus();
1218
1219 is(gFM.focusedElement, container,
1220 description + "The editor doesn't get focus");
1221 is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
1222 description + "IME isn't enabled on HTML editor");
1223 const kIMEStateChangeFlags =
1224 Components.interfaces.nsIPlaintextEditor.eEditorPasswordMask |
1225 Components.interfaces.nsIPlaintextEditor.eEditorReadonlyMask |
1226 Components.interfaces.nsIPlaintextEditor.eEditorDisabledMask;
1227 var editor =
1228 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
1229 getInterface(Components.interfaces.nsIWebNavigation).
1230 QueryInterface(Components.interfaces.nsIDocShell).editor;
1231 var editorIMESupport =
1232 editor.QueryInterface(Components.interfaces.nsIEditorIMESupport);
1233 var flags = editor.flags;
1234
1235 // start composition
1236 synthesizeComposition({ type: "compositionstart" });
1237
1238 // input characters
1239 synthesizeComposition({ type: "compositionupdate",
1240 data: "\u3078\u3093\u3057\u3093" });
1241 synthesizeText(
1242 { "composition":
1243 { "string": "\u3078\u3093\u3057\u3093",
1244 "clauses":
1245 [
1246 { "length": 4, "attr": COMPOSITION_ATTR_RAWINPUT }
1247 ]
1248 },
1249 "caret": { "start": 4, "length": 0 }
1250 });
1251
1252 editor.flags &= ~kIMEStateChangeFlags;
1253 ok(editorIMESupport.composing,
1254 description + "#1 IME composition was committed unexpectedly");
1255 is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
1256 description + "#1 IME isn't enabled on HTML editor");
1257
1258 editor.flags |= ~kIMEStateChangeFlags;
1259 ok(editorIMESupport.composing,
1260 description + "#2 IME composition was committed unexpectedly");
1261 is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
1262 description + "#2 IME isn't enabled on HTML editor");
1263
1264 editor.flags = flags;
1265 ok(editorIMESupport.composing,
1266 description + "#3 IME composition was committed unexpectedly");
1267 is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
1268 description + "#3 IME isn't enabled on HTML editor");
1269
1270 // cancel the composition
1271 synthesizeComposition({ type: "compositionupdate", data: "" });
1272 synthesizeText(
1273 { "composition":
1274 { "string": "",
1275 "clauses":
1276 [
1277 { "length": 0, "attr": 0 }
1278 ]
1279 },
1280 "caret": { "start": 0, "length": 0 }
1281 });
1282
1283 synthesizeComposition({ type: "compositionend", data: "" });
1284
1285 container.removeAttribute("contenteditable");
1286 }
1287
1288 function runEditableSubframeTests()
1289 {
1290 window.open("window_imestate_iframes.html", "_blank",
1291 "width=600,height=600");
1292 }
1293
1294 function runTestPasswordFieldOnDialog()
1295 {
1296 if (!kIMEEnabledSupported) {
1297 return;
1298 }
1299
1300 if (document.activeElement) {
1301 document.activeElement.blur();
1302 }
1303
1304 var dialog;
1305
1306 function WindowObserver()
1307 {
1308 Components.classes["@mozilla.org/observer-service;1"].
1309 getService(Components.interfaces.nsIObserverService).
1310 addObserver(this, "domwindowopened", false);
1311 }
1312
1313 WindowObserver.prototype = {
1314 QueryInterface: function (iid)
1315 {
1316 if (iid.equals(Components.interfaces.nsIObserver) ||
1317 iid.equals(Components.interfaces.nsISupports)) {
1318 return this;
1319 }
1320 },
1321
1322 observe: function (subject, topic, data)
1323 {
1324 if (topic === "domwindowopened") {
1325 ok(true, "dialog window is created");
1326 dialog = subject.QueryInterface(Components.interfaces.nsIDOMWindow);
1327 dialog.addEventListener("load", onPasswordDialogLoad, false);
1328 }
1329 }
1330 };
1331
1332 var observer = new WindowObserver();
1333 var arg1 = new Object(), arg2 = new Object();
1334 Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
1335 getService(Components.interfaces.nsIPromptService).
1336 promptPassword(window, "title", "text", arg1, "msg", arg2);
1337
1338 ok(true, "password dialog was closed");
1339
1340 Components.classes["@mozilla.org/observer-service;1"].
1341 getService(Components.interfaces.nsIObserverService).
1342 removeObserver(observer, "domwindowopened");
1343
1344 var passwordField;
1345
1346 function onPasswordDialogLoad()
1347 {
1348 ok(true, "onPasswordDialogLoad is called");
1349 dialog.removeEventListener("load", onPasswordDialogLoad, false);
1350 passwordField = dialog.document.getElementById("password1Textbox");
1351 passwordField.addEventListener("focus", onPasswordFieldFocus, false);
1352 }
1353
1354 function onPasswordFieldFocus()
1355 {
1356 ok(true, "onPasswordFieldFocus is called");
1357 passwordField.removeEventListener("focus", onPasswordFieldFocus, false);
1358 var utils = dialog.
1359 QueryInterface(Components.interfaces.nsIInterfaceRequestor).
1360 getInterface(Components.interfaces.nsIDOMWindowUtils);
1361 is(utils.IMEStatus, utils.IME_STATUS_PASSWORD,
1362 "IME isn't disabled on a password field of password dialog");
1363 synthesizeKey("VK_ESCAPE", { }, dialog);
1364 }
1365 }
1366
1367 // Bug 580388 and bug 808287
1368 function runEditorReframeTests(aCallback)
1369 {
1370 if (document.activeElement) {
1371 document.activeElement.blur();
1372 }
1373
1374 var input = document.getElementById("text");
1375 input.focus();
1376 input.style.overflow = "visible";
1377
1378 var mozIMEFocusIn = 0;
1379 var mozIMEFocusOut = 0;
1380 var IMEHasFocus = false;
1381
1382 var handler = function (aEvent) {
1383 switch (aEvent.type) {
1384 case "MozIMEFocusIn":
1385 mozIMEFocusIn++;
1386 IMEHasFocus = true;
1387 break;
1388 case "MozIMEFocusOut":
1389 mozIMEFocusOut++;
1390 IMEHasFocus = false;
1391 break;
1392 }
1393 };
1394
1395 var onInput = function (aEvent) {
1396 aEvent.target.style.overflow = "hidden";
1397 }
1398
1399 document.addEventListener("MozIMEFocusIn", handler, true);
1400 document.addEventListener("MozIMEFocusOut", handler, true);
1401 input.addEventListener("input", onInput, true);
1402
1403 sendChar("a");
1404
1405 hitEventLoop(function () {
1406 ok(mozIMEFocusOut > 0, "runEditorReframeTests(): IME focus must be lost at reframing");
1407 ok(mozIMEFocusIn > 0, "runEditorReframeTests(): IME focus must be restored after reframing");
1408 ok(IMEHasFocus, "runEditorReframeTests(): IME must have focus after reframing");
1409
1410 var focusQueryHandler = function(aEvent) {
1411 // Perform a style change and query during focus to trigger reframing
1412 input.style.overflow = "visible";
1413 synthesizeQuerySelectedText();
1414 }
1415 document.addEventListener("MozIMEFocusIn", focusQueryHandler, true);
1416 mozIMEFocusIn = mozIMEFocusOut = 0;
1417
1418 input.blur();
1419 input.focus();
1420 sendChar("a");
1421
1422 hitEventLoop(function() {
1423 ok(IMEHasFocus, "runEditorReframeTests(): IME must have focus after reframing during focus");
1424 ok(mozIMEFocusOut == mozIMEFocusIn, "runEditorReframeTests(): IME focus/blur (" + mozIMEFocusIn + "/" + mozIMEFocusOut + ") must match after reframing during focus");
1425
1426 document.removeEventListener("MozIMEFocusIn", focusQueryHandler, true);
1427 document.removeEventListener("MozIMEFocusIn", handler, true);
1428 document.removeEventListener("MozIMEFocusOut", handler, true);
1429 input.removeEventListener("input", onInput, true);
1430
1431 input.style.overflow = "visible";
1432 input.value = "";
1433
1434 hitEventLoop(aCallback, 20);
1435 }, 20);
1436 }, 20);
1437 }
1438
1439 function runTests()
1440 {
1441 if (!kIMEEnabledSupported && !kIMEOpenSupported)
1442 return;
1443
1444 SpecialPowers.setBoolPref("test.IME", true);
1445
1446 // test for normal contents.
1447 runBasicTest(false, false, "Testing of normal contents");
1448
1449 // test for plugin contents
1450 runPluginTest();
1451
1452 var container = document.getElementById("display");
1453 // test for contentEditable="true"
1454 container.setAttribute("contenteditable", "true");
1455 runBasicTest(true, false, "Testing [contentEditable=\"true\"]");
1456
1457 // test for contentEditable="false"
1458 container.setAttribute("contenteditable", "false");
1459 runBasicTest(false, false, "Testing [contentEditable=\"false\"]");
1460
1461 // test for removing contentEditable
1462 container.setAttribute("contenteditable", "true");
1463 container.removeAttribute("contenteditable");
1464 runBasicTest(false, false, "Testing after contentEditable to be removed");
1465
1466 // test designMode
1467 document.designMode = "on";
1468 runBasicTest(true, true, "Testing designMode=\"on\"");
1469 document.designMode = "off";
1470 document.getElementById("text").focus();
1471 runBasicTest(false, false, "Testing designMode=\"off\"");
1472
1473 // changing input[type] values
1474 // XXX currently, type attribute changing doesn't work fine. bug 559728.
1475 // runTypeChangingTest();
1476
1477 // changing readonly attribute
1478 runReadonlyChangingTest();
1479
1480 // complex contenteditable editor's tests
1481 runComplexContenteditableTests();
1482
1483 // test whether the IME state and composition are not changed unexpectedly
1484 runEditorFlagChangeTests();
1485
1486 // test password field on dialog
1487 // XXX temporary disable against failure
1488 //runTestPasswordFieldOnDialog();
1489
1490 // Asynchronous tests
1491 runEditorReframeTests(function () {
1492 // This will call onFinish(), so, this test must be the last.
1493 runEditableSubframeTests();
1494 });
1495 }
1496
1497 function onFinish()
1498 {
1499 SpecialPowers.clearUserPref("test.IME");
1500 SimpleTest.finish();
1501 }
1502
1503 </script>
1504 </body>
1505
1506 </html>

mercurial