summaryrefslogtreecommitdiffstats
path: root/dom/html/test/forms/test_input_setting_value.html
diff options
context:
space:
mode:
Diffstat (limited to 'dom/html/test/forms/test_input_setting_value.html')
-rw-r--r--dom/html/test/forms/test_input_setting_value.html619
1 files changed, 619 insertions, 0 deletions
diff --git a/dom/html/test/forms/test_input_setting_value.html b/dom/html/test/forms/test_input_setting_value.html
new file mode 100644
index 0000000000..b6ddd66d24
--- /dev/null
+++ b/dom/html/test/forms/test_input_setting_value.html
@@ -0,0 +1,619 @@
+<!DOCTYPE>
+<html>
+<head>
+ <title>Test for setting input value</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"><input type="text"></div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+ const kSetUserInputCancelable = SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input");
+
+ let input = document.querySelector("input[type=text]");
+
+ // Setting value during composition causes committing composition before setting the value.
+ input.focus();
+ let description = 'Setting input value at first "compositionupdate" event: ';
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
+ input.value = "def";
+ is(input.value, "def", `${description}input value should be the specified value at "compositionupdate" event (after setting the value)`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "def", `${description}input value should be the specified value at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ todo_is(input.value, "def", `${description}input value should be the specified value at "input" event`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ todo_is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+
+ input.value = "";
+ description = 'Setting input value at second "compositionupdate" event: ';
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ab",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 },
+ });
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
+ input.value = "def";
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ is(input.value, "def", `${description}input value should be specified value at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "def", `${description}input value should be specified value at "input" event`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+
+ input.value = "";
+ description = 'Setting input value at "input" event for first composition update: ';
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+
+ input.value = "";
+ description = 'Setting input value at "input" event for second composition update: ';
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ab",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 },
+ });
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+
+ input.value = "";
+ description = 'Setting input value and reframing at "input" event for first composition update: ';
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ input.style.width = "1000px";
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and reframing at "input" event for second composition update: ';
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ab",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 },
+ });
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ input.style.width = "1000px";
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and reframing with flushing layout at "input" event for first composition update: ';
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ input.style.width = "1000px";
+ document.documentElement.scrollTop;
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and reframing with flushing layout at "input" event for second composition update: ';
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ab",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 },
+ });
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ input.style.width = "1000px";
+ document.documentElement.scrollTop;
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+ input.style.width = "";
+
+ // autocomplete and correcting misspelled word by spellchecker cause an "input" event with same path as setting input value.
+ input.value = "";
+ description = 'Setting input value at "input" event whose inputType is "insertReplacementText';
+ let inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
+ input.value = "def";
+ is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+
+ input.value = "";
+ description = 'Setting input value and reframing at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
+ input.value = "def";
+ input.style.width = "1000px";
+ is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and reframing with flushing layout at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
+ input.value = "def";
+ input.style.width = "1000px";
+ document.documentElement.scrollTop;
+ is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and destroying the frame at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
+ input.value = "def";
+ input.style.display = "none";
+ is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.style.display = "inline";
+
+ input.value = "";
+ description = 'Changing input type at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before changing type)`);
+ input.type = "button";
+ is(input.value, "abc", `${description}input value should keep inserted value at "input" event (after changing type)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(input.value, "abc", `${description}input value should keep inserted value after the last "input" event`);
+ is(input.type, "button", `${description}input type should be changed correctly`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.type = "text";
+ is(input.value, "abc", `${description}input value should keep inserted value immediately after restoring the type`);
+ todo(SpecialPowers.wrap(input).hasEditor, `${description}restoring input type should create editor if it's focused element`);
+ input.blur();
+ input.focus();
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "abc", `${description}input value should keep inserted value after creating editor`);
+
+ input.value = "";
+ description = 'Changing input type and flush layout at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before changing type)`);
+ input.type = "button";
+ input.getBoundingClientRect().height;
+ is(input.value, "abc", `${description}input value should keep inserted value at "input" event (after changing type)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(input.value, "abc", `${description}input value should keep inserted value after the last "input" event`);
+ is(input.type, "button", `${description}input type should be changed correctly`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.type = "text";
+ is(input.value, "abc", `${description}input value should keep inserted value immediately after restoring the type`);
+ todo(SpecialPowers.wrap(input).hasEditor, `${description}restoring input type should create editor if it's focused element`);
+ input.blur();
+ input.focus();
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "abc", `${description}input value should keep inserted value after creating editor`);
+
+ function testSettingValueFromBeforeInput(aWithEditor, aPreventDefaultOfBeforeInput) {
+ let beforeInputEvents = [];
+ let inputEvents = [];
+ function recordEvent(aEvent) {
+ if (aEvent.type === "beforeinput") {
+ beforeInputEvents.push(aEvent);
+ } else {
+ inputEvents.push(aEvent);
+ }
+ }
+ let condition = `(${aWithEditor ? "with editor" : "without editor"}${aPreventDefaultOfBeforeInput ? ' and canceling "beforeinput" event' : ""}, the pref ${kSetUserInputCancelable ? "allows" : "disallows"} to cancel "beforeinput" event})`;
+ function Reset() {
+ beforeInputEvents = [];
+ inputEvents = [];
+ if (SpecialPowers.wrap(input).hasEditor != aWithEditor) {
+ if (aWithEditor) {
+ input.blur();
+ input.focus(); // Associate `TextEditor` with input
+ if (!SpecialPowers.wrap(input).hasEditor) {
+ ok(false, `${description}Failed to associate TextEditor with the input ${condition}`);
+ return false;
+ }
+ } else {
+ input.blur();
+ input.type = "button";
+ input.type = "text";
+ if (SpecialPowers.wrap(input).hasEditor) {
+ ok(false, `${description}Failed to disassociate TextEditor from the input ${condition}`);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText" ${condition}: `;
+ input.value = "abc";
+ if (!Reset()) {
+ return;
+ }
+ input.addEventListener("beforeinput", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
+ is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
+ is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
+ input.addEventListener("beforeinput", recordEvent);
+ input.addEventListener("input", recordEvent);
+ input.value = "hig";
+ if (aPreventDefaultOfBeforeInput) {
+ aEvent.preventDefault();
+ }
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("def");
+ is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
+ if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
+ } else {
+ // XXX This result is different from Chrome (verified with spellchecker).
+ // Chrome inserts the new text to current value and selected range.
+ // It might be reasonable, but we don't touch this for now since it
+ // requires a lot of changes.
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
+ is(inputEvents.length, 1, `${description}"input" event should be fired`);
+ if (inputEvents.length) {
+ is(inputEvents[0].inputType,
+ "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ is(inputEvents[0].data, "def",
+ `${description}data of "input" event should be the value specified by setUserInput()`);
+ }
+ }
+ input.removeEventListener("beforeinput", recordEvent);
+ input.removeEventListener("input", recordEvent);
+
+ description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText" and changing the type to "button" ${condition}: `;
+ input.value = "abc";
+ if (!Reset()) {
+ return;
+ }
+ input.addEventListener("beforeinput", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
+ is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
+ is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
+ input.addEventListener("beforeinput", recordEvent);
+ input.addEventListener("input", recordEvent);
+ input.value = "hig";
+ input.type = "button";
+ if (aPreventDefaultOfBeforeInput) {
+ aEvent.preventDefault();
+ }
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("def");
+ is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
+ if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
+ } else {
+ // XXX This result is same as Chrome (verified with spellchecker).
+ // But this behavior is not consistent with just setting the value on Chrome.
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
+ // Same as Chrome
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since the input element's type is changed`);
+ }
+ input.type = "text";
+ input.removeEventListener("beforeinput", recordEvent);
+ input.removeEventListener("input", recordEvent);
+
+ description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText" and destroying the frame ${condition}: `;
+ input.value = "abc";
+ if (!Reset()) {
+ return;
+ }
+ input.addEventListener("beforeinput", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
+ is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
+ is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
+ input.addEventListener("beforeinput", recordEvent);
+ input.addEventListener("input", recordEvent);
+ input.value = "hig";
+ input.style.display = "none";
+ if (aPreventDefaultOfBeforeInput) {
+ aEvent.preventDefault();
+ }
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("def");
+ is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
+ if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
+ } else {
+ // XXX This result is same as Chrome (verified with spellchecker).
+ // But this behavior is not consistent with just setting the value on Chrome.
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
+ // Different from Chrome
+ is(inputEvents.length, 1,
+ `${description}"input" event should be fired even if the frame of target is destroyed`);
+ if (inputEvents.length) {
+ is(inputEvents[0].inputType,
+ "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ is(inputEvents[0].data, "def",
+ `${description}data of "input" event should be the value specified by setUserInput()`);
+ }
+ }
+ input.style.display = "inline";
+ input.removeEventListener("beforeinput", recordEvent);
+ input.removeEventListener("input", recordEvent);
+
+ if (aWithEditor) {
+ return;
+ }
+
+ description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText and create editor" ${condition}: `;
+ input.value = "abc";
+ if (!Reset()) {
+ return;
+ }
+ input.addEventListener("beforeinput", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
+ is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
+ is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
+ input.addEventListener("beforeinput", recordEvent);
+ input.addEventListener("input", recordEvent);
+ input.value = "hig";
+ input.focus();
+ if (aPreventDefaultOfBeforeInput) {
+ aEvent.preventDefault();
+ }
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("def");
+ is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
+ if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
+ } else {
+ // XXX This result is different from Chrome (verified with spellchecker).
+ // Chrome inserts the new text to current value and selected range.
+ // It might be reasonable, but we don't touch this for now since it
+ // requires a lot of changes.
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
+ is(inputEvents.length, 1, `${description}"input" event should be fired`);
+ if (inputEvents.length) {
+ is(inputEvents[0].inputType,
+ "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ is(inputEvents[0].data, "def",
+ `${description}data of "input" event should be the value specified by setUserInput()`);
+ }
+ }
+ input.removeEventListener("beforeinput", recordEvent);
+ input.removeEventListener("input", recordEvent);
+ }
+ // testSettingValueFromBeforeInput(true, true);
+ // testSettingValueFromBeforeInput(true, false);
+ testSettingValueFromBeforeInput(false, true);
+ testSettingValueFromBeforeInput(false, false);
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>