Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!DOCTYPE>
<html>
<head>
<title>Test for MozEditableElement.setUserInput()</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<div id="display">
</div>
<div id="content"></div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
// eslint-disable-next-line complexity
SimpleTest.waitForFocus(async () => {
const kSetUserInputCancelable = SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input");
let content = document.getElementById("content");
/**
* Test structure:
* element: the tag name to create.
* type: the type attribute value for the element. If unnecessary omit it.
* input: the values calling setUserInput() with.
* before: used when calling setUserInput() before the element gets focus.
* after: used when calling setUserInput() after the element gets focus.
* result: the results of calling setUserInput().
* before: the element's expected value of calling setUserInput() before the element gets focus.
* after: the element's expected value of calling setUserInput() after the element gets focus.
* fireBeforeInputEvent: true if "beforeinput" event should be fired. Otherwise, false.
* fireInputEvent: true if "input" event should be fired. Otherwise, false.
*/
for (let test of [{element: "input", type: "hidden",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
{element: "input", type: "text",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
{element: "input", type: "search",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
{element: "input", type: "tel",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
{element: "input", type: "url",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
{element: "input", type: "email",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
{element: "input", type: "password",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
// "date" does not support setUserInput, but dispatches "input" event...
{element: "input", type: "date",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
// "month" does not support setUserInput, but dispatches "input" event...
{element: "input", type: "month",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
// "week" does not support setUserInput, but dispatches "input" event...
{element: "input", type: "week",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
// "time" does not support setUserInput, but dispatches "input" event...
{element: "input", type: "time",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
// "datetime-local" does not support setUserInput, but dispatches "input" event...
{element: "input", type: "datetime-local",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
{element: "input", type: "number",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
{element: "input", type: "range",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
// "color" does not support setUserInput, but dispatches "input" event...
{element: "input", type: "color",
input: {before: "#5C5C5C", after: "#FFFFFF"},
result: {before: "#5c5c5c", after:"#ffffff", fireBeforeInputEvent: false, fireInputEvent: true}},
{element: "input", type: "checkbox",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
{element: "input", type: "radio",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
// "file" is not supported by setUserInput? But there is a path...
{element: "input", type: "file",
input: {before: "3", after: "6"},
result: {before: "", after:"", fireBeforeInputEvent: false, fireInputEvent: true}},
{element: "input", type: "submit",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
{element: "input", type: "image",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
{element: "input", type: "reset",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
{element: "input", type: "button",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
{element: "textarea",
input: {before: "3", after: "6"},
result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}}]) {
let tag =
test.type !== undefined ? `<${test.element} type="${test.type}">` :
`<${test.element}>`;
content.innerHTML =
test.element !== "input" ? tag : `${tag}</${test.element}>`;
content.scrollTop; // Flush pending layout.
let target = content.firstChild;
let inputEvents = [], beforeInputEvents = [];
function onBeforeInput(aEvent) {
beforeInputEvents.push(aEvent);
}
function onInput(aEvent) {
inputEvents.push(aEvent);
}
target.addEventListener("beforeinput", onBeforeInput);
target.addEventListener("input", onInput);
// Before setting focus, editor of the element may have not been created yet.
let previousValue = target.value;
SpecialPowers.wrap(target).setUserInput(test.input.before);
if (target.value == previousValue && test.result.before != previousValue) {
todo_is(target.value, test.result.before, `setUserInput("${test.input.before}") before ${tag} gets focus should set its value to "${test.result.before}"`);
} else {
is(target.value, test.result.before, `setUserInput("${test.input.before}") before ${tag} gets focus should set its value to "${test.result.before}"`);
}
if (target.value == previousValue) {
if (test.type === "date" || test.type === "time" || test.type === "datetime-local") {
todo_is(inputEvents.length, 0,
`No "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
} else {
is(inputEvents.length, 0,
`No "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
}
} else {
if (!test.result.fireBeforeInputEvent) {
is(beforeInputEvents.length, 0,
`No "beforeinput" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
} else {
is(beforeInputEvents.length, 1,
`Only one "beforeinput" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
}
if (!test.result.fireInputEvent) {
// HTML spec defines that "input" elements whose type are "hidden",
// "submit", "image", "reset" and "button" shouldn't fire input event
// when its value is changed.
// XXX Perhaps, we shouldn't support setUserInput() with such types.
if (test.type === "hidden" ||
test.type === "submit" ||
test.type === "image" ||
test.type === "reset" ||
test.type === "button") {
todo_is(inputEvents.length, 0,
`No "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
} else {
is(inputEvents.length, 0,
`No "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
}
} else {
is(inputEvents.length, 1,
`Only one "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
}
}
if (inputEvents.length) {
if (SpecialPowers.wrap(target).isInputEventTarget) {
if (test.type === "time") {
todo(inputEvents[0] instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
} else {
if (beforeInputEvents.length && test.result.fireBeforeInputEvent) {
is(beforeInputEvents[0].cancelable, kSetUserInputCancelable,
`"beforeinput" event for "insertReplacementText" should be cancelable when setUserInput("${test.input.before}") is called before ${tag} gets focus unless it's suppressed by the pref`);
is(beforeInputEvents[0].inputType, "insertReplacementText",
`inputType of "beforeinput"event should be "insertReplacementText" when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
is(beforeInputEvents[0].data, test.input.before,
`data of "beforeinput" event should be "${test.input.before}" when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
is(beforeInputEvents[0].dataTransfer, null,
`dataTransfer of "beforeinput" event should be null when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
is(beforeInputEvents[0].getTargetRanges().length, 0,
`getTargetRanges() of "beforeinput" event should return empty array when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
}
ok(inputEvents[0] instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
is(inputEvents[0].inputType, "insertReplacementText",
`inputType of "input" event should be "insertReplacementText" when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
is(inputEvents[0].data, test.input.before,
`data of "input" event should be "${test.input.before}" when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
is(inputEvents[0].dataTransfer, null,
`dataTransfer of "input" event should be null when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
is(inputEvents[0].getTargetRanges().length, 0,
`getTargetRanges() of "input" event should return empty array when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
}
} else {
ok(inputEvents[0] instanceof Event && !(inputEvents[0] instanceof UIEvent),
`"input" event should be dispatched with Event interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
}
is(inputEvents[0].cancelable, false,
`"input" event should be never cancelable (${tag}, before getting focus)`);
is(inputEvents[0].bubbles, true,
`"input" event should always bubble (${tag}, before getting focus)`);
}
beforeInputEvents = [];
inputEvents = [];
target.focus();
previousValue = target.value;
SpecialPowers.wrap(target).setUserInput(test.input.after);
if (target.value == previousValue && test.result.after != previousValue) {
todo_is(target.value, test.result.after, `setUserInput("${test.input.after}") after ${tag} gets focus should set its value to "${test.result.after}"`);
} else {
is(target.value, test.result.after, `setUserInput("${test.input.after}") after ${tag} gets focus should set its value to "${test.result.after}"`);
}
if (target.value == previousValue) {
if (test.type === "date" || test.type === "time" || test.type === "datetime-local") {
todo_is(inputEvents.length, 0,
`No "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
} else {
is(inputEvents.length, 0,
`No "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
}
} else {
if (!test.result.fireBeforeInputEvent) {
is(beforeInputEvents.length, 0,
`No "beforeinput" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
} else {
is(beforeInputEvents.length, 1,
`Only one "beforeinput" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
}
if (!test.result.fireInputEvent) {
// HTML spec defines that "input" elements whose type are "hidden",
// "submit", "image", "reset" and "button" shouldn't fire input event
// when its value is changed.
// XXX Perhaps, we shouldn't support setUserInput() with such types.
if (test.type === "hidden" ||
test.type === "submit" ||
test.type === "image" ||
test.type === "reset" ||
test.type === "button") {
todo_is(inputEvents.length, 0,
`No "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
} else {
is(inputEvents.length, 0,
`No "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
}
} else {
is(inputEvents.length, 1,
`Only one "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
}
}
if (inputEvents.length) {
if (SpecialPowers.wrap(target).isInputEventTarget) {
if (test.type === "time") {
todo(inputEvents[0] instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
} else {
if (beforeInputEvents.length && test.result.fireBeforeInputEvent) {
is(beforeInputEvents[0].cancelable, kSetUserInputCancelable,
`"beforeinput" event should be cancelable when setUserInput("${test.input.after}") is called after ${tag} gets focus unless it's suppressed by the pref`);
is(beforeInputEvents[0].inputType, "insertReplacementText",
`inputType of "beforeinput" event should be "insertReplacementText" when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
is(beforeInputEvents[0].data, test.input.after,
`data of "beforeinput" should be "${test.input.after}" when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
is(beforeInputEvents[0].dataTransfer, null,
`dataTransfer of "beforeinput" should be null when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
is(beforeInputEvents[0].getTargetRanges().length, 0,
`getTargetRanges() of "beforeinput" should return empty array when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
}
ok(inputEvents[0] instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
is(inputEvents[0].inputType, "insertReplacementText",
`inputType of "input" event should be "insertReplacementText" when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
is(inputEvents[0].data, test.input.after,
`data of "input" event should be "${test.input.after}" when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
is(inputEvents[0].dataTransfer, null,
`dataTransfer of "input" event should be null when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
is(inputEvents[0].getTargetRanges().length, 0,
`getTargetRanges() of "input" event should return empty array when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
}
} else {
ok(inputEvents[0] instanceof Event && !(inputEvents[0] instanceof UIEvent),
`"input" event should be dispatched with Event interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
}
is(inputEvents[0].cancelable, false,
`"input" event should be never cancelable (${tag}, after getting focus)`);
is(inputEvents[0].bubbles, true,
`"input" event should always bubble (${tag}, after getting focus)`);
}
target.removeEventListener("input", onInput);
}
function testValidationMessage(aType, aInvalidValue, aValidValue) {
let tag = `<input type="${aType}">`
content.innerHTML = tag;
content.scrollTop; // Flush pending layout.
let target = content.firstChild;
let inputEvents = [];
let validationMessage = "";
function reset() {
inputEvents = [];
validationMessage = "";
}
function onInput(aEvent) {
inputEvents.push(aEvent);
validationMessage = aEvent.target.validationMessage;
}
target.addEventListener("input", onInput);
reset();
SpecialPowers.wrap(target).setUserInput(aInvalidValue);
is(inputEvents.length, 1,
`Only one "input" event should be dispatched when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
isnot(validationMessage, "",
`${tag}.validationMessage should not be empty when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
ok(target.matches(":invalid"),
`The target should have invalid pseudo class when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
reset();
SpecialPowers.wrap(target).setUserInput(aValidValue);
is(inputEvents.length, 1,
`Only one "input" event should be dispatched when setUserInput("${aValidValue}") is called before ${tag} gets focus`);
is(validationMessage, "",
`${tag}.validationMessage should be empty when setUserInput("${aValidValue}") is called before ${tag} gets focus`);
ok(!target.matches(":invalid"),
`The target shouldn't have invalid pseudo class when setUserInput("${aValidValue}") is called before ${tag} gets focus`);
reset();
SpecialPowers.wrap(target).setUserInput(aInvalidValue);
is(inputEvents.length, 1,
`Only one "input" event should be dispatched again when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
isnot(validationMessage, "",
`${tag}.validationMessage should not be empty again when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
ok(target.matches(":invalid"),
`The target should have invalid pseudo class again when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
target.value = "";
target.focus();
reset();
SpecialPowers.wrap(target).setUserInput(aInvalidValue);
is(inputEvents.length, 1,
`Only one "input" event should be dispatched when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
isnot(validationMessage, "",
`${tag}.validationMessage should not be empty when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
ok(target.matches(":invalid"),
`The target should have invalid pseudo class when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
reset();
SpecialPowers.wrap(target).setUserInput(aValidValue);
is(inputEvents.length, 1,
`Only one "input" event should be dispatched when setUserInput("${aValidValue}") is called after ${tag} gets focus`);
is(validationMessage, "",
`${tag}.validationMessage should be empty when setUserInput("${aValidValue}") is called after ${tag} gets focus`);
ok(!target.matches(":invalid"),
`The target shouldn't have invalid pseudo class when setUserInput("${aValidValue}") is called after ${tag} gets focus`);
reset();
SpecialPowers.wrap(target).setUserInput(aInvalidValue);
is(inputEvents.length, 1,
`Only one "input" event should be dispatched again when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
isnot(validationMessage, "",
`${tag}.validationMessage should not be empty again when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
ok(target.matches(":invalid"),
`The target should have invalid pseudo class again when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
target.removeEventListener("input", onInput);
}
testValidationMessage("email", "f", "foo@example.com");
function testValueMissing(aType, aValidValue) {
let tag = aType === "textarea" ? "<textarea required>" : `<input type="${aType}" required>`;
content.innerHTML = `${tag}${aType === "textarea" ? "</textarea>" : ""}`;
content.scrollTop; // Flush pending layout.
let target = content.firstChild;
let inputEvents = [], beforeInputEvents = [];
function reset() {
beforeInputEvents = [];
inputEvents = [];
}
function onBeforeInput(aEvent) {
aEvent.validity = aEvent.target.checkValidity();
beforeInputEvents.push(aEvent);
}
function onInput(aEvent) {
aEvent.validity = aEvent.target.checkValidity();
inputEvents.push(aEvent);
}
target.addEventListener("beforeinput", onBeforeInput);
target.addEventListener("input", onInput);
reset();
SpecialPowers.wrap(target).setUserInput(aValidValue);
is(beforeInputEvents.length, 1, `Calling ${tag}.setUserInput(${aValidValue}) should cause a "beforeinput" event (before gets focus)`);
if (beforeInputEvents.length) {
is(beforeInputEvents[0].validity, false,
`The ${tag} should be invalid at "beforeinput" event (before gets focus)`);
}
is(inputEvents.length, 1, `Calling ${tag}.setUserInput(${aValidValue}) should cause a "input" event (before gets focus)`);
if (inputEvents.length) {
is(inputEvents[0].validity, true,
`The ${tag} should be valid at "input" event (before gets focus)`);
}
target.removeEventListener("beforeinput", onBeforeInput);
target.removeEventListener("input", onInput);
content.innerHTML = "";
content.scrollTop; // Flush pending layout.
content.innerHTML = `${tag}${aType === "textarea" ? "</textarea>" : ""}`;
content.scrollTop; // Flush pending layout.
target = content.firstChild;
target.focus();
target.addEventListener("beforeinput", onBeforeInput);
target.addEventListener("input", onInput);
reset();
SpecialPowers.wrap(target).setUserInput(aValidValue);
is(beforeInputEvents.length, 1, `Calling ${tag}.setUserInput(${aValidValue}) should cause a "beforeinput" event (after gets focus)`);
if (beforeInputEvents.length) {
is(beforeInputEvents[0].validity, false,
`The ${tag} should be invalid at "beforeinput" event (after gets focus)`);
}
is(inputEvents.length, 1, `Calling ${tag}.setUserInput(${aValidValue}) should cause a "input" event (after gets focus)`);
if (inputEvents.length) {
is(inputEvents[0].validity, true,
`The ${tag} should be valid at "input" event (after gets focus)`);
}
target.removeEventListener("beforeinput", onBeforeInput);
target.removeEventListener("input", onInput);
}
testValueMissing("text", "abc");
testValueMissing("password", "abc");
testValueMissing("textarea", "abc");
testValueMissing("email", "foo@example.com");
testValueMissing("url", "https://example.com/");
function testEditorValueAtEachEvent(aType) {
let tag = aType === "textarea" ? "<textarea>" : `<input type="${aType}">`
let closeTag = aType === "textarea" ? "</textarea>" : "";
content.innerHTML = `${tag}${closeTag}`;
content.scrollTop; // Flush pending layout.
let target = content.firstChild;
target.value = "Old Value";
let description = `Setting new value of ${tag} before setting focus: `;
let onBeforeInput = (aEvent) => {
is(target.value, "Old Value",
`${description}The value should not have been modified at "beforeinput" event yet (inputType: "${aEvent.inputType}", data: "${aEvent.data}")`);
};
let onInput = (aEvent) => {
is(target.value, "New Value",
`${description}The value should have been modified at "input" event (inputType: "${aEvent.inputType}", data: "${aEvent.data}"`);
};
target.addEventListener("beforeinput", onBeforeInput);
target.addEventListener("input", onInput);
SpecialPowers.wrap(target).setUserInput("New Value");
description = `Setting new value of ${tag} after setting focus: `;
target.value = "Old Value";
target.focus();
SpecialPowers.wrap(target).setUserInput("New Value");
target.removeEventListener("beforeinput", onBeforeInput);
target.removeEventListener("input", onInput);
// FYI: This is not realistic situation because we should do nothing
// while user composing IME.
// TODO: TextControlState should stop returning setting value as the value
// while committing composition.
description = `Setting new value of ${tag} during composition: `;
target.value = "";
target.focus();
synthesizeCompositionChange({
composition: {
string: "composition string",
clauses: [{length: 18, attr: COMPOSITION_ATTR_RAW_CLAUSE}],
},
caret: {start: 18, length: 0},
});
let onCompositionUpdate = (aEvent) => {
todo_is(target.value, "composition string",
`${description}The value should not have been modified at "compositionupdate" event yet (data: "${aEvent.data}")`);
};
let onCompositionEnd = (aEvent) => {
todo_is(target.value, "composition string",
`${description}The value should not have been modified at "compositionupdate" event yet (data: "${aEvent.data}")`);
};
onBeforeInput = (aEvent) => {
if (aEvent.inputType === "insertCompositionText") {
todo_is(target.value, "composition string",
`${description}The value should not have been modified at "beforeinput" event yet (inputType: "${aEvent.inputType}", data: "${aEvent.data}")`);
} else {
is(target.value, "composition string",
`${description}The value should not have been modified at "beforeinput" event yet (inputType: "${aEvent.inputType}", data: "${aEvent.data}")`);
}
};
onInput = (aEvent) => {
if (aEvent.inputType === "insertCompositionText") {
todo_is(target.value, "composition string",
`${description}The value should not have been modified at "input" event yet (inputType: "${aEvent.inputType}", data: "${aEvent.data}")`);
} else {
is(target.value, "New Value",
`${description}The value should have been modified at "input" event (inputType: "${aEvent.inputType}", data: "${aEvent.data}"`);
}
};
target.addEventListener("compositionupdate", onCompositionUpdate);
target.addEventListener("compositionend", onCompositionEnd);
target.addEventListener("beforeinput", onBeforeInput);
target.addEventListener("input", onInput);
SpecialPowers.wrap(target).setUserInput("New Value");
target.removeEventListener("compositionupdate", onCompositionUpdate);
target.removeEventListener("compositionend", onCompositionEnd);
target.removeEventListener("beforeinput", onBeforeInput);
target.removeEventListener("input", onInput);
}
testEditorValueAtEachEvent("text");
testEditorValueAtEachEvent("textarea");
async function testBeforeInputCancelable(aType) {
let tag = aType === "textarea" ? "<textarea>" : `<input type="${aType}">`
let closeTag = aType === "textarea" ? "</textarea>" : "";
for (const kShouldBeCancelable of [true, false]) {
await SpecialPowers.pushPrefEnv({
set: [["dom.input_event.allow_to_cancel_set_user_input", kShouldBeCancelable]],
});
content.innerHTML = `${tag}${closeTag}`;
content.scrollTop; // Flush pending layout.
let target = content.firstChild;
target.value = "Old Value";
let description = `Setting new value of ${tag} before setting focus (the pref ${kShouldBeCancelable ? "allows" : "disallows"} to cancel beforeinput): `;
let onBeforeInput = (aEvent) => {
is(aEvent.cancelable, kShouldBeCancelable,
`${description}The "beforeinput" event should be ${kShouldBeCancelable ? "cancelable" : "not be cancelable due to suppressed by the pref"}`);
};
let onInput = (aEvent) => {
is(aEvent.cancelable, false,
`${description}The value should have been modified at "input" event (inputType: "${aEvent.inputType}", data: "${aEvent.data}"`);
};
target.addEventListener("beforeinput", onBeforeInput);
target.addEventListener("input", onInput);
SpecialPowers.wrap(target).setUserInput("New Value");
description = `Setting new value of ${tag} after setting focus (the pref ${kShouldBeCancelable ? "allows" : "disallows"} to cancel beforeinput): `;
target.value = "Old Value";
target.focus();
SpecialPowers.wrap(target).setUserInput("New Value");
target.removeEventListener("beforeinput", onBeforeInput);
target.removeEventListener("input", onInput);
}
await SpecialPowers.clearUserPref({
clear: [["dom.input_event.allow_to_cancel_set_user_input"]],
});
}
await testBeforeInputCancelable("text");
await testBeforeInputCancelable("textarea");
SimpleTest.finish();
});
</script>
</body>
</html>