Bug 1874684 - Part 17: Fix uninitialised variable warnings from clang-tidy. r=allstarschh
[gecko.git] / editor / libeditor / tests / test_htmleditor_keyevent_handling.html
blob6fb79212e2ed309b1e7c73b013bb3a7a6ea7dffe
1 <html>
2 <head>
3 <title>Test for key event handler of HTML editor</title>
4 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
5 <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
6 <link rel="stylesheet" type="text/css"
7 href="chrome://mochikit/content/tests/SimpleTest/test.css" />
8 </head>
9 <body>
10 <div id="display">
11 <div id="htmlEditor" contenteditable="true"><br></div>
12 </div>
13 <div id="content" style="display: none">
15 </div>
16 <pre id="test">
17 </pre>
19 <script class="testbody" type="application/javascript">
21 /* eslint-disable no-nested-ternary */
23 SimpleTest.waitForExplicitFinish();
24 SimpleTest.waitForFocus(runTests, window);
26 var htmlEditor = document.getElementById("htmlEditor");
28 const kIsMac = navigator.platform.includes("Mac");
29 const kIsWin = navigator.platform.includes("Win");
30 const kIsLinux = navigator.platform.includes("Linux") || navigator.platform.includes("SunOS");
32 async function runTests() {
33 document.execCommand("stylewithcss", false, "true");
34 document.execCommand("defaultParagraphSeparator", false, "div");
36 var fm = SpecialPowers.Services.focus;
38 var capturingPhase = { fired: false, prevented: false };
39 var bubblingPhase = { fired: false, prevented: false };
41 var listener = {
42 handleEvent: function _hv(aEvent) {
43 is(aEvent.type, "keypress", "unexpected event is handled");
44 switch (aEvent.eventPhase) {
45 case aEvent.CAPTURING_PHASE:
46 capturingPhase.fired = true;
47 capturingPhase.prevented = aEvent.defaultPrevented;
48 break;
49 case aEvent.BUBBLING_PHASE:
50 bubblingPhase.fired = true;
51 bubblingPhase.prevented = aEvent.defaultPrevented;
52 aEvent.preventDefault(); // prevent the browser default behavior
53 break;
54 default:
55 ok(false, "event is handled in unexpected phase");
60 function check(aDescription,
61 aFiredOnCapture, aFiredOnBubbling, aPreventedOnBubbling) {
62 function getDesciption(aExpected) {
63 return aDescription + (aExpected ? " wasn't " : " was ");
65 is(capturingPhase.fired, aFiredOnCapture,
66 getDesciption(aFiredOnCapture) + "fired on capture phase");
67 is(bubblingPhase.fired, aFiredOnBubbling,
68 getDesciption(aFiredOnBubbling) + "fired on bubbling phase");
70 // If the event is fired on bubbling phase and it was already prevented
71 // on capture phase, it must be prevented on bubbling phase too.
72 if (capturingPhase.prevented) {
73 todo(false, aDescription +
74 " was consumed already, so, we cannot test the editor behavior actually");
75 aPreventedOnBubbling = true;
78 is(bubblingPhase.prevented, aPreventedOnBubbling,
79 getDesciption(aPreventedOnBubbling) + "prevented on bubbling phase");
82 SpecialPowers.wrap(window).addEventListener("keypress", listener, { capture: true, mozSystemGroup: true });
83 SpecialPowers.wrap(window).addEventListener("keypress", listener, { capture: false, mozSystemGroup: true });
85 // eslint-disable-next-line complexity
86 async function doTest(
87 aElement,
88 aDescription,
89 aIsReadonly,
90 aIsTabbable,
91 aIsPlaintext
92 ) {
93 function reset(aText) {
94 capturingPhase.fired = false;
95 capturingPhase.prevented = false;
96 bubblingPhase.fired = false;
97 bubblingPhase.prevented = false;
98 aElement.innerHTML = aText;
99 var sel = window.getSelection();
100 var range = document.createRange();
101 range.setStart(aElement, aElement.childNodes.length);
102 sel.removeAllRanges();
103 sel.addRange(range);
106 function resetForIndent(aText) {
107 capturingPhase.fired = false;
108 capturingPhase.prevented = false;
109 bubblingPhase.fired = false;
110 bubblingPhase.prevented = false;
111 aElement.innerHTML = aText;
112 var sel = window.getSelection();
113 var range = document.createRange();
114 var target = document.getElementById("target").firstChild;
115 range.setStart(target, target.length);
116 sel.removeAllRanges();
117 sel.addRange(range);
120 if (document.activeElement) {
121 document.activeElement.blur();
124 aDescription += ": ";
126 aElement.focus();
127 is(SpecialPowers.unwrap(fm.focusedElement), aElement, aDescription + "failed to move focus");
129 // Backspace key:
130 // If native key bindings map the key combination to something, it's consumed.
131 // If editor is readonly, it doesn't consume.
132 // If editor is editable, it consumes backspace and shift+backspace.
133 // Otherwise, editor doesn't consume the event.
134 reset("");
135 synthesizeKey("KEY_Backspace");
136 check(aDescription + "Backspace", true, true, true);
138 reset("");
139 synthesizeKey("KEY_Backspace", {shiftKey: true});
140 check(aDescription + "Shift+Backspace", true, true, true);
142 reset("");
143 synthesizeKey("KEY_Backspace", {ctrlKey: true});
144 check(aDescription + "Ctrl+Backspace", true, true, aIsReadonly || kIsLinux);
146 reset("");
147 synthesizeKey("KEY_Backspace", {altKey: true});
148 check(aDescription + "Alt+Backspace", true, true, aIsReadonly || kIsMac);
150 reset("");
151 synthesizeKey("KEY_Backspace", {metaKey: true});
152 check(aDescription + "Meta+Backspace", true, true, aIsReadonly || kIsMac);
154 // Delete key:
155 // If native key bindings map the key combination to something, it's consumed.
156 // If editor is readonly, it doesn't consume.
157 // If editor is editable, delete is consumed.
158 // Otherwise, editor doesn't consume the event.
159 reset("");
160 synthesizeKey("KEY_Delete");
161 check(aDescription + "Delete", true, true, !aIsReadonly || kIsMac || kIsLinux);
163 reset("");
164 synthesizeKey("KEY_Delete", {shiftKey: true});
165 check(aDescription + "Shift+Delete", true, true, kIsMac || kIsLinux);
167 reset("");
168 synthesizeKey("KEY_Delete", {ctrlKey: true});
169 check(aDescription + "Ctrl+Delete", true, true, kIsLinux);
171 reset("");
172 synthesizeKey("KEY_Delete", {altKey: true});
173 check(aDescription + "Alt+Delete", true, true, kIsMac);
175 reset("");
176 synthesizeKey("KEY_Delete", {metaKey: true});
177 check(aDescription + "Meta+Delete", true, true, false);
179 // Return key:
180 // If editor is readonly, it doesn't consume.
181 // If editor is editable and not single line editor, it consumes Return
182 // and Shift+Return.
183 // Otherwise, editor doesn't consume the event.
184 reset("a");
185 synthesizeKey("KEY_Enter");
186 check(aDescription + "Return",
187 true, true, !aIsReadonly);
188 is(aElement.innerHTML, aIsReadonly ? "a" : "<div>a</div><br>",
189 aDescription + "Return");
191 reset("a");
192 synthesizeKey("KEY_Enter", {shiftKey: true});
193 check(aDescription + "Shift+Return",
194 true, true, !aIsReadonly);
195 is(aElement.innerHTML, aIsReadonly ? "a" : "a<br><br>",
196 aDescription + "Shift+Return");
198 reset("a");
199 synthesizeKey("KEY_Enter", {ctrlKey: true});
200 check(aDescription + "Ctrl+Return", true, true, false);
201 is(aElement.innerHTML, "a", aDescription + "Ctrl+Return");
203 reset("a");
204 synthesizeKey("KEY_Enter", {altKey: true});
205 check(aDescription + "Alt+Return", true, true, false);
206 is(aElement.innerHTML, "a", aDescription + "Alt+Return");
208 reset("a");
209 synthesizeKey("KEY_Enter", {metaKey: true});
210 check(aDescription + "Meta+Return", true, true, false);
211 is(aElement.innerHTML, "a", aDescription + "Meta+Return");
213 // Tab key:
214 // If editor is tabbable, editor doesn't consume all tab key events.
215 // Otherwise, editor consumes tab key event without any modifier keys.
216 reset("a");
217 synthesizeKey("KEY_Tab");
218 check(aDescription + "Tab",
219 true, true, !aIsTabbable && !aIsReadonly);
220 is(aElement.innerHTML,
221 (() => {
222 if (aIsTabbable || aIsReadonly) {
223 return "a";
225 if (aIsPlaintext) {
226 return "a\t";
228 return SpecialPowers.getBoolPref("editor.white_space_normalization.blink_compatible")
229 ? "a&nbsp; &nbsp;&nbsp;"
230 : "a&nbsp;&nbsp;&nbsp; <br>";
231 })(),
232 aDescription + "Tab");
233 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
234 aDescription + "focus moved unexpectedly (Tab)");
236 reset("a");
237 synthesizeKey("KEY_Tab", {shiftKey: true});
238 check(aDescription + "Shift+Tab", true, true, false);
239 is(aElement.innerHTML, "a", aDescription + "Shift+Tab");
240 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
241 aDescription + "focus moved unexpectedly (Shift+Tab)");
243 // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress
244 // event should never be fired.
245 reset("a");
246 synthesizeKey("KEY_Tab", {ctrlKey: true});
247 check(aDescription + "Ctrl+Tab", false, false, false);
248 is(aElement.innerHTML, "a", aDescription + "Ctrl+Tab");
249 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
250 aDescription + "focus moved unexpectedly (Ctrl+Tab)");
252 reset("a");
253 synthesizeKey("KEY_Tab", {altKey: true});
254 check(aDescription + "Alt+Tab", true, true, false);
255 is(aElement.innerHTML, "a", aDescription + "Alt+Tab");
256 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
257 aDescription + "focus moved unexpectedly (Alt+Tab)");
259 reset("a");
260 synthesizeKey("KEY_Tab", {metaKey: true});
261 check(aDescription + "Meta+Tab", true, true, false);
262 is(aElement.innerHTML, "a", aDescription + "Meta+Tab");
263 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
264 aDescription + "focus moved unexpectedly (Meta+Tab)");
266 // Indent/Outdent tests:
267 // UL
268 resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
269 synthesizeKey("KEY_Tab");
270 check(aDescription + "Tab on UL",
271 true, true, !aIsTabbable && !aIsReadonly);
272 is(aElement.innerHTML,
273 aIsReadonly || aIsTabbable ?
274 "<ul><li id=\"target\">ul list item</li></ul>" :
275 aIsPlaintext ? "<ul><li id=\"target\">ul list item\t</li></ul>" :
276 "<ul><ul><li id=\"target\">ul list item</li></ul></ul>",
277 aDescription + "Tab on UL");
278 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
279 aDescription + "focus moved unexpectedly (Tab on UL)");
280 synthesizeKey("KEY_Tab", {shiftKey: true});
281 check(aDescription + "Shift+Tab after Tab on UL",
282 true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
283 is(aElement.innerHTML,
284 aIsReadonly || aIsTabbable || (!aIsPlaintext) ?
285 "<ul><li id=\"target\">ul list item</li></ul>" :
286 "<ul><li id=\"target\">ul list item\t</li></ul>",
287 aDescription + "Shift+Tab after Tab on UL");
288 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
289 aDescription + "focus moved unexpectedly (Shift+Tab after Tab on UL)");
291 resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
292 synthesizeKey("KEY_Tab", {shiftKey: true});
293 check(aDescription + "Shift+Tab on UL",
294 true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
295 is(aElement.innerHTML,
296 aIsReadonly || aIsTabbable || aIsPlaintext ?
297 "<ul><li id=\"target\">ul list item</li></ul>" : "ul list item",
298 aDescription + "Shift+Tab on UL");
299 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
300 aDescription + "focus moved unexpectedly (Shift+Tab on UL)");
302 // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress
303 // event should never be fired.
304 resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
305 synthesizeKey("KEY_Tab", {ctrlKey: true});
306 check(aDescription + "Ctrl+Tab on UL", false, false, false);
307 is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>",
308 aDescription + "Ctrl+Tab on UL");
309 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
310 aDescription + "focus moved unexpectedly (Ctrl+Tab on UL)");
312 resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
313 synthesizeKey("KEY_Tab", {altKey: true});
314 check(aDescription + "Alt+Tab on UL", true, true, false);
315 is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>",
316 aDescription + "Alt+Tab on UL");
317 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
318 aDescription + "focus moved unexpectedly (Alt+Tab on UL)");
320 resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
321 synthesizeKey("KEY_Tab", {metaKey: true});
322 check(aDescription + "Meta+Tab on UL", true, true, false);
323 is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>",
324 aDescription + "Meta+Tab on UL");
325 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
326 aDescription + "focus moved unexpectedly (Meta+Tab on UL)");
328 // OL
329 resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
330 synthesizeKey("KEY_Tab");
331 check(aDescription + "Tab on OL",
332 true, true, !aIsTabbable && !aIsReadonly);
333 is(aElement.innerHTML,
334 aIsReadonly || aIsTabbable ?
335 "<ol><li id=\"target\">ol list item</li></ol>" :
336 aIsPlaintext ? "<ol><li id=\"target\">ol list item\t</li></ol>" :
337 "<ol><ol><li id=\"target\">ol list item</li></ol></ol>",
338 aDescription + "Tab on OL");
339 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
340 aDescription + "focus moved unexpectedly (Tab on OL)");
341 synthesizeKey("KEY_Tab", {shiftKey: true});
342 check(aDescription + "Shift+Tab after Tab on OL",
343 true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
344 is(aElement.innerHTML,
345 aIsReadonly || aIsTabbable || (!aIsPlaintext) ?
346 "<ol><li id=\"target\">ol list item</li></ol>" :
347 "<ol><li id=\"target\">ol list item\t</li></ol>",
348 aDescription + "Shift+Tab after Tab on OL");
349 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
350 aDescription + "focus moved unexpectedly (Shift+Tab after Tab on OL)");
352 resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
353 synthesizeKey("KEY_Tab", {shiftKey: true});
354 check(aDescription + "Shift+Tab on OL",
355 true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
356 is(aElement.innerHTML,
357 aIsReadonly || aIsTabbable || aIsPlaintext ?
358 "<ol><li id=\"target\">ol list item</li></ol>" : "ol list item",
359 aDescription + "Shfit+Tab on OL");
360 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
361 aDescription + "focus moved unexpectedly (Shift+Tab on OL)");
363 // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress
364 // event should never be fired.
365 resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
366 synthesizeKey("KEY_Tab", {ctrlKey: true});
367 check(aDescription + "Ctrl+Tab on OL", false, false, false);
368 is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>",
369 aDescription + "Ctrl+Tab on OL");
370 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
371 aDescription + "focus moved unexpectedly (Ctrl+Tab on OL)");
373 resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
374 synthesizeKey("KEY_Tab", {altKey: true});
375 check(aDescription + "Alt+Tab on OL", true, true, false);
376 is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>",
377 aDescription + "Alt+Tab on OL");
378 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
379 aDescription + "focus moved unexpectedly (Alt+Tab on OL)");
381 resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
382 synthesizeKey("KEY_Tab", {metaKey: true});
383 check(aDescription + "Meta+Tab on OL", true, true, false);
384 is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>",
385 aDescription + "Meta+Tab on OL");
386 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
387 aDescription + "focus moved unexpectedly (Meta+Tab on OL)");
389 // TD
390 resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
391 synthesizeKey("KEY_Tab");
392 check(aDescription + "Tab on TD",
393 true, true, !aIsTabbable && !aIsReadonly);
394 is(aElement.innerHTML,
395 aIsTabbable || aIsReadonly ?
396 "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>" :
397 aIsPlaintext ? "<table><tbody><tr><td id=\"target\">td\t</td></tr></tbody></table>" :
398 "<table><tbody><tr><td id=\"target\">td</td></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>",
399 aDescription + "Tab on TD");
400 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
401 aDescription + "focus moved unexpectedly (Tab on TD)");
402 synthesizeKey("KEY_Tab", {shiftKey: true});
403 check(aDescription + "Shift+Tab after Tab on TD",
404 true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
405 is(aElement.innerHTML,
406 aIsTabbable || aIsReadonly ?
407 "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>" :
408 aIsPlaintext ? "<table><tbody><tr><td id=\"target\">td\t</td></tr></tbody></table>" :
409 "<table><tbody><tr><td id=\"target\">td</td></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>",
410 aDescription + "Shift+Tab after Tab on TD");
411 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
412 aDescription + "focus moved unexpectedly (Shift+Tab after Tab on TD)");
414 resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
415 synthesizeKey("KEY_Tab", {shiftKey: true});
416 check(aDescription + "Shift+Tab on TD", true, true, false);
417 is(aElement.innerHTML,
418 "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
419 aDescription + "Shift+Tab on TD");
420 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
421 aDescription + "focus moved unexpectedly (Shift+Tab on TD)");
423 // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress
424 // event should never be fired.
425 resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
426 synthesizeKey("KEY_Tab", {ctrlKey: true});
427 check(aDescription + "Ctrl+Tab on TD", false, false, false);
428 is(aElement.innerHTML,
429 "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
430 aDescription + "Ctrl+Tab on TD");
431 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
432 aDescription + "focus moved unexpectedly (Ctrl+Tab on TD)");
434 resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
435 synthesizeKey("KEY_Tab", {altKey: true});
436 check(aDescription + "Alt+Tab on TD", true, true, false);
437 is(aElement.innerHTML,
438 "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
439 aDescription + "Alt+Tab on TD");
440 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
441 aDescription + "focus moved unexpectedly (Alt+Tab on TD)");
443 resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
444 synthesizeKey("KEY_Tab", {metaKey: true});
445 check(aDescription + "Meta+Tab on TD", true, true, false);
446 is(aElement.innerHTML,
447 "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
448 aDescription + "Meta+Tab on TD");
449 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
450 aDescription + "focus moved unexpectedly (Meta+Tab on TD)");
452 // TH
453 resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
454 synthesizeKey("KEY_Tab");
455 check(aDescription + "Tab on TH",
456 true, true, !aIsTabbable && !aIsReadonly);
457 is(aElement.innerHTML,
458 aIsTabbable || aIsReadonly ?
459 "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>" :
460 aIsPlaintext ? "<table><tbody><tr><th id=\"target\">th\t</th></tr></tbody></table>" :
461 "<table><tbody><tr><th id=\"target\">th</th></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>",
462 aDescription + "Tab on TH");
463 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
464 aDescription + "focus moved unexpectedly (Tab on TH)");
465 synthesizeKey("KEY_Tab", {shiftKey: true});
466 check(aDescription + "Shift+Tab after Tab on TH",
467 true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
468 is(aElement.innerHTML,
469 aIsTabbable || aIsReadonly ?
470 "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>" :
471 aIsPlaintext ? "<table><tbody><tr><th id=\"target\">th\t</th></tr></tbody></table>" :
472 "<table><tbody><tr><th id=\"target\">th</th></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>",
473 aDescription + "Shift+Tab after Tab on TH");
474 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
475 aDescription + "focus moved unexpectedly (Shift+Tab after Tab on TH)");
477 resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
478 synthesizeKey("KEY_Tab", {shiftKey: true});
479 check(aDescription + "Shift+Tab on TH", true, true, false);
480 is(aElement.innerHTML,
481 "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
482 aDescription + "Shift+Tab on TH");
483 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
484 aDescription + "focus moved unexpectedly (Shift+Tab on TH)");
486 // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress
487 // event should never be fired.
488 resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
489 synthesizeKey("KEY_Tab", {ctrlKey: true});
490 check(aDescription + "Ctrl+Tab on TH", false, false, false);
491 is(aElement.innerHTML,
492 "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
493 aDescription + "Ctrl+Tab on TH");
494 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
495 aDescription + "focus moved unexpectedly (Ctrl+Tab on TH)");
497 resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
498 synthesizeKey("KEY_Tab", {altKey: true});
499 check(aDescription + "Alt+Tab on TH", true, true, false);
500 is(aElement.innerHTML,
501 "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
502 aDescription + "Alt+Tab on TH");
503 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
504 aDescription + "focus moved unexpectedly (Alt+Tab on TH)");
506 resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
507 synthesizeKey("KEY_Tab", {metaKey: true});
508 check(aDescription + "Meta+Tab on TH", true, true, false);
509 is(aElement.innerHTML,
510 "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
511 aDescription + "Meta+Tab on TH");
512 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
513 aDescription + "focus moved unexpectedly (Meta+Tab on TH)");
515 // Esc key:
516 // In all cases, esc key events are not consumed
517 reset("abc");
518 synthesizeKey("KEY_Escape");
519 check(aDescription + "Esc", true, true, false);
521 reset("abc");
522 synthesizeKey("KEY_Escape", {shiftKey: true});
523 check(aDescription + "Shift+Esc", true, true, false);
525 reset("abc");
526 synthesizeKey("KEY_Escape", {ctrlKey: true});
527 check(aDescription + "Ctrl+Esc", true, true, false);
529 reset("abc");
530 synthesizeKey("KEY_Escape", {altKey: true});
531 check(aDescription + "Alt+Esc", true, true, false);
533 reset("abc");
534 synthesizeKey("KEY_Escape", {metaKey: true});
535 check(aDescription + "Meta+Esc", true, true, false);
537 // typical typing tests:
538 reset("");
539 sendString("M");
540 check(aDescription + "M", true, true, !aIsReadonly);
541 sendString("o");
542 check(aDescription + "o", true, true, !aIsReadonly);
543 sendString("z");
544 check(aDescription + "z", true, true, !aIsReadonly);
545 sendString("i");
546 check(aDescription + "i", true, true, !aIsReadonly);
547 sendString("l");
548 check(aDescription + "l", true, true, !aIsReadonly);
549 sendString("l");
550 check(aDescription + "l", true, true, !aIsReadonly);
551 sendString("a");
552 check(aDescription + "a", true, true, !aIsReadonly);
553 sendString(" ");
554 check(aDescription + "' '", true, true, !aIsReadonly);
555 is(aElement.innerHTML,
556 (() => {
557 if (aIsReadonly) {
558 return "";
560 if (aIsPlaintext) {
561 return "Mozilla ";
563 return SpecialPowers.getBoolPref("editor.white_space_normalization.blink_compatible")
564 ? "Mozilla&nbsp;"
565 : "Mozilla <br>";
566 })(),
567 aDescription + "typed \"Mozilla \"");
569 // typing non-BMP character:
570 async function test_typing_surrogate_pair(
571 aTestPerSurrogateKeyPress,
572 aTestIllFormedUTF16KeyValue = false
574 await SpecialPowers.pushPrefEnv({
575 set: [
576 ["dom.event.keypress.dispatch_once_per_surrogate_pair", !aTestPerSurrogateKeyPress],
577 ["dom.event.keypress.key.allow_lone_surrogate", aTestIllFormedUTF16KeyValue],
580 reset("");
581 let events = [];
582 function pushIntoEvents(aEvent) {
583 events.push(aEvent);
585 function getEventData(aKeyboardEventOrInputEvent) {
586 if (!aKeyboardEventOrInputEvent) {
587 return "{}";
589 switch (aKeyboardEventOrInputEvent.type) {
590 case "keydown":
591 case "keypress":
592 case "keyup":
593 return `{ type: "${aKeyboardEventOrInputEvent.type}", key="${
594 aKeyboardEventOrInputEvent.key
595 }", charCode=0x${
596 aKeyboardEventOrInputEvent.charCode.toString(16).toUpperCase()
597 } }`;
598 default:
599 return `{ type: "${aKeyboardEventOrInputEvent.type}", inputType="${
600 aKeyboardEventOrInputEvent.inputType
601 }", data="${aKeyboardEventOrInputEvent.data}" }`;
604 function getEventArrayData(aEvents) {
605 if (!aEvents.length) {
606 return "[]";
608 let result = "[\n";
609 for (const e of aEvents) {
610 result += ` ${getEventData(e)}\n`;
612 return result + "]";
614 aElement.addEventListener("keydown", pushIntoEvents);
615 aElement.addEventListener("keypress", pushIntoEvents);
616 aElement.addEventListener("keyup", pushIntoEvents);
617 aElement.addEventListener("beforeinput", pushIntoEvents);
618 aElement.addEventListener("input", pushIntoEvents);
619 synthesizeKey("\uD842\uDFB7");
620 aElement.removeEventListener("keydown", pushIntoEvents);
621 aElement.removeEventListener("keypress", pushIntoEvents);
622 aElement.removeEventListener("keyup", pushIntoEvents);
623 aElement.removeEventListener("beforeinput", pushIntoEvents);
624 aElement.removeEventListener("input", pushIntoEvents);
625 const settingDescription =
626 `aTestPerSurrogateKeyPress=${
627 aTestPerSurrogateKeyPress
628 }, aTestIllFormedUTF16KeyValue=${aTestIllFormedUTF16KeyValue}`;
629 const allowIllFormedUTF16 =
630 aTestPerSurrogateKeyPress && aTestIllFormedUTF16KeyValue;
632 check(`${aDescription}, ${settingDescription}a surrogate pair`, true, true, !aIsReadonly);
634 aElement.textContent,
635 !aIsReadonly ? "\uD842\uDFB7" : "",
636 `${aDescription}, ${settingDescription}, The typed surrogate pair should've been inserted`
638 if (aIsReadonly) {
640 getEventArrayData(events),
641 getEventArrayData(
642 aTestPerSurrogateKeyPress
644 allowIllFormedUTF16
646 { type: "keydown", key: "\uD842\uDFB7", charCode: 0 },
647 { type: "keypress", key: "\uD842", charCode: 0xD842 },
648 { type: "keypress", key: "\uDFB7", charCode: 0xDFB7 },
649 { type: "keyup", key: "\uD842\uDFB7", charCode: 0 },
652 { type: "keydown", key: "\uD842\uDFB7", charCode: 0 },
653 { type: "keypress", key: "\uD842\uDFB7", charCode: 0xD842 },
654 { type: "keypress", key: "", charCode: 0xDFB7 },
655 { type: "keyup", key: "\uD842\uDFB7", charCode: 0 },
659 { type: "keydown", key: "\uD842\uDFB7", charCode: 0 },
660 { type: "keypress", key: "\uD842\uDFB7", charCode: 0x20BB7 },
661 { type: "keyup", key: "\uD842\uDFB7", charCode: 0 },
664 `${aDescription}, ${
665 settingDescription
666 }, Typing a surrogate pair in readonly editor should not cause input events`
668 } else {
670 getEventArrayData(events),
671 getEventArrayData(
672 aTestPerSurrogateKeyPress
674 allowIllFormedUTF16
676 { type: "keydown", key: "\uD842\uDFB7", charCode: 0 },
677 { type: "keypress", key: "\uD842", charCode: 0xD842 },
678 { type: "beforeinput", data: "\uD842", inputType: "insertText" },
679 { type: "input", data: "\uD842", inputType: "insertText" },
680 { type: "keypress", key: "\uDFB7", charCode: 0xDFB7 },
681 { type: "beforeinput", data: "\uDFB7", inputType: "insertText" },
682 { type: "input", data: "\uDFB7", inputType: "insertText" },
683 { type: "keyup", key: "\uD842\uDFB7", charCode: 0 },
686 { type: "keydown", key: "\uD842\uDFB7", charCode: 0 },
687 { type: "keypress", key: "\uD842\uDFB7", charCode: 0xD842 },
688 { type: "beforeinput", data: "\uD842\uDFB7", inputType: "insertText" },
689 { type: "input", data: "\uD842\uDFB7", inputType: "insertText" },
690 { type: "keypress", key: "", charCode: 0xDFB7 },
691 { type: "keyup", key: "\uD842\uDFB7", charCode: 0 },
695 { type: "keydown", key: "\uD842\uDFB7", charCode: 0 },
696 { type: "keypress", key: "\uD842\uDFB7", charCode: 0x20BB7 },
697 { type: "beforeinput", data: "\uD842\uDFB7", inputType: "insertText" },
698 { type: "input", data: "\uD842\uDFB7", inputType: "insertText" },
699 { type: "keyup", key: "\uD842\uDFB7", charCode: 0 },
702 `${aDescription}, ${
703 settingDescription
704 }, Typing a surrogate pair in editor should cause input events`
708 await test_typing_surrogate_pair(true, true);
709 await test_typing_surrogate_pair(true, false);
710 await test_typing_surrogate_pair(false);
713 await doTest(htmlEditor, "contenteditable=\"true\"", false, true, false);
715 const nsIEditor = SpecialPowers.Ci.nsIEditor;
716 var editor = SpecialPowers.wrap(window).docShell.editor;
717 var flags = editor.flags;
718 // readonly
719 editor.flags = flags | nsIEditor.eEditorReadonlyMask;
720 await doTest(htmlEditor, "readonly HTML editor", true, true, false);
722 // non-tabbable
723 editor.flags = flags & ~(nsIEditor.eEditorAllowInteraction);
724 await doTest(htmlEditor, "non-tabbable HTML editor", false, false, false);
726 // readonly and non-tabbable
727 editor.flags =
728 (flags | nsIEditor.eEditorReadonlyMask) &
729 ~(nsIEditor.eEditorAllowInteraction);
730 await doTest(htmlEditor, "readonly and non-tabbable HTML editor",
731 true, false, false);
733 // plaintext
734 editor.flags = flags | nsIEditor.eEditorPlaintextMask;
735 await doTest(htmlEditor, "HTML editor but plaintext mode", false, true, true);
737 // plaintext and non-tabbable
738 editor.flags = (flags | nsIEditor.eEditorPlaintextMask) &
739 ~(nsIEditor.eEditorAllowInteraction);
740 await doTest(htmlEditor, "non-tabbable HTML editor but plaintext mode",
741 false, false, true);
744 // readonly and plaintext
745 editor.flags = flags | nsIEditor.eEditorPlaintextMask |
746 nsIEditor.eEditorReadonlyMask;
747 await doTest(htmlEditor, "readonly HTML editor but plaintext mode",
748 true, true, true);
750 // readonly, plaintext and non-tabbable
751 editor.flags = (flags | nsIEditor.eEditorPlaintextMask |
752 nsIEditor.eEditorReadonlyMask) &
753 ~(nsIEditor.eEditorAllowInteraction);
754 await doTest(htmlEditor, "readonly and non-tabbable HTML editor but plaintext mode",
755 true, false, true);
757 SpecialPowers.wrap(window).removeEventListener("keypress", listener, { capture: true, mozSystemGroup: true });
758 SpecialPowers.wrap(window).removeEventListener("keypress", listener, { capture: false, mozSystemGroup: true });
760 SimpleTest.finish();
763 </script>
764 </body>
766 </html>