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>