window_nsITextInputProcessor.xhtml (246514B)
1 <?xml version="1.0"?> 2 <?xml-stylesheet href="chrome://global/skin" type="text/css"?> 3 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" 4 type="text/css"?> 5 <window title="Testing nsITextInputProcessor behavior" 6 xmlns:html="http://www.w3.org/1999/xhtml" 7 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 8 onunload="onunload();"> 9 <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> 10 <body xmlns="http://www.w3.org/1999/xhtml"> 11 <div id="display"> 12 <input id="input" type="text"/><input id="anotherInput" type="text"/><br/> 13 <textarea></textarea> 14 <iframe id="iframe" width="300" height="150" 15 src="data:text/html,<textarea id='textarea' cols='20' rows='4'></textarea>"></iframe><br/> 16 <div contenteditable=""><br/></div> 17 </div> 18 <div id="content" style="display: none"> 19 20 </div> 21 <pre id="test"> 22 </pre> 23 </body> 24 25 <script class="testbody" type="application/javascript"> 26 <![CDATA[ 27 28 var SimpleTest = window.arguments[0].SimpleTest; 29 30 SimpleTest.waitForFocus(runTests, window); 31 32 function getHTMLEditor(aWindow) { 33 return SpecialPowers.wrap(aWindow).docShell.editingSession?.getEditorForWindow(aWindow); 34 } 35 36 function ok(aCondition, aMessage) 37 { 38 SimpleTest.ok(aCondition, aMessage); 39 } 40 41 function is(aLeft, aRight, aMessage) 42 { 43 SimpleTest.is(aLeft, aRight, aMessage); 44 } 45 46 function isnot(aLeft, aRight, aMessage) 47 { 48 SimpleTest.isnot(aLeft, aRight, aMessage); 49 } 50 51 function todo_is(aLeft, aRight, aMessage) 52 { 53 SimpleTest.todo_is(aLeft, aRight, aMessage); 54 } 55 56 function info(aMessage) { 57 SimpleTest.info(aMessage); 58 } 59 60 function finish() 61 { 62 window.close(); 63 } 64 65 function onunload() 66 { 67 SimpleTest.finish(); 68 } 69 70 function checkInputEvent(aEvent, aCancelable, aIsComposing, aInputType, aData, aDescription) { 71 if (aEvent.type !== "input" && aEvent.type !== "beforeinput") { 72 console.trace(); 73 throw new Error(`${aDescription}"${aEvent.type}" is not InputEvent`); 74 } 75 ok(InputEvent.isInstance(aEvent), `${aDescription}"${aEvent.type}" event should be dispatched with InputEvent interface`); 76 is(aEvent.cancelable, aCancelable, `${aDescription}"${aEvent.type}" event should ${aCancelable ? "be" : "not be"} cancelable`); 77 is(aEvent.bubbles, true, `${aDescription}"${aEvent.type}" event should always bubble`); 78 is(aEvent.isComposing, aIsComposing, `${aDescription}isComposing of "${aEvent.type}" event should be ${aIsComposing}`); 79 is(aEvent.inputType, aInputType, `${aDescription}inputType of "${aEvent.type}" event should be "${aInputType}"`); 80 is(aEvent.data, aData, `${aDescription}data of "${aEvent.type}" event should be "${aData}"`); 81 is(aEvent.dataTransfer, null, `${aDescription}dataTransfer of "${aEvent.type}" event should be null`); 82 is(aEvent.getTargetRanges().length, 0, `${aDescription}getTargetRanges() of "${aEvent.type}" event should return empty array`); 83 } 84 85 const kIsMac = (navigator.platform.indexOf("Mac") == 0); 86 87 const iframe = document.getElementById("iframe"); 88 let childWindow = iframe.contentWindow; 89 let textareaInFrame; 90 let input = document.getElementById("input"); 91 const textarea = document.querySelector("textarea"); 92 const otherWindow = window.arguments[0]; 93 const otherDocument = otherWindow.document; 94 const inputInChildWindow = otherDocument.getElementById("input"); 95 const contenteditable = document.querySelector("div[contenteditable]"); 96 const { AppConstants } = ChromeUtils.importESModule( 97 "resource://gre/modules/AppConstants.sys.mjs" 98 ); 99 const kLF = "\n"; 100 const kExpectInputBeforeCompositionEnd = SpecialPowers.getBoolPref("dom.input_events.dispatch_before_compositionend"); 101 102 function getNativeText(aXPText) 103 { 104 if (kLF == "\n") { 105 return aXPText; 106 } 107 return aXPText.replace(/\n/g, kLF); 108 } 109 110 function createTIP() 111 { 112 return Cc["@mozilla.org/text-input-processor;1"]. 113 createInstance(Ci.nsITextInputProcessor); 114 } 115 116 function runBeginInputTransactionMethodTests() 117 { 118 var description = "runBeginInputTransactionMethodTests: "; 119 input.value = ""; 120 input.focus(); 121 122 var simpleCallback = function (aTIP, aNotification) 123 { 124 switch (aNotification.type) { 125 case "request-to-commit": 126 aTIP.commitComposition(); 127 break; 128 case "request-to-cancel": 129 aTIP.cancelComposition(); 130 break; 131 } 132 return true; 133 }; 134 135 var TIP1 = createTIP(); 136 var TIP2 = createTIP(); 137 isnot(TIP1, TIP2, 138 description + "TIP instances should be different"); 139 140 // beginInputTransaction() and beginInputTransactionForTests() can take ownership if there is no composition. 141 ok(TIP1.beginInputTransaction(window, simpleCallback), 142 description + "TIP1.beginInputTransaction(window) should succeed because there is no composition"); 143 ok(TIP1.beginInputTransactionForTests(window), 144 description + "TIP1.beginInputTransactionForTests(window) should succeed because there is no composition"); 145 ok(TIP2.beginInputTransaction(window, simpleCallback), 146 description + "TIP2.beginInputTransaction(window) should succeed because there is no composition"); 147 ok(TIP2.beginInputTransactionForTests(window), 148 description + "TIP2.beginInputTransactionForTests(window) should succeed because there is no composition"); 149 150 // Start composition with TIP1, then, other TIPs cannot take ownership during a composition. 151 ok(TIP1.beginInputTransactionForTests(window), 152 description + "TIP1.beginInputTransactionForTests() should succeed because there is no composition"); 153 var composingStr = "foo"; 154 TIP1.setPendingCompositionString(composingStr); 155 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 156 ok(TIP1.flushPendingComposition(), 157 description + "TIP1.flushPendingComposition() should return true becuase it should be valid composition"); 158 is(input.value, composingStr, 159 description + "The input element should have composing string"); 160 161 // Composing nsITextInputProcessor instance shouldn't allow initialize it again. 162 try { 163 TIP1.beginInputTransaction(window, simpleCallback); 164 ok(false, 165 "TIP1.beginInputTransaction(window) should cause throwing an exception because it's composing with different purpose"); 166 } catch (e) { 167 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 168 description + "TIP1.beginInputTransaction(window) should cause throwing an exception including NS_ERROR_ALREADY_INITIALIZED because it's composing for tests"); 169 } 170 try { 171 TIP1.beginInputTransactionForTests(otherWindow); 172 ok(false, 173 "TIP1.beginInputTransactionForTests(otherWindow) should cause throwing an exception because it's composing on different window"); 174 } catch (e) { 175 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 176 description + "TIP1.beginInputTransaction(otherWindow) should cause throwing an exception including NS_ERROR_ALREADY_INITIALIZED because it's composing on this window"); 177 } 178 ok(TIP1.beginInputTransactionForTests(window), 179 description + "TIP1.beginInputTransactionForTests(window) should succeed because TextEventDispatcher was initialized with same purpose"); 180 ok(TIP1.beginInputTransactionForTests(childWindow), 181 description + "TIP1.beginInputTransactionForTests(childWindow) should succeed because TextEventDispatcher was initialized with same purpose and is shared by window and childWindow"); 182 ok(!TIP2.beginInputTransaction(window, simpleCallback), 183 description + "TIP2.beginInputTransaction(window) should not succeed because there is composition synthesized by TIP1"); 184 ok(!TIP2.beginInputTransactionForTests(window), 185 description + "TIP2.beginInputTransactionForTests(window) should not succeed because there is composition synthesized by TIP1"); 186 ok(!TIP2.beginInputTransaction(childWindow, simpleCallback), 187 description + "TIP2.beginInputTransaction(childWindow) should not succeed because there is composition synthesized by TIP1"); 188 ok(!TIP2.beginInputTransactionForTests(childWindow), 189 description + "TIP2.beginInputTransactionForTests(childWindow) should not succeed because there is composition synthesized by TIP1"); 190 ok(TIP2.beginInputTransaction(otherWindow, simpleCallback), 191 description + "TIP2.beginInputTransaction(otherWindow) should succeed because there is composition synthesized by TIP1 but it's in other window"); 192 ok(TIP2.beginInputTransactionForTests(otherWindow), 193 description + "TIP2.beginInputTransactionForTests(otherWindow) should succeed because there is composition synthesized by TIP1 but it's in other window"); 194 195 // Let's confirm that the composing string is NOT committed by above tests. 196 TIP1.commitComposition(); 197 is(input.value, composingStr, 198 description + "TIP1.commitString() without specifying commit string should commit current composition with the last composing string"); 199 ok(!TIP1.hasComposition, 200 description + "TIP1.commitString() without specifying commit string should've end composition"); 201 202 ok(TIP1.beginInputTransaction(window, simpleCallback), 203 description + "TIP1.beginInputTransaction() should succeed because there is no composition #2"); 204 ok(TIP1.beginInputTransactionForTests(window), 205 description + "TIP1.beginInputTransactionForTests() should succeed because there is no composition #2"); 206 ok(TIP2.beginInputTransactionForTests(window), 207 description + "TIP2.beginInputTransactionForTests() should succeed because the composition was already committed #2"); 208 209 // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during startComposition(). 210 var events = []; 211 input.addEventListener("compositionstart", function (aEvent) { 212 events.push(aEvent); 213 // eslint-disable-next-line no-caller 214 input.removeEventListener(aEvent.type, arguments.callee); 215 ok(!TIP2.beginInputTransaction(window, simpleCallback), 216 description + "TIP2 shouldn't be able to begin input transaction from compositionstart event handler during TIP1.startComposition();"); 217 }); 218 TIP1.beginInputTransaction(window, simpleCallback); 219 TIP1.startComposition(); 220 is(events.length, 1, 221 description + "compositionstart event should be fired by TIP1.startComposition()"); 222 TIP1.cancelComposition(); 223 224 // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during flushPendingComposition(). 225 events = []; 226 input.addEventListener("compositionstart", function (aEvent) { 227 events.push(aEvent); 228 ok(!TIP2.beginInputTransaction(window, simpleCallback), 229 description + "TIP2 shouldn't be able to begin input transaction from compositionstart event handler during a call of TIP1.flushPendingComposition();"); 230 }, {once: true}); 231 input.addEventListener("compositionupdate", function (aEvent) { 232 events.push(aEvent); 233 ok(!TIP2.beginInputTransaction(window, simpleCallback), 234 description + "TIP2 shouldn't be able to begin input transaction from compositionupdate event handler during a call of TIP1.flushPendingComposition();"); 235 }, {once: true}); 236 input.addEventListener("text", function (aEvent) { 237 events.push(aEvent); 238 ok(!TIP2.beginInputTransaction(window, simpleCallback), 239 description + "TIP2 shouldn't be able to begin input transaction from text event handler during a call of TIP1.flushPendingComposition();"); 240 }, {once: true}); 241 input.addEventListener("beforeinput", function (aEvent) { 242 events.push(aEvent); 243 ok(!TIP2.beginInputTransaction(window, simpleCallback), 244 description + "TIP2 shouldn't be able to begin input transaction from beforeinput event handler during a call of TIP1.flushPendingComposition();"); 245 }, {once: true}); 246 input.addEventListener("input", function (aEvent) { 247 events.push(aEvent); 248 ok(!TIP2.beginInputTransaction(window, simpleCallback), 249 description + "TIP2 shouldn't be able to begin input transaction from input event handler during a call of TIP1.flushPendingComposition();"); 250 }, {once: true}); 251 TIP1.beginInputTransaction(window, simpleCallback); 252 TIP1.setPendingCompositionString(composingStr); 253 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 254 TIP1.flushPendingComposition(); 255 is(events.length, 5, 256 description + "compositionstart, compositionupdate, text, beforeinput and input events should be fired by TIP1.flushPendingComposition()"); 257 is(events[0].type, "compositionstart", 258 description + "events[0] should be compositionstart"); 259 is(events[1].type, "compositionupdate", 260 description + "events[1] should be compositionupdate"); 261 is(events[2].type, "text", 262 description + "events[2] should be text"); 263 is(events[3].type, "beforeinput", 264 description + "events[3] should be beforeinput"); 265 checkInputEvent(events[3], false, true, "insertCompositionText", composingStr, description); 266 is(events[4].type, "input", 267 description + "events[4] should be input"); 268 checkInputEvent(events[4], false, true, "insertCompositionText", composingStr, description); 269 TIP1.cancelComposition(); 270 271 // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during commitComposition(). 272 (() => { 273 events = []; 274 TIP1.beginInputTransaction(window, simpleCallback); 275 TIP1.setPendingCompositionString(composingStr); 276 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 277 TIP1.flushPendingComposition(); 278 input.addEventListener("text", function (aEvent) { 279 events.push(aEvent); 280 ok(!TIP2.beginInputTransaction(window, simpleCallback), 281 description + "TIP2 shouldn't be able to begin input transaction from text event handler during a call of TIP1.commitComposition();"); 282 }, {once: true}); 283 input.addEventListener("compositionend", function (aEvent) { 284 events.push(aEvent); 285 ok(!TIP2.beginInputTransaction(window, simpleCallback), 286 description + "TIP2 shouldn't be able to begin input transaction from compositionend event handler during a call of TIP1.commitComposition();"); 287 }, {once: true}); 288 input.addEventListener("beforeinput", function (aEvent) { 289 events.push(aEvent); 290 ok(!TIP2.beginInputTransaction(window, simpleCallback), 291 description + "TIP2 shouldn't be able to begin input transaction from beforeinput event handler during a call of TIP1.commitComposition();"); 292 }, {once: true}); 293 function onInput(aEvent) { 294 events.push(aEvent); 295 ok(!TIP2.beginInputTransaction(window, simpleCallback), 296 description + "TIP2 shouldn't be able to begin input transaction from input event handler during a call of TIP1.commitComposition();"); 297 } 298 input.addEventListener("input", onInput); 299 TIP1.commitComposition(); 300 is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4, 301 description + "text, beforeinput, compositionend and input events should be fired by TIP1.commitComposition()"); 302 let index = -1; 303 is(events[++index].type, "text", 304 `${description}events[${index}] should be text`); 305 is(events[++index].type, "beforeinput", 306 `${description}events[${index}] should be beforeinput`); 307 checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description); 308 if (kExpectInputBeforeCompositionEnd) { 309 is(events[++index].type, "input", 310 `${description}events[${index}] should be input`); 311 checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description); 312 } 313 is(events[++index].type, "compositionend", 314 `${description}events[${index}] should be compositionend`); 315 is(events[++index].type, "input", 316 `${description}events[${index}] should be input`); 317 checkInputEvent(events[index], false, false, "insertCompositionText", composingStr, description); 318 input.removeEventListener("input", onInput); 319 })(); 320 321 // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during commitCompositionWith("bar"). 322 (() => { 323 events = []; 324 input.addEventListener("compositionstart", function (aEvent) { 325 events.push(aEvent); 326 ok(!TIP2.beginInputTransaction(window, simpleCallback), 327 description + "TIP2 shouldn't be able to begin input transaction from compositionstart event handler during TIP1.commitCompositionWith(\"bar\");"); 328 }, {once: true}); 329 input.addEventListener("compositionupdate", function (aEvent) { 330 events.push(aEvent); 331 ok(!TIP2.beginInputTransaction(window, simpleCallback), 332 description + "TIP2 shouldn't be able to begin input transaction during compositionupdate event handler TIP1.commitCompositionWith(\"bar\");"); 333 }, {once: true}); 334 input.addEventListener("text", function (aEvent) { 335 events.push(aEvent); 336 ok(!TIP2.beginInputTransaction(window, simpleCallback), 337 description + "TIP2 shouldn't be able to begin input transaction during text event handler TIP1.commitCompositionWith(\"bar\");"); 338 }, {once: true}); 339 input.addEventListener("compositionend", function (aEvent) { 340 events.push(aEvent); 341 ok(!TIP2.beginInputTransaction(window, simpleCallback), 342 description + "TIP2 shouldn't be able to begin input transaction during compositionend event handler TIP1.commitCompositionWith(\"bar\");"); 343 }, {once: true}); 344 input.addEventListener("beforeinput", function (aEvent) { 345 events.push(aEvent); 346 ok(!TIP2.beginInputTransaction(window, simpleCallback), 347 description + "TIP2 shouldn't be able to begin input transaction during beforeinput event handler TIP1.commitCompositionWith(\"bar\");"); 348 }, {once: true}); 349 function onInput(aEvent) { 350 events.push(aEvent); 351 ok(!TIP2.beginInputTransaction(window, simpleCallback), 352 description + "TIP2 shouldn't be able to begin input transaction during input event handler TIP1.commitCompositionWith(\"bar\");"); 353 } 354 input.addEventListener("input", onInput); 355 TIP1.beginInputTransaction(window, simpleCallback); 356 TIP1.commitCompositionWith("bar"); 357 is(events.length, kExpectInputBeforeCompositionEnd ? 7 : 6, 358 description + "compositionstart, compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.commitCompositionWith(\"bar\")"); 359 let index = -1; 360 is(events[++index].type, "compositionstart", 361 `${description}events[${index}] should be compositionstart`); 362 is(events[++index].type, "compositionupdate", 363 `${description}events[${index}] should be compositionupdate`); 364 is(events[++index].type, "text", 365 `${description}events[${index}] should be text`); 366 is(events[++index].type, "beforeinput", 367 `${description}events[${index}] should be beforeinput`); 368 checkInputEvent(events[3], false, true, "insertCompositionText", "bar", description); 369 if (kExpectInputBeforeCompositionEnd) { 370 is(events[++index].type, "input", 371 `${description}events[${index}] should be input`); 372 checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description); 373 } 374 is(events[++index].type, "compositionend", 375 `${description}events[${index}] should be compositionend`); 376 is(events[++index].type, "input", 377 `${description}events[${index}] should be input`); 378 checkInputEvent(events[index], false, false, "insertCompositionText", "bar", description); 379 input.removeEventListener("input", onInput); 380 })(); 381 382 // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during cancelComposition(). 383 (() => { 384 events = []; 385 TIP1.beginInputTransaction(window, simpleCallback); 386 TIP1.setPendingCompositionString(composingStr); 387 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 388 TIP1.flushPendingComposition(); 389 input.addEventListener("compositionupdate", function (aEvent) { 390 events.push(aEvent); 391 ok(!TIP2.beginInputTransaction(window, simpleCallback), 392 description + "TIP2 shouldn't be able to begin input transaction from compositionupdate event handler during a call of TIP1.cancelComposition();"); 393 }, {once: true}); 394 input.addEventListener("text", function (aEvent) { 395 events.push(aEvent); 396 ok(!TIP2.beginInputTransaction(window, simpleCallback), 397 description + "TIP2 shouldn't be able to begin input transaction from text event handler during a call of TIP1.cancelComposition();"); 398 }, {once: true}); 399 input.addEventListener("compositionend", function (aEvent) { 400 events.push(aEvent); 401 ok(!TIP2.beginInputTransaction(window, simpleCallback), 402 description + "TIP2 shouldn't be able to begin input transaction from compositionend event handler during a call of TIP1.cancelComposition();"); 403 }, {once: true}); 404 input.addEventListener("beforeinput", function (aEvent) { 405 events.push(aEvent); 406 ok(!TIP2.beginInputTransaction(window, simpleCallback), 407 description + "TIP2 shouldn't be able to begin input transaction from beforeinput event handler during a call of TIP1.cancelComposition();"); 408 }, {once: true}); 409 function onInput(aEvent) { 410 events.push(aEvent); 411 ok(!TIP2.beginInputTransaction(window, simpleCallback), 412 description + "TIP2 shouldn't be able to begin input transaction from input event handler during a call of TIP1.cancelComposition();"); 413 } 414 input.addEventListener("input", onInput); 415 TIP1.cancelComposition(); 416 is(events.length, kExpectInputBeforeCompositionEnd ? 6 : 5, 417 description + "compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.cancelComposition()"); 418 let index = -1; 419 is(events[++index].type, "compositionupdate", 420 `${description}events[${index}] should be compositionupdate`); 421 is(events[++index].type, "text", 422 `${description}events[${index}] should be text`); 423 is(events[++index].type, "beforeinput", 424 `${description}events[${index}] should be beforeinput`); 425 checkInputEvent(events[index], false, true, "insertCompositionText", "", description); 426 if (kExpectInputBeforeCompositionEnd) { 427 is(events[++index].type, "input", 428 `${description}events[${index}] should be input`); 429 checkInputEvent(events[index], false, true, "insertCompositionText", "", description); 430 } 431 is(events[++index].type, "compositionend", 432 `${description}events[${index}] should be compositionend`); 433 is(events[++index].type, "input", 434 `${description}events[${index}] should be input`); 435 checkInputEvent(events[index], false, false, "insertCompositionText", "", description); 436 input.removeEventListener("input", onInput); 437 })(); 438 439 // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during keydown() and keyup(). 440 events = []; 441 TIP1.beginInputTransaction(window, simpleCallback); 442 input.addEventListener("keydown", function (aEvent) { 443 events.push(aEvent); 444 ok(!TIP2.beginInputTransaction(window, simpleCallback), 445 description + "TIP2 shouldn't be able to begin input transaction from keydown event handler during a call of TIP1.keydown();"); 446 }, {once: true}); 447 input.addEventListener("keypress", function (aEvent) { 448 events.push(aEvent); 449 ok(!TIP2.beginInputTransaction(window, simpleCallback), 450 description + "TIP2 shouldn't be able to begin input transaction from keypress event handler during a call of TIP1.keydown();"); 451 }, {once: true}); 452 input.addEventListener("beforeinput", function (aEvent) { 453 events.push(aEvent); 454 ok(!TIP2.beginInputTransaction(window, simpleCallback), 455 description + "TIP2 shouldn't be able to begin input transaction from beforeinput event handler during a call of TIP1.keydown();"); 456 }, {once: true}); 457 input.addEventListener("input", function (aEvent) { 458 events.push(aEvent); 459 ok(!TIP2.beginInputTransaction(window, simpleCallback), 460 description + "TIP2 shouldn't be able to begin input transaction from input event handler during a call of TIP1.keydown();"); 461 }, {once: true}); 462 input.addEventListener("keyup", function (aEvent) { 463 events.push(aEvent); 464 ok(!TIP2.beginInputTransaction(window, simpleCallback), 465 description + "TIP2 shouldn't be able to begin input transaction from keyup event handler during a call of TIP1.keyup();"); 466 }, {once: true}); 467 var keyA = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); 468 TIP1.keydown(keyA); 469 TIP1.keyup(keyA); 470 is(events.length, 5, 471 description + "keydown, keypress, beforeinput, input, keyup events should be fired by TIP1.keydown() and TIP1.keyup()"); 472 is(events[0].type, "keydown", 473 description + "events[0] should be keydown"); 474 is(events[1].type, "keypress", 475 description + "events[1] should be keypress"); 476 is(events[2].type, "beforeinput", 477 description + "events[2] should be beforeinput"); 478 checkInputEvent(events[2], true, false, "insertText", "a", description); 479 is(events[3].type, "input", 480 description + "events[3] should be input"); 481 checkInputEvent(events[3], false, false, "insertText", "a", description); 482 is(events[4].type, "keyup", 483 description + "events[4] should be keyup"); 484 485 // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during startComposition(). 486 events = []; 487 input.addEventListener("compositionstart", function (aEvent) { 488 events.push(aEvent); 489 // eslint-disable-next-line no-caller 490 input.removeEventListener(aEvent.type, arguments.callee); 491 ok(!TIP2.beginInputTransactionForTests(window), 492 description + "TIP2 shouldn't be able to begin input transaction for tests from compositionstart event handler during TIP1.startComposition();"); 493 }); 494 TIP1.beginInputTransactionForTests(window); 495 TIP1.startComposition(); 496 is(events.length, 1, 497 description + "compositionstart event should be fired by TIP1.startComposition()"); 498 TIP1.cancelComposition(); 499 500 // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during flushPendingComposition(). 501 events = []; 502 input.addEventListener("compositionstart", function (aEvent) { 503 events.push(aEvent); 504 ok(!TIP2.beginInputTransactionForTests(window), 505 description + "TIP2 shouldn't be able to begin input transaction for tests from compositionstart event handler during a call of TIP1.flushPendingComposition();"); 506 }, {once: true}); 507 input.addEventListener("compositionupdate", function (aEvent) { 508 events.push(aEvent); 509 ok(!TIP2.beginInputTransactionForTests(window), 510 description + "TIP2 shouldn't be able to begin input transaction for tests from compositionupdate event handler during a call of TIP1.flushPendingComposition();"); 511 }, {once: true}); 512 input.addEventListener("text", function (aEvent) { 513 events.push(aEvent); 514 ok(!TIP2.beginInputTransactionForTests(window), 515 description + "TIP2 shouldn't be able to begin input transaction for tests from text event handler during a call of TIP1.flushPendingComposition();"); 516 }, {once: true}); 517 input.addEventListener("beforeinput", function (aEvent) { 518 events.push(aEvent); 519 ok(!TIP2.beginInputTransactionForTests(window), 520 description + "TIP2 shouldn't be able to begin input transaction for tests from beforeinput event handler during a call of TIP1.flushPendingComposition();"); 521 }, {once: true}); 522 input.addEventListener("input", function (aEvent) { 523 events.push(aEvent); 524 ok(!TIP2.beginInputTransactionForTests(window), 525 description + "TIP2 shouldn't be able to begin input transaction for tests from input event handler during a call of TIP1.flushPendingComposition();"); 526 }, {once: true}); 527 TIP1.beginInputTransactionForTests(window); 528 TIP1.setPendingCompositionString(composingStr); 529 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 530 TIP1.flushPendingComposition(); 531 is(events.length, 5, 532 description + "compositionstart, compositionupdate, text, beforeinput and input events should be fired by TIP1.flushPendingComposition()"); 533 is(events[0].type, "compositionstart", 534 description + "events[0] should be compositionstart"); 535 is(events[1].type, "compositionupdate", 536 description + "events[1] should be compositionupdate"); 537 is(events[2].type, "text", 538 description + "events[2] should be text"); 539 is(events[3].type, "beforeinput", 540 description + "events[3] should be beforeinput"); 541 checkInputEvent(events[3], false, true, "insertCompositionText", composingStr, description); 542 is(events[4].type, "input", 543 description + "events[4] should be input"); 544 checkInputEvent(events[4], false, true, "insertCompositionText", composingStr, description); 545 TIP1.cancelComposition(); 546 547 // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during commitComposition(). 548 (function () { 549 events = []; 550 TIP1.beginInputTransactionForTests(window, simpleCallback); 551 TIP1.setPendingCompositionString(composingStr); 552 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 553 TIP1.flushPendingComposition(); 554 input.addEventListener("text", function (aEvent) { 555 events.push(aEvent); 556 ok(!TIP2.beginInputTransactionForTests(window), 557 description + "TIP2 shouldn't be able to begin input transaction for tests from text event handler during a call of TIP1.commitComposition();"); 558 }, {once: true}); 559 input.addEventListener("compositionend", function (aEvent) { 560 events.push(aEvent); 561 ok(!TIP2.beginInputTransactionForTests(window), 562 description + "TIP2 shouldn't be able to begin input transaction for tests from compositionend event handler during a call of TIP1.commitComposition();"); 563 }, {once: true}); 564 input.addEventListener("beforeinput", function (aEvent) { 565 events.push(aEvent); 566 ok(!TIP2.beginInputTransactionForTests(window), 567 description + "TIP2 shouldn't be able to begin input transaction for tests from beforeinput event handler during a call of TIP1.commitComposition();"); 568 }, {once: true}); 569 function onInput(aEvent) { 570 events.push(aEvent); 571 ok(!TIP2.beginInputTransactionForTests(window), 572 description + "TIP2 shouldn't be able to begin input transaction for tests from input event handler during a call of TIP1.commitComposition();"); 573 } 574 input.addEventListener("input", onInput); 575 TIP1.commitComposition(); 576 is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4, 577 description + "text, beforeinput, compositionend and input events should be fired by TIP1.commitComposition()"); 578 let index = -1; 579 is(events[++index].type, "text", 580 `${description}events[${index}] should be text`); 581 is(events[++index].type, "beforeinput", 582 `${description}events[${index}] should be beforeinput`); 583 checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description); 584 if (kExpectInputBeforeCompositionEnd) { 585 is(events[++index].type, "input", 586 `${description}events[${index}] should be input`); 587 checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description); 588 } 589 is(events[++index].type, "compositionend", 590 `${description}events[${index}] should be compositionend`); 591 is(events[++index].type, "input", 592 `${description}events[${index}] should be input`); 593 checkInputEvent(events[index], false, false, "insertCompositionText", composingStr, description); 594 input.removeEventListener("input", onInput); 595 })(); 596 597 // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during commitCompositionWith("bar"). 598 (() => { 599 events = []; 600 input.addEventListener("compositionstart", function (aEvent) { 601 events.push(aEvent); 602 ok(!TIP2.beginInputTransactionForTests(window), 603 description + "TIP2 shouldn't be able to begin input transaction for tests from compositionstart event handler during TIP1.commitCompositionWith(\"bar\");"); 604 }, {once: true}); 605 input.addEventListener("compositionupdate", function (aEvent) { 606 events.push(aEvent); 607 ok(!TIP2.beginInputTransactionForTests(window), 608 description + "TIP2 shouldn't be able to begin input transaction for tests during compositionupdate event handler TIP1.commitCompositionWith(\"bar\");"); 609 }, {once: true}); 610 input.addEventListener("text", function (aEvent) { 611 events.push(aEvent); 612 ok(!TIP2.beginInputTransactionForTests(window), 613 description + "TIP2 shouldn't be able to begin input transaction for tests during text event handler TIP1.commitCompositionWith(\"bar\");"); 614 }, {once: true}); 615 input.addEventListener("compositionend", function (aEvent) { 616 events.push(aEvent); 617 ok(!TIP2.beginInputTransactionForTests(window), 618 description + "TIP2 shouldn't be able to begin input transaction for tests during compositionend event handler TIP1.commitCompositionWith(\"bar\");"); 619 }, {once: true}); 620 input.addEventListener("beforeinput", function (aEvent) { 621 events.push(aEvent); 622 ok(!TIP2.beginInputTransactionForTests(window), 623 description + "TIP2 shouldn't be able to begin input transaction for tests during beforeinput event handler TIP1.commitCompositionWith(\"bar\");"); 624 }, {once: true}); 625 function onInput(aEvent) { 626 events.push(aEvent); 627 ok(!TIP2.beginInputTransactionForTests(window), 628 description + "TIP2 shouldn't be able to begin input transaction for tests during input event handler TIP1.commitCompositionWith(\"bar\");"); 629 } 630 input.addEventListener("input", onInput); 631 TIP1.beginInputTransactionForTests(window); 632 TIP1.commitCompositionWith("bar"); 633 is(events.length, kExpectInputBeforeCompositionEnd ? 7 : 6, 634 description + "compositionstart, compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.commitCompositionWith(\"bar\")"); 635 let index = -1; 636 is(events[++index].type, "compositionstart", 637 `${description}events[${index}] should be compositionstart`); 638 is(events[++index].type, "compositionupdate", 639 `${description}events[${index}] should be compositionupdate`); 640 is(events[++index].type, "text", 641 `${description}events[${index}] should be text`); 642 is(events[++index].type, "beforeinput", 643 `${description}events[${index}] should be beforeinput`); 644 checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description); 645 if (kExpectInputBeforeCompositionEnd) { 646 is(events[++index].type, "input", 647 `${description}events[${index}] should be input`); 648 checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description); 649 } 650 is(events[++index].type, "compositionend", 651 `${description}events[${index}] should be compositionend`); 652 is(events[++index].type, "input", 653 `${description}events[${index}] should be input`); 654 checkInputEvent(events[index], false, false, "insertCompositionText", "bar", description); 655 input.removeEventListener("input", onInput); 656 })(); 657 658 // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during cancelComposition(). 659 (() => { 660 events = []; 661 TIP1.beginInputTransactionForTests(window, simpleCallback); 662 TIP1.setPendingCompositionString(composingStr); 663 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 664 TIP1.flushPendingComposition(); 665 input.addEventListener("compositionupdate", function (aEvent) { 666 events.push(aEvent); 667 ok(!TIP2.beginInputTransactionForTests(window), 668 description + "TIP2 shouldn't be able to begin input transaction for tests from compositionupdate event handler during a call of TIP1.cancelComposition();"); 669 }, {once: true}); 670 input.addEventListener("text", function (aEvent) { 671 events.push(aEvent); 672 ok(!TIP2.beginInputTransactionForTests(window), 673 description + "TIP2 shouldn't be able to begin input transaction for tests from text event handler during a call of TIP1.cancelComposition();"); 674 }, {once: true}); 675 input.addEventListener("compositionend", function (aEvent) { 676 events.push(aEvent); 677 ok(!TIP2.beginInputTransactionForTests(window), 678 description + "TIP2 shouldn't be able to begin input transaction for tests from compositionend event handler during a call of TIP1.cancelComposition();"); 679 }, {once: true}); 680 input.addEventListener("beforeinput", function (aEvent) { 681 events.push(aEvent); 682 ok(!TIP2.beginInputTransactionForTests(window), 683 description + "TIP2 shouldn't be able to begin input transaction for tests from beforeinput event handler during a call of TIP1.cancelComposition();"); 684 }, {once: true}); 685 function onInput(aEvent) { 686 events.push(aEvent); 687 ok(!TIP2.beginInputTransactionForTests(window), 688 description + "TIP2 shouldn't be able to begin input transaction for tests from input event handler during a call of TIP1.cancelComposition();"); 689 } 690 input.addEventListener("input", onInput); 691 TIP1.cancelComposition(); 692 is(events.length, kExpectInputBeforeCompositionEnd ? 6 : 5, 693 description + "compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.cancelComposition()"); 694 let index = -1; 695 is(events[++index].type, "compositionupdate", 696 `${description}events[${index}] should be compositionupdate`); 697 is(events[++index].type, "text", 698 `${description}events[${index}] should be text`); 699 is(events[++index].type, "beforeinput", 700 `${description}events[${index}] should be beforeinput`); 701 checkInputEvent(events[index], false, true, "insertCompositionText", "", description); 702 if (kExpectInputBeforeCompositionEnd) { 703 is(events[++index].type, "input", 704 `${description}events[${index}] should be input`); 705 checkInputEvent(events[index], false, true, "insertCompositionText", "", description); 706 } 707 is(events[++index].type, "compositionend", 708 `${description}events[${index}] should be compositionend`); 709 is(events[++index].type, "input", 710 `${description}events[${index}] should be input`); 711 checkInputEvent(events[index], false, false, "insertCompositionText", "", description); 712 input.removeEventListener("input", onInput); 713 })(); 714 715 // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during keydown() and keyup(). 716 events = []; 717 TIP1.beginInputTransactionForTests(window); 718 input.addEventListener("keydown", function (aEvent) { 719 events.push(aEvent); 720 ok(!TIP2.beginInputTransactionForTests(window), 721 description + "TIP2 shouldn't be able to begin input transaction for tests for tests from keydown event handler during a call of TIP1.keydown();"); 722 }, {once: true}); 723 input.addEventListener("keypress", function (aEvent) { 724 events.push(aEvent); 725 ok(!TIP2.beginInputTransactionForTests(window), 726 description + "TIP2 shouldn't be able to begin input transaction for tests from keypress event handler during a call of TIP1.keydown();"); 727 }, {once: true}); 728 input.addEventListener("beforeinput", function (aEvent) { 729 events.push(aEvent); 730 ok(!TIP2.beginInputTransactionForTests(window), 731 description + "TIP2 shouldn't be able to begin input transaction for tests from beforeinput event handler during a call of TIP1.keydown();"); 732 }, {once: true}); 733 input.addEventListener("input", function (aEvent) { 734 events.push(aEvent); 735 ok(!TIP2.beginInputTransactionForTests(window), 736 description + "TIP2 shouldn't be able to begin input transaction for tests from input event handler during a call of TIP1.keydown();"); 737 }, {once: true}); 738 input.addEventListener("keyup", function (aEvent) { 739 events.push(aEvent); 740 ok(!TIP2.beginInputTransactionForTests(window), 741 description + "TIP2 shouldn't be able to begin input transaction for tests from keyup event handler during a call of TIP1.keyup();"); 742 }, {once: true}); 743 keyA = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); 744 TIP1.keydown(keyA); 745 TIP1.keyup(keyA); 746 is(events.length, 5, 747 description + "keydown, keypress, beforeinput, input, keyup events should be fired by TIP1.keydown() and TIP1.keyup()"); 748 is(events[0].type, "keydown", 749 description + "events[0] should be keydown"); 750 is(events[1].type, "keypress", 751 description + "events[1] should be keypress"); 752 is(events[2].type, "beforeinput", 753 description + "events[2] should be beforeinput"); 754 checkInputEvent(events[2], true, false, "insertText", "a", description); 755 is(events[3].type, "input", 756 description + "events[3] should be input"); 757 checkInputEvent(events[3], false, false, "insertText", "a", description); 758 is(events[4].type, "keyup", 759 description + "events[4] should be keyup"); 760 761 // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during startComposition(). 762 events = []; 763 input.addEventListener("compositionstart", function (aEvent) { 764 events.push(aEvent); 765 try { 766 TIP1.beginInputTransaction(otherWindow, simpleCallback); 767 ok(false, 768 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should throw an exception during startComposition()"); 769 } catch (e) { 770 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 771 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should cause NS_ERROR_ALREADY_INITIALIZED during startComposition()"); 772 } 773 }, {once: true}); 774 TIP1.beginInputTransaction(window, simpleCallback); 775 TIP1.startComposition(); 776 is(events.length, 1, 777 description + "compositionstart event should be fired by TIP1.startComposition()"); 778 TIP1.cancelComposition(); 779 780 // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during flushPendingComposition(). 781 events = []; 782 input.addEventListener("compositionstart", function (aEvent) { 783 events.push(aEvent); 784 try { 785 TIP1.beginInputTransaction(otherWindow, simpleCallback); 786 ok(false, 787 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should throw an exception during flushPendingComposition()"); 788 } catch (e) { 789 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 790 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 791 } 792 }, {once: true}); 793 input.addEventListener("compositionupdate", function (aEvent) { 794 events.push(aEvent); 795 try { 796 TIP1.beginInputTransaction(otherWindow, simpleCallback); 797 ok(false, 798 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should throw an exception during flushPendingComposition()"); 799 } catch (e) { 800 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 801 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 802 } 803 }, {once: true}); 804 input.addEventListener("text", function (aEvent) { 805 events.push(aEvent); 806 try { 807 TIP1.beginInputTransaction(otherWindow, simpleCallback); 808 ok(false, 809 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should throw an exception during flushPendingComposition()"); 810 } catch (e) { 811 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 812 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 813 } 814 }, {once: true}); 815 input.addEventListener("beforeinput", function (aEvent) { 816 events.push(aEvent); 817 try { 818 TIP1.beginInputTransaction(otherWindow, simpleCallback); 819 ok(false, 820 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during flushPendingComposition()"); 821 } catch (e) { 822 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 823 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 824 } 825 }, {once: true}); 826 input.addEventListener("input", function (aEvent) { 827 events.push(aEvent); 828 try { 829 TIP1.beginInputTransaction(otherWindow, simpleCallback); 830 ok(false, 831 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during flushPendingComposition()"); 832 } catch (e) { 833 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 834 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 835 } 836 }, {once: true}); 837 TIP1.beginInputTransaction(window, simpleCallback); 838 TIP1.setPendingCompositionString(composingStr); 839 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 840 TIP1.flushPendingComposition(); 841 is(events.length, 5, 842 description + "compositionstart, compositionupdate, text, beforeinput and input events should be fired by TIP1.flushPendingComposition()"); 843 is(events[0].type, "compositionstart", 844 description + "events[0] should be compositionstart"); 845 is(events[1].type, "compositionupdate", 846 description + "events[1] should be compositionupdate"); 847 is(events[2].type, "text", 848 description + "events[2] should be text"); 849 is(events[3].type, "beforeinput", 850 description + "events[3] should be beforeinput"); 851 checkInputEvent(events[3], false, true, "insertCompositionText", composingStr, description); 852 is(events[4].type, "input", 853 description + "events[4] should be input"); 854 checkInputEvent(events[4], false, true, "insertCompositionText", composingStr, description); 855 TIP1.cancelComposition(); 856 857 // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during commitComposition(). 858 (function () { 859 events = []; 860 TIP1.beginInputTransaction(window, simpleCallback); 861 TIP1.setPendingCompositionString(composingStr); 862 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 863 TIP1.flushPendingComposition(); 864 input.addEventListener("text", function (aEvent) { 865 events.push(aEvent); 866 try { 867 TIP1.beginInputTransaction(otherWindow, simpleCallback); 868 ok(false, 869 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should throw an exception during commitComposition()"); 870 } catch (e) { 871 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 872 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()"); 873 } 874 }, {once: true}); 875 input.addEventListener("compositionend", function (aEvent) { 876 events.push(aEvent); 877 try { 878 TIP1.beginInputTransaction(otherWindow, simpleCallback); 879 ok(false, 880 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should throw an exception during commitComposition()"); 881 } catch (e) { 882 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 883 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()"); 884 } 885 }, {once: true}); 886 input.addEventListener("beforeinput", function (aEvent) { 887 events.push(aEvent); 888 try { 889 TIP1.beginInputTransaction(otherWindow, simpleCallback); 890 ok(false, 891 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during commitComposition()"); 892 } catch (e) { 893 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 894 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()"); 895 } 896 }, {once: true}); 897 function onInput(aEvent) { 898 events.push(aEvent); 899 try { 900 TIP1.beginInputTransaction(otherWindow, simpleCallback); 901 ok(false, 902 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during commitComposition()"); 903 } catch (e) { 904 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 905 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()"); 906 } 907 } 908 input.addEventListener("input", onInput); 909 TIP1.commitComposition(); 910 is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4, 911 description + "text, beforeinput, compositionend and input events should be fired by TIP1.commitComposition()"); 912 let index = -1; 913 is(events[++index].type, "text", 914 `${description}events[${index}] should be text`); 915 is(events[++index].type, "beforeinput", 916 `${description}events[${index}] should be beforeinput`); 917 checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description); 918 if (kExpectInputBeforeCompositionEnd) { 919 is(events[++index].type, "input", 920 `${description}events[${index}] should be input`); 921 checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description); 922 } 923 is(events[++index].type, "compositionend", 924 `${description}events[${index}] should be compositionend`); 925 is(events[++index].type, "input", 926 `${description}events[${index}] should be input`); 927 checkInputEvent(events[index], false, false, "insertCompositionText", composingStr, description); 928 input.removeEventListener("input", onInput); 929 })(); 930 931 // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during commitCompositionWith("bar");. 932 (() => { 933 events = []; 934 input.addEventListener("compositionstart", function (aEvent) { 935 events.push(aEvent); 936 try { 937 TIP1.beginInputTransaction(otherWindow, simpleCallback); 938 ok(false, 939 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should throw an exception during commitCompositionWith(\"bar\")"); 940 } catch (e) { 941 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 942 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 943 } 944 }, {once: true}); 945 input.addEventListener("compositionupdate", function (aEvent) { 946 events.push(aEvent); 947 try { 948 TIP1.beginInputTransaction(otherWindow, simpleCallback); 949 ok(false, 950 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should throw an exception during commitCompositionWith(\"bar\")"); 951 } catch (e) { 952 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 953 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 954 } 955 }, {once: true}); 956 input.addEventListener("text", function (aEvent) { 957 events.push(aEvent); 958 try { 959 TIP1.beginInputTransaction(otherWindow, simpleCallback); 960 ok(false, 961 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should throw an exception during commitCompositionWith(\"bar\")"); 962 } catch (e) { 963 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 964 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 965 } 966 }, {once: true}); 967 input.addEventListener("compositionend", function (aEvent) { 968 events.push(aEvent); 969 try { 970 TIP1.beginInputTransaction(otherWindow, simpleCallback); 971 ok(false, 972 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should throw an exception during commitCompositionWith(\"bar\")"); 973 } catch (e) { 974 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 975 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 976 } 977 }, {once: true}); 978 input.addEventListener("beforeinput", function (aEvent) { 979 events.push(aEvent); 980 try { 981 TIP1.beginInputTransaction(otherWindow, simpleCallback); 982 ok(false, 983 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during commitCompositionWith(\"bar\")"); 984 } catch (e) { 985 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 986 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 987 } 988 }, {once: true}); 989 function onInput(aEvent) { 990 events.push(aEvent); 991 try { 992 TIP1.beginInputTransaction(otherWindow, simpleCallback); 993 ok(false, 994 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during commitCompositionWith(\"bar\")"); 995 } catch (e) { 996 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 997 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 998 } 999 } 1000 input.addEventListener("input", onInput); 1001 TIP1.beginInputTransaction(window, simpleCallback); 1002 TIP1.commitCompositionWith("bar"); 1003 is(events.length, kExpectInputBeforeCompositionEnd ? 7 : 6, 1004 description + "compositionstart, compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.commitCompositionWith(\"bar\")"); 1005 let index = -1; 1006 is(events[++index].type, "compositionstart", 1007 `${description}events[${index}] should be compositionstart`); 1008 is(events[++index].type, "compositionupdate", 1009 `${description}events[${index}] should be compositionupdate`); 1010 is(events[++index].type, "text", 1011 `${description}events[${index}] should be text`); 1012 is(events[++index].type, "beforeinput", 1013 `${description}events[${index}] should be beforeinput`); 1014 checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description); 1015 if (kExpectInputBeforeCompositionEnd) { 1016 is(events[++index].type, "input", 1017 `${description}events[${index}] should be input`); 1018 checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description); 1019 } 1020 is(events[++index].type, "compositionend", 1021 `${description}events[${index}] should be compositionend`); 1022 is(events[++index].type, "input", 1023 `${description}events[${index}] should be input`); 1024 checkInputEvent(events[index], false, false, "insertCompositionText", "bar", description); 1025 input.removeEventListener("input", onInput); 1026 })(); 1027 1028 // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during cancelComposition();. 1029 (() => { 1030 events = []; 1031 TIP1.beginInputTransaction(window, simpleCallback); 1032 TIP1.setPendingCompositionString(composingStr); 1033 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 1034 TIP1.flushPendingComposition(); 1035 input.addEventListener("compositionupdate", function (aEvent) { 1036 events.push(aEvent); 1037 try { 1038 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1039 ok(false, 1040 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should throw an exception during cancelComposition()"); 1041 } catch (e) { 1042 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1043 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1044 } 1045 }, {once: true}); 1046 input.addEventListener("text", function (aEvent) { 1047 events.push(aEvent); 1048 try { 1049 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1050 ok(false, 1051 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should throw an exception during cancelComposition()"); 1052 } catch (e) { 1053 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1054 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1055 } 1056 }, {once: true}); 1057 input.addEventListener("compositionend", function (aEvent) { 1058 events.push(aEvent); 1059 try { 1060 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1061 ok(false, 1062 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should throw an exception during cancelComposition()"); 1063 } catch (e) { 1064 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1065 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1066 } 1067 }, {once: true}); 1068 input.addEventListener("beforeinput", function (aEvent) { 1069 events.push(aEvent); 1070 try { 1071 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1072 ok(false, 1073 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during cancelComposition()"); 1074 } catch (e) { 1075 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1076 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1077 } 1078 }, {once: true}); 1079 function onInput(aEvent) { 1080 events.push(aEvent); 1081 try { 1082 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1083 ok(false, 1084 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during cancelComposition()"); 1085 } catch (e) { 1086 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1087 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1088 } 1089 } 1090 input.addEventListener("input", onInput); 1091 TIP1.cancelComposition(); 1092 is(events.length, kExpectInputBeforeCompositionEnd ? 6 : 5, 1093 description + "compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.cancelComposition()"); 1094 let index = -1; 1095 is(events[++index].type, "compositionupdate", 1096 `${description}events[${index}] should be compositionupdate`); 1097 is(events[++index].type, "text", 1098 `${description}events[${index}] should be text`); 1099 is(events[++index].type, "beforeinput", 1100 `${description}events[${index}] should be beforeinput`); 1101 checkInputEvent(events[index], false, true, "insertCompositionText", "", description); 1102 if (kExpectInputBeforeCompositionEnd) { 1103 is(events[++index].type, "input", 1104 `${description}events[${index}] should be input`); 1105 checkInputEvent(events[index], false, true, "insertCompositionText", "", description); 1106 } 1107 is(events[++index].type, "compositionend", 1108 `${description}events[${index}] should be compositionend`); 1109 is(events[++index].type, "input", 1110 `${description}events[${index}] should be input`); 1111 checkInputEvent(events[index], false, false, "insertCompositionText", "", description); 1112 input.removeEventListener("input", onInput); 1113 })(); 1114 1115 // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during keydown() and keyup();. 1116 events = []; 1117 TIP1.beginInputTransaction(window, simpleCallback); 1118 input.addEventListener("keydown", function (aEvent) { 1119 events.push(aEvent); 1120 try { 1121 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1122 ok(false, 1123 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keydown\" should throw an exception during keydown()"); 1124 } catch (e) { 1125 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1126 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keydown\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()"); 1127 } 1128 }, {once: true}); 1129 input.addEventListener("keypress", function (aEvent) { 1130 events.push(aEvent); 1131 try { 1132 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1133 ok(false, 1134 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keypress\" should throw an exception during keydown()"); 1135 } catch (e) { 1136 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1137 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keypress\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()"); 1138 } 1139 }, {once: true}); 1140 input.addEventListener("beforeinput", function (aEvent) { 1141 events.push(aEvent); 1142 try { 1143 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1144 ok(false, 1145 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during keydown()"); 1146 } catch (e) { 1147 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1148 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()"); 1149 } 1150 }, {once: true}); 1151 input.addEventListener("input", function (aEvent) { 1152 events.push(aEvent); 1153 try { 1154 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1155 ok(false, 1156 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during keydown()"); 1157 } catch (e) { 1158 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1159 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()"); 1160 } 1161 }, {once: true}); 1162 input.addEventListener("keyup", function (aEvent) { 1163 events.push(aEvent); 1164 try { 1165 TIP1.beginInputTransaction(otherWindow, simpleCallback); 1166 ok(false, 1167 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keyup\" should throw an exception during keyup()"); 1168 } catch (e) { 1169 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1170 description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keyup\" should cause NS_ERROR_ALREADY_INITIALIZED during keyup()"); 1171 } 1172 }, {once: true}); 1173 keyA = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); 1174 TIP1.keydown(keyA); 1175 TIP1.keyup(keyA); 1176 is(events.length, 5, 1177 description + "keydown, keypress, beforeinput, input, keyup events should be fired by TIP1.keydown() and TIP1.keyup()"); 1178 is(events[0].type, "keydown", 1179 description + "events[0] should be keydown"); 1180 is(events[1].type, "keypress", 1181 description + "events[1] should be keypress"); 1182 is(events[2].type, "beforeinput", 1183 description + "events[2] should be beforeinput"); 1184 checkInputEvent(events[2], true, false, "insertText", "a", description); 1185 is(events[3].type, "input", 1186 description + "events[3] should be input"); 1187 checkInputEvent(events[3], false, false, "insertText", "a", description); 1188 is(events[4].type, "keyup", 1189 description + "events[4] should be keyup"); 1190 1191 // Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during startComposition(). 1192 events = []; 1193 input.addEventListener("compositionstart", function (aEvent) { 1194 events.push(aEvent); 1195 try { 1196 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1197 ok(false, 1198 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionstart\" should throw an exception during startComposition()"); 1199 } catch (e) { 1200 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1201 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionstart\" should cause NS_ERROR_ALREADY_INITIALIZED during startComposition()"); 1202 } 1203 }, {once: true}); 1204 TIP1.beginInputTransactionForTests(window, simpleCallback); 1205 TIP1.startComposition(); 1206 is(events.length, 1, 1207 description + "compositionstart event should be fired by TIP1.startComposition()"); 1208 TIP1.cancelComposition(); 1209 1210 // Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during flushPendingComposition(). 1211 events = []; 1212 input.addEventListener("compositionstart", function (aEvent) { 1213 events.push(aEvent); 1214 try { 1215 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1216 ok(false, 1217 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionstart\" should throw an exception during flushPendingComposition()"); 1218 } catch (e) { 1219 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1220 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionstart\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 1221 } 1222 }, {once: true}); 1223 input.addEventListener("compositionupdate", function (aEvent) { 1224 events.push(aEvent); 1225 try { 1226 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1227 ok(false, 1228 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionupdate\" should throw an exception during flushPendingComposition()"); 1229 } catch (e) { 1230 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1231 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionupdate\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 1232 } 1233 }, {once: true}); 1234 input.addEventListener("text", function (aEvent) { 1235 events.push(aEvent); 1236 try { 1237 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1238 ok(false, 1239 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"text\" should throw an exception during flushPendingComposition()"); 1240 } catch (e) { 1241 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1242 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 1243 } 1244 }, {once: true}); 1245 input.addEventListener("beforeinput", function (aEvent) { 1246 events.push(aEvent); 1247 try { 1248 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1249 ok(false, 1250 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during flushPendingComposition()"); 1251 } catch (e) { 1252 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1253 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 1254 } 1255 }, {once: true}); 1256 input.addEventListener("input", function (aEvent) { 1257 events.push(aEvent); 1258 try { 1259 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1260 ok(false, 1261 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should throw an exception during flushPendingComposition()"); 1262 } catch (e) { 1263 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1264 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()"); 1265 } 1266 }, {once: true}); 1267 TIP1.beginInputTransactionForTests(window, simpleCallback); 1268 TIP1.setPendingCompositionString(composingStr); 1269 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 1270 TIP1.flushPendingComposition(); 1271 is(events.length, 5, 1272 description + "compositionstart, compositionupdate, text, beforeinput and input events should be fired by TIP1.flushPendingComposition()"); 1273 is(events[0].type, "compositionstart", 1274 description + "events[0] should be compositionstart"); 1275 is(events[1].type, "compositionupdate", 1276 description + "events[1] should be compositionupdate"); 1277 is(events[2].type, "text", 1278 description + "events[2] should be text"); 1279 is(events[3].type, "beforeinput", 1280 description + "events[3] should be beforeinput"); 1281 checkInputEvent(events[3], false, true, "insertCompositionText", composingStr, description); 1282 is(events[4].type, "input", 1283 description + "events[4] should be input"); 1284 checkInputEvent(events[4], false, true, "insertCompositionText", composingStr, description); 1285 TIP1.cancelComposition(); 1286 1287 // Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during commitComposition(). 1288 (() => { 1289 events = []; 1290 TIP1.beginInputTransactionForTests(window, simpleCallback); 1291 TIP1.setPendingCompositionString(composingStr); 1292 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 1293 TIP1.flushPendingComposition(); 1294 input.addEventListener("text", function (aEvent) { 1295 events.push(aEvent); 1296 try { 1297 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1298 ok(false, 1299 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"text\" should throw an exception during commitComposition()"); 1300 } catch (e) { 1301 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1302 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()"); 1303 } 1304 }, {once: true}); 1305 input.addEventListener("compositionend", function (aEvent) { 1306 events.push(aEvent); 1307 try { 1308 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1309 ok(false, 1310 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionend\" should throw an exception during commitComposition()"); 1311 } catch (e) { 1312 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1313 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionend\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()"); 1314 } 1315 }, {once: true}); 1316 input.addEventListener("beforeinput", function (aEvent) { 1317 events.push(aEvent); 1318 try { 1319 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1320 ok(false, 1321 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during commitComposition()"); 1322 } catch (e) { 1323 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1324 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()"); 1325 } 1326 }, {once: true}); 1327 function onInput(aEvent) { 1328 events.push(aEvent); 1329 try { 1330 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1331 ok(false, 1332 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should throw an exception during commitComposition()"); 1333 } catch (e) { 1334 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1335 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()"); 1336 } 1337 } 1338 input.addEventListener("input", onInput); 1339 TIP1.commitComposition(); 1340 is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4, 1341 description + "text, beforeinput, compositionend and input events should be fired by TIP1.commitComposition()"); 1342 let index = -1; 1343 is(events[++index].type, "text", 1344 `${description}events[${index}] should be text`); 1345 is(events[++index].type, "beforeinput", 1346 `${description}events[${index}] should be beforeinput`); 1347 checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description); 1348 if (kExpectInputBeforeCompositionEnd) { 1349 is(events[++index].type, "input", 1350 `${description}events[${index}] should be input`); 1351 checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description); 1352 } 1353 is(events[++index].type, "compositionend", 1354 `${description}events[${index}] should be compositionend`); 1355 is(events[++index].type, "input", 1356 `${description}events[${index}] should be input`); 1357 checkInputEvent(events[index], false, false, "insertCompositionText", composingStr, description); 1358 input.removeEventListener("input", onInput); 1359 })(); 1360 1361 // Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during commitCompositionWith("bar");. 1362 (() => { 1363 events = []; 1364 input.addEventListener("compositionstart", function (aEvent) { 1365 events.push(aEvent); 1366 try { 1367 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1368 ok(false, 1369 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionstart\" should throw an exception during commitCompositionWith(\"bar\")"); 1370 } catch (e) { 1371 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1372 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionstart\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 1373 } 1374 }, {once: true}); 1375 input.addEventListener("compositionupdate", function (aEvent) { 1376 events.push(aEvent); 1377 try { 1378 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1379 ok(false, 1380 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionupdate\" should throw an exception during commitCompositionWith(\"bar\")"); 1381 } catch (e) { 1382 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1383 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionupdate\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 1384 } 1385 }, {once: true}); 1386 input.addEventListener("text", function (aEvent) { 1387 events.push(aEvent); 1388 try { 1389 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1390 ok(false, 1391 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"text\" should throw an exception during commitCompositionWith(\"bar\")"); 1392 } catch (e) { 1393 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1394 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 1395 } 1396 }, {once: true}); 1397 input.addEventListener("compositionend", function (aEvent) { 1398 events.push(aEvent); 1399 try { 1400 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1401 ok(false, 1402 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionend\" should throw an exception during commitCompositionWith(\"bar\")"); 1403 } catch (e) { 1404 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1405 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionend\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 1406 } 1407 }, {once: true}); 1408 input.addEventListener("beforeinput", function (aEvent) { 1409 events.push(aEvent); 1410 try { 1411 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1412 ok(false, 1413 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during commitCompositionWith(\"bar\")"); 1414 } catch (e) { 1415 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1416 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 1417 } 1418 }, {once: true}); 1419 function onInput(aEvent) { 1420 events.push(aEvent); 1421 try { 1422 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1423 ok(false, 1424 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should throw an exception during commitCompositionWith(\"bar\")"); 1425 } catch (e) { 1426 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1427 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")"); 1428 } 1429 } 1430 input.addEventListener("input", onInput); 1431 TIP1.beginInputTransactionForTests(window, simpleCallback); 1432 TIP1.commitCompositionWith("bar"); 1433 is(events.length, kExpectInputBeforeCompositionEnd ? 7 : 6, 1434 description + "compositionstart, compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.commitCompositionWith(\"bar\")"); 1435 let index = -1; 1436 is(events[++index].type, "compositionstart", 1437 `${description}events[${index}] should be compositionstart`); 1438 is(events[++index].type, "compositionupdate", 1439 `${description}events[${index}] should be compositionupdate`); 1440 is(events[++index].type, "text", 1441 `${description}events[${index}] should be text`); 1442 is(events[++index].type, "beforeinput", 1443 `${description}events[${index}] should be beforeinput`); 1444 checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description); 1445 if (kExpectInputBeforeCompositionEnd) { 1446 is(events[++index].type, "input", 1447 `${description}events[${index}] should be input`); 1448 checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description); 1449 } 1450 is(events[++index].type, "compositionend", 1451 `${description}events[${index}] should be compositionend`); 1452 is(events[++index].type, "input", 1453 `${description}events[${index}] should be input`); 1454 checkInputEvent(events[index], false, false, "insertCompositionText", "bar", description); 1455 input.removeEventListener("input", onInput); 1456 })(); 1457 1458 // Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during cancelComposition();. 1459 (() => { 1460 events = []; 1461 TIP1.beginInputTransactionForTests(window, simpleCallback); 1462 TIP1.setPendingCompositionString(composingStr); 1463 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 1464 TIP1.flushPendingComposition(); 1465 input.addEventListener("compositionupdate", function (aEvent) { 1466 events.push(aEvent); 1467 try { 1468 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1469 ok(false, 1470 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionupdate\" should throw an exception during cancelComposition()"); 1471 } catch (e) { 1472 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1473 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionupdate\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1474 } 1475 }, {once: true}); 1476 input.addEventListener("text", function (aEvent) { 1477 events.push(aEvent); 1478 try { 1479 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1480 ok(false, 1481 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"text\" should throw an exception during cancelComposition()"); 1482 } catch (e) { 1483 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1484 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1485 } 1486 }, {once: true}); 1487 input.addEventListener("compositionend", function (aEvent) { 1488 events.push(aEvent); 1489 try { 1490 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1491 ok(false, 1492 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionend\" should throw an exception during cancelComposition()"); 1493 } catch (e) { 1494 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1495 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"compositionend\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1496 } 1497 }, {once: true}); 1498 input.addEventListener("beforeinput", function (aEvent) { 1499 events.push(aEvent); 1500 try { 1501 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1502 ok(false, 1503 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during cancelComposition()"); 1504 } catch (e) { 1505 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1506 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1507 } 1508 }, {once: true}); 1509 function onInput(aEvent) { 1510 events.push(aEvent); 1511 try { 1512 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1513 ok(false, 1514 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should throw an exception during cancelComposition()"); 1515 } catch (e) { 1516 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1517 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()"); 1518 } 1519 } 1520 input.addEventListener("input", onInput); 1521 TIP1.cancelComposition(); 1522 is(events.length, kExpectInputBeforeCompositionEnd ? 6 : 5, 1523 description + "compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.cancelComposition()"); 1524 let index = -1; 1525 is(events[++index].type, "compositionupdate", 1526 `${description}events[${index}] should be compositionupdate`); 1527 is(events[++index].type, "text", 1528 `${description}events[${index}] should be text`); 1529 is(events[++index].type, "beforeinput", 1530 `${description}events[${index}] should be beforeinput`); 1531 checkInputEvent(events[index], false, true, "insertCompositionText", "", description); 1532 if (kExpectInputBeforeCompositionEnd) { 1533 is(events[++index].type, "input", 1534 `${description}events[${index}] should be input`); 1535 checkInputEvent(events[index], false, true, "insertCompositionText", "", description); 1536 } 1537 is(events[++index].type, "compositionend", 1538 `${description}events[${index}] should be compositionend`); 1539 is(events[++index].type, "input", 1540 `${description}events[${index}] should be input`); 1541 checkInputEvent(events[index], false, false, "insertCompositionText", "", description); 1542 input.removeEventListener("input", onInput); 1543 })(); 1544 1545 // Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during keydown() and keyup();. 1546 events = []; 1547 TIP1.beginInputTransactionForTests(window, simpleCallback); 1548 input.addEventListener("keydown", function (aEvent) { 1549 events.push(aEvent); 1550 try { 1551 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1552 ok(false, 1553 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"keydown\" should throw an exception during keydown()"); 1554 } catch (e) { 1555 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1556 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"keydown\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()"); 1557 } 1558 }, {once: true}); 1559 input.addEventListener("keypress", function (aEvent) { 1560 events.push(aEvent); 1561 try { 1562 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1563 ok(false, 1564 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"keypress\" should throw an exception during keydown()"); 1565 } catch (e) { 1566 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1567 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"keypress\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()"); 1568 } 1569 }, {once: true}); 1570 input.addEventListener("beforeinput", function (aEvent) { 1571 events.push(aEvent); 1572 try { 1573 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1574 ok(false, 1575 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during keydown()"); 1576 } catch (e) { 1577 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1578 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()"); 1579 } 1580 }, {once: true}); 1581 input.addEventListener("input", function (aEvent) { 1582 events.push(aEvent); 1583 try { 1584 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1585 ok(false, 1586 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should throw an exception during keydown()"); 1587 } catch (e) { 1588 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1589 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()"); 1590 } 1591 }, {once: true}); 1592 input.addEventListener("keyup", function (aEvent) { 1593 events.push(aEvent); 1594 try { 1595 TIP1.beginInputTransactionForTests(otherWindow, simpleCallback); 1596 ok(false, 1597 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"keyup\" should throw an exception during keyup()"); 1598 } catch (e) { 1599 ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"), 1600 description + "TIP1.beginInputTransactionForTests(otherWindow, simpleCallback) called from \"keyup\" should cause NS_ERROR_ALREADY_INITIALIZED during keyup()"); 1601 } 1602 }, {once: true}); 1603 keyA = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); 1604 TIP1.keydown(keyA); 1605 TIP1.keyup(keyA); 1606 is(events.length, 5, 1607 description + "keydown, keypress, beforeinput, input, keyup events should be fired by TIP1.keydown() and TIP1.keyup()"); 1608 is(events[0].type, "keydown", 1609 description + "events[0] should be keydown"); 1610 is(events[1].type, "keypress", 1611 description + "events[1] should be keypress"); 1612 is(events[2].type, "beforeinput", 1613 description + "events[2] should be beforeinput"); 1614 checkInputEvent(events[2], true, false, "insertText", "a", description); 1615 is(events[3].type, "input", 1616 description + "events[3] should be input"); 1617 checkInputEvent(events[3], false, false, "insertText", "a", description); 1618 is(events[4].type, "keyup", 1619 description + "events[4] should be keyup"); 1620 1621 // Let's check if startComposition() throws an exception after ownership is stolen. 1622 input.value = ""; 1623 ok(TIP1.beginInputTransactionForTests(window), 1624 description + "TIP1.beginInputTransactionForTests() should succeed because there is no composition"); 1625 ok(TIP2.beginInputTransactionForTests(window), 1626 description + "TIP2.beginInputTransactionForTests() should succeed because there is no composition"); 1627 try { 1628 TIP1.startComposition(); 1629 ok(false, 1630 description + "TIP1.startComposition() should cause throwing an exception because TIP2 took the ownership"); 1631 TIP1.cancelComposition(); 1632 } catch (e) { 1633 ok(e.message.includes("NS_ERROR_NOT_INITIALIZED"), 1634 description + "TIP1.startComposition() should cause throwing an exception including NS_ERROR_NOT_INITIALIZED"); 1635 } finally { 1636 is(input.value, "", 1637 description + "The input element should not have commit string"); 1638 } 1639 1640 // Let's check if flushPendingComposition() throws an exception after ownership is stolen. 1641 ok(TIP1.beginInputTransactionForTests(window), 1642 description + "TIP1.beginInputTransactionForTests() should succeed because there is no composition"); 1643 ok(TIP2.beginInputTransactionForTests(window), 1644 description + "TIP2.beginInputTransactionForTests() should succeed because there is no composition"); 1645 input.value = ""; 1646 try { 1647 TIP1.setPendingCompositionString(composingStr); 1648 TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE); 1649 TIP1.flushPendingComposition() 1650 ok(false, 1651 description + "TIP1.flushPendingComposition() should cause throwing an exception because TIP2 took the ownership"); 1652 TIP1.cancelComposition(); 1653 } catch (e) { 1654 ok(e.message.includes("NS_ERROR_NOT_INITIALIZED"), 1655 description + "TIP1.flushPendingComposition() should cause throwing an exception including NS_ERROR_NOT_INITIALIZED"); 1656 } finally { 1657 is(input.value, "", 1658 description + "The input element should not have commit string"); 1659 } 1660 1661 // Let's check if commitCompositionWith("bar") throws an exception after ownership is stolen. 1662 ok(TIP1.beginInputTransactionForTests(window), 1663 description + "TIP1.beginInputTransactionForTests() should succeed because there is no composition"); 1664 ok(TIP2.beginInputTransactionForTests(window), 1665 description + "TIP2.beginInputTransactionForTests() should succeed because there is no composition"); 1666 input.value = ""; 1667 try { 1668 TIP1.commitCompositionWith("bar"); 1669 ok(false, 1670 description + "TIP1.commitCompositionWith(\"bar\") should cause throwing an exception because TIP2 took the ownership"); 1671 } catch (e) { 1672 ok(e.message.includes("NS_ERROR_NOT_INITIALIZED"), 1673 description + "TIP1.commitCompositionWith(\"bar\") should cause throwing an exception including NS_ERROR_NOT_INITIALIZED"); 1674 } finally { 1675 is(input.value, "", 1676 description + "The input element should not have commit string"); 1677 } 1678 1679 // Let's check if keydown() throws an exception after ownership is stolen. 1680 ok(TIP1.beginInputTransactionForTests(window), 1681 description + "TIP1.beginInputTransactionForTests() should succeed because there is no composition"); 1682 ok(TIP2.beginInputTransactionForTests(window), 1683 description + "TIP2.beginInputTransactionForTests() should succeed because there is no composition"); 1684 input.value = ""; 1685 try { 1686 let keyF = new KeyboardEvent("", { key: "f", code: "KeyF", keyCode: KeyboardEvent.DOM_VK_F }); 1687 TIP1.keydown(keyF); 1688 ok(false, 1689 description + "TIP1.keydown(keyF) should cause throwing an exception because TIP2 took the ownership"); 1690 } catch (e) { 1691 ok(e.message.includes("NS_ERROR_NOT_INITIALIZED"), 1692 description + "TIP1.keydown(keyF) should cause throwing an exception including NS_ERROR_NOT_INITIALIZED"); 1693 } finally { 1694 is(input.value, "", 1695 description + "The input element should not be modified"); 1696 } 1697 1698 // Let's check if keyup() throws an exception after ownership is stolen. 1699 ok(TIP1.beginInputTransactionForTests(window), 1700 description + "TIP1.beginInputTransactionForTests() should succeed because there is no composition"); 1701 ok(TIP2.beginInputTransactionForTests(window), 1702 description + "TIP2.beginInputTransactionForTests() should succeed because there is no composition"); 1703 input.value = ""; 1704 try { 1705 let keyF = new KeyboardEvent("", { key: "f", code: "KeyF", keyCode: KeyboardEvent.DOM_VK_F }); 1706 TIP1.keyup(keyF); 1707 ok(false, 1708 description + "TIP1.keyup(keyF) should cause throwing an exception because TIP2 took the ownership"); 1709 } catch (e) { 1710 ok(e.message.includes("NS_ERROR_NOT_INITIALIZED"), 1711 description + "TIP1.keyup(keyF) should cause throwing an exception including NS_ERROR_NOT_INITIALIZED"); 1712 } finally { 1713 is(input.value, "", 1714 description + "The input element should not be modified"); 1715 } 1716 1717 // aCallback of nsITextInputProcessor.beginInputTransaction() must not be omitted. 1718 try { 1719 TIP1.beginInputTransaction(window); 1720 ok(false, 1721 description + "TIP1.beginInputTransaction(window) should be failed since aCallback is omitted"); 1722 } catch (e) { 1723 ok(e.message.includes("Not enough arguments"), 1724 description + "TIP1.beginInputTransaction(window) should cause throwing an exception including \"Not enough arguments\" since aCallback is omitted"); 1725 } 1726 1727 // aCallback of nsITextInputProcessor.beginInputTransaction() must not be undefined. 1728 try { 1729 TIP1.beginInputTransaction(window, undefined); 1730 ok(false, 1731 description + "TIP1.beginInputTransaction(window, undefined) should be failed since aCallback is undefined"); 1732 } catch (e) { 1733 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 1734 description + "TIP1.beginInputTransaction(window, undefined) should cause throwing an exception including NS_ERROR_ILLEGAL_VALUE since aCallback is undefined"); 1735 } 1736 1737 // aCallback of nsITextInputProcessor.beginInputTransaction() must not be null. 1738 try { 1739 TIP1.beginInputTransaction(window, null); 1740 ok(false, 1741 description + "TIP1.beginInputTransaction(window, null) should be failed since aCallback is null"); 1742 } catch (e) { 1743 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 1744 description + "TIP1.beginInputTransaction(window, null) should cause throwing an exception including NS_ERROR_ILLEGAL_VALUE since aCallback is null"); 1745 } 1746 } 1747 1748 function runReleaseTests() 1749 { 1750 var description = "runReleaseTests(): "; 1751 1752 var TIP = createTIP(); 1753 ok(TIP.beginInputTransactionForTests(window), 1754 description + "TIP.beginInputTransactionForTests() should succeed"); 1755 1756 input.value = ""; 1757 input.focus(); 1758 1759 TIP.setPendingCompositionString("foo"); 1760 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 1761 TIP.setCaretInPendingComposition(3); 1762 TIP.flushPendingComposition(); 1763 is(input.value, "foo", 1764 description + "the input should have composition string"); 1765 1766 // Release the TIP 1767 TIP = null; 1768 // Needs to run GC forcibly for testing this. 1769 Cu.forceGC(); 1770 1771 is(input.value, "", 1772 description + "the input should be empty because the composition should be canceled"); 1773 1774 TIP = createTIP(); 1775 ok(TIP.beginInputTransactionForTests(window), 1776 description + "TIP.beginInputTransactionForTests() should succeed #2"); 1777 } 1778 1779 function runCompositionTests() 1780 { 1781 var description = "runCompositionTests(): "; 1782 1783 var TIP = createTIP(); 1784 ok(TIP.beginInputTransactionForTests(window), 1785 description + "TIP.beginInputTransactionForTests() should succeed"); 1786 1787 var events; 1788 1789 function reset() 1790 { 1791 events = []; 1792 } 1793 1794 function handler(aEvent) 1795 { 1796 events.push({ "type": aEvent.type, "data": aEvent.data }); 1797 } 1798 1799 window.addEventListener("compositionstart", handler); 1800 window.addEventListener("compositionupdate", handler); 1801 window.addEventListener("compositionend", handler); 1802 1803 input.value = ""; 1804 input.focus(); 1805 1806 // nsITextInputProcessor.startComposition() 1807 reset(); 1808 TIP.startComposition(); 1809 is(events.length, 1, 1810 description + "startComposition() should cause only compositionstart"); 1811 is(events[0].type, "compositionstart", 1812 description + "startComposition() should cause only compositionstart"); 1813 is(input.value, "", 1814 description + "startComposition() shouldn't modify the focused editor"); 1815 1816 // Setting composition string "foo" as a raw clause 1817 TIP.setPendingCompositionString("foo"); 1818 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 1819 TIP.setCaretInPendingComposition(3); 1820 1821 reset(); 1822 TIP.flushPendingComposition(); 1823 is(events.length, 1, 1824 description + "flushPendingComposition() after startComposition() should cause compositionupdate"); 1825 is(events[0].type, "compositionupdate", 1826 description + "flushPendingComposition() after startComposition() should cause compositionupdate"); 1827 is(events[0].data, "foo", 1828 description + "compositionupdate caused by flushPendingComposition() should have new composition string in its data"); 1829 is(input.value, "foo", 1830 description + "modifying composition string should cause modifying the focused editor"); 1831 1832 // Changing the raw clause to a selected clause 1833 TIP.setPendingCompositionString("foo"); 1834 TIP.appendClauseToPendingComposition(3, TIP.ATTR_SELECTED_CLAUSE); 1835 1836 reset(); 1837 TIP.flushPendingComposition(); 1838 is(events.length, 0, 1839 description + "flushPendingComposition() changing only clause information shouldn't cause compositionupdate"); 1840 is(input.value, "foo", 1841 description + "modifying composition clause shouldn't cause modifying the focused editor"); 1842 1843 // Separating the selected clause to two clauses 1844 TIP.setPendingCompositionString("foo"); 1845 TIP.appendClauseToPendingComposition(2, TIP.ATTR_SELECTED_CLAUSE); 1846 TIP.appendClauseToPendingComposition(1, TIP.ATTR_CONVERTED_CLAUSE); 1847 TIP.setCaretInPendingComposition(2); 1848 1849 reset(); 1850 TIP.flushPendingComposition(); 1851 is(events.length, 0, 1852 description + "flushPendingComposition() separating a clause information shouldn't cause compositionupdate"); 1853 is(input.value, "foo", 1854 description + "separating composition clause shouldn't cause modifying the focused editor"); 1855 1856 // Modifying the composition string 1857 TIP.setPendingCompositionString("FOo"); 1858 TIP.appendClauseToPendingComposition(2, TIP.ATTR_SELECTED_CLAUSE); 1859 TIP.appendClauseToPendingComposition(1, TIP.ATTR_CONVERTED_CLAUSE); 1860 TIP.setCaretInPendingComposition(2); 1861 1862 reset(); 1863 TIP.flushPendingComposition(); 1864 is(events.length, 1, 1865 description + "flushPendingComposition() causing modifying composition string should cause compositionupdate"); 1866 is(events[0].type, "compositionupdate", 1867 description + "flushPendingComposition() causing modifying composition string should cause compositionupdate"); 1868 is(events[0].data, "FOo", 1869 description + "compositionupdate caused by flushPendingComposition() should have new composition string in its data"); 1870 is(input.value, "FOo", 1871 description + "modifying composition clause shouldn't cause modifying the focused editor"); 1872 1873 // Committing the composition string 1874 reset(); 1875 TIP.commitComposition(); 1876 is(events.length, 1, 1877 description + "commitComposition() should cause compositionend but shouldn't cause compositionupdate"); 1878 is(events[0].type, "compositionend", 1879 description + "commitComposition() should cause compositionend"); 1880 is(events[0].data, "FOo", 1881 description + "compositionend caused by commitComposition() should have the committed string in its data"); 1882 is(input.value, "FOo", 1883 description + "commitComposition() shouldn't cause modifying the focused editor"); 1884 1885 // Starting new composition without a call of startComposition() 1886 TIP.setPendingCompositionString("bar"); 1887 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 1888 1889 reset(); 1890 TIP.flushPendingComposition(); 1891 is(events.length, 2, 1892 description + "flushPendingComposition() without a call of startComposition() should cause both compositionstart and compositionupdate"); 1893 is(events[0].type, "compositionstart", 1894 description + "flushPendingComposition() without a call of startComposition() should cause compositionstart"); 1895 is(events[1].type, "compositionupdate", 1896 description + "flushPendingComposition() without a call of startComposition() should cause compositionupdate after compositionstart"); 1897 is(events[1].data, "bar", 1898 description + "compositionupdate caused by flushPendingComposition() without a call of startComposition() should have the composition string in its data"); 1899 is(input.value, "FOobar", 1900 description + "new composition string should cause appending composition string to the focused editor"); 1901 1902 // Canceling the composition 1903 reset(); 1904 TIP.cancelComposition(); 1905 is(events.length, 2, 1906 description + "cancelComposition() should cause both compositionupdate and compositionend"); 1907 is(events[0].type, "compositionupdate", 1908 description + "cancelComposition() should cause compositionupdate"); 1909 is(events[0].data, "", 1910 description + "compositionupdate caused by cancelComposition() should have empty string in its data"); 1911 is(events[1].type, "compositionend", 1912 description + "cancelComposition() should cause compositionend after compositionupdate"); 1913 is(events[1].data, "", 1914 description + "compositionend caused by cancelComposition() should have empty string in its data"); 1915 is(input.value, "FOo", 1916 description + "canceled composition string should be removed from the focused editor"); 1917 1918 // Starting composition explicitly and canceling it 1919 reset(); 1920 TIP.startComposition(); 1921 TIP.cancelComposition(); 1922 is(events.length, 2, 1923 description + "canceling composition immediately after startComposition() should cause compositionstart and compositionend"); 1924 is(events[0].type, "compositionstart", 1925 description + "canceling composition immediately after startComposition() should cause compositionstart first"); 1926 is(events[1].type, "compositionend", 1927 description + "canceling composition immediately after startComposition() should cause compositionend after compositionstart"); 1928 is(events[1].data, "", 1929 description + "compositionend caused by canceling composition should have empty string in its data"); 1930 is(input.value, "FOo", 1931 description + "canceling composition shouldn't modify the focused editor"); 1932 1933 // Create composition for next test. 1934 TIP.setPendingCompositionString("bar"); 1935 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 1936 TIP.flushPendingComposition(); 1937 is(input.value, "FOobar", 1938 description + "The focused editor should have new composition string \"bar\""); 1939 1940 // Allow to set empty composition string 1941 reset(); 1942 TIP.flushPendingComposition(); 1943 is(events.length, 1, 1944 description + "making composition string empty should cause only compositionupdate"); 1945 is(events[0].type, "compositionupdate", 1946 description + "making composition string empty should cause compositionupdate"); 1947 is(events[0].data, "", 1948 description + "compositionupdate caused by making composition string empty should have empty string in its data"); 1949 1950 // Allow to insert new composition string without compositionend/compositionstart 1951 TIP.setPendingCompositionString("buzz"); 1952 TIP.appendClauseToPendingComposition(4, TIP.ATTR_RAW_CLAUSE); 1953 1954 reset(); 1955 TIP.flushPendingComposition(); 1956 is(events.length, 1, 1957 description + "modifying composition string from empty string should cause only compositionupdate"); 1958 is(events[0].type, "compositionupdate", 1959 description + "modifying composition string from empty string should cause compositionupdate"); 1960 is(events[0].data, "buzz", 1961 description + "compositionupdate caused by modifying composition string from empty string should have new composition string in its data"); 1962 is(input.value, "FOobuzz", 1963 description + "new composition string should be appended to the focused editor"); 1964 1965 // Committing with different string 1966 reset(); 1967 TIP.commitCompositionWith("bar"); 1968 is(events.length, 2, 1969 description + "committing with different string should cause compositionupdate and compositionend"); 1970 is(events[0].type, "compositionupdate", 1971 description + "committing with different string should cause compositionupdate first"); 1972 is(events[0].data, "bar", 1973 description + "compositionupdate caused by committing with different string should have the committing string in its data"); 1974 is(events[1].type, "compositionend", 1975 description + "committing with different string should cause compositionend after compositionupdate"); 1976 is(events[1].data, "bar", 1977 description + "compositionend caused by committing with different string should have the committing string in its data"); 1978 is(input.value, "FOobar", 1979 description + "new committed string should be appended to the focused editor"); 1980 1981 // Appending new composition string 1982 TIP.setPendingCompositionString("buzz"); 1983 TIP.appendClauseToPendingComposition(4, TIP.ATTR_RAW_CLAUSE); 1984 TIP.flushPendingComposition(); 1985 is(input.value, "FOobarbuzz", 1986 description + "new composition string should be appended to the focused editor"); 1987 1988 // Committing with same string 1989 reset(); 1990 TIP.commitCompositionWith("buzz"); 1991 is(events.length, 1, 1992 description + "committing with same string should cause only compositionend"); 1993 is(events[0].type, "compositionend", 1994 description + "committing with same string should cause compositionend"); 1995 is(events[0].data, "buzz", 1996 description + "compositionend caused by committing with same string should have the committing string in its data"); 1997 is(input.value, "FOobarbuzz", 1998 description + "new committed string should be appended to the focused editor"); 1999 2000 // Inserting commit string directly 2001 reset(); 2002 TIP.commitCompositionWith("boo!"); 2003 is(events.length, 3, 2004 description + "committing text directly should cause compositionstart, compositionupdate and compositionend"); 2005 is(events[0].type, "compositionstart", 2006 description + "committing text directly should cause compositionstart first"); 2007 is(events[1].type, "compositionupdate", 2008 description + "committing text directly should cause compositionupdate after compositionstart"); 2009 is(events[1].data, "boo!", 2010 description + "compositionupdate caused by committing text directly should have the committing text in its data"); 2011 is(events[2].type, "compositionend", 2012 description + "committing text directly should cause compositionend after compositionupdate"); 2013 is(events[2].data, "boo!", 2014 description + "compositionend caused by committing text directly should have the committing text in its data"); 2015 is(input.value, "FOobarbuzzboo!", 2016 description + "committing text directly should append the committing text to the focused editor"); 2017 2018 window.removeEventListener("compositionstart", handler); 2019 window.removeEventListener("compositionupdate", handler); 2020 window.removeEventListener("compositionend", handler); 2021 } 2022 2023 function runCompositionWithKeyEventTests() 2024 { 2025 var description = "runCompositionWithKeyEventTests(): "; 2026 2027 var TIP = createTIP(); 2028 ok(TIP.beginInputTransactionForTests(window), 2029 description + "TIP.beginInputTransactionForTests() should succeed"); 2030 2031 var events; 2032 2033 function reset() 2034 { 2035 events = []; 2036 } 2037 2038 function handler(aEvent) 2039 { 2040 events.push(aEvent); 2041 } 2042 2043 window.addEventListener("compositionstart", handler); 2044 window.addEventListener("compositionupdate", handler); 2045 window.addEventListener("compositionend", handler); 2046 window.addEventListener("keydown", handler); 2047 window.addEventListener("keypress", handler); 2048 window.addEventListener("keyup", handler); 2049 2050 input.value = ""; 2051 input.focus(); 2052 2053 var printableKeyEvent = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); 2054 var enterKeyEvent = new KeyboardEvent("", { key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }); 2055 var escKeyEvent = new KeyboardEvent("", { key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE }); 2056 var convertKeyEvent = new KeyboardEvent("", { key: "Convert", code: "Convert", keyCode: KeyboardEvent.DOM_VK_CONVERT }); 2057 var backspaceKeyEvent = new KeyboardEvent("", { key: "Backspace", code: "Backspace", keyCode: KeyboardEvent.DOM_VK_BACK_SPACE }); 2058 2059 // nsITextInputProcessor.startComposition() 2060 // Keypress event shouldn't be fired during composition 2061 reset(); 2062 TIP.startComposition(printableKeyEvent); 2063 is(events.length, 3, 2064 description + "TIP.startComposition(printableKeyEvent) should cause keydown, compositionstart and keyup (keypress event shouldn't be fired during composition)"); 2065 is(events[0].type, "keydown", 2066 description + "TIP.startComposition(printableKeyEvent) should cause keydown (keypress event shouldn't be fired during composition)"); 2067 is(events[1].type, "compositionstart", 2068 description + "TIP.startComposition(printableKeyEvent) should cause compositionstart (keypress event shouldn't be fired during composition)"); 2069 is(events[2].type, "keyup", 2070 description + "TIP.startComposition(printableKeyEvent) should cause keyup (keypress event shouldn't be fired during composition)"); 2071 2072 // TIP.flushPendingComposition(printableKeyEvent) should cause keydown and keyup events 2073 TIP.setPendingCompositionString("foo"); 2074 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 2075 TIP.setCaretInPendingComposition(3); 2076 2077 reset(); 2078 TIP.flushPendingComposition(printableKeyEvent); 2079 is(events.length, 3, 2080 description + "TIP.flushPendingComposition(printableKeyEvent) should cause keydown, compositionupdate and keyup (keypress event shouldn't be fired during composition)"); 2081 is(events[0].type, "keydown", 2082 description + "TIP.flushPendingComposition(printableKeyEvent) should cause keydown (keypress event shouldn't be fired during composition)"); 2083 is(events[1].type, "compositionupdate", 2084 description + "TIP.flushPendingComposition(printableKeyEvent) should cause compositionupdate (keypress event shouldn't be fired during composition)"); 2085 is(events[2].type, "keyup", 2086 description + "TIP.flushPendingComposition(printableKeyEvent) should cause keyup (keypress event shouldn't be fired during composition)"); 2087 2088 // TIP.commitComposition(enterKeyEvent) should cause keydown and keyup events 2089 reset(); 2090 TIP.commitComposition(enterKeyEvent); 2091 is(events.length, 3, 2092 description + "TIP.commitComposition(enterKeyEvent) should cause keydown, compositionend and keyup (keypress event shouldn't be fired during composition)"); 2093 is(events[0].type, "keydown", 2094 description + "TIP.commitComposition(enterKeyEvent) should cause keydown (keypress event shouldn't be fired during composition)"); 2095 is(events[1].type, "compositionend", 2096 description + "TIP.commitComposition(enterKeyEvent) should cause compositionend (keypress event shouldn't be fired during composition)"); 2097 is(events[2].type, "keyup", 2098 description + "TIP.commitComposition(enterKeyEvent) should cause keyup (keypress event shouldn't be fired during composition)"); 2099 2100 // TIP.cancelComposition(escKeyEvent) should cause keydown and keyup events 2101 TIP.startComposition(); 2102 reset(); 2103 TIP.cancelComposition(escKeyEvent); 2104 is(events.length, 3, 2105 description + "TIP.cancelComposition(escKeyEvent) should cause keydown, compositionend and keyup (keypress event shouldn't be fired during composition)"); 2106 is(events[0].type, "keydown", 2107 description + "TIP.cancelComposition(escKeyEvent) should cause keydown (keypress event shouldn't be fired during composition)"); 2108 is(events[1].type, "compositionend", 2109 description + "TIP.cancelComposition(escKeyEvent) should cause compositionend (keypress event shouldn't be fired during composition)"); 2110 is(events[2].type, "keyup", 2111 description + "TIP.cancelComposition(escKeyEvent) should cause keyup (keypress event shouldn't be fired during composition)"); 2112 2113 var printableKeydownEvent = new KeyboardEvent("keydown", { key: "b", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B }); 2114 var enterKeydownEvent = new KeyboardEvent("keydown", { key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }); 2115 var escKeydownEvent = new KeyboardEvent("keydown", { key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE }); 2116 2117 // TIP.startComposition(printableKeydownEvent) shouldn't cause keyup event 2118 reset(); 2119 TIP.startComposition(printableKeydownEvent); 2120 is(events.length, 2, 2121 description + "TIP.startComposition(printableKeydownEvent) should cause keydown and compositionstart (keyup event shouldn't be fired)"); 2122 is(events[0].type, "keydown", 2123 description + "TIP.startComposition(printableKeydownEvent) should cause keydown (keyup event shouldn't be fired)"); 2124 is(events[1].type, "compositionstart", 2125 description + "TIP.startComposition(printableKeydownEvent) should cause compositionstart (keyup event shouldn't be fired)"); 2126 2127 // TIP.flushPendingComposition(printableKeydownEvent) shouldn't cause keyup event 2128 TIP.setPendingCompositionString("foo"); 2129 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 2130 TIP.setCaretInPendingComposition(3); 2131 2132 reset(); 2133 TIP.flushPendingComposition(printableKeydownEvent); 2134 is(events.length, 2, 2135 description + "TIP.flushPendingComposition(printableKeydownEvent) should cause keydown and compositionupdate (keyup event shouldn't be fired)"); 2136 is(events[0].type, "keydown", 2137 description + "TIP.flushPendingComposition(printableKeydownEvent) should cause keydown (keyup event shouldn't be fired)"); 2138 is(events[1].type, "compositionupdate", 2139 description + "TIP.flushPendingComposition(printableKeydownEvent) should cause compositionupdate (keyup event shouldn't be fired)"); 2140 2141 // TIP.commitComposition(enterKeydownEvent) shouldn't cause keyup event 2142 reset(); 2143 TIP.commitComposition(enterKeydownEvent); 2144 is(events.length, 2, 2145 description + "TIP.commitComposition(enterKeydownEvent) should cause keydown and compositionend (keyup event shouldn't be fired)"); 2146 is(events[0].type, "keydown", 2147 description + "TIP.commitComposition(enterKeydownEvent) should cause keydown (keyup event shouldn't be fired)"); 2148 is(events[1].type, "compositionend", 2149 description + "TIP.commitComposition(enterKeydownEvent) should cause compositionend (keyup event shouldn't be fired)"); 2150 2151 // TIP.cancelComposition(escKeydownEvent) shouldn't cause keyup event 2152 TIP.startComposition(); 2153 reset(); 2154 TIP.cancelComposition(escKeydownEvent); 2155 is(events.length, 2, 2156 description + "TIP.cancelComposition(escKeydownEvent) should cause keydown and compositionend (keyup event shouldn't be fired)"); 2157 is(events[0].type, "keydown", 2158 description + "TIP.cancelComposition(escKeydownEvent) should cause keydown (keyup event shouldn't be fired)"); 2159 is(events[1].type, "compositionend", 2160 description + "TIP.cancelComposition(escKeydownEvent) should cause compositionend (keyup event shouldn't be fired)"); 2161 2162 window.removeEventListener("compositionstart", handler); 2163 window.removeEventListener("compositionupdate", handler); 2164 window.removeEventListener("compositionend", handler); 2165 window.removeEventListener("keydown", handler); 2166 window.removeEventListener("keypress", handler); 2167 window.removeEventListener("keyup", handler); 2168 } 2169 2170 function runConsumingKeydownBeforeCompositionTests() 2171 { 2172 var description = "runConsumingKeydownBeforeCompositionTests(): "; 2173 2174 var TIP = createTIP(); 2175 ok(TIP.beginInputTransactionForTests(window), 2176 description + "TIP.beginInputTransactionForTests() should succeed"); 2177 2178 var events; 2179 2180 function reset() 2181 { 2182 events = []; 2183 } 2184 2185 function handler(aEvent) 2186 { 2187 events.push(aEvent); 2188 if (aEvent.type == "keydown") { 2189 aEvent.preventDefault(); 2190 } 2191 } 2192 2193 window.addEventListener("compositionstart", handler); 2194 window.addEventListener("compositionupdate", handler); 2195 window.addEventListener("compositionend", handler); 2196 window.addEventListener("keydown", handler); 2197 window.addEventListener("keypress", handler); 2198 window.addEventListener("keyup", handler); 2199 2200 input.value = ""; 2201 input.focus(); 2202 2203 var printableKeyEvent = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); 2204 var enterKeyEvent = new KeyboardEvent("", { key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }); 2205 var escKeyEvent = new KeyboardEvent("", { key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE }); 2206 2207 // If composition is already started, TIP.flushPendingComposition(printableKeyEvent) shouldn't be canceled. 2208 TIP.startComposition(); 2209 ok(TIP.hasComposition, 2210 description + "Before TIP.flushPendingComposition(printableKeyEvent), composition should've been created"); 2211 reset(); 2212 TIP.setPendingCompositionString("foo"); 2213 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 2214 TIP.setCaretInPendingComposition(3); 2215 ok(TIP.flushPendingComposition(printableKeyEvent), 2216 description + "TIP.flushPendingComposition(printableKeyEvent) should return true even if preceding keydown is consumed because there was a composition already"); 2217 is(events.length, 3, 2218 description + "TIP.flushPendingComposition(printableKeyEvent) should cause only keydown and keyup events"); 2219 is(events[0].type, "keydown", 2220 description + "TIP.flushPendingComposition(printableKeyEvent) should cause keydown event first"); 2221 is(events[1].type, "compositionupdate", 2222 description + "TIP.flushPendingComposition(printableKeyEvent) should cause compositionupdate event after keydown"); 2223 is(events[2].type, "keyup", 2224 description + "TIP.flushPendingComposition(printableKeyEvent) should cause keyup event after compositionupdate"); 2225 ok(TIP.hasComposition, 2226 description + "TIP.flushPendingComposition(printableKeyEvent) shouldn't cause canceling composition"); 2227 is(input.value, "foo", 2228 description + "TIP.flushPendingComposition(printableKeyEvent) should cause inserting text even if preceding keydown is consumed because there was a composition already"); 2229 2230 // If composition is already started, TIP.commitComposition(enterKeyEvent) shouldn't be canceled. 2231 reset(); 2232 TIP.commitComposition(enterKeyEvent); 2233 is(events.length, 3, 2234 description + "TIP.commitComposition(enterKeyEvent) should cause keydown, compositionend and keyup events"); 2235 is(events[0].type, "keydown", 2236 description + "TIP.commitComposition(enterKeyEvent) should cause keydown event first"); 2237 is(events[1].type, "compositionend", 2238 description + "TIP.commitComposition(enterKeyEvent) should cause compositionend event after keydown"); 2239 is(events[2].type, "keyup", 2240 description + "TIP.commitComposition(enterKeyEvent) should cause keyup event after compositionend"); 2241 ok(!TIP.hasComposition, 2242 description + "TIP.commitComposition(enterKeyEvent) should cause committing composition even if preceding keydown is consumed because there was a composition already"); 2243 is(input.value, "foo", 2244 description + "TIP.commitComposition(enterKeyEvent) should commit composition even if preceding keydown is consumed because there was a composition already"); 2245 2246 // cancelComposition() should work even if preceding keydown event is consumed. 2247 input.value = ""; 2248 TIP.setPendingCompositionString("foo"); 2249 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 2250 TIP.setCaretInPendingComposition(3); 2251 TIP.flushPendingComposition(); 2252 ok(TIP.hasComposition, 2253 description + "Before TIP.cancelComposition(escKeyEvent), composition should've been created"); 2254 is(input.value, "foo", 2255 description + "Before TIP.cancelComposition(escKeyEvent) should have composition string"); 2256 reset(); 2257 TIP.cancelComposition(escKeyEvent); 2258 is(events.length, 4, 2259 description + "TIP.cancelComposition(escKeyEvent) should cause keydown, compositionupdate, compositionend and keyup events even if preceding keydown is consumed because there was a composition already"); 2260 is(events[0].type, "keydown", 2261 description + "TIP.cancelComposition(escKeyEvent) should cause keydown event first"); 2262 is(events[1].type, "compositionupdate", 2263 description + "TIP.cancelComposition(escKeyEvent) should cause compositionupdate event after keydown"); 2264 is(events[2].type, "compositionend", 2265 description + "TIP.cancelComposition(escKeyEvent) should cause compositionend event after compositionupdate"); 2266 is(events[3].type, "keyup", 2267 description + "TIP.cancelComposition(escKeyEvent) should cause keyup event after compositionend"); 2268 ok(!TIP.hasComposition, 2269 description + "TIP.cancelComposition(escKeyEvent) should cause canceling composition even if preceding keydown is consumed because there was a composition already"); 2270 is(input.value, "", 2271 description + "TIP.cancelComposition(escKeyEvent) should cancel composition even if preceding keydown is consumed because there was a composition already"); 2272 2273 window.removeEventListener("compositionstart", handler); 2274 window.removeEventListener("compositionupdate", handler); 2275 window.removeEventListener("compositionend", handler); 2276 window.removeEventListener("keydown", handler); 2277 window.removeEventListener("keypress", handler); 2278 window.removeEventListener("keyup", handler); 2279 } 2280 2281 async function runKeyTests() 2282 { 2283 var description = "runKeyTests(): "; 2284 const kModifiers = 2285 [ "Alt", "AltGraph", "CapsLock", "Control", "Fn", "FnLock", "Meta", "NumLock", 2286 "ScrollLock", "Shift", "Symbol", "SymbolLock", "OS" ]; 2287 2288 var TIP = createTIP(); 2289 ok(TIP.beginInputTransactionForTests(window), 2290 description + "TIP.beginInputTransactionForTests() should succeed"); 2291 2292 var events; 2293 var doPreventDefaults; 2294 2295 function reset() 2296 { 2297 events = []; 2298 doPreventDefaults = []; 2299 } 2300 2301 function handler(aEvent) 2302 { 2303 events.push(aEvent); 2304 if (doPreventDefaults.includes(aEvent.type)) { 2305 aEvent.preventDefault(); 2306 } 2307 } 2308 2309 function checkKeyAttrs(aMethodDescription, aEvent, aExpectedData) 2310 { 2311 var desc = description + aMethodDescription + ", type=\"" + aEvent.type + "\", key=\"" + aEvent.key + "\", code=\"" + aEvent.code + "\": "; 2312 var defaultValues = { 2313 key: "Unidentified", code: "", keyCode: 0, charCode: 0, 2314 location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD, repeat: false, isComposing: false, 2315 shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, 2316 defaultPrevented: false 2317 }; 2318 function expectedValue(aAttr) 2319 { 2320 return aExpectedData[aAttr] !== undefined ? aExpectedData[aAttr] : defaultValues[aAttr]; 2321 } 2322 is(aEvent.type, aExpectedData.type, 2323 desc + " should cause keydown event"); 2324 if (aEvent.type != aExpectedData.type) { 2325 return; 2326 } 2327 is(aEvent.defaultPrevented, expectedValue("defaultPrevented"), 2328 desc + ".defaultPrevented is wrong"); 2329 is(aEvent.key, expectedValue("key"), 2330 desc + ".key is wrong"); 2331 is(aEvent.code, expectedValue("code"), 2332 desc + ".code is wrong"); 2333 is(aEvent.location, expectedValue("location"), 2334 desc + ".location is wrong"); 2335 is(aEvent.repeat, expectedValue("repeat"), 2336 desc + ".repeat is wrong"); 2337 is(aEvent.isComposing, expectedValue("isComposing"), 2338 desc + ".isComposing is wrong"); 2339 is(aEvent.keyCode, expectedValue("keyCode"), 2340 desc + ".keyCode is wrong"); 2341 is(aEvent.charCode, expectedValue("charCode"), 2342 desc + ".charCode is wrong"); 2343 is(aEvent.shiftKey, expectedValue("shiftKey"), 2344 desc + ".shiftKey is wrong"); 2345 is(aEvent.ctrlKey, expectedValue("ctrlKey"), 2346 desc + ".ctrlKey is wrong"); 2347 is(aEvent.altKey, expectedValue("altKey"), 2348 desc + ".altKey is wrong"); 2349 is(aEvent.metaKey, expectedValue("metaKey"), 2350 desc + ".metaKey is wrong"); 2351 for (var i = 0; i < kModifiers.length; i++) { 2352 is(aEvent.getModifierState(kModifiers[i]), aExpectedData[kModifiers[i]] !== undefined ? aExpectedData[kModifiers[i]] : false, 2353 desc + ".getModifierState(\"" + kModifiers[i] + "\") is wrong"); 2354 } 2355 } 2356 2357 window.addEventListener("keydown", handler); 2358 window.addEventListener("keypress", handler); 2359 window.addEventListener("keyup", handler); 2360 2361 input.value = ""; 2362 input.focus(); 2363 2364 2365 // Printable key test: 2366 // Emulates pressing 'a' key. 2367 var keyA = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); 2368 2369 reset(); 2370 var doDefaultKeydown = TIP.keydown(keyA); 2371 2372 is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED, 2373 description + "TIP.keydown(keyA) should return 0x02 because the keypress event should be consumed by the input element"); 2374 is(events.length, 2, 2375 description + "TIP.keydown(keyA) should cause keydown and keypress event"); 2376 checkKeyAttrs("TIP.keydown(keyA)", events[0], 2377 { type: "keydown", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0 }); 2378 checkKeyAttrs("TIP.keydown(keyA)", events[1], 2379 { type: "keypress", key: "a", code: "KeyA", keyCode: 0, charCode: "a".charCodeAt(0), defaultPrevented: true }); 2380 is(input.value, "a", 2381 description + "input.value should be \"a\" which is inputted by TIP.keydown(keyA)"); 2382 2383 // Emulates releasing 'a' key. 2384 reset(); 2385 var doDefaultKeyup = TIP.keyup(keyA); 2386 ok(doDefaultKeyup, 2387 description + "TIP.keyup(keyA) should return true"); 2388 is(events.length, 1, 2389 description + "TIP.keyup(keyA) should cause keyup event"); 2390 checkKeyAttrs("TIP.keyup(keyA)", events[0], 2391 { type: "keyup", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0 }); 2392 is(input.value, "a", 2393 description + "input.value should stay \"a\" which was inputted by TIP.keydown(keyA)"); 2394 2395 2396 // Non-printable key test: 2397 // Emulates pressing Enter key. 2398 var keyEnter = new KeyboardEvent("", { key: "Enter", code: "Enter" }); 2399 2400 reset(); 2401 doDefaultKeydown = TIP.keydown(keyEnter); 2402 2403 is(doDefaultKeydown, 0, 2404 description + "TIP.keydown(keyEnter) should return 0"); 2405 is(events.length, 2, 2406 description + "TIP.keydown(keyEnter) should cause keydown and keypress event"); 2407 checkKeyAttrs("TIP.keydown(keyEnter)", events[0], 2408 { type: "keydown", key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }); 2409 checkKeyAttrs("TIP.keydown(keyEnter)", events[1], 2410 { type: "keypress", key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }); 2411 is(input.value, "a", 2412 description + "input.value should stay \"a\" which was inputted by TIP.keydown(keyA)"); 2413 2414 // Emulates releasing Enter key. 2415 reset(); 2416 doDefaultKeyup = TIP.keyup(keyEnter); 2417 ok(doDefaultKeyup, 2418 description + "TIP.keyup(keyEnter) should return true"); 2419 is(events.length, 1, 2420 description + "TIP.keyup(keyEnter) should cause keyup event"); 2421 checkKeyAttrs("TIP.keyup(keyEnter)", events[0], 2422 { type: "keyup", key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }); 2423 is(input.value, "a", 2424 description + "input.value should stay \"a\" which was inputted by TIP.keydown(keyA)"); 2425 2426 2427 // KEY_DEFAULT_PREVENTED should cause defaultPrevented = true and not cause keypress event 2428 var keyB = new KeyboardEvent("", { key: "b", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B }); 2429 2430 reset(); 2431 doDefaultKeydown = TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED); 2432 doDefaultKeyup = TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED); 2433 2434 is(doDefaultKeydown, TIP.KEYDOWN_IS_CONSUMED, 2435 description + "TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) should return 0x01 because it's marked as consumed at dispatching the event"); 2436 ok(!doDefaultKeyup, 2437 description + "TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED) should return false because it's marked as consumed at dispatching the event"); 2438 is(events.length, 2, 2439 description + "TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) and TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED) should cause keydown and keyup event"); 2440 checkKeyAttrs("TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) and TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED)", events[0], 2441 { type: "keydown", key: "b", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B, defaultPrevented: true }); 2442 checkKeyAttrs("TIP.keydown(keyB, TIP.KEY_DEFAULT_PREVENTED) and TIP.keyup(keyB, TIP.KEY_DEFAULT_PREVENTED)", events[1], 2443 { type: "keyup", key: "b", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B, defaultPrevented: true }); 2444 is(input.value, "a", 2445 description + "input.value shouldn't be modified by default prevented key events"); 2446 2447 // Assume that KeyX causes inputting text "abc" 2448 input.value = ""; 2449 var keyABC = new KeyboardEvent("", { key: "abc", code: "KeyX", keyCode: KeyboardEvent.DOM_VK_A }); 2450 2451 reset(); 2452 doDefaultKeydown = TIP.keydown(keyABC); 2453 doDefaultKeyup = TIP.keyup(keyABC); 2454 2455 is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED, 2456 description + "TIP.keydown(keyABC) should return false because the keypress events should be consumed by the input element"); 2457 ok(doDefaultKeyup, 2458 description + "TIP.keyup(keyABC) should return true"); 2459 is(events.length, 5, 2460 description + "TIP.keydown(keyABC) and TIP.keyup(keyABC) should cause keydown, keypress, keypress, keypress and keyup event"); 2461 checkKeyAttrs("TIP.keydown(keyABC) and TIP.keyup(keyABC)", events[0], 2462 { type: "keydown", key: "abc", code: "KeyX", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, defaultPrevented: false }); 2463 checkKeyAttrs("TIP.keydown(keyABC) and TIP.keyup(keyABC)", events[1], 2464 { type: "keypress", key: "abc".charAt(0), code: "KeyX", keyCode: 0, charCode: "abc".charCodeAt(0), defaultPrevented: true }); 2465 checkKeyAttrs("TIP.keydown(keyABC) and TIP.keyup(keyABC)", events[2], 2466 { type: "keypress", key: "abc".charAt(1), code: "KeyX", keyCode: 0, charCode: "abc".charCodeAt(1), defaultPrevented: true }); 2467 checkKeyAttrs("TIP.keydown(keyABC) and TIP.keyup(keyABC)", events[3], 2468 { type: "keypress", key: "abc".charAt(2), code: "KeyX", keyCode: 0, charCode: "abc".charCodeAt(2), defaultPrevented: true }); 2469 checkKeyAttrs("TIP.keydown(keyABC) and TIP.keyup(keyABC)", events[4], 2470 { type: "keyup", key: "abc", code: "KeyX", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, defaultPrevented: false }); 2471 is(input.value, "abc", 2472 description + "input.value should be \"abc\""); 2473 2474 // Emulates pressing and releasing a key which introduces a surrogate pair. 2475 async function test_press_and_release_surrogate_pair_key( 2476 aTestPerSurrogateKeyPress, 2477 aTestIllFormedUTF16KeyValue = false 2478 ) { 2479 await SpecialPowers.pushPrefEnv({ 2480 set: [ 2481 ["dom.event.keypress.dispatch_once_per_surrogate_pair", !aTestPerSurrogateKeyPress], 2482 ["dom.event.keypress.key.allow_lone_surrogate", aTestIllFormedUTF16KeyValue], 2483 ], 2484 }); 2485 2486 const settingDescription = 2487 `aTestPerSurrogateKeyPress=${aTestPerSurrogateKeyPress}, aTestIllFormedUTF16KeyValue=${aTestIllFormedUTF16KeyValue}`; 2488 const allowIllFormedUTF16 = aTestPerSurrogateKeyPress && aTestIllFormedUTF16KeyValue; 2489 const keySurrogatePair = new KeyboardEvent("", { key: "\uD842\uDFB7", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); 2490 2491 input.value = ""; 2492 reset(); 2493 doDefaultKeydown = TIP.keydown(keySurrogatePair); 2494 doDefaultKeyup = TIP.keyup(keySurrogatePair); 2495 2496 is( 2497 doDefaultKeydown, 2498 TIP.KEYPRESS_IS_CONSUMED, 2499 `${ 2500 description 2501 }TIP.keydown(keySurrogatePair), ${ 2502 settingDescription 2503 }, should return 0x02 because the keypress event should be consumed by the input element` 2504 ); 2505 is( 2506 doDefaultKeyup, 2507 true, 2508 `${description}TIP.keyup(keySurrogatePair) should return true` 2509 ); 2510 is( 2511 events.length, 2512 aTestPerSurrogateKeyPress ? 4 : 3, 2513 `${description}TIP.keydown(keySurrogatePair), ${ 2514 settingDescription 2515 }, should cause keydown, keypress${ 2516 aTestPerSurrogateKeyPress ? ", keypress" : "" 2517 } and keyup event` 2518 ); 2519 checkKeyAttrs( 2520 `${description}TIP.keydown(keySurrogatePair), ${settingDescription}`, 2521 events[0], 2522 { 2523 type: "keydown", 2524 key: "\uD842\uDFB7", 2525 code: "KeyA", 2526 keyCode: KeyboardEvent.DOM_VK_A, 2527 charCode: 0, 2528 } 2529 ); 2530 if (aTestPerSurrogateKeyPress) { 2531 checkKeyAttrs( 2532 `${description}TIP.keydown(keySurrogatePair), ${settingDescription}`, 2533 events[1], 2534 { 2535 type: "keypress", 2536 key: allowIllFormedUTF16 2537 ? "\uD842" 2538 : "\uD842\uDFB7", // First keypress should have the surrogate pair 2539 code: "KeyA", 2540 keyCode: 0, 2541 charCode: "\uD842\uDFB7".charCodeAt(0), 2542 defaultPrevented: true, 2543 } 2544 ); 2545 checkKeyAttrs( 2546 `${description}TIP.keydown(keySurrogatePair), ${settingDescription}`, 2547 events[2], 2548 { 2549 type: "keypress", 2550 key: allowIllFormedUTF16 2551 ? "\uDFB7" 2552 : "", // But the following keypress should have empty string, instead 2553 code: "KeyA", 2554 keyCode: 0, 2555 charCode: "\uD842\uDFB7".charCodeAt(1), 2556 defaultPrevented: true, 2557 } 2558 ); 2559 } else { 2560 checkKeyAttrs( 2561 `${description}TIP.keydown(keySurrogatePair), ${settingDescription}`, 2562 events[1], 2563 { 2564 type: "keypress", 2565 key: "\uD842\uDFB7", 2566 code: "KeyA", 2567 keyCode: 0, 2568 charCode: 0x20BB7, 2569 defaultPrevented: true, 2570 } 2571 ); 2572 } 2573 checkKeyAttrs( 2574 `${description}TIP.keyup(keySurrogatePair), ${settingDescription}`, 2575 events[aTestPerSurrogateKeyPress ? 3 : 2], 2576 { 2577 type: "keyup", 2578 key: "\uD842\uDFB7", 2579 code: "KeyA", 2580 keyCode: KeyboardEvent.DOM_VK_A, 2581 charCode: 0, 2582 } 2583 ); 2584 is( 2585 input.value, 2586 "\uD842\uDFB7", 2587 `${description}${settingDescription}, input.value should be the surrogate pair` 2588 ); 2589 }; 2590 2591 await test_press_and_release_surrogate_pair_key(true, true); 2592 await test_press_and_release_surrogate_pair_key(true, false); 2593 await test_press_and_release_surrogate_pair_key(false); 2594 2595 // If KEY_FORCE_PRINTABLE_KEY is specified, registered key names can be a printable key which inputs the specified value. 2596 input.value = ""; 2597 var keyEnterPrintable = new KeyboardEvent("", { key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }); 2598 2599 reset(); 2600 doDefaultKeydown = TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY); 2601 doDefaultKeyup = TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY); 2602 2603 is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED, 2604 description + "TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should return 0x02 because the keypress events should be consumed by the input element"); 2605 ok(doDefaultKeyup, 2606 description + "TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should return true"); 2607 is(events.length, 7, 2608 description + "TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) should cause keydown, keypress, keypress, keypress, keypress, keypress and keyup event"); 2609 checkKeyAttrs("TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)", events[0], 2610 { type: "keydown", key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN, charCode: 0, defaultPrevented: false }); 2611 checkKeyAttrs("TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)", events[1], 2612 { type: "keypress", key: "Enter".charAt(0), code: "Enter", keyCode: 0, charCode: "Enter".charCodeAt(0), defaultPrevented: true }); 2613 checkKeyAttrs("TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)", events[2], 2614 { type: "keypress", key: "Enter".charAt(1), code: "Enter", keyCode: 0, charCode: "Enter".charCodeAt(1), defaultPrevented: true }); 2615 checkKeyAttrs("TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)", events[3], 2616 { type: "keypress", key: "Enter".charAt(2), code: "Enter", keyCode: 0, charCode: "Enter".charCodeAt(2), defaultPrevented: true }); 2617 checkKeyAttrs("TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)", events[4], 2618 { type: "keypress", key: "Enter".charAt(3), code: "Enter", keyCode: 0, charCode: "Enter".charCodeAt(3), defaultPrevented: true }); 2619 checkKeyAttrs("TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)", events[5], 2620 { type: "keypress", key: "Enter".charAt(4), code: "Enter", keyCode: 0, charCode: "Enter".charCodeAt(4), defaultPrevented: true }); 2621 checkKeyAttrs("TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY) and TIP.keyup(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)", events[6], 2622 { type: "keyup", key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN, charCode: 0, defaultPrevented: false }); 2623 is(input.value, "Enter", 2624 description + "input.value should be \"Enter\""); 2625 2626 // modifiers should be ignored. 2627 var keyWithModifiers = new KeyboardEvent("", { key: "Escape", code: "Escape", shiftKey: true, ctrlKey: true, altKey: true, metaKey: true }); 2628 2629 reset(); 2630 doDefaultKeydown = TIP.keydown(keyWithModifiers); 2631 doDefaultKeyup = TIP.keyup(keyWithModifiers); 2632 2633 is(doDefaultKeydown, 0, 2634 description + "TIP.keydown(keyWithModifiers) should return 0"); 2635 ok(doDefaultKeyup, 2636 description + "TIP.keyup(keyWithModifiers) should return true"); 2637 is(events.length, 3, 2638 description + "TIP.keydown(keyWithModifiers) and TIP.keyup(keyWithModifiers) should cause keydown, keypress and keyup event"); 2639 checkKeyAttrs("TIP.keydown(keyWithModifiers) and TIP.keyup(keyWithModifiers)", events[0], 2640 { type: "keydown", key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE }); 2641 checkKeyAttrs("TIP.keydown(keyWithModifiers) and TIP.keyup(keyWithModifiers)", events[1], 2642 { type: "keypress", key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE }); 2643 checkKeyAttrs("TIP.keydown(keyWithModifiers) and TIP.keyup(keyWithModifiers)", events[2], 2644 { type: "keyup", key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE }); 2645 is(input.value, "Enter", 2646 description + "input.value should stay \"Enter\" which was inputted by TIP.keydown(keyEnterPrintable, TIP.KEY_FORCE_PRINTABLE_KEY)"); 2647 2648 // Call preventDefault() at keydown 2649 input.value = ""; 2650 reset(); 2651 doPreventDefaults = [ "keydown" ]; 2652 doDefaultKeydown = TIP.keydown(keyA); 2653 doDefaultKeyup = TIP.keyup(keyA); 2654 2655 is(doDefaultKeydown, TIP.KEYDOWN_IS_CONSUMED, 2656 description + "TIP.keydown(keyA) should return 0x01 because keydown event's preventDefault should be called"); 2657 ok(doDefaultKeyup, 2658 description + "TIP.keyup(keyA) should return true"); 2659 is(events.length, 2, 2660 description + "TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keydown should cause keydown and keyup event"); 2661 checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keydown", events[0], 2662 { type: "keydown", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, defaultPrevented: true }); 2663 checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keydown", events[1], 2664 { type: "keyup", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, defaultPrevented: false }); 2665 is(input.value, "", 2666 description + "input.value shouldn't be modified by TIP.keyup(keyA) if the keydown event is consumed"); 2667 2668 // Call preventDefault() at keypress 2669 reset(); 2670 doPreventDefaults = [ "keypress" ]; 2671 doDefaultKeydown = TIP.keydown(keyA); 2672 doDefaultKeyup = TIP.keyup(keyA); 2673 2674 is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED, 2675 description + "TIP.keydown(keyA) should return 0x02 because keypress event's preventDefault should be called"); 2676 ok(doDefaultKeyup, 2677 description + "TIP.keyup(keyA) should return true"); 2678 is(events.length, 3, 2679 description + "TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keypress should cause keydown, keypress and keyup event"); 2680 checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keypress", events[0], 2681 { type: "keydown", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, defaultPrevented: false }); 2682 checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keypress", events[1], 2683 { type: "keypress", key: "a", code: "KeyA", keyCode: 0, charCode: "a".charCodeAt(0), defaultPrevented: true }); 2684 checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keypress", events[2], 2685 { type: "keyup", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, defaultPrevented: false }); 2686 is(input.value, "", 2687 description + "input.value shouldn't be modified by TIP.keyup(keyA) if the keypress event is consumed"); 2688 2689 // Call preventDefault() at keyup 2690 input.value = ""; 2691 reset(); 2692 doPreventDefaults = [ "keyup" ]; 2693 doDefaultKeydown = TIP.keydown(keyA); 2694 doDefaultKeyup = TIP.keyup(keyA); 2695 2696 is(doDefaultKeydown, TIP.KEYPRESS_IS_CONSUMED, 2697 description + "TIP.keydown(keyA) should return 0x02 because the key event should be consumed by the input element"); 2698 ok(!doDefaultKeyup, 2699 description + "TIP.keyup(keyA) should return false because keyup event's preventDefault should be called"); 2700 is(events.length, 3, 2701 description + "TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keyup should cause keydown, keypress and keyup event"); 2702 checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keyup", events[0], 2703 { type: "keydown", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, defaultPrevented: false }); 2704 checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keyup", events[1], 2705 { type: "keypress", key: "a", code: "KeyA", keyCode: 0, charCode: "a".charCodeAt(0), defaultPrevented: true }); 2706 checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keyup", events[2], 2707 { type: "keyup", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, defaultPrevented: true }); 2708 is(input.value, "a", 2709 description + "input.value should be \"a\" by TIP.keyup(keyA) even if the keyup event is consumed"); 2710 2711 // key events during composition 2712 ok(TIP.startComposition(), "TIP.startComposition() should start composition"); 2713 2714 input.value = ""; 2715 2716 reset(); 2717 TIP.keydown(keyA); 2718 is(events.length, 1, 2719 description + "TIP.keydown(keyA) should cause keydown event even composition if it's enabled by the pref"); 2720 checkKeyAttrs("TIP.keydown(keyA) during composition", events[0], 2721 { type: "keydown", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, isComposing: true }); 2722 reset(); 2723 TIP.keyup(keyA); 2724 is(events.length, 1, 2725 description + "TIP.keyup(keyA) should cause keyup event even composition if it's enabled by the pref"); 2726 checkKeyAttrs("TIP.keyup(keyA) during composition", events[0], 2727 { type: "keyup", key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, isComposing: true }); 2728 2729 TIP.cancelComposition(); 2730 2731 // Test .location computation 2732 const kCodeToLocation = [ 2733 { code: "BracketLeft", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2734 { code: "BracketRight", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2735 { code: "Comma", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2736 { code: "Digit0", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2737 { code: "Digit1", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2738 { code: "Digit2", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2739 { code: "Digit3", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2740 { code: "Digit4", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2741 { code: "Digit5", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2742 { code: "Digit6", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2743 { code: "Digit7", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2744 { code: "Digit8", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2745 { code: "Digit9", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2746 { code: "Equal", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2747 { code: "Minus", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2748 { code: "Period", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2749 { code: "Slash", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2750 { code: "AltLeft", location: KeyboardEvent.DOM_KEY_LOCATION_LEFT }, 2751 { code: "AltRight", location: KeyboardEvent.DOM_KEY_LOCATION_RIGHT }, 2752 { code: "CapsLock", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2753 { code: "ContextMenu", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2754 { code: "ControlLeft", location: KeyboardEvent.DOM_KEY_LOCATION_LEFT }, 2755 { code: "ControlRight", location: KeyboardEvent.DOM_KEY_LOCATION_RIGHT }, 2756 { code: "Enter", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2757 { code: "MetaLeft", location: KeyboardEvent.DOM_KEY_LOCATION_LEFT }, 2758 { code: "MetaRight", location: KeyboardEvent.DOM_KEY_LOCATION_RIGHT }, 2759 { code: "ShiftLeft", location: KeyboardEvent.DOM_KEY_LOCATION_LEFT }, 2760 { code: "ShiftRight", location: KeyboardEvent.DOM_KEY_LOCATION_RIGHT }, 2761 { code: "Space", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2762 { code: "Tab", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2763 { code: "ArrowDown", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2764 { code: "ArrowLeft", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2765 { code: "ArrowRight", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2766 { code: "ArrowUp", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2767 { code: "NumLock", location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD }, 2768 { code: "Numpad0", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2769 { code: "Numpad1", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2770 { code: "Numpad2", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2771 { code: "Numpad3", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2772 { code: "Numpad4", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2773 { code: "Numpad5", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2774 { code: "Numpad6", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2775 { code: "Numpad7", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2776 { code: "Numpad8", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2777 { code: "Numpad9", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2778 { code: "NumpadAdd", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2779 { code: "NumpadBackspace", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2780 { code: "NumpadClear", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2781 { code: "NumpadClearEntry", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2782 { code: "NumpadComma", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2783 { code: "NumpadDecimal", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2784 { code: "NumpadDivide", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2785 { code: "NumpadEnter", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2786 { code: "NumpadEqual", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2787 { code: "NumpadMemoryAdd", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2788 { code: "NumpadMemoryClear", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2789 { code: "NumpadMemoryRecall", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2790 { code: "NumpadMemoryStore", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2791 { code: "NumpadMemorySubtract", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2792 { code: "NumpadMultiply", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2793 { code: "NumpadParenLeft", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2794 { code: "NumpadParenRight", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2795 { code: "NumpadSubtract", location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD }, 2796 ]; 2797 for (let i = 0; i < kCodeToLocation.length; i++) { 2798 let keyEvent = new KeyboardEvent("", { code: kCodeToLocation[i].code }); 2799 reset(); 2800 doPreventDefaults = [ "keypress" ]; 2801 // If the location isn't initialized or initialized with 0, it should be computed from the code value. 2802 TIP.keydown(keyEvent); 2803 TIP.keyup(keyEvent); 2804 let longDesc = description + "testing computation of .location of \"" + kCodeToLocation[i].code + "\", "; 2805 is(events.length, 3, 2806 longDesc + "keydown, keypress and keyup events should be fired"); 2807 for (let j = 0; j < events.length; j++) { 2808 is(events[j].location, kCodeToLocation[i].location, 2809 longDesc + " type=\"" + events[j].type + "\", location value is wrong"); 2810 } 2811 // However, if KEY_KEEP_KEY_LOCATION_STANDARD is specified, .location value should be kept as DOM_KEY_LOCATION_STANDARD (0). 2812 reset(); 2813 doPreventDefaults = [ "keypress" ]; 2814 TIP.keydown(keyEvent, TIP.KEY_KEEP_KEY_LOCATION_STANDARD); 2815 TIP.keyup(keyEvent, TIP.KEY_KEEP_KEY_LOCATION_STANDARD); 2816 longDesc = description + "testing if .location is forcibly set to DOM_KEY_LOCATION_STANDARD, "; 2817 is(events.length, 3, 2818 longDesc + "keydown, keypress and keyup events should be fired"); 2819 for (let j = 0; j < events.length; j++) { 2820 is(events[j].location, KeyboardEvent.DOM_KEY_LOCATION_STANDARD, 2821 longDesc + " type=\"" + events[j].type + "\", location value is not 0"); 2822 } 2823 // If .location is initialized with non-zero value, the value shouldn't be computed again. 2824 let keyEventWithLocation = new KeyboardEvent("", { code: kCodeToLocation[i].code, location: 0xFF }); 2825 reset(); 2826 doPreventDefaults = [ "keypress" ]; 2827 TIP.keydown(keyEventWithLocation); 2828 TIP.keyup(keyEventWithLocation); 2829 longDesc = description + "testing if .location is not computed for \"" + kCodeToLocation[i].location + "\", "; 2830 is(events.length, 3, 2831 longDesc + "keydown, keypress and keyup events should be fired"); 2832 for (let j = 0; j < events.length; j++) { 2833 is(events[j].location, 0xFF, 2834 longDesc + " type=\"" + events[j].type + "\", location shouldn't be computed if it's initialized with non-zero value"); 2835 } 2836 } 2837 2838 // Test .keyCode value computation 2839 const kKeyToKeyCode = [ 2840 { key: "Cancel", keyCode: KeyboardEvent.DOM_VK_CANCEL }, 2841 { key: "Help", keyCode: KeyboardEvent.DOM_VK_HELP }, 2842 { key: "Backspace", keyCode: KeyboardEvent.DOM_VK_BACK_SPACE }, 2843 { key: "Tab", keyCode: KeyboardEvent.DOM_VK_TAB }, 2844 { key: "Clear", keyCode: KeyboardEvent.DOM_VK_CLEAR }, 2845 { key: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }, 2846 { key: "Shift", keyCode: KeyboardEvent.DOM_VK_SHIFT, isModifier: true }, 2847 { key: "Control", keyCode: KeyboardEvent.DOM_VK_CONTROL, isModifier: true }, 2848 { key: "Alt", keyCode: KeyboardEvent.DOM_VK_ALT, isModifier: true }, 2849 { key: "Pause", keyCode: KeyboardEvent.DOM_VK_PAUSE }, 2850 { key: "CapsLock", keyCode: KeyboardEvent.DOM_VK_CAPS_LOCK, isModifier: true, isLockableModifier: true }, 2851 { key: "Hiragana", keyCode: KeyboardEvent.DOM_VK_KANA }, 2852 { key: "Katakana", keyCode: KeyboardEvent.DOM_VK_KANA }, 2853 { key: "HiraganaKatakana", keyCode: KeyboardEvent.DOM_VK_KANA }, 2854 { key: "KanaMode", keyCode: KeyboardEvent.DOM_VK_KANA }, 2855 { key: "HangulMode", keyCode: KeyboardEvent.DOM_VK_HANGUL }, 2856 { key: "Eisu", keyCode: KeyboardEvent.DOM_VK_EISU }, 2857 { key: "JunjaMode", keyCode: KeyboardEvent.DOM_VK_JUNJA }, 2858 { key: "FinalMode", keyCode: KeyboardEvent.DOM_VK_FINAL }, 2859 { key: "HanjaMode", keyCode: KeyboardEvent.DOM_VK_HANJA }, 2860 { key: "KanjiMode", keyCode: KeyboardEvent.DOM_VK_KANJI }, 2861 { key: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE }, 2862 { key: "Convert", keyCode: KeyboardEvent.DOM_VK_CONVERT }, 2863 { key: "NonConvert", keyCode: KeyboardEvent.DOM_VK_NONCONVERT }, 2864 { key: "Accept", keyCode: KeyboardEvent.DOM_VK_ACCEPT }, 2865 { key: "ModeChange", keyCode: KeyboardEvent.DOM_VK_MODECHANGE }, 2866 { key: "PageUp", keyCode: KeyboardEvent.DOM_VK_PAGE_UP }, 2867 { key: "PageDown", keyCode: KeyboardEvent.DOM_VK_PAGE_DOWN }, 2868 { key: "End", keyCode: KeyboardEvent.DOM_VK_END }, 2869 { key: "Home", keyCode: KeyboardEvent.DOM_VK_HOME }, 2870 { key: "ArrowLeft", keyCode: KeyboardEvent.DOM_VK_LEFT }, 2871 { key: "ArrowUp", keyCode: KeyboardEvent.DOM_VK_UP }, 2872 { key: "ArrowRight", keyCode: KeyboardEvent.DOM_VK_RIGHT }, 2873 { key: "ArrowDown", keyCode: KeyboardEvent.DOM_VK_DOWN }, 2874 { key: "Select", keyCode: KeyboardEvent.DOM_VK_SELECT }, 2875 { key: "Print", keyCode: KeyboardEvent.DOM_VK_PRINT }, 2876 { key: "Execute", keyCode: KeyboardEvent.DOM_VK_EXECUTE }, 2877 { key: "PrintScreen", keyCode: KeyboardEvent.DOM_VK_PRINTSCREEN }, 2878 { key: "Insert", keyCode: KeyboardEvent.DOM_VK_INSERT }, 2879 { key: "Delete", keyCode: KeyboardEvent.DOM_VK_DELETE }, 2880 { key: "ContextMenu", keyCode: KeyboardEvent.DOM_VK_CONTEXT_MENU }, 2881 { key: "F1", keyCode: KeyboardEvent.DOM_VK_F1 }, 2882 { key: "F2", keyCode: KeyboardEvent.DOM_VK_F2 }, 2883 { key: "F3", keyCode: KeyboardEvent.DOM_VK_F3 }, 2884 { key: "F4", keyCode: KeyboardEvent.DOM_VK_F4 }, 2885 { key: "F5", keyCode: KeyboardEvent.DOM_VK_F5 }, 2886 { key: "F6", keyCode: KeyboardEvent.DOM_VK_F6 }, 2887 { key: "F7", keyCode: KeyboardEvent.DOM_VK_F7 }, 2888 { key: "F8", keyCode: KeyboardEvent.DOM_VK_F8 }, 2889 { key: "F9", keyCode: KeyboardEvent.DOM_VK_F9 }, 2890 { key: "F10", keyCode: KeyboardEvent.DOM_VK_F10 }, 2891 { key: "F11", keyCode: KeyboardEvent.DOM_VK_F11 }, 2892 { key: "F12", keyCode: KeyboardEvent.DOM_VK_F12 }, 2893 { key: "F13", keyCode: KeyboardEvent.DOM_VK_F13 }, 2894 { key: "F14", keyCode: KeyboardEvent.DOM_VK_F14 }, 2895 { key: "F15", keyCode: KeyboardEvent.DOM_VK_F15 }, 2896 { key: "F16", keyCode: KeyboardEvent.DOM_VK_F16 }, 2897 { key: "F17", keyCode: KeyboardEvent.DOM_VK_F17 }, 2898 { key: "F18", keyCode: KeyboardEvent.DOM_VK_F18 }, 2899 { key: "F19", keyCode: KeyboardEvent.DOM_VK_F19 }, 2900 { key: "F20", keyCode: KeyboardEvent.DOM_VK_F20 }, 2901 { key: "F21", keyCode: KeyboardEvent.DOM_VK_F21 }, 2902 { key: "F22", keyCode: KeyboardEvent.DOM_VK_F22 }, 2903 { key: "F23", keyCode: KeyboardEvent.DOM_VK_F23 }, 2904 { key: "F24", keyCode: KeyboardEvent.DOM_VK_F24 }, 2905 { key: "NumLock", keyCode: KeyboardEvent.DOM_VK_NUM_LOCK, isModifier: true, isLockableModifier: true }, 2906 { key: "ScrollLock", keyCode: KeyboardEvent.DOM_VK_SCROLL_LOCK, isModifier: true, isLockableModifier: true }, 2907 { key: "AudioVolumeMute", keyCode: KeyboardEvent.DOM_VK_VOLUME_MUTE }, 2908 { key: "AudioVolumeDown", keyCode: KeyboardEvent.DOM_VK_VOLUME_DOWN }, 2909 { key: "AudioVolumeUp", keyCode: KeyboardEvent.DOM_VK_VOLUME_UP }, 2910 { key: "Meta", keyCode: kIsMac 2911 ? KeyboardEvent.DOM_VK_META 2912 : KeyboardEvent.DOM_VK_WIN, isModifier: true }, 2913 { key: "AltGraph", keyCode: KeyboardEvent.DOM_VK_ALTGR, isModifier: true }, 2914 { key: "Attn", keyCode: KeyboardEvent.DOM_VK_ATTN }, 2915 { key: "CrSel", keyCode: KeyboardEvent.DOM_VK_CRSEL }, 2916 { key: "ExSel", keyCode: KeyboardEvent.DOM_VK_EXSEL }, 2917 { key: "EraseEof", keyCode: KeyboardEvent.DOM_VK_EREOF }, 2918 { key: "Play", keyCode: KeyboardEvent.DOM_VK_PLAY }, 2919 { key: "ZoomToggle", keyCode: KeyboardEvent.DOM_VK_ZOOM }, 2920 { key: "ZoomIn", keyCode: KeyboardEvent.DOM_VK_ZOOM }, 2921 { key: "ZoomOut", keyCode: KeyboardEvent.DOM_VK_ZOOM }, 2922 { key: "Unidentified", keyCode: 0 }, 2923 { key: "a", keyCode: 0, isPrintable: true }, 2924 { key: "A", keyCode: 0, isPrintable: true }, 2925 { key: " ", keyCode: 0, isPrintable: true }, 2926 { key: "", keyCode: 0, isPrintable: true }, 2927 ]; 2928 2929 for (let i = 0; i < kKeyToKeyCode.length; i++) { 2930 let keyEvent = new KeyboardEvent("", { key: kKeyToKeyCode[i].key }); 2931 var causeKeypress = !kKeyToKeyCode[i].isModifier; 2932 var baseFlags = kKeyToKeyCode[i].isPrintable ? 0 : TIP.KEY_NON_PRINTABLE_KEY; 2933 reset(); 2934 doPreventDefaults = [ "keypress" ]; 2935 // If the keyCode isn't initialized or initialized with 0, it should be computed from the key value only when it's a printable key. 2936 TIP.keydown(keyEvent, baseFlags); 2937 TIP.keyup(keyEvent, baseFlags); 2938 let longDesc = description + "testing computation of .keyCode of \"" + kKeyToKeyCode[i].key + "\", "; 2939 is(events.length, causeKeypress ? 3 : 2, 2940 longDesc + "keydown" + (causeKeypress ? ", keypress" : "") + " and keyup events should be fired"); 2941 for (let j = 0; j < events.length; j++) { 2942 is(events[j].keyCode, events[j].type == "keypress" && kKeyToKeyCode[i].isPrintable ? 0 : kKeyToKeyCode[i].keyCode, 2943 longDesc + " type=\"" + events[j].type + "\", keyCode value is wrong"); 2944 } 2945 // However, if KEY_KEEP_KEYCODE_ZERO is specified, .keyCode value should be kept as 0. 2946 reset(); 2947 doPreventDefaults = [ "keypress" ]; 2948 TIP.keydown(keyEvent, TIP.KEY_KEEP_KEYCODE_ZERO | baseFlags); 2949 TIP.keyup(keyEvent, TIP.KEY_KEEP_KEYCODE_ZERO | baseFlags); 2950 longDesc = description + "testing if .keyCode is forcibly set to KEY_KEEP_KEYCODE_ZERO, "; 2951 is(events.length, causeKeypress ? 3 : 2, 2952 longDesc + "keydown" + (causeKeypress ? ", keypress" : "") + " and keyup events should be fired"); 2953 for (let j = 0; j < events.length; j++) { 2954 is(events[j].keyCode, 0, 2955 longDesc + " type=\"" + events[j].type + "\", keyCode value is not 0"); 2956 } 2957 // If .keyCode is initialized with non-zero value, the value shouldn't be computed again. 2958 let keyEventWithLocation = new KeyboardEvent("", { key: kKeyToKeyCode[i].key, keyCode: 0xFF }); 2959 reset(); 2960 doPreventDefaults = [ "keypress" ]; 2961 TIP.keydown(keyEventWithLocation, baseFlags); 2962 TIP.keyup(keyEventWithLocation, baseFlags); 2963 longDesc = description + "testing if .keyCode is not computed for \"" + kKeyToKeyCode[i].key + "\", "; 2964 is(events.length, causeKeypress ? 3 : 2, 2965 longDesc + "keydown" + (causeKeypress ? ", keypress" : "") + " and keyup events should be fired"); 2966 for (let j = 0; j < events.length; j++) { 2967 is(events[j].keyCode, events[j].type == "keypress" && kKeyToKeyCode[i].isPrintable ? 0 : 0xFF, 2968 longDesc + " type=\"" + events[j].type + "\", keyCode shouldn't be computed if it's initialized with non-zero value"); 2969 } 2970 // Unlock lockable modifier if the key is a lockable modifier key. 2971 if (kKeyToKeyCode[i].isLockableModifier) { 2972 TIP.keydown(keyEvent, baseFlags); 2973 TIP.keyup(keyEvent, baseFlags); 2974 } 2975 } 2976 2977 // Modifier state tests 2978 var sharedTIP = createTIP(); 2979 ok(sharedTIP.beginInputTransactionForTests(otherWindow), 2980 description + "sharedTIP.beginInputTransactionForTests(otherWindow) should return true"); 2981 TIP.shareModifierStateOf(sharedTIP); 2982 var independentTIP = createTIP(); 2983 const kModifierKeys = [ 2984 { key: "Alt", code: "AltLeft", isLockable: false }, 2985 { key: "Alt", code: "AltRight", isLockable: false }, 2986 { key: "AltGraph", code: "AltRight", isLockable: false }, 2987 { key: "CapsLock", code: "CapsLock", isLockable: true }, 2988 { key: "Control", code: "ControlLeft", isLockable: false }, 2989 { key: "Control", code: "ControlRight", isLockable: false }, 2990 { key: "Fn", code: "Fn", isLockable: false }, 2991 { key: "FnLock", code: "", isLockable: true }, 2992 { key: "Meta", code: "MetaLeft", isLockable: false }, 2993 { key: "Meta", code: "MetaRight", isLockable: false }, 2994 { key: "NumLock", code: "NumLock", isLockable: true }, 2995 { key: "ScrollLock", code: "ScrollLock", isLockable: true }, 2996 { key: "Shift", code: "ShiftLeft", isLockable: false }, 2997 { key: "Shift", code: "ShiftRight", isLockable: false }, 2998 { key: "Symbol", code: "", isLockable: false }, 2999 { key: "SymbolLock", code: "", isLockable: true }, 3000 ]; 3001 3002 function checkModifiers(aTestDesc, aEvent, aType, aKey, aCode, aModifiers) 3003 { 3004 var desc = description + aTestDesc + ", type=\"" + aEvent.type + "\", key=\"" + aEvent.key + "\", code=\"" + aEvent.code + "\""; 3005 is(aEvent.type, aType, 3006 desc + ", .type value is wrong"); 3007 if (aEvent.type != aType) { 3008 return; 3009 } 3010 is(aEvent.key, aKey, 3011 desc + ", .key value is wrong"); 3012 is(aEvent.code, aCode, 3013 desc + ", .code value is wrong"); 3014 is(aEvent.altKey, aModifiers.includes("Alt"), 3015 desc + ", .altKey value is wrong"); 3016 is(aEvent.ctrlKey, aModifiers.includes("Control"), 3017 desc + ", .ctrlKey value is wrong"); 3018 is(aEvent.metaKey, aModifiers.includes("Meta"), 3019 desc + ", .metaKey value is wrong"); 3020 is(aEvent.shiftKey, aModifiers.includes("Shift"), 3021 desc + ", .shiftKey value is wrong"); 3022 /* eslint-disable-next-line no-shadow */ 3023 for (var i = 0; i < kModifiers.length; i++) { 3024 is(aEvent.getModifierState(kModifiers[i]), aModifiers.includes(kModifiers[i]), 3025 desc + ", .getModifierState(\"" + kModifiers[i] + "\") returns wrong value"); 3026 } 3027 } 3028 3029 function checkAllTIPModifiers(aTestDesc, aModifiers) 3030 { 3031 /* eslint-disable-next-line no-shadow */ 3032 for (var i = 0; i < kModifiers.length; i++) { 3033 is(TIP.getModifierState(kModifiers[i]), aModifiers.includes(kModifiers[i]), 3034 aTestDesc + ", TIP.getModifierState(\"" + kModifiers[i] + "\") returns wrong value"); 3035 is(sharedTIP.getModifierState(kModifiers[i]), TIP.getModifierState(kModifiers[i]), 3036 aTestDesc + ", sharedTIP.getModifierState(\"" + kModifiers[i] + "\") returns different value from TIP"); 3037 is(independentTIP.getModifierState(kModifiers[i]), false, 3038 aTestDesc + ", independentTIP.getModifierState(\"" + kModifiers[i] + "\") should return false"); 3039 } 3040 } 3041 3042 // First, all modifiers must be false. 3043 reset(); 3044 doPreventDefaults = [ "keypress" ]; 3045 TIP.keydown(keyA); 3046 TIP.keyup(keyA); 3047 3048 is(events.length, 3, 3049 description + "TIP.keydown(keyA) and TIP.keyup(keyA) should cause keydown, keypress and keyup"); 3050 checkModifiers("Before dispatching modifier key events", events[0], "keydown", "a", "KeyA", []); 3051 checkModifiers("Before dispatching modifier key events", events[1], "keypress", "a", "KeyA", []); 3052 checkModifiers("Before dispatching modifier key events", events[2], "keyup", "a", "KeyA", []); 3053 3054 // Test each modifier keydown/keyup causes activating/inactivating the modifier state. 3055 for (var i = 0; i < kModifierKeys.length; i++) { 3056 reset(); 3057 doPreventDefaults = [ "keypress" ]; 3058 var modKey = new KeyboardEvent("", { key: kModifierKeys[i].key, code: kModifierKeys[i].code }); 3059 let testDesc = "A modifier key \"" + kModifierKeys[i].key + "\" (\"" + kModifierKeys[i].code + "\") and a printable key"; 3060 if (!kModifierKeys[i].isLockable) { 3061 TIP.keydown(modKey); 3062 checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" keydown", [ kModifierKeys[i].key ]); 3063 TIP.keydown(keyA); 3064 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ kModifierKeys[i].key ]); 3065 TIP.keyup(keyA); 3066 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ kModifierKeys[i].key ]); 3067 TIP.keyup(modKey); 3068 checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" keyup", [ ]); 3069 is(events.length, 5, 3070 description + testDesc + " should cause 5 events"); 3071 checkModifiers(testDesc, events[0], "keydown", kModifierKeys[i].key, kModifierKeys[i].code, [ kModifierKeys[i].key ]); 3072 checkModifiers(testDesc, events[1], "keydown", "a", "KeyA", [ kModifierKeys[i].key ]); 3073 checkModifiers(testDesc, events[2], "keypress", "a", "KeyA", [ kModifierKeys[i].key ]); 3074 checkModifiers(testDesc, events[3], "keyup", "a", "KeyA", [ kModifierKeys[i].key ]); 3075 checkModifiers(testDesc, events[4], "keyup", kModifierKeys[i].key, kModifierKeys[i].code, [ ]); 3076 3077 // KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT shouldn't cause key events of modifier keys, but should modify the modifier state. 3078 reset(); 3079 doPreventDefaults = [ "keypress" ]; 3080 testDesc = "A modifier key \"" + kModifierKeys[i].key + "\" (\"" + kModifierKeys[i].code + "\") with KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT and a printable key"; 3081 TIP.keydown(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); 3082 TIP.keydown(keyA); 3083 TIP.keyup(keyA); 3084 TIP.keyup(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); 3085 TIP.keydown(keyA); 3086 TIP.keyup(keyA); 3087 is(events.length, 6, 3088 description + testDesc + " should cause 6 events"); 3089 checkModifiers(testDesc, events[0], "keydown", "a", "KeyA", [ kModifierKeys[i].key ]); 3090 checkModifiers(testDesc, events[1], "keypress", "a", "KeyA", [ kModifierKeys[i].key ]); 3091 checkModifiers(testDesc, events[2], "keyup", "a", "KeyA", [ kModifierKeys[i].key ]); 3092 checkModifiers(testDesc, events[3], "keydown", "a", "KeyA", [ ]); 3093 checkModifiers(testDesc, events[4], "keypress", "a", "KeyA", [ ]); 3094 checkModifiers(testDesc, events[5], "keyup", "a", "KeyA", [ ]); 3095 } else { 3096 TIP.keydown(modKey); 3097 checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" first keydown", [ kModifierKeys[i].key ]); 3098 TIP.keyup(modKey); 3099 checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" first keyup", [ kModifierKeys[i].key ]); 3100 TIP.keydown(keyA); 3101 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ kModifierKeys[i].key ]); 3102 TIP.keyup(keyA); 3103 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ kModifierKeys[i].key ]); 3104 TIP.keydown(modKey); 3105 checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" second keydown", [ ]); 3106 TIP.keyup(modKey); 3107 checkAllTIPModifiers(testDesc + ", \"" + kModifierKeys[i].key + "\" second keyup", [ ]); 3108 is(events.length, 7, 3109 description + testDesc + " should cause 7 events"); 3110 checkModifiers(testDesc, events[0], "keydown", kModifierKeys[i].key, kModifierKeys[i].code, [ kModifierKeys[i].key ]); 3111 checkModifiers(testDesc, events[1], "keyup", kModifierKeys[i].key, kModifierKeys[i].code, [ kModifierKeys[i].key ]); 3112 checkModifiers(testDesc, events[2], "keydown", "a", "KeyA", [ kModifierKeys[i].key ]); 3113 checkModifiers(testDesc, events[3], "keypress", "a", "KeyA", [ kModifierKeys[i].key ]); 3114 checkModifiers(testDesc, events[4], "keyup", "a", "KeyA", [ kModifierKeys[i].key ]); 3115 checkModifiers(testDesc, events[5], "keydown", kModifierKeys[i].key, kModifierKeys[i].code, [ ]); 3116 checkModifiers(testDesc, events[6], "keyup", kModifierKeys[i].key, kModifierKeys[i].code, [ ]); 3117 3118 // KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT shouldn't cause key events of modifier keys, but should modify the modifier state. 3119 reset(); 3120 doPreventDefaults = [ "keypress" ]; 3121 testDesc = "A modifier key \"" + kModifierKeys[i].key + "\" (\"" + kModifierKeys[i].code + "\") with KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT and a printable key"; 3122 TIP.keydown(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); 3123 TIP.keyup(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); 3124 TIP.keydown(keyA); 3125 TIP.keyup(keyA); 3126 TIP.keydown(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); 3127 TIP.keyup(modKey, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); 3128 TIP.keydown(keyA); 3129 TIP.keyup(keyA); 3130 is(events.length, 6, 3131 description + testDesc + " should cause 6 events"); 3132 checkModifiers(testDesc, events[0], "keydown", "a", "KeyA", [ kModifierKeys[i].key ]); 3133 checkModifiers(testDesc, events[1], "keypress", "a", "KeyA", [ kModifierKeys[i].key ]); 3134 checkModifiers(testDesc, events[2], "keyup", "a", "KeyA", [ kModifierKeys[i].key ]); 3135 checkModifiers(testDesc, events[3], "keydown", "a", "KeyA", [ ]); 3136 checkModifiers(testDesc, events[4], "keypress", "a", "KeyA", [ ]); 3137 checkModifiers(testDesc, events[5], "keyup", "a", "KeyA", [ ]); 3138 } 3139 } 3140 3141 // Modifier state should be inactivated only when all pressed modifiers are released 3142 var shiftLeft = new KeyboardEvent("", { key: "Shift", code: "ShiftLeft" }); 3143 var shiftRight = new KeyboardEvent("", { key: "Shift", code: "ShiftRight" }); 3144 var shiftVirtual = new KeyboardEvent("", { key: "Shift", code: "" }); 3145 var altGrVirtual = new KeyboardEvent("", { key: "AltGraph", code: "" }); 3146 var ctrlVirtual = new KeyboardEvent("", { key: "Control", code: "" }); 3147 3148 let testDesc = "ShiftLeft press -> ShiftRight press -> ShiftRight release -> ShiftLeft release"; 3149 reset(); 3150 doPreventDefaults = [ "keypress" ]; 3151 TIP.keydown(shiftLeft); 3152 checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]); 3153 TIP.keydown(shiftRight); 3154 checkAllTIPModifiers(testDesc + ", Right-Shift keydown", [ "Shift" ]); 3155 TIP.keydown(keyA); 3156 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]); 3157 TIP.keyup(keyA); 3158 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]); 3159 TIP.keyup(shiftRight); 3160 checkAllTIPModifiers(testDesc + ", Right-Shift keyup", [ "Shift" ]); 3161 TIP.keydown(keyA); 3162 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Right-Shift keyup)", [ "Shift" ]); 3163 TIP.keyup(keyA); 3164 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Right-Shift keyup)", [ "Shift" ]); 3165 TIP.keyup(shiftLeft); 3166 checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ ]); 3167 3168 is(events.length, 10, 3169 description + testDesc + " should cause 10 events"); 3170 checkModifiers(testDesc, events[0], "keydown", "Shift", "ShiftLeft", [ "Shift" ]); 3171 checkModifiers(testDesc, events[1], "keydown", "Shift", "ShiftRight", [ "Shift" ]); 3172 checkModifiers(testDesc, events[2], "keydown", "a", "KeyA", [ "Shift" ]); 3173 checkModifiers(testDesc, events[3], "keypress", "a", "KeyA", [ "Shift" ]); 3174 checkModifiers(testDesc, events[4], "keyup", "a", "KeyA", [ "Shift" ]); 3175 checkModifiers(testDesc, events[5], "keyup", "Shift", "ShiftRight", [ "Shift" ]); 3176 checkModifiers(testDesc, events[6], "keydown", "a", "KeyA", [ "Shift" ]); 3177 checkModifiers(testDesc, events[7], "keypress", "a", "KeyA", [ "Shift" ]); 3178 checkModifiers(testDesc, events[8], "keyup", "a", "KeyA", [ "Shift" ]); 3179 checkModifiers(testDesc, events[9], "keyup", "Shift", "ShiftLeft", [ ]); 3180 3181 testDesc = "ShiftLeft press -> ShiftRight press -> ShiftLeft release -> ShiftRight release"; 3182 reset(); 3183 doPreventDefaults = [ "keypress" ]; 3184 TIP.keydown(shiftLeft); 3185 checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]); 3186 TIP.keydown(shiftRight); 3187 checkAllTIPModifiers(testDesc + ", Right-Shift keydown", [ "Shift" ]); 3188 TIP.keydown(keyA); 3189 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]); 3190 TIP.keyup(keyA); 3191 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]); 3192 TIP.keyup(shiftLeft); 3193 checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ "Shift" ]); 3194 TIP.keydown(keyA); 3195 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Left-Shift keyup)", [ "Shift" ]); 3196 TIP.keyup(keyA); 3197 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Left-Shift keyup)", [ "Shift" ]); 3198 TIP.keyup(shiftRight); 3199 checkAllTIPModifiers(testDesc + ", Right-Shift keyup", [ ]); 3200 3201 is(events.length, 10, 3202 description + testDesc + " should cause 10 events"); 3203 checkModifiers(testDesc, events[0], "keydown", "Shift", "ShiftLeft", [ "Shift" ]); 3204 checkModifiers(testDesc, events[1], "keydown", "Shift", "ShiftRight", [ "Shift" ]); 3205 checkModifiers(testDesc, events[2], "keydown", "a", "KeyA", [ "Shift" ]); 3206 checkModifiers(testDesc, events[3], "keypress", "a", "KeyA", [ "Shift" ]); 3207 checkModifiers(testDesc, events[4], "keyup", "a", "KeyA", [ "Shift" ]); 3208 checkModifiers(testDesc, events[5], "keyup", "Shift", "ShiftLeft", [ "Shift" ]); 3209 checkModifiers(testDesc, events[6], "keydown", "a", "KeyA", [ "Shift" ]); 3210 checkModifiers(testDesc, events[7], "keypress", "a", "KeyA", [ "Shift" ]); 3211 checkModifiers(testDesc, events[8], "keyup", "a", "KeyA", [ "Shift" ]); 3212 checkModifiers(testDesc, events[9], "keyup", "Shift", "ShiftRight", [ ]); 3213 3214 testDesc = "ShiftLeft press -> virtual Shift press -> virtual Shift release -> ShiftLeft release"; 3215 reset(); 3216 doPreventDefaults = [ "keypress" ]; 3217 TIP.keydown(shiftLeft); 3218 checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]); 3219 TIP.keydown(shiftVirtual); 3220 checkAllTIPModifiers(testDesc + ", Virtual-Shift keydown", [ "Shift" ]); 3221 TIP.keydown(keyA); 3222 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]); 3223 TIP.keyup(keyA); 3224 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]); 3225 TIP.keyup(shiftVirtual); 3226 checkAllTIPModifiers(testDesc + ", Virtual-Shift keyup", [ "Shift" ]); 3227 TIP.keydown(keyA); 3228 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-Shift keyup)", [ "Shift" ]); 3229 TIP.keyup(keyA); 3230 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-Shift keyup)", [ "Shift" ]); 3231 TIP.keyup(shiftLeft); 3232 checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ ]); 3233 3234 is(events.length, 10, 3235 description + testDesc + " should cause 10 events"); 3236 checkModifiers(testDesc, events[0], "keydown", "Shift", "ShiftLeft", [ "Shift" ]); 3237 checkModifiers(testDesc, events[1], "keydown", "Shift", "", [ "Shift" ]); 3238 checkModifiers(testDesc, events[2], "keydown", "a", "KeyA", [ "Shift" ]); 3239 checkModifiers(testDesc, events[3], "keypress", "a", "KeyA", [ "Shift" ]); 3240 checkModifiers(testDesc, events[4], "keyup", "a", "KeyA", [ "Shift" ]); 3241 checkModifiers(testDesc, events[5], "keyup", "Shift", "", [ "Shift" ]); 3242 checkModifiers(testDesc, events[6], "keydown", "a", "KeyA", [ "Shift" ]); 3243 checkModifiers(testDesc, events[7], "keypress", "a", "KeyA", [ "Shift" ]); 3244 checkModifiers(testDesc, events[8], "keyup", "a", "KeyA", [ "Shift" ]); 3245 checkModifiers(testDesc, events[9], "keyup", "Shift", "ShiftLeft", [ ]); 3246 3247 testDesc = "virtual Shift press -> ShiftRight press -> ShiftRight release -> virtual Shift release"; 3248 reset(); 3249 doPreventDefaults = [ "keypress" ]; 3250 TIP.keydown(shiftVirtual); 3251 checkAllTIPModifiers(testDesc + ", Virtual-Shift keydown", [ "Shift" ]); 3252 TIP.keydown(shiftRight); 3253 checkAllTIPModifiers(testDesc + ", Right-Shift keydown", [ "Shift" ]); 3254 TIP.keydown(keyA); 3255 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]); 3256 TIP.keyup(keyA); 3257 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]); 3258 TIP.keyup(shiftRight); 3259 checkAllTIPModifiers(testDesc + ", Right-Shift keyup", [ "Shift" ]); 3260 TIP.keydown(keyA); 3261 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Right-Shift keyup)", [ "Shift" ]); 3262 TIP.keyup(keyA); 3263 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Right-Shift keyup)", [ "Shift" ]); 3264 TIP.keyup(shiftVirtual); 3265 checkAllTIPModifiers(testDesc + ", Virtual-Shift keyup", [ ]); 3266 3267 is(events.length, 10, 3268 description + testDesc + " should cause 10 events"); 3269 checkModifiers(testDesc, events[0], "keydown", "Shift", "", [ "Shift" ]); 3270 checkModifiers(testDesc, events[1], "keydown", "Shift", "ShiftRight", [ "Shift" ]); 3271 checkModifiers(testDesc, events[2], "keydown", "a", "KeyA", [ "Shift" ]); 3272 checkModifiers(testDesc, events[3], "keypress", "a", "KeyA", [ "Shift" ]); 3273 checkModifiers(testDesc, events[4], "keyup", "a", "KeyA", [ "Shift" ]); 3274 checkModifiers(testDesc, events[5], "keyup", "Shift", "ShiftRight", [ "Shift" ]); 3275 checkModifiers(testDesc, events[6], "keydown", "a", "KeyA", [ "Shift" ]); 3276 checkModifiers(testDesc, events[7], "keypress", "a", "KeyA", [ "Shift" ]); 3277 checkModifiers(testDesc, events[8], "keyup", "a", "KeyA", [ "Shift" ]); 3278 checkModifiers(testDesc, events[9], "keyup", "Shift", "", [ ]); 3279 3280 testDesc = "ShiftLeft press -> ShiftRight press -> ShiftRight release -> ShiftRight release -> ShiftLeft release"; 3281 reset(); 3282 doPreventDefaults = [ "keypress" ]; 3283 TIP.keydown(shiftLeft); 3284 checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]); 3285 TIP.keydown(shiftRight); 3286 checkAllTIPModifiers(testDesc + ", Right-Shift keydown", [ "Shift" ]); 3287 TIP.keydown(keyA); 3288 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]); 3289 TIP.keyup(keyA); 3290 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]); 3291 TIP.keyup(shiftRight); 3292 checkAllTIPModifiers(testDesc + ", Right-Shift keyup", [ "Shift" ]); 3293 TIP.keydown(keyA); 3294 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-Shift keyup)", [ "Shift" ]); 3295 TIP.keyup(keyA); 3296 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-Shift keyup)", [ "Shift" ]); 3297 TIP.keyup(shiftRight); 3298 checkAllTIPModifiers(testDesc + ", Right-Shift keyup again", [ "Shift" ]); 3299 TIP.keydown(keyA); 3300 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-Shift keyup again)", [ "Shift" ]); 3301 TIP.keyup(keyA); 3302 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-Shift keyup again)", [ "Shift" ]); 3303 TIP.keyup(shiftLeft); 3304 checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ ]); 3305 3306 is(events.length, 14, 3307 description + testDesc + " should cause 14 events"); 3308 checkModifiers(testDesc, events[0], "keydown", "Shift", "ShiftLeft", [ "Shift" ]); 3309 checkModifiers(testDesc, events[1], "keydown", "Shift", "ShiftRight", [ "Shift" ]); 3310 checkModifiers(testDesc, events[2], "keydown", "a", "KeyA", [ "Shift" ]); 3311 checkModifiers(testDesc, events[3], "keypress", "a", "KeyA", [ "Shift" ]); 3312 checkModifiers(testDesc, events[4], "keyup", "a", "KeyA", [ "Shift" ]); 3313 checkModifiers(testDesc, events[5], "keyup", "Shift", "ShiftRight", [ "Shift" ]); 3314 checkModifiers(testDesc, events[6], "keydown", "a", "KeyA", [ "Shift" ]); 3315 checkModifiers(testDesc, events[7], "keypress", "a", "KeyA", [ "Shift" ]); 3316 checkModifiers(testDesc, events[8], "keyup", "a", "KeyA", [ "Shift" ]); 3317 checkModifiers(testDesc, events[9], "keyup", "Shift", "ShiftRight", [ "Shift" ]); 3318 checkModifiers(testDesc, events[10], "keydown", "a", "KeyA", [ "Shift" ]); 3319 checkModifiers(testDesc, events[11], "keypress", "a", "KeyA", [ "Shift" ]); 3320 checkModifiers(testDesc, events[12], "keyup", "a", "KeyA", [ "Shift" ]); 3321 checkModifiers(testDesc, events[13], "keyup", "Shift", "ShiftLeft", [ ]); 3322 3323 testDesc = "ShiftLeft press -> ShiftLeft press -> ShiftLeft release -> ShiftLeft release"; 3324 reset(); 3325 doPreventDefaults = [ "keypress" ]; 3326 TIP.keydown(shiftLeft); 3327 checkAllTIPModifiers(testDesc + ", Left-Shift keydown", [ "Shift" ]); 3328 TIP.keydown(shiftLeft); 3329 checkAllTIPModifiers(testDesc + ", Left-Shift keydown again", [ "Shift" ]); 3330 TIP.keydown(keyA); 3331 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift" ]); 3332 TIP.keyup(keyA); 3333 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift" ]); 3334 TIP.keyup(shiftLeft); 3335 checkAllTIPModifiers(testDesc + ", Left-Shift keyup", [ ]); 3336 TIP.keydown(keyA); 3337 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Left-Shift keyup)", [ ]); 3338 TIP.keyup(keyA); 3339 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Left-Shift keyup)", [ ]); 3340 TIP.keyup(shiftLeft); 3341 checkAllTIPModifiers(testDesc + ", Left-Shift keyup again", [ ]); 3342 TIP.keydown(keyA); 3343 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Left-Shift keyup again)", [ ]); 3344 TIP.keyup(keyA); 3345 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Left-Shift keyup again)", [ ]); 3346 3347 is(events.length, 13, 3348 description + testDesc + " should cause 13 events"); 3349 checkModifiers(testDesc, events[0], "keydown", "Shift", "ShiftLeft", [ "Shift" ]); 3350 checkModifiers(testDesc, events[1], "keydown", "Shift", "ShiftLeft", [ "Shift" ]); 3351 checkModifiers(testDesc, events[2], "keydown", "a", "KeyA", [ "Shift" ]); 3352 checkModifiers(testDesc, events[3], "keypress", "a", "KeyA", [ "Shift" ]); 3353 checkModifiers(testDesc, events[4], "keyup", "a", "KeyA", [ "Shift" ]); 3354 checkModifiers(testDesc, events[5], "keyup", "Shift", "ShiftLeft", [ ]); 3355 checkModifiers(testDesc, events[6], "keydown", "a", "KeyA", [ ]); 3356 checkModifiers(testDesc, events[7], "keypress", "a", "KeyA", [ ]); 3357 checkModifiers(testDesc, events[8], "keyup", "a", "KeyA", [ ]); 3358 checkModifiers(testDesc, events[9], "keyup", "Shift", "ShiftLeft", [ ]); 3359 checkModifiers(testDesc, events[10], "keydown", "a", "KeyA", [ ]); 3360 checkModifiers(testDesc, events[11], "keypress", "a", "KeyA", [ ]); 3361 checkModifiers(testDesc, events[12], "keyup", "a", "KeyA", [ ]); 3362 3363 testDesc = "virtual Shift press -> virtual AltGraph press -> virtual AltGraph release -> virtual Shift release"; 3364 reset(); 3365 doPreventDefaults = [ "keypress" ]; 3366 TIP.keydown(shiftVirtual); 3367 checkAllTIPModifiers(testDesc + ", Virtual-Shift keydown", [ "Shift" ]); 3368 TIP.keydown(altGrVirtual); 3369 checkAllTIPModifiers(testDesc + ", Virtual-AltGraph keydown", [ "Shift", "AltGraph" ]); 3370 TIP.keydown(keyA); 3371 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift", "AltGraph" ]); 3372 TIP.keyup(keyA); 3373 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift", "AltGraph" ]); 3374 TIP.keyup(altGrVirtual); 3375 checkAllTIPModifiers(testDesc + ", Virtual-AltGraph keyup", [ "Shift" ]); 3376 TIP.keydown(keyA); 3377 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-AltGraph keyup)", [ "Shift" ]); 3378 TIP.keyup(keyA); 3379 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-AltGraph keyup)", [ "Shift" ]); 3380 TIP.keyup(shiftVirtual); 3381 checkAllTIPModifiers(testDesc + ", Virtual-Shift keyup", [ ]); 3382 3383 is(events.length, 10, 3384 description + testDesc + " should cause 10 events"); 3385 checkModifiers(testDesc, events[0], "keydown", "Shift", "", [ "Shift" ]); 3386 checkModifiers(testDesc, events[1], "keydown", "AltGraph", "", [ "Shift", "AltGraph" ]); 3387 checkModifiers(testDesc, events[2], "keydown", "a", "KeyA", [ "Shift", "AltGraph" ]); 3388 checkModifiers(testDesc, events[3], "keypress", "a", "KeyA", [ "Shift", "AltGraph" ]); 3389 checkModifiers(testDesc, events[4], "keyup", "a", "KeyA", [ "Shift", "AltGraph" ]); 3390 checkModifiers(testDesc, events[5], "keyup", "AltGraph", "", [ "Shift" ]); 3391 checkModifiers(testDesc, events[6], "keydown", "a", "KeyA", [ "Shift" ]); 3392 checkModifiers(testDesc, events[7], "keypress", "a", "KeyA", [ "Shift" ]); 3393 checkModifiers(testDesc, events[8], "keyup", "a", "KeyA", [ "Shift" ]); 3394 checkModifiers(testDesc, events[9], "keyup", "Shift", "", [ ]); 3395 3396 testDesc = "virtual Shift press -> virtual AltGraph press -> virtual Shift release -> virtual AltGr release"; 3397 reset(); 3398 doPreventDefaults = [ "keypress" ]; 3399 TIP.keydown(shiftVirtual); 3400 checkAllTIPModifiers(testDesc + ", Virtual-Shift keydown", [ "Shift" ]); 3401 TIP.keydown(altGrVirtual); 3402 checkAllTIPModifiers(testDesc + ", Virtual-AltGraph keydown", [ "Shift", "AltGraph" ]); 3403 TIP.keydown(keyA); 3404 checkAllTIPModifiers(testDesc + ", \"a\" keydown", [ "Shift", "AltGraph" ]); 3405 TIP.keyup(keyA); 3406 checkAllTIPModifiers(testDesc + ", \"a\" keyup", [ "Shift", "AltGraph" ]); 3407 TIP.keyup(shiftVirtual); 3408 checkAllTIPModifiers(testDesc + ", Virtual-Shift keyup", [ "AltGraph" ]); 3409 TIP.keydown(keyA); 3410 checkAllTIPModifiers(testDesc + ", \"a\" keydown (after Virtual-Shift keyup)", [ "AltGraph" ]); 3411 TIP.keyup(keyA); 3412 checkAllTIPModifiers(testDesc + ", \"a\" keyup (after Virtual-Shift keyup)", [ "AltGraph" ]); 3413 TIP.keyup(altGrVirtual); 3414 checkAllTIPModifiers(testDesc + ", Virtual-AltGraph keyup", [ ]); 3415 3416 is(events.length, 10, 3417 description + testDesc + " should cause 10 events"); 3418 checkModifiers(testDesc, events[0], "keydown", "Shift", "", [ "Shift" ]); 3419 checkModifiers(testDesc, events[1], "keydown", "AltGraph", "", [ "Shift", "AltGraph" ]); 3420 checkModifiers(testDesc, events[2], "keydown", "a", "KeyA", [ "Shift", "AltGraph" ]); 3421 checkModifiers(testDesc, events[3], "keypress", "a", "KeyA", [ "Shift", "AltGraph" ]); 3422 checkModifiers(testDesc, events[4], "keyup", "a", "KeyA", [ "Shift", "AltGraph" ]); 3423 checkModifiers(testDesc, events[5], "keyup", "Shift", "", [ "AltGraph" ]); 3424 checkModifiers(testDesc, events[6], "keydown", "a", "KeyA", [ "AltGraph" ]); 3425 checkModifiers(testDesc, events[7], "keypress", "a", "KeyA", [ "AltGraph" ]); 3426 checkModifiers(testDesc, events[8], "keyup", "a", "KeyA", [ "AltGraph" ]); 3427 checkModifiers(testDesc, events[9], "keyup", "AltGraph", "", [ ]); 3428 3429 // shareModifierStateOf(null) should cause resetting the modifier state 3430 function checkTIPModifiers(aTestDesc, aTIP, aModifiers) 3431 { 3432 /* eslint-disable-next-line no-shadow */ 3433 for (var i = 0; i < kModifiers.length; i++) { 3434 is(aTIP.getModifierState(kModifiers[i]), aModifiers.includes(kModifiers[i]), 3435 description + aTestDesc + ", aTIP.getModifierState(\"" + kModifiers[i] + "\") returns wrong value"); 3436 } 3437 } 3438 TIP.keydown(shiftVirtual); 3439 TIP.keydown(altGrVirtual); 3440 sharedTIP.shareModifierStateOf(null); 3441 checkTIPModifiers("sharedTIP.sharedModifierStateOf(null) shouldn't cause TIP's modifiers reset", TIP, [ "Shift", "AltGraph" ]); 3442 checkTIPModifiers("sharedTIP.sharedModifierStateOf(null) should cause sharedTIP modifiers reset", sharedTIP, [ ]); 3443 3444 // sharedTIP.shareModifierStateOf(null) should be unlinked from TIP. 3445 TIP.keydown(ctrlVirtual); 3446 checkTIPModifiers("TIP.keydown(ctrlVirtual) should cause TIP's modifiers set", TIP, [ "Shift", "AltGraph", "Control" ]); 3447 checkTIPModifiers("TIP.keydown(ctrlVirtual) shouldn't cause sharedTIP modifiers set", sharedTIP, [ ]); 3448 3449 // beginInputTransactionForTests() shouldn't cause modifier state reset. 3450 ok(TIP.beginInputTransactionForTests(otherWindow), 3451 description + "TIP.beginInputTransactionForTests(otherWindow) should return true"); 3452 checkTIPModifiers("TIP.beginInputTransactionForTests(otherWindow) shouldn't cause TIP's modifiers set", TIP, [ "Shift", "AltGraph", "Control" ]); 3453 TIP.keyup(shiftLeft); 3454 TIP.keyup(altGrVirtual); 3455 TIP.keyup(ctrlVirtual); 3456 checkTIPModifiers("TIP should keep modifier's physical key state", TIP, [ "Shift" ]); 3457 ok(TIP.beginInputTransactionForTests(window), 3458 description + "TIP.beginInputTransactionForTests(window) should return true"); 3459 checkTIPModifiers("TIP.beginInputTransactionForTests(window) shouldn't cause TIP's modifiers set", TIP, [ "Shift" ]); 3460 TIP.keyup(shiftVirtual); 3461 checkTIPModifiers("TIP should keep modifier's physical key state", TIP, [ ]); 3462 3463 window.removeEventListener("keydown", handler); 3464 window.removeEventListener("keypress", handler); 3465 window.removeEventListener("keyup", handler); 3466 } 3467 3468 function runInsertTextWithKeyPressTests() { 3469 const description = "runInsertTextWithKeyPressTests(): "; 3470 3471 var TIP = createTIP(); 3472 ok(TIP.beginInputTransactionForTests(window), 3473 description + "TIP.beginInputTransactionForTests() should succeed"); 3474 3475 input.value = ""; 3476 input.focus(); 3477 3478 let events = []; 3479 function handler(aEvent) { 3480 events.push({ 3481 type: aEvent.type, 3482 key: aEvent.key, 3483 code: aEvent.code, 3484 shiftKey: aEvent.shiftKey, 3485 altKey: aEvent.altKey, 3486 ctrlKey: aEvent.ctrlKey, 3487 metaKey: aEvent.metaKey, 3488 }); 3489 } 3490 function stringifyEvents(aEvents) { 3491 if (!aEvents.length) { 3492 return "[]"; 3493 } 3494 function stringifyEvent(aEvent) { 3495 return `{ type: "${aEvent.type}", key: "${aEvent.key}", code: ${aEvent.code}, shiftKey: ${ 3496 aEvent.shiftKey 3497 }, altKey: ${aEvent.altKey}, ctrlKey: ${aEvent.ctrlKey}, metaKey: ${aEvent.metaKey}}`; 3498 } 3499 let result = ""; 3500 for (const event of aEvents) { 3501 if (result == "") { 3502 result = "[ "; 3503 } else { 3504 result += ", "; 3505 } 3506 result += stringifyEvent(event); 3507 } 3508 return result + " ]"; 3509 } 3510 window.addEventListener("keydown", handler); 3511 window.addEventListener("keypress", handler); 3512 window.addEventListener("keyup", handler); 3513 3514 events = []; 3515 input.value = ""; 3516 TIP.insertTextWithKeyPress("X"); 3517 is( 3518 input.value, 3519 "X", 3520 `${description}insertTextWithKeyPress without optional args should cause inserting the string` 3521 ); 3522 is( 3523 stringifyEvents(events), 3524 stringifyEvents([ 3525 { type: "keypress", key: "X", code: "", shiftKey: false, altKey: false, ctrlKey: false, metaKey: false }, 3526 ]), 3527 `${description}insertTextWithPress without optional args should cause only a "keypress" event` 3528 ); 3529 3530 events = []; 3531 input.value = ""; 3532 TIP.insertTextWithKeyPress("Y", new KeyboardEvent("keydown", {key: "Alt", code: "AltLeft"})); 3533 is( 3534 input.value, 3535 "Y", 3536 `${description}insertTextWithKeyPress with Alt keydown event should cause inserting the string` 3537 ); 3538 // The key value should be the inserted string and the code value should be same as the source event's. 3539 is( 3540 stringifyEvents(events), 3541 stringifyEvents([ 3542 { type: "keypress", key: "Y", code: "AltLeft", shiftKey: false, altKey: false, ctrlKey: false, metaKey: false }, 3543 ]), 3544 `${description}insertTextWithKeyPress with Alt keydown event should cause only a "keypress" event whose code is "AltLeft"` 3545 ); 3546 3547 events = []; 3548 input.value = ""; 3549 TIP.insertTextWithKeyPress("Z", new KeyboardEvent("keydown", {key: "Alt", code: "AltLeft", altKey: true})); 3550 is( 3551 input.value, 3552 "Z", 3553 `${description}insertTextWithKeyPress with Alt keydown whose altKey is true should cause inserting the string` 3554 ); 3555 // TIP should use its internal modifier state instead of specified modifier state. 3556 is( 3557 stringifyEvents(events), 3558 stringifyEvents([ 3559 { type: "keypress", key: "Z", code: "AltLeft", shiftKey: false, altKey: false, ctrlKey: false, metaKey: false }, 3560 ]), 3561 `${description}insertTextWithKeyPress with Alt keydown whose altKey is true should cause only a "keypress" event whose altKey is false"` 3562 ); 3563 3564 TIP.keydown(new KeyboardEvent("keydown", { key: "Alt" })); 3565 events = []; 3566 input.value = ""; 3567 TIP.insertTextWithKeyPress("X", new KeyboardEvent("keydown", {key: "Shift", code: "ShiftLeft", shiftKey: true})); 3568 is( 3569 input.value, 3570 kIsMac ? "X" : "", 3571 `${description}insertTextWithKeyPress after Alt keydown should${kIsMac ? "" : " not"} cause inserting the string` 3572 ); 3573 is( 3574 stringifyEvents(events), 3575 stringifyEvents([ 3576 { type: "keypress", key: "X", code: "ShiftLeft", shiftKey: false, altKey: true, ctrlKey: false, metaKey: false }, 3577 ]), 3578 `${description}insertTextWithPress after Alt keydown should cause only a "keypress" event whose altKey is true"` 3579 ); 3580 TIP.keyup(new KeyboardEvent("keyup ", { key: "Alt" })); 3581 3582 window.removeEventListener("keydown", handler); 3583 window.removeEventListener("keypress", handler); 3584 window.removeEventListener("keyup", handler); 3585 } 3586 3587 function runErrorTests() 3588 { 3589 var description = "runErrorTests(): "; 3590 3591 var TIP = createTIP(); 3592 ok(TIP.beginInputTransactionForTests(window), 3593 description + "TIP.beginInputTransactionForTests() should succeed"); 3594 3595 input.value = ""; 3596 input.focus(); 3597 3598 // startComposition() should throw an exception if there is already a composition 3599 TIP.startComposition(); 3600 try { 3601 TIP.startComposition(); 3602 ok(false, 3603 description + "startComposition() should fail if it was already called"); 3604 } catch (e) { 3605 ok(e.message.includes("NS_ERROR_FAILURE"), 3606 description + "startComposition() should cause NS_ERROR_FAILURE if there is already composition"); 3607 } finally { 3608 TIP.cancelComposition(); 3609 } 3610 3611 // cancelComposition() should throw an exception if there is no composition 3612 try { 3613 TIP.cancelComposition(); 3614 ok(false, 3615 description + "cancelComposition() should fail if there is no composition"); 3616 } catch (e) { 3617 ok(e.message.includes("NS_ERROR_FAILURE"), 3618 description + "cancelComposition() should cause NS_ERROR_FAILURE if there is no composition"); 3619 } 3620 3621 // commitComposition() without commit string should throw an exception if there is no composition 3622 try { 3623 TIP.commitComposition(); 3624 ok(false, 3625 description + "commitComposition() should fail if there is no composition"); 3626 } catch (e) { 3627 ok(e.message.includes("NS_ERROR_FAILURE"), 3628 description + "commitComposition() should cause NS_ERROR_FAILURE if there is no composition"); 3629 } 3630 3631 // commitCompositionWith("") should throw an exception if there is no composition 3632 try { 3633 TIP.commitCompositionWith(""); 3634 ok(false, 3635 description + "commitCompositionWith(\"\") should fail if there is no composition"); 3636 } catch (e) { 3637 ok(e.message.includes("NS_ERROR_FAILURE"), 3638 description + "commitCompositionWith(\"\") should cause NS_ERROR_FAILURE if there is no composition"); 3639 } 3640 3641 // Pending composition string should allow to flush without clause information (for compatibility) 3642 try { 3643 TIP.setPendingCompositionString("foo"); 3644 TIP.flushPendingComposition(); 3645 ok(true, 3646 description + "flushPendingComposition() should succeed even if appendClauseToPendingComposition() has never been called"); 3647 TIP.cancelComposition(); 3648 } catch (e) { 3649 ok(false, 3650 description + "flushPendingComposition() shouldn't cause an exception even if appendClauseToPendingComposition() has never been called"); 3651 } 3652 3653 // Pending composition string must be filled by clause information 3654 try { 3655 TIP.setPendingCompositionString("foo"); 3656 TIP.appendClauseToPendingComposition(2, TIP.ATTR_RAW_CLAUSE); 3657 TIP.flushPendingComposition(); 3658 ok(false, 3659 description + "flushPendingComposition() should fail if appendClauseToPendingComposition() doesn't fill all composition string"); 3660 TIP.cancelComposition(); 3661 } catch (e) { 3662 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3663 description + "flushPendingComposition() should cause NS_ERROR_ILLEGAL_VALUE if appendClauseToPendingComposition() doesn't fill all composition string"); 3664 } 3665 3666 // Pending composition string must not be shorter than appended clause length 3667 try { 3668 TIP.setPendingCompositionString("foo"); 3669 TIP.appendClauseToPendingComposition(4, TIP.ATTR_RAW_CLAUSE); 3670 TIP.flushPendingComposition(); 3671 ok(false, 3672 description + "flushPendingComposition() should fail if appendClauseToPendingComposition() appends longer clause information"); 3673 TIP.cancelComposition(); 3674 } catch (e) { 3675 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3676 description + "flushPendingComposition() should cause NS_ERROR_ILLEGAL_VALUE if appendClauseToPendingComposition() appends longer clause information"); 3677 } 3678 3679 // Pending composition must not have clause information with empty string 3680 try { 3681 TIP.appendClauseToPendingComposition(1, TIP.ATTR_RAW_CLAUSE); 3682 TIP.flushPendingComposition(); 3683 ok(false, 3684 description + "flushPendingComposition() should fail if there is a clause with empty string"); 3685 TIP.cancelComposition(); 3686 } catch (e) { 3687 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3688 description + "flushPendingComposition() should cause NS_ERROR_ILLEGAL_VALUE if there is a clause with empty string"); 3689 } 3690 3691 // Appending a clause whose length is 0 should cause an exception 3692 try { 3693 TIP.appendClauseToPendingComposition(0, TIP.ATTR_RAW_CLAUSE); 3694 ok(false, 3695 description + "appendClauseToPendingComposition() should fail if the length is 0"); 3696 TIP.flushPendingComposition(); 3697 TIP.cancelComposition(); 3698 } catch (e) { 3699 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3700 description + "appendClauseToPendingComposition() should cause NS_ERROR_ILLEGAL_VALUE if the length is 0"); 3701 } 3702 3703 // Appending a clause whose attribute is invalid should cause an exception 3704 try { 3705 TIP.setPendingCompositionString("foo"); 3706 TIP.appendClauseToPendingComposition(3, 0); 3707 ok(false, 3708 description + "appendClauseToPendingComposition() should fail if the attribute is invalid"); 3709 TIP.flushPendingComposition(); 3710 TIP.cancelComposition(); 3711 } catch (e) { 3712 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3713 description + "appendClauseToPendingComposition() should cause NS_ERROR_ILLEGAL_VALUE if the attribute is invalid"); 3714 } 3715 3716 // Setting caret position outside of composition string should cause an exception 3717 try { 3718 TIP.setPendingCompositionString("foo"); 3719 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 3720 TIP.setCaretInPendingComposition(4); 3721 TIP.flushPendingComposition(); 3722 ok(false, 3723 description + "flushPendingComposition() should fail if caret position is out of composition string"); 3724 TIP.cancelComposition(); 3725 } catch (e) { 3726 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3727 description + "flushPendingComposition() should cause NS_ERROR_ILLEGAL_VALUE if caret position is out of composition string"); 3728 } 3729 3730 // Calling keydown() with a KeyboardEvent initialized with invalid code value should cause an exception. 3731 input.value = ""; 3732 try { 3733 let keyInvalidCode = new KeyboardEvent("", { key: "f", code: "InvalidCodeValue", keyCode: KeyboardEvent.DOM_VK_F }); 3734 TIP.keydown(keyInvalidCode); 3735 ok(false, 3736 description + "TIP.keydown(keyInvalidCode) should cause throwing an exception because its code value is not registered"); 3737 } catch (e) { 3738 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3739 description + "TIP.keydown(keyInvalidCode) should cause throwing an exception including NS_ERROR_ILLEGAL_VALUE"); 3740 } finally { 3741 is(input.value, "", 3742 description + "The input element should not be modified"); 3743 } 3744 3745 // Calling keyup() with a KeyboardEvent initialized with invalid code value should cause an exception. 3746 input.value = ""; 3747 try { 3748 let keyInvalidCode = new KeyboardEvent("", { key: "f", code: "InvalidCodeValue", keyCode: KeyboardEvent.DOM_VK_F }); 3749 TIP.keyup(keyInvalidCode); 3750 ok(false, 3751 description + "TIP.keyup(keyInvalidCode) should cause throwing an exception because its code value is not registered"); 3752 } catch (e) { 3753 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3754 description + "TIP.keyup(keyInvalidCode) should cause throwing an exception including NS_ERROR_ILLEGAL_VALUE"); 3755 } finally { 3756 is(input.value, "", 3757 description + "The input element should not be modified"); 3758 } 3759 3760 // Calling keydown(KEY_NON_PRINTABLE_KEY) with a KeyboardEvent initialized with non-key name should cause an exception. 3761 input.value = ""; 3762 try { 3763 let keyInvalidKey = new KeyboardEvent("", { key: "ESCAPE", code: "Escape", keyCode: KeyboardEvent.DOM_VK_Escape}); 3764 TIP.keydown(keyInvalidKey, TIP.KEY_NON_PRINTABLE_KEY); 3765 ok(false, 3766 description + "TIP.keydown(keyInvalidKey, TIP.KEY_NON_PRINTABLE_KEY) should cause throwing an exception because its key value is not registered"); 3767 } catch (e) { 3768 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3769 description + "keydown(keyInvalidKey, TIP.KEY_NON_PRINTABLE_KEY) should cause throwing an exception including NS_ERROR_ILLEGAL_VALUE"); 3770 } finally { 3771 is(input.value, "", 3772 description + "The input element should not be modified"); 3773 } 3774 3775 // Calling keyup(KEY_NON_PRINTABLE_KEY) with a KeyboardEvent initialized with non-key name should cause an exception. 3776 input.value = ""; 3777 try { 3778 let keyInvalidKey = new KeyboardEvent("", { key: "ESCAPE", code: "Escape", keyCode: KeyboardEvent.DOM_VK_Escape}); 3779 TIP.keydown(keyInvalidKey, TIP.KEY_NON_PRINTABLE_KEY); 3780 ok(false, 3781 description + "TIP.keyup(keyInvalidKey, TIP.KEY_NON_PRINTABLE_KEY) should cause throwing an exception because its key value is not registered"); 3782 } catch (e) { 3783 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3784 description + "keyup(keyInvalidKey, TIP.KEY_NON_PRINTABLE_KEY) should cause throwing an exception including NS_ERROR_ILLEGAL_VALUE"); 3785 } finally { 3786 is(input.value, "", 3787 description + "The input element should not be modified"); 3788 } 3789 3790 // KEY_KEEP_KEY_LOCATION_STANDARD flag should be used only when .location is not initialized with non-zero value. 3791 try { 3792 let keyEvent = new KeyboardEvent("", { code: "Enter", location: KeyboardEvent.DOM_KEY_LOCATION_LEFT }); 3793 TIP.keydown(keyEvent, TIP.KEY_KEEP_KEY_LOCATION_STANDARD); 3794 ok(false, 3795 description + "keydown(KEY_KEEP_KEY_LOCATION_STANDARD) should fail if the .location of the key event is initialized with non-zero value"); 3796 } catch (e) { 3797 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3798 description + "keydown(KEY_KEEP_KEY_LOCATION_STANDARD) should cause NS_ERROR_ILLEGAL_VALUE if the .location of the key event is initialized with nonzero value"); 3799 } 3800 try { 3801 let keyEvent = new KeyboardEvent("", { code: "Enter", location: KeyboardEvent.DOM_KEY_LOCATION_LEFT }); 3802 TIP.keyup(keyEvent, TIP.KEY_KEEP_KEY_LOCATION_STANDARD); 3803 ok(false, 3804 description + "keyup(KEY_KEEP_KEY_LOCATION_STANDARD) should fail if the .location of the key event is initialized with non-zero value"); 3805 } catch (e) { 3806 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3807 description + "keyup(KEY_KEEP_KEY_LOCATION_STANDARD) should cause NS_ERROR_ILLEGAL_VALUE if the .location of the key event is initialized with nonzero value"); 3808 } 3809 3810 // KEY_KEEP_KEYCODE_ZERO flag should be used only when .keyCode is not initialized with non-zero value. 3811 try { 3812 let keyEvent = new KeyboardEvent("", { key: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }); 3813 TIP.keydown(keyEvent, TIP.KEY_KEEP_KEYCODE_ZERO); 3814 ok(false, 3815 description + "keydown(KEY_KEEP_KEYCODE_ZERO) should fail if the .keyCode of the key event is initialized with non-zero value"); 3816 } catch (e) { 3817 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3818 description + "keydown(KEY_KEEP_KEYCODE_ZERO) should cause NS_ERROR_ILLEGAL_VALUE if the .keyCode of the key event is initialized with nonzero value"); 3819 } 3820 try { 3821 let keyEvent = new KeyboardEvent("", { key: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN }); 3822 TIP.keyup(keyEvent, TIP.KEY_KEEP_KEYCODE_ZERO); 3823 ok(false, 3824 description + "keyup(KEY_KEEP_KEYCODE_ZERO) should fail if the .keyCode of the key event is initialized with non-zero value"); 3825 } catch (e) { 3826 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3827 description + "keyup(KEY_KEEP_KEYCODE_ZERO) should cause NS_ERROR_ILLEGAL_VALUE if the .keyCode of the key event is initialized with nonzero value"); 3828 } 3829 3830 // Specifying KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT with non-modifier key, it should cause an exception. 3831 try { 3832 let keyEvent = new KeyboardEvent("", { key: "a", code: "ShiftLeft" }); 3833 TIP.keyup(keyEvent, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); 3834 ok(false, 3835 description + "keydown(KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) should fail if the .key value isn't a modifier key"); 3836 } catch (e) { 3837 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3838 description + "keydown(KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) should cause NS_ERROR_ILLEGAL_VALUE if the .key value isn't a modifier key"); 3839 } 3840 try { 3841 let keyEvent = new KeyboardEvent("", { key: "Enter", code: "ShiftLeft" }); 3842 TIP.keyup(keyEvent, TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); 3843 ok(false, 3844 description + "keydown(KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) should fail if the .key value isn't a modifier key"); 3845 } catch (e) { 3846 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3847 description + "keydown(KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) should cause NS_ERROR_ILLEGAL_VALUE if the .key value isn't a modifier key"); 3848 } 3849 3850 // The type of key events specified to composition methods should be "" or "keydown". 3851 var kKeyEventTypes = [ 3852 { type: "keydown", valid: true }, 3853 { type: "keypress", valid: false }, 3854 { type: "keyup", valid: false }, 3855 { type: "", valid: true }, 3856 { type: "mousedown", valid: false }, 3857 { type: "foo", valid: false }, 3858 ]; 3859 for (var i = 0; i < kKeyEventTypes[i].length; i++) { 3860 var keyEvent = 3861 new KeyboardEvent(kKeyEventTypes[i].type, { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); 3862 var testDescription = description + "type=\"" + kKeyEventTypes[i].type + "\", "; 3863 try { 3864 TIP.startComposition(keyEvent); 3865 ok(kKeyEventTypes[i].valid, 3866 testDescription + "TIP.startComposition(keyEvent) should not accept the event type"); 3867 TIP.cancelComposition(); 3868 } catch (e) { 3869 ok(!kKeyEventTypes[i].valid, 3870 testDescription + "TIP.startComposition(keyEvent) should not throw an exception for the event type"); 3871 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3872 testDescription + "TIP.startComposition(keyEvent) should cause NS_ERROR_ILLEGAL_VALUE if the key event type isn't valid"); 3873 } 3874 try { 3875 TIP.setPendingCompositionString("foo"); 3876 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 3877 TIP.setCaretInPendingComposition(3); 3878 TIP.flushPendingComposition(keyEvent); 3879 ok(kKeyEventTypes[i].valid, 3880 testDescription + "TIP.flushPendingComposition(keyEvent) should not accept the event type"); 3881 TIP.cancelComposition(); 3882 } catch (e) { 3883 ok(!kKeyEventTypes[i].valid, 3884 testDescription + "TIP.flushPendingComposition(keyEvent) should not throw an exception for the event type"); 3885 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3886 testDescription + "TIP.flushPendingComposition(keyEvent) should cause NS_ERROR_ILLEGAL_VALUE if the key event type isn't valid"); 3887 } 3888 try { 3889 TIP.startComposition(); 3890 TIP.commitComposition(keyEvent); 3891 ok(kKeyEventTypes[i].valid, 3892 testDescription + "TIP.commitComposition(keyEvent) should not accept the event type"); 3893 } catch (e) { 3894 ok(!kKeyEventTypes[i].valid, 3895 testDescription + "TIP.commitComposition(keyEvent) should not throw an exception for the event type"); 3896 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3897 testDescription + "TIP.commitComposition(keyEvent) should cause NS_ERROR_ILLEGAL_VALUE if the key event type isn't valid"); 3898 TIP.cancelComposition(); 3899 } 3900 try { 3901 TIP.commitCompositionWith("foo", keyEvent); 3902 ok(kKeyEventTypes[i].valid, 3903 testDescription + "TIP.commitCompositionWith(\"foo\", keyEvent) should not accept the event type"); 3904 } catch (e) { 3905 ok(!kKeyEventTypes[i].valid, 3906 testDescription + "TIP.commitCompositionWith(\"foo\", keyEvent) should not throw an exception for the event type"); 3907 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3908 testDescription + "TIP.commitCompositionWith(\"foo\", keyEvent) should cause NS_ERROR_ILLEGAL_VALUE if the key event type isn't valid"); 3909 } 3910 try { 3911 TIP.startComposition(); 3912 TIP.cancelComposition(keyEvent); 3913 ok(kKeyEventTypes[i].valid, 3914 testDescription + "TIP.cancelComposition(keyEvent) should not accept the event type"); 3915 } catch (e) { 3916 ok(!kKeyEventTypes[i].valid, 3917 testDescription + "TIP.cancelComposition(keyEvent) should not throw an exception for the event type"); 3918 ok(e.message.includes("NS_ERROR_ILLEGAL_VALUE"), 3919 testDescription + "TIP.cancelComposition(keyEvent) should cause NS_ERROR_ILLEGAL_VALUE if the key event type isn't valid"); 3920 TIP.cancelComposition(); 3921 } 3922 input.value = ""; 3923 } 3924 } 3925 3926 function runCommitCompositionTests() 3927 { 3928 var description = "runCommitCompositionTests(): "; 3929 3930 var TIP = createTIP(); 3931 ok(TIP.beginInputTransactionForTests(window), 3932 description + "TIP.beginInputTransactionForTests() should succeed"); 3933 3934 input.focus(); 3935 3936 // commitComposition() should commit the composition with the last data. 3937 input.value = ""; 3938 TIP.setPendingCompositionString("foo"); 3939 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 3940 TIP.setCaretInPendingComposition(3); 3941 TIP.flushPendingComposition(); 3942 TIP.commitComposition(); 3943 is(input.value, "foo", 3944 description + "commitComposition() should commit the composition with the last data"); 3945 3946 // commitCompositionWith("") should commit the composition with empty string. 3947 input.value = ""; 3948 TIP.setPendingCompositionString("foo"); 3949 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 3950 TIP.setCaretInPendingComposition(3); 3951 TIP.flushPendingComposition(); 3952 TIP.commitCompositionWith(""); 3953 is(input.value, "", 3954 description + "commitCompositionWith(\"\") should commit the composition with empty string"); 3955 3956 function doCommit(aText) 3957 { 3958 TIP.commitCompositionWith(aText); 3959 } 3960 3961 // doCommit() should commit the composition with the last data. 3962 input.value = ""; 3963 TIP.setPendingCompositionString("foo"); 3964 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 3965 TIP.setCaretInPendingComposition(3); 3966 TIP.flushPendingComposition(); 3967 doCommit(); 3968 todo_is(input.value, "foo", 3969 description + "doCommit() should commit the composition with the last data"); 3970 3971 // doCommit("") should commit the composition with empty string. 3972 input.value = ""; 3973 TIP.setPendingCompositionString("foo"); 3974 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 3975 TIP.setCaretInPendingComposition(3); 3976 TIP.flushPendingComposition(); 3977 doCommit(""); 3978 is(input.value, "", 3979 description + "doCommit(\"\") should commit the composition with empty string"); 3980 3981 // doCommit(null) should commit the composition with empty string. 3982 input.value = ""; 3983 TIP.setPendingCompositionString("foo"); 3984 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 3985 TIP.setCaretInPendingComposition(3); 3986 TIP.flushPendingComposition(); 3987 doCommit(null); 3988 is(input.value, "", 3989 description + "doCommit(null) should commit the composition with empty string"); 3990 3991 // doCommit(undefined) should commit the composition with the last data. 3992 input.value = ""; 3993 TIP.setPendingCompositionString("foo"); 3994 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 3995 TIP.setCaretInPendingComposition(3); 3996 TIP.flushPendingComposition(); 3997 doCommit(undefined); 3998 todo_is(input.value, "foo", 3999 description + "doCommit(undefined) should commit the composition with the last data"); 4000 4001 function doCommitWithNullCheck(aText) 4002 { 4003 TIP.commitCompositionWith(aText ? aText : ""); 4004 } 4005 4006 // doCommitWithNullCheck() should commit the composition with the last data. 4007 input.value = ""; 4008 TIP.setPendingCompositionString("foo"); 4009 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 4010 TIP.setCaretInPendingComposition(3); 4011 TIP.flushPendingComposition(); 4012 doCommitWithNullCheck(); 4013 is(input.value, "", 4014 description + "doCommitWithNullCheck() should commit the composition with empty string"); 4015 4016 // doCommitWithNullCheck("") should commit the composition with empty string. 4017 input.value = ""; 4018 TIP.setPendingCompositionString("foo"); 4019 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 4020 TIP.setCaretInPendingComposition(3); 4021 TIP.flushPendingComposition(); 4022 doCommitWithNullCheck(""); 4023 is(input.value, "", 4024 description + "doCommitWithNullCheck(\"\") should commit the composition with empty string"); 4025 4026 // doCommitWithNullCheck(null) should commit the composition with empty string. 4027 input.value = ""; 4028 TIP.setPendingCompositionString("foo"); 4029 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 4030 TIP.setCaretInPendingComposition(3); 4031 TIP.flushPendingComposition(); 4032 doCommitWithNullCheck(null); 4033 is(input.value, "", 4034 description + "doCommitWithNullCheck(null) should commit the composition with empty string"); 4035 4036 // doCommitWithNullCheck(undefined) should commit the composition with the last data. 4037 input.value = ""; 4038 TIP.setPendingCompositionString("foo"); 4039 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 4040 TIP.setCaretInPendingComposition(3); 4041 TIP.flushPendingComposition(); 4042 doCommitWithNullCheck(undefined); 4043 is(input.value, "", 4044 description + "doCommitWithNullCheck(undefined) should commit the composition with empty string"); 4045 } 4046 4047 function runUnloadTests1() 4048 { 4049 return new Promise(resolve => { 4050 let description = "runUnloadTests1(): "; 4051 4052 let TIP1 = createTIP(); 4053 ok(TIP1.beginInputTransactionForTests(childWindow), 4054 description + "TIP1.beginInputTransactionForTests() should succeed"); 4055 4056 let oldSrc = iframe.src; 4057 let parentWindow = window; 4058 4059 iframe.addEventListener("load", function () { 4060 ok(true, description + "dummy page is loaded"); 4061 childWindow = iframe.contentWindow; 4062 textareaInFrame = null; 4063 iframe.addEventListener("load", function () { 4064 ok(true, description + "old iframe is restored"); 4065 // And also restore the iframe information with restored contents. 4066 childWindow = iframe.contentWindow; 4067 textareaInFrame = iframe.contentDocument.getElementById("textarea"); 4068 SimpleTest.executeSoon(resolve); 4069 }, {capture: true, once: true}); 4070 4071 // The composition should be committed internally. So, another TIP should 4072 // be able to steal the rights to using TextEventDispatcher. 4073 let TIP2 = createTIP(); 4074 ok(TIP2.beginInputTransactionForTests(parentWindow), 4075 description + "TIP2.beginInputTransactionForTests() should succeed"); 4076 4077 input.focus(); 4078 input.value = ""; 4079 4080 TIP2.setPendingCompositionString("foo"); 4081 TIP2.appendClauseToPendingComposition(3, TIP2.ATTR_RAW_CLAUSE); 4082 TIP2.setCaretInPendingComposition(3); 4083 TIP2.flushPendingComposition(); 4084 is(input.value, "foo", 4085 description + "the input in the parent document should have composition string"); 4086 4087 TIP2.cancelComposition(); 4088 4089 // Restore the old iframe content. 4090 iframe.src = oldSrc; 4091 }, {capture: true, once: true}); 4092 4093 // Start composition in the iframe. 4094 textareaInFrame.value = ""; 4095 textareaInFrame.focus(); 4096 4097 TIP1.setPendingCompositionString("foo"); 4098 TIP1.appendClauseToPendingComposition(3, TIP1.ATTR_RAW_CLAUSE); 4099 TIP1.setCaretInPendingComposition(3); 4100 TIP1.flushPendingComposition(); 4101 is(textareaInFrame.value, "foo", 4102 description + "the textarea in the iframe should have composition string"); 4103 4104 // Load different web page on the frame. 4105 iframe.src = "data:text/html,<body>dummy page</body>"; 4106 }); 4107 } 4108 4109 function runUnloadTests2() 4110 { 4111 return new Promise(resolve => { 4112 let description = "runUnloadTests2(): "; 4113 4114 let TIP = createTIP(); 4115 ok(TIP.beginInputTransactionForTests(childWindow), 4116 description + "TIP.beginInputTransactionForTests() should succeed"); 4117 4118 let oldSrc = iframe.src; 4119 4120 iframe.addEventListener("load", function () { 4121 ok(true, description + "dummy page is loaded"); 4122 childWindow = iframe.contentWindow; 4123 textareaInFrame = null; 4124 iframe.addEventListener("load", function () { 4125 ok(true, description + "old iframe is restored"); 4126 // And also restore the iframe information with restored contents. 4127 childWindow = iframe.contentWindow; 4128 textareaInFrame = iframe.contentDocument.getElementById("textarea"); 4129 SimpleTest.executeSoon(resolve); 4130 }, {capture: true, once: true}); 4131 4132 input.focus(); 4133 input.value = ""; 4134 4135 // TIP should be still available in the same top level widget. 4136 TIP.setPendingCompositionString("bar"); 4137 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 4138 TIP.setCaretInPendingComposition(3); 4139 TIP.flushPendingComposition(); 4140 if (input.value == "") { 4141 // XXX TextInputProcessor or TextEventDispatcher may have a bug. 4142 todo_is(input.value, "bar", 4143 description + "the input in the parent document should have composition string"); 4144 } else { 4145 is(input.value, "bar", 4146 description + "the input in the parent document should have composition string"); 4147 } 4148 4149 TIP.cancelComposition(); 4150 4151 // Restore the old iframe content. 4152 iframe.src = oldSrc; 4153 }, {capture: true, once: true}); 4154 4155 // Start composition in the iframe. 4156 textareaInFrame.value = ""; 4157 textareaInFrame.focus(); 4158 4159 TIP.setPendingCompositionString("foo"); 4160 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 4161 TIP.setCaretInPendingComposition(3); 4162 TIP.flushPendingComposition(); 4163 is(textareaInFrame.value, "foo", 4164 description + "the textarea in the iframe should have composition string"); 4165 4166 // Load different web page on the frame. 4167 iframe.src = "data:text/html,<body>dummy page</body>"; 4168 }); 4169 } 4170 4171 async function runCallbackTests(aForTests) 4172 { 4173 let description = "runCallbackTests(aForTests=" + aForTests + "): "; 4174 4175 input.value = ""; 4176 input.focus(); 4177 input.blur(); 4178 4179 let TIP = createTIP(); 4180 let notifications = []; 4181 let waitingNextNotification; 4182 function callback(aTIP, aNotification) 4183 { 4184 if (aTIP == TIP) { 4185 notifications.push(aNotification); 4186 } 4187 switch (aNotification.type) { 4188 case "request-to-commit": 4189 aTIP.commitComposition(); 4190 break; 4191 case "request-to-cancel": 4192 aTIP.cancelComposition(); 4193 break; 4194 } 4195 if (waitingNextNotification) { 4196 SimpleTest.executeSoon(waitingNextNotification); 4197 waitingNextNotification = undefined; 4198 } 4199 return true; 4200 } 4201 4202 function dumpUnexpectedNotifications(aExpectedCount) 4203 { 4204 if (notifications.length <= aExpectedCount) { 4205 return; 4206 } 4207 for (let i = aExpectedCount; i < notifications.length; i++) { 4208 ok(false, 4209 description + "Unexpected notification: " + notifications[i].type); 4210 } 4211 } 4212 4213 function waitUntilNotificationsReceived() 4214 { 4215 return new Promise(resolve => { 4216 if (notifications.length) { 4217 SimpleTest.executeSoon(resolve); 4218 } else { 4219 waitingNextNotification = resolve; 4220 } 4221 }); 4222 } 4223 4224 function checkPositionChangeNotification(aNotification, aDescription) 4225 { 4226 is(!aNotification || aNotification.type, "notify-position-change", 4227 aDescription + " should cause position change notification"); 4228 } 4229 4230 function checkSelectionChangeNotification(aNotification, aDescription, aExpected) 4231 { 4232 is(aNotification.type, "notify-selection-change", 4233 aDescription + " should cause selection change notification"); 4234 if (aNotification.type != "notify-selection-change") { 4235 return; 4236 } 4237 is(aNotification.hasRange, aExpected.hasRange !== false, 4238 `${aDescription} should cause selection change notification whose hasRange is ${aExpected.hasRange}`); 4239 if (aNotification.hasRange) { 4240 is(aNotification.offset, aExpected.offset, 4241 `${aDescription} should cause selection change notification whose offset is ${aExpected.offset}`); 4242 is(aNotification.text, aExpected.text, 4243 `${aDescription} should cause selection change notification whose text is "${aExpected.text}"`); 4244 is(aNotification.length, aExpected.text.length, 4245 `${aDescription} should cause selection change notification whose length is ${aExpected.text.length}`); 4246 is(aNotification.reversed, aExpected.reversed || false, 4247 `${aDescription} should cause selection change notification whose reversed is ${aExpected.reversed || false}`); 4248 } 4249 is(aNotification.collapsed, aExpected.hasRange === false || !aExpected.text.length, 4250 `${aDescription} should cause selection change notification whose collapsed is ${aExpected.hasRange === false || !aExpected.text.length}`); 4251 is(aNotification.writingMode, aExpected.writingMode || "horizontal-tb", 4252 `${aDescription} should cause selection change notification whose writingMode is ${aExpected.writingMode || "horizontal-tb"}`); 4253 is(aNotification.causedByComposition, aExpected.causedByComposition || false, 4254 `${aDescription} should cause selection change notification whose causedByComposition is ${aExpected.causedByComposition || false}`); 4255 is(aNotification.causedBySelectionEvent, aExpected.causedBySelectionEvent || false, 4256 `${aDescription} should cause selection change notification whose causedBySelectionEvent is ${aExpected.causedBySelectionEvent || false}`); 4257 is(aNotification.occurredDuringComposition, aExpected.occurredDuringComposition || false, 4258 `${aDescription} should cause cause selection change notification whose occurredDuringComposition is ${aExpected.occurredDuringComposition || false}`); 4259 } 4260 4261 function checkTextChangeNotification(aNotification, aDescription, aExpected) 4262 { 4263 is(aNotification.type, "notify-text-change", 4264 aDescription + " should cause text change notification"); 4265 if (aNotification.type != "notify-text-change") { 4266 return; 4267 } 4268 is(aNotification.offset, aExpected.offset, 4269 aDescription + " should cause text change notification whose offset is " + aExpected.offset); 4270 is(aNotification.removedLength, aExpected.removedLength, 4271 aDescription + " should cause text change notification whose removedLength is " + aExpected.removedLength); 4272 is(aNotification.addedLength, aExpected.addedLength, 4273 aDescription + " should cause text change notification whose addedLength is " + aExpected.addedLength); 4274 is(aNotification.causedOnlyByComposition, aExpected.causedOnlyByComposition || false, 4275 aDescription + " should cause text change notification whose causedOnlyByComposition is " + (aExpected.causedOnlyByComposition || false)); 4276 is(aNotification.includingChangesDuringComposition, aExpected.includingChangesDuringComposition || false, 4277 aDescription + " should cause text change notification whose includingChangesDuringComposition is " + (aExpected.includingChangesDuringComposition || false)); 4278 is(aNotification.includingChangesWithoutComposition, typeof aExpected.includingChangesWithoutComposition === "boolean" ? aExpected.includingChangesWithoutComposition : true, 4279 aDescription + " should cause text change notification whose includingChangesWithoutComposition is " + (typeof aExpected.includingChangesWithoutComposition === "boolean" ? aExpected.includingChangesWithoutComposition : true)); 4280 } 4281 4282 if (aForTests) { 4283 TIP.beginInputTransactionForTests(window, callback); 4284 } else { 4285 TIP.beginInputTransaction(window, callback); 4286 } 4287 4288 notifications = []; 4289 input.focus(); 4290 is(notifications.length, 1, 4291 description + "input.focus() should cause a notification"); 4292 is(notifications[0].type, "notify-focus", 4293 description + "input.focus() should cause \"notify-focus\""); 4294 dumpUnexpectedNotifications(1); 4295 4296 notifications = []; 4297 input.blur(); 4298 is(notifications.length, 1, 4299 description + "input.blur() should cause a notification"); 4300 is(notifications[0].type, "notify-blur", 4301 description + "input.blur() should cause \"notify-focus\""); 4302 dumpUnexpectedNotifications(1); 4303 4304 input.focus(); 4305 await waitUntilNotificationsReceived(); 4306 notifications = []; 4307 TIP.setPendingCompositionString("foo"); 4308 TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE); 4309 TIP.flushPendingComposition(); 4310 is(notifications.length, 3, 4311 description + "creating composition string 'foo' should cause 3 notifications"); 4312 checkTextChangeNotification(notifications[0], description + "creating composition string 'foo'", 4313 { offset: 0, removedLength: 0, addedLength: 3, 4314 causedOnlyByComposition: true, includingChangesDuringComposition: false, includingChangesWithoutComposition: false}); 4315 checkSelectionChangeNotification(notifications[1], description + "creating composition string 'foo'", 4316 { offset: 3, text: "", causedByComposition: true, occurredDuringComposition: true }); 4317 checkPositionChangeNotification(notifications[2], description + "creating composition string 'foo'"); 4318 dumpUnexpectedNotifications(3); 4319 4320 notifications = []; 4321 synthesizeMouseAtCenter(input, {}); 4322 is(notifications.length, 3, 4323 description + "synthesizeMouseAtCenter(input, {}) during composition should cause 3 notifications"); 4324 is(notifications[0].type, "request-to-commit", 4325 description + "synthesizeMouseAtCenter(input, {}) during composition should cause \"request-to-commit\""); 4326 checkTextChangeNotification(notifications[1], description + "synthesizeMouseAtCenter(input, {}) during composition", 4327 { offset: 0, removedLength: 3, addedLength: 3, 4328 causedOnlyByComposition: true, includingChangesDuringComposition: false, includingChangesWithoutComposition: false}); 4329 checkPositionChangeNotification(notifications[2], description + "synthesizeMouseAtCenter(input, {}) during composition"); 4330 dumpUnexpectedNotifications(3); 4331 4332 input.focus(); 4333 await waitUntilNotificationsReceived(); 4334 notifications = []; 4335 // XXX On macOS, window.moveBy() doesn't cause notify-position-change. 4336 // Investigate this later (although, we cannot notify position change to 4337 // native IME on macOS). 4338 // Wayland also does not support it. 4339 var isWayland = Services.prefs.getBoolPref("widget.wayland.test-workarounds.enabled", false); 4340 if (!kIsMac && !isWayland) { 4341 window.moveBy(0, 10); 4342 await waitUntilNotificationsReceived(); 4343 is(notifications.length, 1, 4344 description + "window.moveBy(0, 10) should cause a notification"); 4345 checkPositionChangeNotification(notifications[0], description + "window.moveBy(0, 10)"); 4346 dumpUnexpectedNotifications(1); 4347 4348 notifications = []; 4349 window.moveBy(10, 0); 4350 await waitUntilNotificationsReceived(); 4351 is(notifications.length, 1, 4352 description + "window.moveBy(10, 0) should cause a notification"); 4353 checkPositionChangeNotification(notifications[0], description + "window.moveBy(10, 0)"); 4354 dumpUnexpectedNotifications(1); 4355 } 4356 4357 input.focus(); 4358 input.value = "abc" 4359 notifications = []; 4360 input.selectionStart = input.selectionEnd = 0; 4361 await waitUntilNotificationsReceived(); 4362 notifications = []; 4363 let rightArrowKeyEvent = 4364 new KeyboardEvent("", { key: "ArrowRight", code: "ArrowRight", keyCode: KeyboardEvent.DOM_VK_RIGHT }); 4365 TIP.keydown(rightArrowKeyEvent); 4366 TIP.keyup(rightArrowKeyEvent); 4367 is(notifications.length, 1, 4368 description + "ArrowRight key press should cause a notification"); 4369 checkSelectionChangeNotification(notifications[0], description + "ArrowRight key press", { offset: 1, text: "" }); 4370 dumpUnexpectedNotifications(1); 4371 4372 notifications = []; 4373 let shiftKeyEvent = 4374 new KeyboardEvent("", { key: "Shift", code: "ShiftLeft", keyCode: KeyboardEvent.DOM_VK_SHIFT }); 4375 let leftArrowKeyEvent = 4376 new KeyboardEvent("", { key: "ArrowLeft", code: "ArrowLeft", keyCode: KeyboardEvent.DOM_VK_LEFT }); 4377 TIP.keydown(shiftKeyEvent); 4378 TIP.keydown(leftArrowKeyEvent); 4379 TIP.keyup(leftArrowKeyEvent); 4380 TIP.keyup(shiftKeyEvent); 4381 is(notifications.length, 1, 4382 description + "ArrowLeft key press with Shift should cause a notification"); 4383 checkSelectionChangeNotification(notifications[0], description + "ArrowLeft key press with Shift", { offset: 0, text: "a", reversed: true }); 4384 dumpUnexpectedNotifications(1); 4385 4386 TIP.keydown(rightArrowKeyEvent); 4387 TIP.keyup(rightArrowKeyEvent); 4388 notifications = []; 4389 TIP.keydown(shiftKeyEvent); 4390 TIP.keydown(rightArrowKeyEvent); 4391 TIP.keyup(rightArrowKeyEvent); 4392 TIP.keyup(shiftKeyEvent); 4393 is(notifications.length, 1, 4394 description + "ArrowRight key press with Shift should cause a notification"); 4395 checkSelectionChangeNotification(notifications[0], description + "ArrowRight key press with Shift", { offset: 1, text: "b" }); 4396 dumpUnexpectedNotifications(1); 4397 4398 notifications = []; 4399 input.editor.selection.removeAllRanges(); 4400 await waitUntilNotificationsReceived(); 4401 is(notifications.length, 1, 4402 `${description}Removing all selection ranges should cause a selection change notification`); 4403 checkSelectionChangeNotification( 4404 notifications[0], 4405 `${description}Removing all selection ranges in editor`, 4406 { hasRange: false } 4407 ); 4408 dumpUnexpectedNotifications(1); 4409 4410 notifications = []; 4411 let TIP2 = createTIP(); 4412 if (aForTests) { 4413 TIP2.beginInputTransactionForTests(window, callback); 4414 } else { 4415 TIP2.beginInputTransaction(window, callback); 4416 } 4417 is(notifications.length, 1, 4418 description + "Initializing another TIP should cause a notification"); 4419 is(notifications[0].type, "notify-end-input-transaction", 4420 description + "Initializing another TIP should cause \"notify-detached\""); 4421 dumpUnexpectedNotifications(1); 4422 } 4423 4424 async function runFocusNotificationTestAfterDrop() { 4425 const inputs = document.querySelectorAll("input[type=text]"); 4426 inputs[0].value = "abc"; 4427 inputs[1].value = ""; 4428 4429 const TIP = createTIP(); 4430 let notifications = []; 4431 function callback(aTIP, aNotification) 4432 { 4433 if (aTIP != TIP) { 4434 return true; 4435 } 4436 switch (aNotification.type) { 4437 case "request-to-commit": 4438 aTIP.commitComposition(); 4439 break; 4440 case "request-to-cancel": 4441 aTIP.cancelComposition(); 4442 break; 4443 case "notify-focus": 4444 case "notify-blur": 4445 notifications.push(aNotification.type); 4446 break; 4447 } 4448 return true; 4449 } 4450 4451 inputs[0].focus(); 4452 TIP.beginInputTransactionForTests(window, callback); 4453 inputs[0].select(); 4454 try { 4455 notifications = []; 4456 await synthesizePlainDragAndDrop({ 4457 srcSelection: SpecialPowers.wrap(inputs[0]).editor.selection, 4458 destElement: inputs[1], 4459 }); 4460 } catch (ex) { 4461 ok(false, `runFocusNotificationTestAfterDrop: unexpected error during DnD (${ex.message})`); 4462 return; 4463 } 4464 is( 4465 document.activeElement, 4466 inputs[1], 4467 "runFocusNotificationTestAfterDrop: Dropping to the second <input> should make it focused" 4468 ); 4469 ok( 4470 notifications.length > 1, 4471 "runFocusNotificationTestAfterDrop: At least two notifications should be fired" 4472 ); 4473 if (notifications.length) { 4474 is( 4475 notifications[notifications.length - 1], 4476 "notify-focus", 4477 "runFocusNotificationTestAfterDrop: focus notification should've been fired at last" 4478 ); 4479 } 4480 } 4481 4482 async function runQuerySelectionEventTestAtTextChangeNotification() { 4483 contenteditable.innerHTML = "<p>abc</p><p>def</p>"; 4484 contenteditable.focus(); 4485 // Ensure to send notify-focus from IMEContentObserver 4486 await new Promise( 4487 resolve => requestAnimationFrame( 4488 () => requestAnimationFrame(resolve) 4489 ) 4490 ); 4491 document.execCommand("selectall"); 4492 // Ensure to send notify-selection-change from IMEContentObserver 4493 await new Promise( 4494 resolve => requestAnimationFrame( 4495 () => requestAnimationFrame(resolve) 4496 ) 4497 ); 4498 4499 const kTestName = "runQuerySelectionEventTestAtTextChangeNotification"; 4500 await new Promise(resolve => { 4501 const TIP = createTIP(); 4502 TIP.beginInputTransactionForTests(window, (aTIP, aNotification) => { 4503 if (aTIP != TIP) { 4504 return true; 4505 } 4506 switch (aNotification.type) { 4507 case "request-to-commit": 4508 aTIP.commitComposition(); 4509 break; 4510 case "request-to-cancel": 4511 aTIP.cancelComposition(); 4512 break; 4513 case "notify-text-change": { 4514 const textContent = synthesizeQueryTextContent(0, 100); 4515 if (textContent?.text.includes("abc")) { 4516 break; // Different notification which we want to test, wait next one. 4517 } 4518 ok( 4519 textContent?.succeeded, 4520 `${kTestName}: query text content should succeed from notify-text-change handler` 4521 ); 4522 const selectedText = synthesizeQuerySelectedText(); 4523 ok( 4524 selectedText?.succeeded, 4525 `${kTestName}: query selected text should succeed from notify-text-change handler` 4526 ); 4527 if (textContent?.succeeded && selectedText?.succeeded) { 4528 is( 4529 selectedText.text, 4530 textContent.text, 4531 `${kTestName}: selected text should be same as all text` 4532 ); 4533 } 4534 resolve(); 4535 break; 4536 } 4537 } 4538 return true; 4539 }); 4540 // TODO: We want to do this while selection is batched but can flush 4541 // pending notifications, however, I have no idea how to do it. 4542 contenteditable.firstChild.remove(); 4543 }); 4544 } 4545 4546 async function runIMEStateUpdateTests() { 4547 const TIP = createTIP(); 4548 let notifications = []; 4549 function callback(aTIP, aNotification) 4550 { 4551 if (aTIP != TIP) { 4552 return true; 4553 } 4554 switch (aNotification.type) { 4555 case "request-to-commit": 4556 aTIP.commitComposition(); 4557 break; 4558 case "request-to-cancel": 4559 aTIP.cancelComposition(); 4560 break; 4561 case "notify-focus": 4562 case "notify-blur": 4563 notifications.push(aNotification.type); 4564 break; 4565 } 4566 return true; 4567 } 4568 4569 contenteditable.focus(); 4570 TIP.beginInputTransactionForTests(window, callback); 4571 await new Promise(resolve => requestAnimationFrame(() => 4572 requestAnimationFrame(resolve) 4573 )); // wait for flushing pending notifications if there is. 4574 4575 // run IMEStateManager::UpdateIMEState to disable IME 4576 notifications = []; 4577 const editor = getHTMLEditor(window); 4578 editor.flags |= Ci.nsIEditor.eEditorReadonlyMask; 4579 await new Promise(resolve => requestAnimationFrame(() => 4580 requestAnimationFrame(resolve) 4581 )); // wait for flush pending notification even if handled asynchronously. 4582 is( 4583 notifications.length ? notifications[0] : undefined, 4584 "notify-blur", 4585 "runIMEStateUpdateTests: Making the HTMLEditor readonly should cause a blur notification" 4586 ); 4587 is( 4588 notifications.length, 4589 1, 4590 `runIMEStateUpdateTests: Making the HTMLEditor readonly should not cause any other notifications, but got ${ 4591 notifications.length > 1 ? notifications[1] : "" 4592 } notification` 4593 ); 4594 is( 4595 SpecialPowers.getDOMWindowUtils(window)?.IMEStatus, 4596 Ci.nsIDOMWindowUtils.IME_STATUS_DISABLED, 4597 `runIMEStateUpdateTests: Making the HTMLEditor readonly should make IME disabled` 4598 ); 4599 4600 // run IMEStateManager::UpdateIMEState to enable IME 4601 notifications = []; 4602 editor.flags &= ~Ci.nsIEditor.eEditorReadonlyMask; 4603 await new Promise(resolve => requestAnimationFrame(() => 4604 requestAnimationFrame(resolve) 4605 )); // wait for flush pending notification even if handled asynchronously. 4606 is( 4607 notifications.length ? notifications[0] : undefined, 4608 "notify-focus", 4609 "runIMEStateUpdateTests: Making the HTMLEditor editable should cause a focus notification without blur notification" 4610 ); 4611 is( 4612 notifications.length, 4613 1, 4614 `runIMEStateUpdateTests: Making the HTMLEditor editable should not cause any other notifications, but got ${ 4615 notifications.length > 1 ? notifications[1] : "" 4616 } notification` 4617 ); 4618 is( 4619 SpecialPowers.getDOMWindowUtils(window)?.IMEStatus, 4620 Ci.nsIDOMWindowUtils.IME_STATUS_ENABLED, 4621 `runIMEStateUpdateTests: Making the HTMLEditor readonly should make IME disabled` 4622 ); 4623 } 4624 4625 async function runTextNotificationChangesDuringNoFrame() { 4626 const TIP = createTIP(); 4627 let onTextChange; 4628 function callback(aTIP, aNotification) 4629 { 4630 if (aTIP != TIP) { 4631 return true; 4632 } 4633 switch (aNotification.type) { 4634 case "request-to-commit": 4635 aTIP.commitComposition(); 4636 break; 4637 case "request-to-cancel": 4638 aTIP.cancelComposition(); 4639 break; 4640 case "notify-text-change": 4641 if (onTextChange) { 4642 onTextChange(aNotification); 4643 } 4644 break; 4645 } 4646 return true; 4647 } 4648 4649 function promiseTextChangeNotification() { 4650 return new Promise(resolve => onTextChange = resolve); 4651 } 4652 4653 function waitForTick() { 4654 return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); 4655 } 4656 4657 input = document.querySelector("input[type=text]"); 4658 input.focus(); 4659 TIP.beginInputTransactionForTests(window, callback); 4660 4661 await (async function test_text_change_notification_for_value_set_during_no_frame() { 4662 const description = "runTextNotificationChangesDuringNoFrame: test_text_change_notification_for_value_set_during_no_frame"; 4663 input.value = "Start"; 4664 input.style.display = "inline"; 4665 input.getBoundingClientRect(); 4666 await waitForTick(); 4667 const waitNotifications = promiseTextChangeNotification(); 4668 input.style.display = "block"; 4669 input.value = "Changed"; 4670 info(`${description}: waiting for notifications...`); 4671 const notification = await waitNotifications; 4672 is( 4673 notification?.offset, 4674 0, 4675 `${description}: offset should be 0` 4676 ); 4677 is( 4678 notification?.removedLength, 4679 "Start".length, 4680 `${description}: removedLength should be the length of the old value` 4681 ); 4682 is( 4683 notification?.addedLength, 4684 "Changed".length, 4685 `${description}: addedLength should be the length of the new value` 4686 ); 4687 })(); 4688 4689 await (async function test_text_change_notification_for_multiple_value_set_during_no_frame() { 4690 const description = "runTextNotificationChangesDuringNoFrame: test_text_change_notification_for_multiple_value_set_during_no_frame"; 4691 input.value = "Start"; 4692 input.style.display = "inline"; 4693 input.getBoundingClientRect(); 4694 await waitForTick(); 4695 const waitNotifications = promiseTextChangeNotification(); 4696 input.style.display = "block"; 4697 input.value = "Changed"; 4698 input.value = "Again!"; 4699 info(`${description}: waiting for notifications...`); 4700 const notification = await waitNotifications; 4701 is( 4702 notification?.offset, 4703 0, 4704 `${description}: offset should be 0` 4705 ); 4706 is( 4707 notification?.removedLength, 4708 "Start".length, 4709 `${description}: removedLength should be the length of the old value` 4710 ); 4711 is( 4712 notification?.addedLength, 4713 "Again!".length, 4714 `${description}: addedLength should be the length of the new value` 4715 ); 4716 })(); 4717 4718 await (async function test_text_change_notification_for_value_set_and_typing_character_during_no_frame() { 4719 const description = "runTextNotificationChangesDuringNoFrame: test_text_change_notification_for_value_set_and_typing_character_during_no_frame"; 4720 input.value = "Start"; 4721 input.style.display = "inline"; 4722 input.getBoundingClientRect(); 4723 await waitForTick(); 4724 const waitNotifications = promiseTextChangeNotification(); 4725 input.style.display = "block"; 4726 input.value = "Change"; 4727 const dKey = new KeyboardEvent("", { code: "KeyD", key: "d", keyCode: KeyboardEvent.DOM_VK_D }); 4728 TIP.keydown(dKey); 4729 TIP.keyup(dKey); 4730 info(`${description}: waiting for notifications...`); 4731 const notification = await waitNotifications; 4732 is( 4733 notification?.offset, 4734 0, 4735 `${description}: offset should be 0` 4736 ); 4737 is( 4738 notification?.removedLength, 4739 "Start".length, 4740 `${description}: removedLength should be the length of the old value` 4741 ); 4742 is( 4743 notification?.addedLength, 4744 "Change".length, 4745 `${description}: addedLength should be the length of the new (set) value` 4746 ); 4747 })(); 4748 4749 input.style.display = ""; 4750 4751 textarea.focus(); 4752 TIP.beginInputTransaction(window, callback); 4753 4754 await (async function test_text_change_notification_for_multi_line_value_set_during_no_frame() { 4755 const description = "runTextNotificationChangesDuringNoFrame: test_text_change_notification_for_multi_line_value_set_during_no_frame"; 4756 textarea.value = "Start\n2nd Line"; 4757 textarea.style.display = "inline"; 4758 textarea.getBoundingClientRect(); 4759 await waitForTick(); 4760 const waitNotifications = promiseTextChangeNotification(); 4761 textarea.style.display = "block"; 4762 textarea.value = "Changed\n2nd Line"; 4763 info(`${description}: waiting for notifications...`); 4764 const notification = await waitNotifications; 4765 is( 4766 notification?.offset, 4767 0, 4768 `${description}: offset should be 0` 4769 ); 4770 is( 4771 notification?.removedLength, 4772 getNativeText("Start\n2nd Line").length, 4773 `${description}: removedLength should be the length of the old value` 4774 ); 4775 is( 4776 notification?.addedLength, 4777 getNativeText("Changed\n2nd Line").length, 4778 `${description}: addedLength should be the length of the new value` 4779 ); 4780 })(); 4781 4782 textarea.style.display = ""; 4783 } 4784 4785 async function runTests() 4786 { 4787 await SpecialPowers.pushPrefEnv({ 4788 set: [["test.ime_content_observer.assert_invalid_cache", true]], 4789 }); 4790 4791 textareaInFrame = iframe.contentDocument.getElementById("textarea"); 4792 runBeginInputTransactionMethodTests(); 4793 runReleaseTests(); 4794 runCompositionTests(); 4795 runCompositionWithKeyEventTests(); 4796 runConsumingKeydownBeforeCompositionTests(); 4797 await runKeyTests(); 4798 runInsertTextWithKeyPressTests(); 4799 runErrorTests(); 4800 runCommitCompositionTests(); 4801 await runCallbackTests(false); 4802 await runCallbackTests(true); 4803 await runTextNotificationChangesDuringNoFrame(); 4804 await runFocusNotificationTestAfterDrop(); 4805 await runUnloadTests1(); 4806 await runUnloadTests2(); 4807 await runQuerySelectionEventTestAtTextChangeNotification(); 4808 await runIMEStateUpdateTests(); 4809 4810 finish(); 4811 } 4812 4813 ]]> 4814 </script> 4815 4816 </window>