summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/tests/test_htmleditor_keyevent_handling.html
diff options
context:
space:
mode:
Diffstat (limited to 'editor/libeditor/tests/test_htmleditor_keyevent_handling.html')
-rw-r--r--editor/libeditor/tests/test_htmleditor_keyevent_handling.html676
1 files changed, 676 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..f46e2a3767
--- /dev/null
+++ b/editor/libeditor/tests/test_htmleditor_keyevent_handling.html
@@ -0,0 +1,676 @@
+<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");
+
+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
+ 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);
+
+ reset("");
+ synthesizeKey("KEY_Backspace", {osKey: true});
+ check(aDescription + "OS+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);
+
+ reset("");
+ synthesizeKey("KEY_Delete", {osKey: true});
+ check(aDescription + "OS+Delete", true, true, kIsMac);
+
+ // 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");
+
+ reset("a");
+ synthesizeKey("KEY_Enter", {osKey: true});
+ check(aDescription + "OS+Return", true, true, false);
+ is(aElement.innerHTML, "a", aDescription + "OS+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&nbsp; &nbsp;&nbsp;"
+ : "a&nbsp;&nbsp;&nbsp; <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)");
+
+ reset("a");
+ synthesizeKey("KEY_Tab", {osKey: true});
+ check(aDescription + "OS+Tab", true, true, false);
+ is(aElement.innerHTML, "a", aDescription + "OS+Tab");
+ is(SpecialPowers.unwrap(fm.focusedElement), aElement,
+ aDescription + "focus moved unexpectedly (OS+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)");
+
+ resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
+ synthesizeKey("KEY_Tab", {osKey: true});
+ check(aDescription + "OS+Tab on UL", true, true, false);
+ is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>",
+ aDescription + "OS+Tab on UL");
+ is(SpecialPowers.unwrap(fm.focusedElement), aElement,
+ aDescription + "focus moved unexpectedly (OS+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)");
+
+ resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
+ synthesizeKey("KEY_Tab", {osKey: true});
+ check(aDescription + "OS+Tab on OL", true, true, false);
+ is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>",
+ aDescription + "OS+Tab on OL");
+ is(SpecialPowers.unwrap(fm.focusedElement), aElement,
+ aDescription + "focus moved unexpectedly (OS+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)");
+
+ resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
+ synthesizeKey("KEY_Tab", {osKey: true});
+ check(aDescription + "OS+Tab on TD", true, true, false);
+ is(aElement.innerHTML,
+ "<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
+ aDescription + "OS+Tab on TD");
+ is(SpecialPowers.unwrap(fm.focusedElement), aElement,
+ aDescription + "focus moved unexpectedly (OS+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)");
+
+ resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
+ synthesizeKey("KEY_Tab", {osKey: true});
+ check(aDescription + "OS+Tab on TH", true, true, false);
+ is(aElement.innerHTML,
+ "<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
+ aDescription + "OS+Tab on TH");
+ is(SpecialPowers.unwrap(fm.focusedElement), aElement,
+ aDescription + "focus moved unexpectedly (OS+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);
+
+ reset("abc");
+ synthesizeKey("KEY_Escape", {osKey: true});
+ check(aDescription + "OS+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&nbsp;"
+ : "Mozilla <br>";
+ })(),
+ aDescription + "typed \"Mozilla \"");
+ }
+
+ 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;
+ doTest(htmlEditor, "readonly HTML editor", true, true, false);
+
+ // non-tabbable
+ editor.flags = flags & ~(nsIEditor.eEditorAllowInteraction);
+ doTest(htmlEditor, "non-tabbable HTML editor", false, false, false);
+
+ // readonly and non-tabbable
+ editor.flags =
+ (flags | nsIEditor.eEditorReadonlyMask) &
+ ~(nsIEditor.eEditorAllowInteraction);
+ doTest(htmlEditor, "readonly and non-tabbable HTML editor",
+ true, false, false);
+
+ // plaintext
+ editor.flags = flags | nsIEditor.eEditorPlaintextMask;
+ doTest(htmlEditor, "HTML editor but plaintext mode", false, true, true);
+
+ // plaintext and non-tabbable
+ editor.flags = (flags | nsIEditor.eEditorPlaintextMask) &
+ ~(nsIEditor.eEditorAllowInteraction);
+ doTest(htmlEditor, "non-tabbable HTML editor but plaintext mode",
+ false, false, true);
+
+
+ // readonly and plaintext
+ editor.flags = flags | nsIEditor.eEditorPlaintextMask |
+ nsIEditor.eEditorReadonlyMask;
+ 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);
+ 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>