tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_input_setting_value.html (32250B)


      1 <!DOCTYPE>
      2 <html>
      3 <head>
      4  <title>Test for setting input value</title>
      5  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      6  <script src="/tests/SimpleTest/EventUtils.js"></script>
      7  <link rel="stylesheet" href="/tests/SimpleTest/test.css">
      8 </head>
      9 <body>
     10 <div id="display">
     11 </div>
     12 <div id="content"><input type="text"></div>
     13 <pre id="test">
     14 </pre>
     15 
     16 <script class="testbody" type="application/javascript">
     17 SimpleTest.waitForExplicitFinish();
     18 SimpleTest.waitForFocus(() => {
     19  const kSetUserInputCancelable = SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input");
     20 
     21  let input = document.querySelector("input[type=text]");
     22 
     23  // Setting value during composition causes committing composition before setting the value.
     24  input.focus();
     25  let description = 'Setting input value at first "compositionupdate" event: ';
     26  input.addEventListener("compositionupdate", (aEvent) => {
     27    is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
     28    input.value = "def";
     29    is(input.value, "def", `${description}input value should be the specified value at "compositionupdate" event (after setting the value)`);
     30  }, {once: true});
     31  input.addEventListener("compositionend", (aEvent) => {
     32    todo_is(input.value, "def", `${description}input value should be the specified value at "compositionend" event`);
     33  }, {once: true});
     34  input.addEventListener("input", (aEvent) => {
     35    todo_is(input.value, "def", `${description}input value should be the specified value at "input" event`);
     36  }, {once: true});
     37  synthesizeCompositionChange(
     38    { "composition":
     39      { "string": "abc",
     40        "clauses":
     41        [
     42          { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
     43        ]
     44      },
     45      "caret": { "start": 3, "length": 0 },
     46    });
     47  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
     48     `${description}native anonymous text node should have exactly same value as value of <input> element`);
     49  todo_is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
     50 
     51  input.value = "";
     52  description = 'Setting input value at second "compositionupdate" event: ';
     53  synthesizeCompositionChange(
     54    { "composition":
     55      { "string": "ab",
     56        "clauses":
     57        [
     58          { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
     59        ]
     60      },
     61      "caret": { "start": 2, "length": 0 },
     62    });
     63  input.addEventListener("compositionupdate", (aEvent) => {
     64    is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
     65    input.value = "def";
     66  }, {once: true});
     67  input.addEventListener("compositionend", (aEvent) => {
     68    is(input.value, "def", `${description}input value should be specified value at "compositionend" event`);
     69  }, {once: true});
     70  input.addEventListener("input", (aEvent) => {
     71    is(input.value, "def", `${description}input value should be specified value at "input" event`);
     72  }, {once: true});
     73  synthesizeCompositionChange(
     74    { "composition":
     75      { "string": "abc",
     76        "clauses":
     77        [
     78          { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
     79        ]
     80      },
     81      "caret": { "start": 3, "length": 0 },
     82    });
     83  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
     84     `${description}native anonymous text node should have exactly same value as value of <input> element`);
     85  is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
     86 
     87  input.value = "";
     88  description = 'Setting input value at "input" event for first composition update: ';
     89  input.addEventListener("compositionupdate", (aEvent) => {
     90    is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
     91  }, {once: true});
     92  input.addEventListener("compositionend", (aEvent) => {
     93    todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
     94  }, {once: true});
     95  input.addEventListener("input", (aEvent) => {
     96    is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
     97    input.value = "def";
     98    is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
     99  }, {once: true});
    100  synthesizeCompositionChange(
    101    { "composition":
    102      { "string": "abc",
    103        "clauses":
    104        [
    105          { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    106        ]
    107      },
    108      "caret": { "start": 3, "length": 0 },
    109    });
    110  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    111     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    112  is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
    113 
    114  input.value = "";
    115  description = 'Setting input value at "input" event for second composition update: ';
    116  synthesizeCompositionChange(
    117    { "composition":
    118      { "string": "ab",
    119        "clauses":
    120        [
    121          { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    122        ]
    123      },
    124      "caret": { "start": 2, "length": 0 },
    125    });
    126  input.addEventListener("compositionupdate", (aEvent) => {
    127    is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
    128  }, {once: true});
    129  input.addEventListener("compositionend", (aEvent) => {
    130    todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
    131  }, {once: true});
    132  input.addEventListener("input", (aEvent) => {
    133    is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
    134    input.value = "def";
    135    is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
    136  }, {once: true});
    137  synthesizeCompositionChange(
    138    { "composition":
    139      { "string": "abc",
    140        "clauses":
    141        [
    142          { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    143        ]
    144      },
    145      "caret": { "start": 3, "length": 0 },
    146    });
    147  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    148     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    149  is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
    150 
    151  input.value = "";
    152  description = 'Setting input value and reframing at "input" event for first composition update: ';
    153  input.addEventListener("compositionupdate", (aEvent) => {
    154    is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
    155  }, {once: true});
    156  input.addEventListener("compositionend", (aEvent) => {
    157    todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
    158  }, {once: true});
    159  input.addEventListener("input", (aEvent) => {
    160    is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
    161    input.value = "def";
    162    input.style.width = "1000px";
    163    is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
    164  }, {once: true});
    165  synthesizeCompositionChange(
    166    { "composition":
    167      { "string": "abc",
    168        "clauses":
    169        [
    170          { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    171        ]
    172      },
    173      "caret": { "start": 3, "length": 0 },
    174    });
    175  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    176     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    177  is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
    178  input.style.width = "";
    179 
    180  input.value = "";
    181  description = 'Setting input value and reframing at "input" event for second composition update: ';
    182  synthesizeCompositionChange(
    183    { "composition":
    184      { "string": "ab",
    185        "clauses":
    186        [
    187          { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    188        ]
    189      },
    190      "caret": { "start": 2, "length": 0 },
    191    });
    192  input.addEventListener("compositionupdate", (aEvent) => {
    193    is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
    194  }, {once: true});
    195  input.addEventListener("compositionend", (aEvent) => {
    196    todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
    197  }, {once: true});
    198  input.addEventListener("input", (aEvent) => {
    199    is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
    200    input.value = "def";
    201    input.style.width = "1000px";
    202    is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
    203  }, {once: true});
    204  synthesizeCompositionChange(
    205    { "composition":
    206      { "string": "abc",
    207        "clauses":
    208        [
    209          { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    210        ]
    211      },
    212      "caret": { "start": 3, "length": 0 },
    213    });
    214  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    215     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    216  is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
    217  input.style.width = "";
    218 
    219  input.value = "";
    220  description = 'Setting input value and reframing with flushing layout at "input" event for first composition update: ';
    221  input.addEventListener("compositionupdate", (aEvent) => {
    222    is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
    223  }, {once: true});
    224  input.addEventListener("compositionend", (aEvent) => {
    225    todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
    226  }, {once: true});
    227  input.addEventListener("input", (aEvent) => {
    228    is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
    229    input.value = "def";
    230    input.style.width = "1000px";
    231    document.documentElement.scrollTop;
    232    is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
    233  }, {once: true});
    234  synthesizeCompositionChange(
    235    { "composition":
    236      { "string": "abc",
    237        "clauses":
    238        [
    239          { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    240        ]
    241      },
    242      "caret": { "start": 3, "length": 0 },
    243    });
    244  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    245     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    246  is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
    247  input.style.width = "";
    248 
    249  input.value = "";
    250  description = 'Setting input value and reframing with flushing layout at "input" event for second composition update: ';
    251  synthesizeCompositionChange(
    252    { "composition":
    253      { "string": "ab",
    254        "clauses":
    255        [
    256          { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    257        ]
    258      },
    259      "caret": { "start": 2, "length": 0 },
    260    });
    261  input.addEventListener("compositionupdate", (aEvent) => {
    262    is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
    263  }, {once: true});
    264  input.addEventListener("compositionend", (aEvent) => {
    265    todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
    266  }, {once: true});
    267  input.addEventListener("input", (aEvent) => {
    268    is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
    269    input.value = "def";
    270    input.style.width = "1000px";
    271    document.documentElement.scrollTop;
    272    is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
    273  }, {once: true});
    274  synthesizeCompositionChange(
    275    { "composition":
    276      { "string": "abc",
    277        "clauses":
    278        [
    279          { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    280        ]
    281      },
    282      "caret": { "start": 3, "length": 0 },
    283    });
    284  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    285     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    286  is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
    287  input.style.width = "";
    288 
    289  // autocomplete and correcting misspelled word by spellchecker cause an "input" event with same path as setting input value.
    290  input.value = "";
    291  description = 'Setting input value at "input" event whose inputType is "insertReplacementText';
    292  let inputEventFired = false;
    293  input.addEventListener("input", (aEvent) => {
    294    is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
    295    inputEventFired = true;
    296    is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
    297    input.value = "def";
    298    is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
    299  }, {once: true});
    300  SpecialPowers.wrap(input).setUserInput("abc");
    301  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    302     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    303  is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
    304  ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
    305 
    306  input.value = "";
    307  description = 'Setting input value and reframing at "input" event whose inputType is "insertReplacementText';
    308  inputEventFired = false;
    309  input.addEventListener("input", (aEvent) => {
    310    is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
    311    inputEventFired = true;
    312    is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
    313    input.value = "def";
    314    input.style.width = "1000px";
    315    is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
    316  }, {once: true});
    317  SpecialPowers.wrap(input).setUserInput("abc");
    318  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    319     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    320  is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
    321  ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
    322  input.style.width = "";
    323 
    324  input.value = "";
    325  description = 'Setting input value and reframing with flushing layout at "input" event whose inputType is "insertReplacementText';
    326  inputEventFired = false;
    327  input.addEventListener("input", (aEvent) => {
    328    is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
    329    inputEventFired = true;
    330    is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
    331    input.value = "def";
    332    input.style.width = "1000px";
    333    document.documentElement.scrollTop;
    334    is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
    335  }, {once: true});
    336  SpecialPowers.wrap(input).setUserInput("abc");
    337  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    338     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    339  is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
    340  ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
    341  input.style.width = "";
    342 
    343  input.value = "";
    344  description = 'Setting input value and destroying the frame at "input" event whose inputType is "insertReplacementText';
    345  inputEventFired = false;
    346  input.addEventListener("input", (aEvent) => {
    347    is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
    348    inputEventFired = true;
    349    is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
    350    input.value = "def";
    351    input.style.display = "none";
    352    is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
    353  }, {once: true});
    354  SpecialPowers.wrap(input).setUserInput("abc");
    355  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    356     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    357  is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
    358  ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
    359  input.style.display = "inline";
    360 
    361  input.value = "";
    362  description = 'Changing input type at "input" event whose inputType is "insertReplacementText';
    363  inputEventFired = false;
    364  input.addEventListener("input", (aEvent) => {
    365    is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
    366    inputEventFired = true;
    367    is(input.value, "abc", `${description}input value should be inserted value at "input" event (before changing type)`);
    368    input.type = "button";
    369    is(input.value, "abc", `${description}input value should keep inserted value at "input" event (after changing type)`);
    370  }, {once: true});
    371  SpecialPowers.wrap(input).setUserInput("abc");
    372  is(input.value, "abc", `${description}input value should keep inserted value after the last "input" event`);
    373  is(input.type, "button", `${description}input type should be changed correctly`);
    374  ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
    375  input.type = "text";
    376  is(input.value, "abc", `${description}input value should keep inserted value immediately after restoring the type`);
    377  todo(SpecialPowers.wrap(input).hasEditor, `${description}restoring input type should create editor if it's focused element`);
    378  input.blur();
    379  input.focus();
    380  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    381     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    382  is(input.value, "abc", `${description}input value should keep inserted value after creating editor`);
    383 
    384  input.value = "";
    385  description = 'Changing input type and flush layout at "input" event whose inputType is "insertReplacementText';
    386  inputEventFired = false;
    387  input.addEventListener("input", (aEvent) => {
    388    is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
    389    inputEventFired = true;
    390    is(input.value, "abc", `${description}input value should be inserted value at "input" event (before changing type)`);
    391    input.type = "button";
    392    input.getBoundingClientRect().height;
    393    is(input.value, "abc", `${description}input value should keep inserted value at "input" event (after changing type)`);
    394  }, {once: true});
    395  SpecialPowers.wrap(input).setUserInput("abc");
    396  is(input.value, "abc", `${description}input value should keep inserted value after the last "input" event`);
    397  is(input.type, "button", `${description}input type should be changed correctly`);
    398  ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
    399  input.type = "text";
    400  is(input.value, "abc", `${description}input value should keep inserted value immediately after restoring the type`);
    401  todo(SpecialPowers.wrap(input).hasEditor, `${description}restoring input type should create editor if it's focused element`);
    402  input.blur();
    403  input.focus();
    404  is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
    405     `${description}native anonymous text node should have exactly same value as value of <input> element`);
    406  is(input.value, "abc", `${description}input value should keep inserted value after creating editor`);
    407 
    408  function testSettingValueFromBeforeInput(aWithEditor, aPreventDefaultOfBeforeInput) {
    409    let beforeInputEvents = [];
    410    let inputEvents = [];
    411    function recordEvent(aEvent) {
    412      if (aEvent.type === "beforeinput") {
    413        beforeInputEvents.push(aEvent);
    414      } else {
    415        inputEvents.push(aEvent);
    416      }
    417    }
    418    let condition = `(${aWithEditor ? "with editor" : "without editor"}${aPreventDefaultOfBeforeInput ? ' and canceling "beforeinput" event' : ""}, the pref ${kSetUserInputCancelable ? "allows" : "disallows"} to cancel "beforeinput" event})`;
    419    function Reset() {
    420      beforeInputEvents = [];
    421      inputEvents = [];
    422      if (SpecialPowers.wrap(input).hasEditor != aWithEditor) {
    423        if (aWithEditor) {
    424          input.blur();
    425          input.focus(); // Associate `TextEditor` with input
    426          if (!SpecialPowers.wrap(input).hasEditor) {
    427            ok(false, `${description}Failed to associate TextEditor with the input ${condition}`);
    428            return false;
    429          }
    430        } else {
    431          input.blur();
    432          input.type = "button";
    433          input.type = "text";
    434          if (SpecialPowers.wrap(input).hasEditor) {
    435            ok(false, `${description}Failed to disassociate TextEditor from the input ${condition}`);
    436            return false;
    437          }
    438        }
    439      }
    440      return true;
    441    }
    442 
    443    description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText" ${condition}: `;
    444    input.value = "abc";
    445    if (!Reset()) {
    446      return;
    447    }
    448    input.addEventListener("beforeinput", (aEvent) => {
    449      is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
    450      is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
    451      is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
    452      input.addEventListener("beforeinput", recordEvent);
    453      input.addEventListener("input", recordEvent);
    454      input.value = "hig";
    455      if (aPreventDefaultOfBeforeInput) {
    456        aEvent.preventDefault();
    457      }
    458    }, {once: true});
    459    SpecialPowers.wrap(input).setUserInput("def");
    460    is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
    461    if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
    462      is(input.value, "hig",
    463         `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
    464      is(inputEvents.length, 0,
    465         `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
    466    } else {
    467      // XXX This result is different from Chrome (verified with spellchecker).
    468      //     Chrome inserts the new text to current value and selected range.
    469      //     It might be reasonable, but we don't touch this for now since it
    470      //     requires a lot of changes.
    471      is(input.value, "hig",
    472         `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
    473      is(inputEvents.length, 1, `${description}"input" event should be fired`);
    474      if (inputEvents.length) {
    475        is(inputEvents[0].inputType,
    476           "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
    477        is(inputEvents[0].data, "def",
    478           `${description}data of "input" event should be the value specified by setUserInput()`);
    479      }
    480    }
    481    input.removeEventListener("beforeinput", recordEvent);
    482    input.removeEventListener("input", recordEvent);
    483 
    484    description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText" and changing the type to "button" ${condition}: `;
    485    input.value = "abc";
    486    if (!Reset()) {
    487      return;
    488    }
    489    input.addEventListener("beforeinput", (aEvent) => {
    490      is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
    491      is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
    492      is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
    493      input.addEventListener("beforeinput", recordEvent);
    494      input.addEventListener("input", recordEvent);
    495      input.value = "hig";
    496      input.type = "button";
    497      if (aPreventDefaultOfBeforeInput) {
    498        aEvent.preventDefault();
    499      }
    500    }, {once: true});
    501    SpecialPowers.wrap(input).setUserInput("def");
    502    is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
    503    if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
    504      is(input.value, "hig",
    505         `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
    506      is(inputEvents.length, 0,
    507         `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
    508    } else {
    509      // XXX This result is same as Chrome (verified with spellchecker).
    510      //     But this behavior is not consistent with just setting the value on Chrome.
    511      is(input.value, "hig",
    512         `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
    513      // Same as Chrome
    514      is(inputEvents.length, 0,
    515         `${description}"input" event shouldn't be fired since the input element's type is changed`);
    516    }
    517    input.type = "text";
    518    input.removeEventListener("beforeinput", recordEvent);
    519    input.removeEventListener("input", recordEvent);
    520 
    521    description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText" and destroying the frame ${condition}: `;
    522    input.value = "abc";
    523    if (!Reset()) {
    524      return;
    525    }
    526    input.addEventListener("beforeinput", (aEvent) => {
    527      is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
    528      is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
    529      is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
    530      input.addEventListener("beforeinput", recordEvent);
    531      input.addEventListener("input", recordEvent);
    532      input.value = "hig";
    533      input.style.display = "none";
    534      if (aPreventDefaultOfBeforeInput) {
    535        aEvent.preventDefault();
    536      }
    537    }, {once: true});
    538    SpecialPowers.wrap(input).setUserInput("def");
    539    is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
    540    if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
    541      is(input.value, "hig",
    542         `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
    543      is(inputEvents.length, 0,
    544         `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
    545    } else {
    546      // XXX This result is same as Chrome (verified with spellchecker).
    547      //     But this behavior is not consistent with just setting the value on Chrome.
    548      is(input.value, "hig",
    549         `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
    550      // Different from Chrome
    551      is(inputEvents.length, 1,
    552         `${description}"input" event should be fired even if the frame of target is destroyed`);
    553      if (inputEvents.length) {
    554        is(inputEvents[0].inputType,
    555           "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
    556        is(inputEvents[0].data, "def",
    557           `${description}data of "input" event should be the value specified by setUserInput()`);
    558      }
    559    }
    560    input.style.display = "inline";
    561    input.removeEventListener("beforeinput", recordEvent);
    562    input.removeEventListener("input", recordEvent);
    563 
    564    if (aWithEditor) {
    565      return;
    566    }
    567 
    568    description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText and create editor" ${condition}: `;
    569    input.value = "abc";
    570    if (!Reset()) {
    571      return;
    572    }
    573    input.addEventListener("beforeinput", (aEvent) => {
    574      is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
    575      is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
    576      is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
    577      input.addEventListener("beforeinput", recordEvent);
    578      input.addEventListener("input", recordEvent);
    579      input.value = "hig";
    580      input.focus();
    581      if (aPreventDefaultOfBeforeInput) {
    582        aEvent.preventDefault();
    583      }
    584    }, {once: true});
    585    SpecialPowers.wrap(input).setUserInput("def");
    586    is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
    587    if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
    588      is(input.value, "hig",
    589         `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
    590      is(inputEvents.length, 0,
    591         `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
    592    } else {
    593      // XXX This result is different from Chrome (verified with spellchecker).
    594      //     Chrome inserts the new text to current value and selected range.
    595      //     It might be reasonable, but we don't touch this for now since it
    596      //     requires a lot of changes.
    597      is(input.value, "hig",
    598         `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
    599      is(inputEvents.length, 1, `${description}"input" event should be fired`);
    600      if (inputEvents.length) {
    601        is(inputEvents[0].inputType,
    602           "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
    603        is(inputEvents[0].data, "def",
    604           `${description}data of "input" event should be the value specified by setUserInput()`);
    605      }
    606    }
    607    input.removeEventListener("beforeinput", recordEvent);
    608    input.removeEventListener("input", recordEvent);
    609  }
    610  // testSettingValueFromBeforeInput(true, true);
    611  // testSettingValueFromBeforeInput(true, false);
    612  testSettingValueFromBeforeInput(false, true);
    613  testSettingValueFromBeforeInput(false, false);
    614 
    615  SimpleTest.finish();
    616 });
    617 </script>
    618 </body>
    619 </html>