diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /editor/libeditor/tests/test_htmleditor_keyevent_handling.html | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'editor/libeditor/tests/test_htmleditor_keyevent_handling.html')
-rw-r--r-- | editor/libeditor/tests/test_htmleditor_keyevent_handling.html | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/editor/libeditor/tests/test_htmleditor_keyevent_handling.html b/editor/libeditor/tests/test_htmleditor_keyevent_handling.html new file mode 100644 index 0000000000..58666beb35 --- /dev/null +++ b/editor/libeditor/tests/test_htmleditor_keyevent_handling.html @@ -0,0 +1,766 @@ +<html> +<head> + <title>Test for key event handler of HTML editor</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +</head> +<body> +<div id="display"> + <div id="htmlEditor" contenteditable="true"><br></div> +</div> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> + +<script class="testbody" type="application/javascript"> + +/* eslint-disable no-nested-ternary */ + +SimpleTest.waitForExplicitFinish(); +SimpleTest.waitForFocus(runTests, window); + +var htmlEditor = document.getElementById("htmlEditor"); + +const kIsMac = navigator.platform.includes("Mac"); +const kIsWin = navigator.platform.includes("Win"); +const kIsLinux = navigator.platform.includes("Linux") || navigator.platform.includes("SunOS"); + +async function runTests() { + document.execCommand("stylewithcss", false, "true"); + document.execCommand("defaultParagraphSeparator", false, "div"); + + var fm = SpecialPowers.Services.focus; + + var capturingPhase = { fired: false, prevented: false }; + var bubblingPhase = { fired: false, prevented: false }; + + var listener = { + handleEvent: function _hv(aEvent) { + is(aEvent.type, "keypress", "unexpected event is handled"); + switch (aEvent.eventPhase) { + case aEvent.CAPTURING_PHASE: + capturingPhase.fired = true; + capturingPhase.prevented = aEvent.defaultPrevented; + break; + case aEvent.BUBBLING_PHASE: + bubblingPhase.fired = true; + bubblingPhase.prevented = aEvent.defaultPrevented; + aEvent.preventDefault(); // prevent the browser default behavior + break; + default: + ok(false, "event is handled in unexpected phase"); + } + }, + }; + + function check(aDescription, + aFiredOnCapture, aFiredOnBubbling, aPreventedOnBubbling) { + function getDesciption(aExpected) { + return aDescription + (aExpected ? " wasn't " : " was "); + } + is(capturingPhase.fired, aFiredOnCapture, + getDesciption(aFiredOnCapture) + "fired on capture phase"); + is(bubblingPhase.fired, aFiredOnBubbling, + getDesciption(aFiredOnBubbling) + "fired on bubbling phase"); + + // If the event is fired on bubbling phase and it was already prevented + // on capture phase, it must be prevented on bubbling phase too. + if (capturingPhase.prevented) { + todo(false, aDescription + + " was consumed already, so, we cannot test the editor behavior actually"); + aPreventedOnBubbling = true; + } + + is(bubblingPhase.prevented, aPreventedOnBubbling, + getDesciption(aPreventedOnBubbling) + "prevented on bubbling phase"); + } + + SpecialPowers.addSystemEventListener(window, "keypress", listener, true); + SpecialPowers.addSystemEventListener(window, "keypress", listener, false); + + // eslint-disable-next-line complexity + async function doTest( + aElement, + aDescription, + aIsReadonly, + aIsTabbable, + aIsPlaintext + ) { + function reset(aText) { + capturingPhase.fired = false; + capturingPhase.prevented = false; + bubblingPhase.fired = false; + bubblingPhase.prevented = false; + aElement.innerHTML = aText; + var sel = window.getSelection(); + var range = document.createRange(); + range.setStart(aElement, aElement.childNodes.length); + sel.removeAllRanges(); + sel.addRange(range); + } + + function resetForIndent(aText) { + capturingPhase.fired = false; + capturingPhase.prevented = false; + bubblingPhase.fired = false; + bubblingPhase.prevented = false; + aElement.innerHTML = aText; + var sel = window.getSelection(); + var range = document.createRange(); + var target = document.getElementById("target").firstChild; + range.setStart(target, target.length); + sel.removeAllRanges(); + sel.addRange(range); + } + + if (document.activeElement) { + document.activeElement.blur(); + } + + aDescription += ": "; + + aElement.focus(); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, aDescription + "failed to move focus"); + + // Backspace key: + // If native key bindings map the key combination to something, it's consumed. + // If editor is readonly, it doesn't consume. + // If editor is editable, it consumes backspace and shift+backspace. + // Otherwise, editor doesn't consume the event. + reset(""); + synthesizeKey("KEY_Backspace"); + check(aDescription + "Backspace", true, true, true); + + reset(""); + synthesizeKey("KEY_Backspace", {shiftKey: true}); + check(aDescription + "Shift+Backspace", true, true, true); + + reset(""); + synthesizeKey("KEY_Backspace", {ctrlKey: true}); + check(aDescription + "Ctrl+Backspace", true, true, aIsReadonly || kIsLinux); + + reset(""); + synthesizeKey("KEY_Backspace", {altKey: true}); + check(aDescription + "Alt+Backspace", true, true, aIsReadonly || kIsMac); + + reset(""); + synthesizeKey("KEY_Backspace", {metaKey: true}); + check(aDescription + "Meta+Backspace", true, true, aIsReadonly || kIsMac); + + // Delete key: + // If native key bindings map the key combination to something, it's consumed. + // If editor is readonly, it doesn't consume. + // If editor is editable, delete is consumed. + // Otherwise, editor doesn't consume the event. + reset(""); + synthesizeKey("KEY_Delete"); + check(aDescription + "Delete", true, true, !aIsReadonly || kIsMac || kIsLinux); + + reset(""); + synthesizeKey("KEY_Delete", {shiftKey: true}); + check(aDescription + "Shift+Delete", true, true, kIsMac || kIsLinux); + + reset(""); + synthesizeKey("KEY_Delete", {ctrlKey: true}); + check(aDescription + "Ctrl+Delete", true, true, kIsLinux); + + reset(""); + synthesizeKey("KEY_Delete", {altKey: true}); + check(aDescription + "Alt+Delete", true, true, kIsMac); + + reset(""); + synthesizeKey("KEY_Delete", {metaKey: true}); + check(aDescription + "Meta+Delete", true, true, false); + + // Return key: + // If editor is readonly, it doesn't consume. + // If editor is editable and not single line editor, it consumes Return + // and Shift+Return. + // Otherwise, editor doesn't consume the event. + reset("a"); + synthesizeKey("KEY_Enter"); + check(aDescription + "Return", + true, true, !aIsReadonly); + is(aElement.innerHTML, aIsReadonly ? "a" : "<div>a</div><br>", + aDescription + "Return"); + + reset("a"); + synthesizeKey("KEY_Enter", {shiftKey: true}); + check(aDescription + "Shift+Return", + true, true, !aIsReadonly); + is(aElement.innerHTML, aIsReadonly ? "a" : "a<br><br>", + aDescription + "Shift+Return"); + + reset("a"); + synthesizeKey("KEY_Enter", {ctrlKey: true}); + check(aDescription + "Ctrl+Return", true, true, false); + is(aElement.innerHTML, "a", aDescription + "Ctrl+Return"); + + reset("a"); + synthesizeKey("KEY_Enter", {altKey: true}); + check(aDescription + "Alt+Return", true, true, false); + is(aElement.innerHTML, "a", aDescription + "Alt+Return"); + + reset("a"); + synthesizeKey("KEY_Enter", {metaKey: true}); + check(aDescription + "Meta+Return", true, true, false); + is(aElement.innerHTML, "a", aDescription + "Meta+Return"); + + // Tab key: + // If editor is tabbable, editor doesn't consume all tab key events. + // Otherwise, editor consumes tab key event without any modifier keys. + reset("a"); + synthesizeKey("KEY_Tab"); + check(aDescription + "Tab", + true, true, !aIsTabbable && !aIsReadonly); + is(aElement.innerHTML, + (() => { + if (aIsTabbable || aIsReadonly) { + return "a"; + } + if (aIsPlaintext) { + return "a\t"; + } + return SpecialPowers.getBoolPref("editor.white_space_normalization.blink_compatible") + ? "a " + : "a <br>"; + })(), + aDescription + "Tab"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Tab)"); + + reset("a"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + check(aDescription + "Shift+Tab", true, true, false); + is(aElement.innerHTML, "a", aDescription + "Shift+Tab"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Shift+Tab)"); + + // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress + // event should never be fired. + reset("a"); + synthesizeKey("KEY_Tab", {ctrlKey: true}); + check(aDescription + "Ctrl+Tab", false, false, false); + is(aElement.innerHTML, "a", aDescription + "Ctrl+Tab"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Ctrl+Tab)"); + + reset("a"); + synthesizeKey("KEY_Tab", {altKey: true}); + check(aDescription + "Alt+Tab", true, true, false); + is(aElement.innerHTML, "a", aDescription + "Alt+Tab"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Alt+Tab)"); + + reset("a"); + synthesizeKey("KEY_Tab", {metaKey: true}); + check(aDescription + "Meta+Tab", true, true, false); + is(aElement.innerHTML, "a", aDescription + "Meta+Tab"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Meta+Tab)"); + + // Indent/Outdent tests: + // UL + resetForIndent("<ul><li id=\"target\">ul list item</li></ul>"); + synthesizeKey("KEY_Tab"); + check(aDescription + "Tab on UL", + true, true, !aIsTabbable && !aIsReadonly); + is(aElement.innerHTML, + aIsReadonly || aIsTabbable ? + "<ul><li id=\"target\">ul list item</li></ul>" : + aIsPlaintext ? "<ul><li id=\"target\">ul list item\t</li></ul>" : + "<ul><ul><li id=\"target\">ul list item</li></ul></ul>", + aDescription + "Tab on UL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Tab on UL)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + check(aDescription + "Shift+Tab after Tab on UL", + true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext); + is(aElement.innerHTML, + aIsReadonly || aIsTabbable || (!aIsPlaintext) ? + "<ul><li id=\"target\">ul list item</li></ul>" : + "<ul><li id=\"target\">ul list item\t</li></ul>", + aDescription + "Shift+Tab after Tab on UL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Shift+Tab after Tab on UL)"); + + resetForIndent("<ul><li id=\"target\">ul list item</li></ul>"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + check(aDescription + "Shift+Tab on UL", + true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext); + is(aElement.innerHTML, + aIsReadonly || aIsTabbable || aIsPlaintext ? + "<ul><li id=\"target\">ul list item</li></ul>" : "ul list item", + aDescription + "Shift+Tab on UL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Shift+Tab on UL)"); + + // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress + // event should never be fired. + resetForIndent("<ul><li id=\"target\">ul list item</li></ul>"); + synthesizeKey("KEY_Tab", {ctrlKey: true}); + check(aDescription + "Ctrl+Tab on UL", false, false, false); + is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>", + aDescription + "Ctrl+Tab on UL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Ctrl+Tab on UL)"); + + resetForIndent("<ul><li id=\"target\">ul list item</li></ul>"); + synthesizeKey("KEY_Tab", {altKey: true}); + check(aDescription + "Alt+Tab on UL", true, true, false); + is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>", + aDescription + "Alt+Tab on UL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Alt+Tab on UL)"); + + resetForIndent("<ul><li id=\"target\">ul list item</li></ul>"); + synthesizeKey("KEY_Tab", {metaKey: true}); + check(aDescription + "Meta+Tab on UL", true, true, false); + is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>", + aDescription + "Meta+Tab on UL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Meta+Tab on UL)"); + + // OL + resetForIndent("<ol><li id=\"target\">ol list item</li></ol>"); + synthesizeKey("KEY_Tab"); + check(aDescription + "Tab on OL", + true, true, !aIsTabbable && !aIsReadonly); + is(aElement.innerHTML, + aIsReadonly || aIsTabbable ? + "<ol><li id=\"target\">ol list item</li></ol>" : + aIsPlaintext ? "<ol><li id=\"target\">ol list item\t</li></ol>" : + "<ol><ol><li id=\"target\">ol list item</li></ol></ol>", + aDescription + "Tab on OL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Tab on OL)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + check(aDescription + "Shift+Tab after Tab on OL", + true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext); + is(aElement.innerHTML, + aIsReadonly || aIsTabbable || (!aIsPlaintext) ? + "<ol><li id=\"target\">ol list item</li></ol>" : + "<ol><li id=\"target\">ol list item\t</li></ol>", + aDescription + "Shift+Tab after Tab on OL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Shift+Tab after Tab on OL)"); + + resetForIndent("<ol><li id=\"target\">ol list item</li></ol>"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + check(aDescription + "Shift+Tab on OL", + true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext); + is(aElement.innerHTML, + aIsReadonly || aIsTabbable || aIsPlaintext ? + "<ol><li id=\"target\">ol list item</li></ol>" : "ol list item", + aDescription + "Shfit+Tab on OL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Shift+Tab on OL)"); + + // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress + // event should never be fired. + resetForIndent("<ol><li id=\"target\">ol list item</li></ol>"); + synthesizeKey("KEY_Tab", {ctrlKey: true}); + check(aDescription + "Ctrl+Tab on OL", false, false, false); + is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>", + aDescription + "Ctrl+Tab on OL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Ctrl+Tab on OL)"); + + resetForIndent("<ol><li id=\"target\">ol list item</li></ol>"); + synthesizeKey("KEY_Tab", {altKey: true}); + check(aDescription + "Alt+Tab on OL", true, true, false); + is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>", + aDescription + "Alt+Tab on OL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Alt+Tab on OL)"); + + resetForIndent("<ol><li id=\"target\">ol list item</li></ol>"); + synthesizeKey("KEY_Tab", {metaKey: true}); + check(aDescription + "Meta+Tab on OL", true, true, false); + is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>", + aDescription + "Meta+Tab on OL"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Meta+Tab on OL)"); + + // TD + resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>"); + synthesizeKey("KEY_Tab"); + check(aDescription + "Tab on TD", + true, true, !aIsTabbable && !aIsReadonly); + is(aElement.innerHTML, + aIsTabbable || aIsReadonly ? + "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>" : + aIsPlaintext ? "<table><tbody><tr><td id=\"target\">td\t</td></tr></tbody></table>" : + "<table><tbody><tr><td id=\"target\">td</td></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>", + aDescription + "Tab on TD"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Tab on TD)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + check(aDescription + "Shift+Tab after Tab on TD", + true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext); + is(aElement.innerHTML, + aIsTabbable || aIsReadonly ? + "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>" : + aIsPlaintext ? "<table><tbody><tr><td id=\"target\">td\t</td></tr></tbody></table>" : + "<table><tbody><tr><td id=\"target\">td</td></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>", + aDescription + "Shift+Tab after Tab on TD"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Shift+Tab after Tab on TD)"); + + resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + check(aDescription + "Shift+Tab on TD", true, true, false); + is(aElement.innerHTML, + "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>", + aDescription + "Shift+Tab on TD"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Shift+Tab on TD)"); + + // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress + // event should never be fired. + resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>"); + synthesizeKey("KEY_Tab", {ctrlKey: true}); + check(aDescription + "Ctrl+Tab on TD", false, false, false); + is(aElement.innerHTML, + "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>", + aDescription + "Ctrl+Tab on TD"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Ctrl+Tab on TD)"); + + resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>"); + synthesizeKey("KEY_Tab", {altKey: true}); + check(aDescription + "Alt+Tab on TD", true, true, false); + is(aElement.innerHTML, + "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>", + aDescription + "Alt+Tab on TD"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Alt+Tab on TD)"); + + resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>"); + synthesizeKey("KEY_Tab", {metaKey: true}); + check(aDescription + "Meta+Tab on TD", true, true, false); + is(aElement.innerHTML, + "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>", + aDescription + "Meta+Tab on TD"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Meta+Tab on TD)"); + + // TH + resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>"); + synthesizeKey("KEY_Tab"); + check(aDescription + "Tab on TH", + true, true, !aIsTabbable && !aIsReadonly); + is(aElement.innerHTML, + aIsTabbable || aIsReadonly ? + "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>" : + aIsPlaintext ? "<table><tbody><tr><th id=\"target\">th\t</th></tr></tbody></table>" : + "<table><tbody><tr><th id=\"target\">th</th></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>", + aDescription + "Tab on TH"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Tab on TH)"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + check(aDescription + "Shift+Tab after Tab on TH", + true, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext); + is(aElement.innerHTML, + aIsTabbable || aIsReadonly ? + "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>" : + aIsPlaintext ? "<table><tbody><tr><th id=\"target\">th\t</th></tr></tbody></table>" : + "<table><tbody><tr><th id=\"target\">th</th></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>", + aDescription + "Shift+Tab after Tab on TH"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Shift+Tab after Tab on TH)"); + + resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>"); + synthesizeKey("KEY_Tab", {shiftKey: true}); + check(aDescription + "Shift+Tab on TH", true, true, false); + is(aElement.innerHTML, + "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>", + aDescription + "Shift+Tab on TH"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Shift+Tab on TH)"); + + // Ctrl+Tab should be consumed by tabbrowser at keydown, so, keypress + // event should never be fired. + resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>"); + synthesizeKey("KEY_Tab", {ctrlKey: true}); + check(aDescription + "Ctrl+Tab on TH", false, false, false); + is(aElement.innerHTML, + "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>", + aDescription + "Ctrl+Tab on TH"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Ctrl+Tab on TH)"); + + resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>"); + synthesizeKey("KEY_Tab", {altKey: true}); + check(aDescription + "Alt+Tab on TH", true, true, false); + is(aElement.innerHTML, + "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>", + aDescription + "Alt+Tab on TH"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Alt+Tab on TH)"); + + resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>"); + synthesizeKey("KEY_Tab", {metaKey: true}); + check(aDescription + "Meta+Tab on TH", true, true, false); + is(aElement.innerHTML, + "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>", + aDescription + "Meta+Tab on TH"); + is(SpecialPowers.unwrap(fm.focusedElement), aElement, + aDescription + "focus moved unexpectedly (Meta+Tab on TH)"); + + // Esc key: + // In all cases, esc key events are not consumed + reset("abc"); + synthesizeKey("KEY_Escape"); + check(aDescription + "Esc", true, true, false); + + reset("abc"); + synthesizeKey("KEY_Escape", {shiftKey: true}); + check(aDescription + "Shift+Esc", true, true, false); + + reset("abc"); + synthesizeKey("KEY_Escape", {ctrlKey: true}); + check(aDescription + "Ctrl+Esc", true, true, false); + + reset("abc"); + synthesizeKey("KEY_Escape", {altKey: true}); + check(aDescription + "Alt+Esc", true, true, false); + + reset("abc"); + synthesizeKey("KEY_Escape", {metaKey: true}); + check(aDescription + "Meta+Esc", true, true, false); + + // typical typing tests: + reset(""); + sendString("M"); + check(aDescription + "M", true, true, !aIsReadonly); + sendString("o"); + check(aDescription + "o", true, true, !aIsReadonly); + sendString("z"); + check(aDescription + "z", true, true, !aIsReadonly); + sendString("i"); + check(aDescription + "i", true, true, !aIsReadonly); + sendString("l"); + check(aDescription + "l", true, true, !aIsReadonly); + sendString("l"); + check(aDescription + "l", true, true, !aIsReadonly); + sendString("a"); + check(aDescription + "a", true, true, !aIsReadonly); + sendString(" "); + check(aDescription + "' '", true, true, !aIsReadonly); + is(aElement.innerHTML, + (() => { + if (aIsReadonly) { + return ""; + } + if (aIsPlaintext) { + return "Mozilla "; + } + return SpecialPowers.getBoolPref("editor.white_space_normalization.blink_compatible") + ? "Mozilla " + : "Mozilla <br>"; + })(), + aDescription + "typed \"Mozilla \""); + + // typing non-BMP character: + async function test_typing_surrogate_pair( + aTestPerSurrogateKeyPress, + aTestIllFormedUTF16KeyValue = false + ) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.event.keypress.dispatch_once_per_surrogate_pair", !aTestPerSurrogateKeyPress], + ["dom.event.keypress.key.allow_lone_surrogate", aTestIllFormedUTF16KeyValue], + ], + }); + reset(""); + let events = []; + function pushIntoEvents(aEvent) { + events.push(aEvent); + } + function getEventData(aKeyboardEventOrInputEvent) { + if (!aKeyboardEventOrInputEvent) { + return "{}"; + } + switch (aKeyboardEventOrInputEvent.type) { + case "keydown": + case "keypress": + case "keyup": + return `{ type: "${aKeyboardEventOrInputEvent.type}", key="${ + aKeyboardEventOrInputEvent.key + }", charCode=0x${ + aKeyboardEventOrInputEvent.charCode.toString(16).toUpperCase() + } }`; + default: + return `{ type: "${aKeyboardEventOrInputEvent.type}", inputType="${ + aKeyboardEventOrInputEvent.inputType + }", data="${aKeyboardEventOrInputEvent.data}" }`; + } + } + function getEventArrayData(aEvents) { + if (!aEvents.length) { + return "[]"; + } + let result = "[\n"; + for (const e of aEvents) { + result += ` ${getEventData(e)}\n`; + } + return result + "]"; + } + aElement.addEventListener("keydown", pushIntoEvents); + aElement.addEventListener("keypress", pushIntoEvents); + aElement.addEventListener("keyup", pushIntoEvents); + aElement.addEventListener("beforeinput", pushIntoEvents); + aElement.addEventListener("input", pushIntoEvents); + synthesizeKey("\uD842\uDFB7"); + aElement.removeEventListener("keydown", pushIntoEvents); + aElement.removeEventListener("keypress", pushIntoEvents); + aElement.removeEventListener("keyup", pushIntoEvents); + aElement.removeEventListener("beforeinput", pushIntoEvents); + aElement.removeEventListener("input", pushIntoEvents); + const settingDescription = + `aTestPerSurrogateKeyPress=${ + aTestPerSurrogateKeyPress + }, aTestIllFormedUTF16KeyValue=${aTestIllFormedUTF16KeyValue}`; + const allowIllFormedUTF16 = + aTestPerSurrogateKeyPress && aTestIllFormedUTF16KeyValue; + + check(`${aDescription}, ${settingDescription}a surrogate pair`, true, true, !aIsReadonly); + is( + aElement.textContent, + !aIsReadonly ? "\uD842\uDFB7" : "", + `${aDescription}, ${settingDescription}, The typed surrogate pair should've been inserted` + ); + if (aIsReadonly) { + is( + getEventArrayData(events), + getEventArrayData( + aTestPerSurrogateKeyPress + ? ( + allowIllFormedUTF16 + ? [ + { type: "keydown", key: "\uD842\uDFB7", charCode: 0 }, + { type: "keypress", key: "\uD842", charCode: 0xD842 }, + { type: "keypress", key: "\uDFB7", charCode: 0xDFB7 }, + { type: "keyup", key: "\uD842\uDFB7", charCode: 0 }, + ] + : [ + { type: "keydown", key: "\uD842\uDFB7", charCode: 0 }, + { type: "keypress", key: "\uD842\uDFB7", charCode: 0xD842 }, + { type: "keypress", key: "", charCode: 0xDFB7 }, + { type: "keyup", key: "\uD842\uDFB7", charCode: 0 }, + ] + ) + : [ + { type: "keydown", key: "\uD842\uDFB7", charCode: 0 }, + { type: "keypress", key: "\uD842\uDFB7", charCode: 0x20BB7 }, + { type: "keyup", key: "\uD842\uDFB7", charCode: 0 }, + ] + ), + `${aDescription}, ${ + settingDescription + }, Typing a surrogate pair in readonly editor should not cause input events` + ); + } else { + is( + getEventArrayData(events), + getEventArrayData( + aTestPerSurrogateKeyPress + ? ( + allowIllFormedUTF16 + ? [ + { type: "keydown", key: "\uD842\uDFB7", charCode: 0 }, + { type: "keypress", key: "\uD842", charCode: 0xD842 }, + { type: "beforeinput", data: "\uD842", inputType: "insertText" }, + { type: "input", data: "\uD842", inputType: "insertText" }, + { type: "keypress", key: "\uDFB7", charCode: 0xDFB7 }, + { type: "beforeinput", data: "\uDFB7", inputType: "insertText" }, + { type: "input", data: "\uDFB7", inputType: "insertText" }, + { type: "keyup", key: "\uD842\uDFB7", charCode: 0 }, + ] + : [ + { type: "keydown", key: "\uD842\uDFB7", charCode: 0 }, + { type: "keypress", key: "\uD842\uDFB7", charCode: 0xD842 }, + { type: "beforeinput", data: "\uD842\uDFB7", inputType: "insertText" }, + { type: "input", data: "\uD842\uDFB7", inputType: "insertText" }, + { type: "keypress", key: "", charCode: 0xDFB7 }, + { type: "keyup", key: "\uD842\uDFB7", charCode: 0 }, + ] + ) + : [ + { type: "keydown", key: "\uD842\uDFB7", charCode: 0 }, + { type: "keypress", key: "\uD842\uDFB7", charCode: 0x20BB7 }, + { type: "beforeinput", data: "\uD842\uDFB7", inputType: "insertText" }, + { type: "input", data: "\uD842\uDFB7", inputType: "insertText" }, + { type: "keyup", key: "\uD842\uDFB7", charCode: 0 }, + ] + ), + `${aDescription}, ${ + settingDescription + }, Typing a surrogate pair in editor should cause input events` + ); + } + } + await test_typing_surrogate_pair(true, true); + await test_typing_surrogate_pair(true, false); + await test_typing_surrogate_pair(false); + } + + await doTest(htmlEditor, "contenteditable=\"true\"", false, true, false); + + const nsIEditor = SpecialPowers.Ci.nsIEditor; + var editor = SpecialPowers.wrap(window).docShell.editor; + var flags = editor.flags; + // readonly + editor.flags = flags | nsIEditor.eEditorReadonlyMask; + await doTest(htmlEditor, "readonly HTML editor", true, true, false); + + // non-tabbable + editor.flags = flags & ~(nsIEditor.eEditorAllowInteraction); + await doTest(htmlEditor, "non-tabbable HTML editor", false, false, false); + + // readonly and non-tabbable + editor.flags = + (flags | nsIEditor.eEditorReadonlyMask) & + ~(nsIEditor.eEditorAllowInteraction); + await doTest(htmlEditor, "readonly and non-tabbable HTML editor", + true, false, false); + + // plaintext + editor.flags = flags | nsIEditor.eEditorPlaintextMask; + await doTest(htmlEditor, "HTML editor but plaintext mode", false, true, true); + + // plaintext and non-tabbable + editor.flags = (flags | nsIEditor.eEditorPlaintextMask) & + ~(nsIEditor.eEditorAllowInteraction); + await doTest(htmlEditor, "non-tabbable HTML editor but plaintext mode", + false, false, true); + + + // readonly and plaintext + editor.flags = flags | nsIEditor.eEditorPlaintextMask | + nsIEditor.eEditorReadonlyMask; + await doTest(htmlEditor, "readonly HTML editor but plaintext mode", + true, true, true); + + // readonly, plaintext and non-tabbable + editor.flags = (flags | nsIEditor.eEditorPlaintextMask | + nsIEditor.eEditorReadonlyMask) & + ~(nsIEditor.eEditorAllowInteraction); + await doTest(htmlEditor, "readonly and non-tabbable HTML editor but plaintext mode", + true, false, true); + + SpecialPowers.removeSystemEventListener(window, "keypress", listener, true); + SpecialPowers.removeSystemEventListener(window, "keypress", listener, false); + + SimpleTest.finish(); +} + +</script> +</body> + +</html> |