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