Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!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>