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" />
11 <div id=
"htmlEditor" contenteditable=
"true"><br></div>
13 <div id=
"content" style=
"display: none">
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 };
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;
49 case aEvent.BUBBLING_PHASE:
50 bubblingPhase.fired = true;
51 bubblingPhase.prevented = aEvent.defaultPrevented;
52 aEvent.preventDefault(); // prevent the browser default behavior
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(
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();
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();
120 if (document.activeElement) {
121 document.activeElement.blur();
124 aDescription +=
": ";
127 is(SpecialPowers.unwrap(fm.focusedElement), aElement, aDescription +
"failed to move focus");
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.
135 synthesizeKey(
"KEY_Backspace");
136 check(aDescription +
"Backspace", true, true, true);
139 synthesizeKey(
"KEY_Backspace", {shiftKey: true});
140 check(aDescription +
"Shift+Backspace", true, true, true);
143 synthesizeKey(
"KEY_Backspace", {ctrlKey: true});
144 check(aDescription +
"Ctrl+Backspace", true, true, aIsReadonly || kIsLinux);
147 synthesizeKey(
"KEY_Backspace", {altKey: true});
148 check(aDescription +
"Alt+Backspace", true, true, aIsReadonly || kIsMac);
151 synthesizeKey(
"KEY_Backspace", {metaKey: true});
152 check(aDescription +
"Meta+Backspace", true, true, aIsReadonly || kIsMac);
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.
160 synthesizeKey(
"KEY_Delete");
161 check(aDescription +
"Delete", true, true, !aIsReadonly || kIsMac || kIsLinux);
164 synthesizeKey(
"KEY_Delete", {shiftKey: true});
165 check(aDescription +
"Shift+Delete", true, true, kIsMac || kIsLinux);
168 synthesizeKey(
"KEY_Delete", {ctrlKey: true});
169 check(aDescription +
"Ctrl+Delete", true, true, kIsLinux);
172 synthesizeKey(
"KEY_Delete", {altKey: true});
173 check(aDescription +
"Alt+Delete", true, true, kIsMac);
176 synthesizeKey(
"KEY_Delete", {metaKey: true});
177 check(aDescription +
"Meta+Delete", true, true, false);
180 // If editor is readonly, it doesn't consume.
181 // If editor is editable and not single line editor, it consumes Return
183 // Otherwise, editor doesn't consume the event.
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");
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");
199 synthesizeKey(
"KEY_Enter", {ctrlKey: true});
200 check(aDescription +
"Ctrl+Return", true, true, false);
201 is(aElement.innerHTML,
"a", aDescription +
"Ctrl+Return");
204 synthesizeKey(
"KEY_Enter", {altKey: true});
205 check(aDescription +
"Alt+Return", true, true, false);
206 is(aElement.innerHTML,
"a", aDescription +
"Alt+Return");
209 synthesizeKey(
"KEY_Enter", {metaKey: true});
210 check(aDescription +
"Meta+Return", true, true, false);
211 is(aElement.innerHTML,
"a", aDescription +
"Meta+Return");
214 // If editor is tabbable, editor doesn't consume all tab key events.
215 // Otherwise, editor consumes tab key event without any modifier keys.
217 synthesizeKey(
"KEY_Tab");
218 check(aDescription +
"Tab",
219 true, true, !aIsTabbable && !aIsReadonly);
220 is(aElement.innerHTML,
222 if (aIsTabbable || aIsReadonly) {
228 return SpecialPowers.getBoolPref(
"editor.white_space_normalization.blink_compatible")
229 ?
"a "
230 :
"a <br>";
232 aDescription +
"Tab");
233 is(SpecialPowers.unwrap(fm.focusedElement), aElement,
234 aDescription +
"focus moved unexpectedly (Tab)");
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.
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)");
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)");
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:
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)");
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)");
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)");
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)");
516 // In all cases, esc key events are not consumed
518 synthesizeKey(
"KEY_Escape");
519 check(aDescription +
"Esc", true, true, false);
522 synthesizeKey(
"KEY_Escape", {shiftKey: true});
523 check(aDescription +
"Shift+Esc", true, true, false);
526 synthesizeKey(
"KEY_Escape", {ctrlKey: true});
527 check(aDescription +
"Ctrl+Esc", true, true, false);
530 synthesizeKey(
"KEY_Escape", {altKey: true});
531 check(aDescription +
"Alt+Esc", true, true, false);
534 synthesizeKey(
"KEY_Escape", {metaKey: true});
535 check(aDescription +
"Meta+Esc", true, true, false);
537 // typical typing tests:
540 check(aDescription +
"M", true, true, !aIsReadonly);
542 check(aDescription +
"o", true, true, !aIsReadonly);
544 check(aDescription +
"z", true, true, !aIsReadonly);
546 check(aDescription +
"i", true, true, !aIsReadonly);
548 check(aDescription +
"l", true, true, !aIsReadonly);
550 check(aDescription +
"l", true, true, !aIsReadonly);
552 check(aDescription +
"a", true, true, !aIsReadonly);
554 check(aDescription +
"' '", true, true, !aIsReadonly);
555 is(aElement.innerHTML,
563 return SpecialPowers.getBoolPref(
"editor.white_space_normalization.blink_compatible")
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({
576 [
"dom.event.keypress.dispatch_once_per_surrogate_pair", !aTestPerSurrogateKeyPress],
577 [
"dom.event.keypress.key.allow_lone_surrogate", aTestIllFormedUTF16KeyValue],
582 function pushIntoEvents(aEvent) {
585 function getEventData(aKeyboardEventOrInputEvent) {
586 if (!aKeyboardEventOrInputEvent) {
589 switch (aKeyboardEventOrInputEvent.type) {
593 return `{ type:
"${aKeyboardEventOrInputEvent.type}",
key=
"${
594 aKeyboardEventOrInputEvent.key
596 aKeyboardEventOrInputEvent.charCode.toString(
16).toUpperCase()
599 return `{ type:
"${aKeyboardEventOrInputEvent.type}",
inputType=
"${
600 aKeyboardEventOrInputEvent.inputType
601 }",
data=
"${aKeyboardEventOrInputEvent.data}" }`;
604 function getEventArrayData(aEvents) {
605 if (!aEvents.length) {
609 for (const e of aEvents) {
610 result += ` ${getEventData(e)}\n`;
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`
640 getEventArrayData(events),
642 aTestPerSurrogateKeyPress
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 },
666 }, Typing a surrogate pair in readonly editor should not cause input events`
670 getEventArrayData(events),
672 aTestPerSurrogateKeyPress
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 },
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;
719 editor.flags = flags | nsIEditor.eEditorReadonlyMask;
720 await doTest(htmlEditor,
"readonly HTML editor", true, true, false);
723 editor.flags = flags & ~(nsIEditor.eEditorAllowInteraction);
724 await doTest(htmlEditor,
"non-tabbable HTML editor", false, false, false);
726 // readonly and non-tabbable
728 (flags | nsIEditor.eEditorReadonlyMask) &
729 ~(nsIEditor.eEditorAllowInteraction);
730 await doTest(htmlEditor,
"readonly and non-tabbable HTML editor",
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",
744 // readonly and plaintext
745 editor.flags = flags | nsIEditor.eEditorPlaintextMask |
746 nsIEditor.eEditorReadonlyMask;
747 await doTest(htmlEditor,
"readonly HTML editor but plaintext mode",
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",
757 SpecialPowers.wrap(window).removeEventListener(
"keypress", listener, { capture: true, mozSystemGroup: true });
758 SpecialPowers.wrap(window).removeEventListener(
"keypress", listener, { capture: false, mozSystemGroup: true });