diff options
Diffstat (limited to '')
-rw-r--r-- | dom/html/test/forms/test_input_setting_value.html | 619 |
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> |