Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<html>
<head>
<title>Test for key event handler of HTML editor</title>
<link rel="stylesheet" type="text/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.wrap(window).addEventListener("keypress", listener, { capture: true, mozSystemGroup: true });
SpecialPowers.wrap(window).addEventListener("keypress", listener, { capture: false, mozSystemGroup: true });
// 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&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)");
// 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&nbsp;"
: "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.wrap(window).removeEventListener("keypress", listener, { capture: true, mozSystemGroup: true });
SpecialPowers.wrap(window).removeEventListener("keypress", listener, { capture: false, mozSystemGroup: true });
SimpleTest.finish();
}
</script>
</body>
</html>