tor-browser

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

test_sanityEventUtils.html (35062B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <title>Profiling test suite for EventUtils</title>
      5  <script type="text/javascript">
      6  var start = new Date();
      7  </script>
      8  <script src="/tests/SimpleTest/EventUtils.js"></script>
      9  <script type="text/javascript">
     10  var loadTime = new Date();
     11  </script>
     12  <script src="/tests/SimpleTest/SimpleTest.js"></script>
     13  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     14 </head>
     15 <body onload="starttest()">
     16 <input type="radio" id="radioTarget1" name="group">Radio Target 1</input>
     17 <input id="textBoxA">
     18 <input id="textBoxB">
     19 <input id="testMouseEvent" type="button" value="click">
     20 <input id="testKeyEvent" >
     21 <input id="testStrEvent" >
     22 <div id="scrollB" style="width: 190px;height: 250px;overflow:auto">
     23 <p>blah blah blah blah</p>
     24 <p>blah blah blah blah</p>
     25 <p>blah blah blah blah</p>
     26 <p>blah blah blah blah</p>
     27 <p>blah blah blah blah</p>
     28 <p>blah blah blah blah</p>
     29 <p>blah blah blah blah</p>
     30 <p>blah blah blah blah</p>
     31 </div>
     32 <script class="testbody" type="text/javascript">
     33 info("\nProfile::EventUtilsLoadTime: " + (loadTime - start) + "\n");
     34 function starttest() {
     35  SimpleTest.waitForFocus(
     36    async function () {
     37      SimpleTest.waitForExplicitFinish();
     38      var startTime = new Date();
     39      var check = false;
     40      function doCheck() {
     41        check = true;
     42      }
     43 
     44      const kIsHeadless = await SpecialPowers.spawnChrome([], () => {
     45        return Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless;
     46      });
     47 
     48      if (navigator.appVersion.includes("Android")) {
     49        // This is the workaround for test failure on debug build.
     50        await SpecialPowers.pushPrefEnv({set: [["formhelper.autozoom.force-disable.test-only", true]]});
     51      }
     52 
     53      /* test send* functions */
     54      $("testMouseEvent").addEventListener("click", doCheck, {once: true});
     55      sendMouseEvent({type:'click'}, "testMouseEvent");
     56      is(check, true, 'sendMouseEvent should dispatch click event');
     57 
     58      await (async function testSynthesizeNativeMouseEvent() {
     59        let events = [];
     60        let listener = event => events.push(event);
     61        let preventDefault = event => event.preventDefault();
     62        // promiseNativeMouseEventAndWaitForEvent uses capturing listeners
     63        // internally, so use capturing listeners also here.
     64        $("testMouseEvent").addEventListener("mousedown", listener, true);
     65        $("testMouseEvent").addEventListener("mouseup", listener, true);
     66        // Clicking with modifiers may open context menu so that we should prevent to open it.
     67        window.addEventListener("contextmenu", preventDefault, { capture: true });
     68        for (const test of [
     69            {
     70              description: "ShiftLeft",
     71              modifiers: { shiftKey: true },
     72            },
     73            {
     74              description: "ShiftRight",
     75              modifiers: { shiftRightKey: true },
     76            },
     77            {
     78              description: "CtrlLeft",
     79              modifiers: { ctrlKey: true },
     80            },
     81            {
     82              description: "CtrlRight",
     83              modifiers: { ctrlRightKey: true },
     84            },
     85            {
     86              description: "AltLeft",
     87              modifiers: { altKey: true },
     88            },
     89            {
     90              description: "AltRight",
     91              modifiers: { altRightKey: true },
     92            },
     93            {
     94              description: "MetaLeft",
     95              modifiers: { metaKey: true },
     96              skip: () => {
     97                // We've not supported "Meta" as Windows logo key or Super/Hyper keys.
     98                return navigator.platform.includes("Win") || navigator.platform.includes("Linux");
     99              },
    100            },
    101            {
    102              description: "MetaRight",
    103              modifiers: { metaRightKey: true },
    104              skip: () => {
    105                // We've not supported "Meta" as Windows logo key or Super/Hyper keys.
    106                return navigator.platform.includes("Win") || navigator.platform.includes("Linux");
    107              },
    108            },
    109            {
    110              description: "CapsLock",
    111              modifiers: { capsLockKey: true },
    112            },
    113            {
    114              description: "NumLock",
    115              modifiers: { numLockKey: true },
    116              skip: () => {
    117                // macOS does not have `NumLock` key nor state.
    118                return navigator.platform.includes("Mac");
    119              },
    120            },
    121            {
    122              description: "Ctrl+Shift",
    123              modifiers: { ctrlKey: true, shiftKey: true },
    124              skip: () => {
    125                // We forcibly open context menu on macOS so the following test
    126                // will fail to receive mouse events.
    127                return navigator.platform.includes("Mac");
    128              },
    129            },
    130            {
    131              description: "Alt+Shift",
    132              modifiers: { altKey: true, shiftKey: true },
    133            },
    134            {
    135              description: "Meta+Shift",
    136              modifiers: { metaKey: true, shiftKey: true },
    137              skip: () => {
    138                // We've not supported "Meta" as Windows logo key or Super/Hyper keys.
    139                return navigator.platform.includes("Win") || navigator.platform.includes("Linux");
    140              },
    141            },
    142          ]) {
    143          if (test.skip && test.skip()) {
    144            continue;
    145          }
    146          events = [];
    147          info(`testSynthesizeNativeMouseEvent: sending native mouse click (${test.description})`);
    148          await promiseNativeMouseEventAndWaitForEvent({
    149            type: "click",
    150            target: $("testMouseEvent"),
    151            atCenter: true,
    152            modifiers: test.modifiers,
    153            eventTypeToWait: "mouseup",
    154          });
    155          is(events.length, 2,
    156            `testSynthesizeNativeMouseEvent: a pair of "mousedown" and "mouseup" events should be fired (${test.description})`);
    157          is(events[0]?.type, "mousedown",
    158            `testSynthesizeNativeMouseEvent: "mousedown" should be fired (${test.description})`);
    159          is(events[1]?.type, "mouseup",
    160            `testSynthesizeNativeMouseEvent: "mouseup" should be fired (${test.description})`);
    161          if (events.length !== 2) {
    162            continue;
    163          }
    164          for (const mod of [{ keyName: "Alt",      propNames: [ "altKey", "altRightKey" ]},
    165                             { keyName: "Control",  propNames: [ "ctrlKey", "ctrlRightKey" ]},
    166                             { keyName: "Shift",    propNames: [ "shiftKey", "shiftRightKey" ]},
    167                             { keyName: "Meta",     propNames: [ "metaKey", "metaRightKey" ]},
    168                             { keyName: "CapsLock", propNames: [ "capsLockKey" ]},
    169                             { keyName: "NumLock",  propNames: [ "numLockKey" ]},
    170                            ]) {
    171            const activeExpected =
    172              (test.modifiers.hasOwnProperty(mod.propNames[0]) &&
    173               test.modifiers[mod.propNames[0]]) ||
    174                (mod.propNames.length !== 1 &&
    175                 test.modifiers.hasOwnProperty(mod.propNames[1]) &&
    176                 test.modifiers[mod.propNames[1]]);
    177            const checkFn = activeExpected && (
    178              // Bug 1693240: We don't support setting modifiers while posting a mouse event on Windows.
    179              navigator.platform.includes("Win") ||
    180              // Bug 1693237: We don't support setting modifiers on Android.
    181              navigator.appVersion.includes("Android") ||
    182              // In Headless mode, modifiers are not supported by this kind of APIs.
    183              kIsHeadless) ? todo_is : is;
    184            checkFn(events[0]?.getModifierState(mod.keyName), activeExpected,
    185              `testSynthesizeNativeMouseEvent: "mousedown".getModifierState("${mod.keyName}") should return ${activeExpected} (${test.description}`);
    186            checkFn(events[1]?.getModifierState(mod.keyName), activeExpected,
    187              `testSynthesizeNativeMouseEvent: "mouseup".getModifierState("${mod.keyName}") should return ${activeExpected} (${test.description}`);
    188          }
    189        }
    190        const supportsX1AndX2Buttons =
    191          // On Windows, it triggers APP_COMMAND.  Therefore, this test is unloaded.
    192          !navigator.platform.includes("Win") &&
    193          // On macOS, it seems that no API to specify X1 and X2 button at creating an NSEvent.
    194          !navigator.platform.includes("Mac") &&
    195          // On Linux, it seems that X1 button and X2 button events are not synthesized correctly.
    196          !navigator.platform.includes("Linux");
    197        for (let i = 0; i < (supportsX1AndX2Buttons ? 5 : 3); i++) {
    198          events = [];
    199          info(`testSynthesizeNativeMouseEvent: sending native mouse click (button=${i})`);
    200          await promiseNativeMouseEventAndWaitForEvent({
    201            type: "click",
    202            target: $("testMouseEvent"),
    203            atCenter: true,
    204            button: i,
    205            eventTypeToWait: "mouseup",
    206          });
    207          is(events.length, 2,
    208            `testSynthesizeNativeMouseEvent: a pair of "mousedown" and "mouseup" events should be fired (button=${i})`);
    209          is(events[0]?.type, "mousedown",
    210            `testSynthesizeNativeMouseEvent: "mousedown" should be fired (button=${i})`);
    211          is(events[1]?.type, "mouseup",
    212            `testSynthesizeNativeMouseEvent: "mouseup" should be fired (button=${i})`);
    213          if (events.length !== 2) {
    214            continue;
    215          }
    216          is(events[0].button, i,
    217            `testSynthesizeNativeMouseEvent: button of "mousedown" event should be ${i}`);
    218          is(events[1].button, i,
    219            `testSynthesizeNativeMouseEvent: button of "mouseup" event should be ${i}`);
    220        }
    221        $("testMouseEvent").removeEventListener("mousedown", listener);
    222        $("testMouseEvent").removeEventListener("mouseup", listener);
    223        window.removeEventListener("contextmenu", preventDefault, { capture: true });
    224      })();
    225 
    226      check = false;
    227      $("testKeyEvent").addEventListener("keypress", doCheck, {once: true});
    228      $("testKeyEvent").focus();
    229      sendChar("x");
    230      is($("testKeyEvent").value, "x", "sendChar should work");
    231      is(check, true, "sendChar should dispatch keyPress");
    232      $("testKeyEvent").value = "";
    233 
    234      $("testStrEvent").focus();
    235      sendString("string");
    236      is($("testStrEvent").value, "string", "sendString should work");
    237      $("testStrEvent").value = "";
    238 
    239      var keydown = false;
    240      var keypress = false;
    241      $("testKeyEvent").focus();
    242      $("testKeyEvent").addEventListener("keydown", function() { keydown = true; }, {once: true});
    243      $("testKeyEvent").addEventListener("keypress", function() { keypress = true; }, {once: true});
    244      sendKey("DOWN");
    245      ok(keydown, "sendKey should dispatch keyDown");
    246      ok(!keypress, "sendKey shouldn't dispatch keyPress for non-printable key");
    247 
    248      /* test synthesizeMouse* */
    249      //focus trick enables us to run this in iframes
    250      $("radioTarget1").addEventListener('focus', function () {
    251        synthesizeMouse($("radioTarget1"), 1, 1, {});
    252        is($("radioTarget1").checked, true, "synthesizeMouse should work")
    253        $("radioTarget1").checked = false;
    254        disableNonTestMouseEvents(true);
    255        synthesizeMouse($("radioTarget1"), 1, 1, {});
    256        is($("radioTarget1").checked, true, "synthesizeMouse should still work with non-test mouse events disabled");
    257        $("radioTarget1").checked = false;
    258        disableNonTestMouseEvents(false);
    259      }, {once: true});
    260      $("radioTarget1").focus();
    261 
    262      //focus trick enables us to run this in iframes
    263      $("textBoxA").addEventListener("focus", function () {
    264        check = false;
    265        $("textBoxA").addEventListener("click", function() { check = true; }, { once: true });
    266        synthesizeMouseAtCenter($("textBoxA"), {});
    267        is(check, true, 'synthesizeMouse should dispatch mouse event');
    268 
    269        check = false;
    270        $("scrollB").addEventListener("click", function() { check = true; }, { once: true });
    271        synthesizeMouseExpectEvent($("scrollB"), 1, 1, {}, $("scrollB"), "click", "synthesizeMouseExpectEvent should fire click event");
    272        is(check, true, 'synthesizeMouse should dispatch mouse event');
    273      }, {once: true});
    274      $("textBoxA").focus();
    275 
    276      /**
    277       * TODO: testing synthesizeWheel requires a setTimeout
    278       * since there is delay between the scroll event and a check, so for now just test
    279       * that we can successfully call it to avoid having setTimeout vary the runtime metric.
    280       * Testing of this method is currently done here:
    281       * toolkit/content/tests/chrome/test_mousescroll.xul
    282       */
    283      synthesizeWheel($("scrollB"), 5, 5, {'deltaY': 10.0, deltaMode: WheelEvent.DOM_DELTA_LINE});
    284 
    285      /* test synthesizeKey* */
    286      check = false;
    287      $("testKeyEvent").addEventListener("keypress", doCheck, {once:true});
    288      $("testKeyEvent").focus();
    289      sendString("a");
    290      is($("testKeyEvent").value, "a", "synthesizeKey should work");
    291      is(check, true, "synthesizeKey should dispatch keyPress");
    292      $("testKeyEvent").value = "";
    293 
    294      // If |.code| value is not specified explicitly, it should be computed
    295      // from the |.key| value or |.keyCode| value.  If a printable key is
    296      // specified, the |.code| value should be guessed with US-English
    297      // keyboard layout.
    298      for (let test of [{ arg: "KEY_Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN },
    299                        { arg: "VK_RETURN", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN },
    300                        { arg: "KEY_Backspace", code: "Backspace", keyCode: KeyboardEvent.DOM_VK_BACK_SPACE },
    301                        { arg: "KEY_Delete", code: "Delete", keyCode: KeyboardEvent.DOM_VK_DELETE },
    302                        { arg: "KEY_Home", code: "Home", keyCode: KeyboardEvent.DOM_VK_HOME },
    303                        { arg: "KEY_End", code: "End", keyCode: KeyboardEvent.DOM_VK_END },
    304                        { arg: "KEY_ArrowDown", code: "ArrowDown", keyCode: KeyboardEvent.DOM_VK_DOWN },
    305                        { arg: "KEY_ArrowUp", code: "ArrowUp", keyCode: KeyboardEvent.DOM_VK_UP },
    306                        { arg: "KEY_ArrowLeft", code: "ArrowLeft", keyCode: KeyboardEvent.DOM_VK_LEFT },
    307                        { arg: "KEY_ArrowRight", code: "ArrowRight", keyCode: KeyboardEvent.DOM_VK_RIGHT },
    308                        { arg: "KEY_Shift", code: "ShiftLeft", keyCode: KeyboardEvent.DOM_VK_SHIFT },
    309                        { arg: "KEY_Control", code: "ControlLeft", keyCode: KeyboardEvent.DOM_VK_CONTROL },
    310                        { arg: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A },
    311                        { arg: "B", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B },
    312                        { arg: " ", code: "Space", keyCode: KeyboardEvent.DOM_VK_SPACE },
    313                        { arg: "0", code: "Digit0", keyCode: KeyboardEvent.DOM_VK_0 },
    314                        { arg: "(", code: "Digit9", keyCode: KeyboardEvent.DOM_VK_9 },
    315                        { arg: "!", code: "Digit1", keyCode: KeyboardEvent.DOM_VK_1 },
    316                        { arg: "[", code: "BracketLeft", keyCode: KeyboardEvent.DOM_VK_OPEN_BRACKET },
    317                        { arg: ";", code: "Semicolon", keyCode: KeyboardEvent.DOM_VK_SEMICOLON },
    318                        { arg: "\"", code: "Quote", keyCode: KeyboardEvent.DOM_VK_QUOTE },
    319                        { arg: "~", code: "Backquote", keyCode: KeyboardEvent.DOM_VK_BACK_QUOTE },
    320                        { arg: "<", code: "Comma", keyCode: KeyboardEvent.DOM_VK_COMMA },
    321                        { arg: ".", code: "Period", keyCode: KeyboardEvent.DOM_VK_PERIOD }]) {
    322        let testKeydown, keyup;
    323        $("testKeyEvent").focus();
    324        $("testKeyEvent").addEventListener("keydown", (e) => { testKeydown = e; }, {once: true});
    325        $("testKeyEvent").addEventListener("keyup", (e) => { keyup = e; }, {once: true});
    326        synthesizeKey(test.arg);
    327        is(testKeydown.code, test.code, `Synthesizing "${test.arg}" should set code value of "keydown" to "${test.code}"`);
    328        is(testKeydown.keyCode, test.keyCode, `Synthesizing "${test.arg}" should set keyCode value of "keydown" to "${test.keyCode}"`);
    329        is(keyup.code, test.code, `Synthesizing "${test.arg}" key should set code value of "keyup" to "${test.code}"`);
    330        is(keyup.keyCode, test.keyCode, `Synthesizing "${test.arg}" key should set keyCode value of "keyup" to "${test.keyCode}"`);
    331        $("testKeyEvent").value = "";
    332      }
    333 
    334      /* test synthesizeComposition */
    335      var description = "";
    336      var keydownEvent = null;
    337      var keyupEvent = null;
    338      function onKeyDown(aEvent) {
    339        ok(!keydownEvent, description + "keydown should be fired only once" + (keydownEvent ? keydownEvent.key : "") + ", " + (keyupEvent ? keyupEvent.key : ""));
    340        keydownEvent = aEvent;
    341      }
    342      function onKeyUp(aEvent) {
    343        ok(!keyupEvent, description + "keyup should be fired only once");
    344        keyupEvent = aEvent;
    345      }
    346      function resetKeyDownAndKeyUp(aDescription) {
    347        description = aDescription + ": ";
    348        keydownEvent = null;
    349        keyupEvent = null;
    350        check = false;
    351      }
    352      function checkKeyDownAndKeyUp(aKeyDown, aKeyUp) {
    353        if (aKeyDown) {
    354          is(keydownEvent.keyCode, aKeyDown.keyCode,
    355             description + "keydown event should be dispatched (checking keyCode)");
    356          is(keydownEvent.key, aKeyDown.key,
    357             description + "keydown event should be dispatched (checking key)");
    358        } else {
    359          is(keydownEvent, null,
    360             description + "keydown event shouldn't be fired");
    361        }
    362        if (aKeyUp) {
    363          is(keyupEvent.keyCode, aKeyUp.keyCode,
    364             description + "keyup event should be dispatched (checking keyCode)");
    365          is(keyupEvent.key, aKeyUp.key,
    366             description + "keyup event should be dispatched (checking key)");
    367        } else {
    368          is(keyupEvent, null,
    369             description + "keyup event shouldn't be fired");
    370        }
    371      }
    372      $("textBoxB").addEventListener("keydown", onKeyDown);
    373      $("textBoxB").addEventListener("keyup", onKeyUp);
    374 
    375      $("textBoxB").focus();
    376 
    377      // If key event is not specified, fake keydown and keyup events which are
    378      // marked as "processed by IME" should be fired.
    379      resetKeyDownAndKeyUp("synthesizing eCompositionStart without specifying keyboard event");
    380      window.addEventListener("compositionstart", doCheck, {once: true});
    381      synthesizeComposition({type: "compositionstart"});
    382      ok(check, description + "synthesizeComposition() should dispatch compositionstart");
    383      checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    384                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
    385 
    386      resetKeyDownAndKeyUp("trying to synthesize eCompositionUpdate directly without specifying keyboard event");
    387      window.addEventListener("compositionupdate", doCheck, {once: true});
    388      synthesizeComposition({type: "compositionupdate", data: "a"});
    389      ok(!check, description + "synthesizeComposition() should not dispatch compositionupdate without error");
    390      checkKeyDownAndKeyUp(null, null);
    391 
    392      resetKeyDownAndKeyUp("synthesizing eCompositionChange without specifying keyboard event");
    393      window.addEventListener("text", doCheck, {once: true});
    394      synthesizeCompositionChange(
    395        { "composition":
    396          { "string": "a",
    397            "clauses":
    398            [
    399              { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    400            ]
    401          },
    402          "caret": { "start": 1, "length": 0 }
    403        }
    404      );
    405      ok(check, description + "synthesizeCompositionChange should cause dispatching a DOM text event");
    406      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    407                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
    408 
    409      resetKeyDownAndKeyUp("synthesizing eCompositionChange for removing clauses without specifying keyboard event");
    410      synthesizeCompositionChange(
    411        { "composition":
    412          { "string": "a",
    413            "clauses":
    414            [
    415              { "length": 0, "attr": 0 }
    416            ]
    417          },
    418          "caret": { "start": 1, "length": 0 }
    419        }
    420      );
    421      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    422                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
    423 
    424      resetKeyDownAndKeyUp("trying to synthesize eCompositionEnd directly without specifying keyboard event");
    425      window.addEventListener("compositionend", doCheck, {once: true});
    426      synthesizeComposition({type: "compositionend", data: "a"});
    427      ok(!check, description + "synthesizeComposition() should not dispatch compositionend");
    428      checkKeyDownAndKeyUp(null, null);
    429 
    430      resetKeyDownAndKeyUp("synthesizing eCompositionCommit without specifying keyboard event");
    431      synthesizeComposition({type: "compositioncommit", data: "a"});
    432      ok(check, description + "synthesizeComposition() should dispatch compositionend");
    433      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    434                           {inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
    435 
    436      var querySelectedText = synthesizeQuerySelectedText();
    437      ok(querySelectedText, "query selected text event result is null");
    438      ok(querySelectedText.succeeded, "query selected text event failed");
    439      is(querySelectedText.offset, 1,
    440         "query selected text event returns wrong offset");
    441      is(querySelectedText.text, "",
    442         "query selected text event returns wrong selected text");
    443      $("textBoxB").value = "";
    444 
    445      querySelectedText = synthesizeQuerySelectedText();
    446      ok(querySelectedText, "query selected text event result is null");
    447      ok(querySelectedText.succeeded, "query selected text event failed");
    448      is(querySelectedText.offset, 0,
    449         "query selected text event returns wrong offset");
    450      is(querySelectedText.text, "",
    451         "query selected text event returns wrong selected text");
    452      var endTime = new Date();
    453      info("\nProfile::EventUtilsRunTime: " + (endTime-startTime) + "\n");
    454 
    455      // In most cases, automated tests shouldn't try to synthesize
    456      // compositionstart manually.  Let's check if synthesizeCompositionChange()
    457      // dispatches compositionstart automatically.
    458      resetKeyDownAndKeyUp("synthesizing eCompositionChange without specifying keyboard event when there is no composition");
    459      window.addEventListener("compositionstart", doCheck, {once: true});
    460      synthesizeCompositionChange(
    461        { "composition":
    462          { "string": "a",
    463            "clauses":
    464            [
    465              { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
    466            ]
    467          },
    468          "caret": { "start": 1, "length": 0 }
    469        }
    470      );
    471      ok(check, description + "synthesizeCompositionChange should dispatch \"compositionstart\" automatically if there is no composition");
    472      checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    473                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
    474 
    475      resetKeyDownAndKeyUp("synthesizing eCompositionCommitAsIs without specifying keyboard event");
    476      synthesizeComposition({type: "compositioncommitasis"});
    477      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    478                           {inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
    479 
    480      // If key event is specified, keydown event which is marked as "processed
    481      // by IME" should be fired and keyup event which is NOT marked as so
    482      // should be fired too.
    483      resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event");
    484      synthesizeComposition({type: "compositionstart", key: {key: "a"}});
    485      checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    486                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_A, key: "a"});
    487 
    488      resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event");
    489      synthesizeCompositionChange(
    490        {"composition":
    491          {"string": "b", "clauses": [
    492             {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
    493           ]},
    494          "caret": {"start": 1, "length": 0},
    495          "key": {key: "b"},
    496        }
    497      );
    498      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    499                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"});
    500 
    501      resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event");
    502      synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter"}});
    503      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    504                           {inComposition: false, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"});
    505 
    506      // keyup shouldn't be dispatched automatically if type is specified as keydown
    507      resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose type is keydown");
    508      synthesizeComposition({type: "compositionstart", key: {key: "a", type: "keydown"}});
    509      checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    510                           null);
    511 
    512      resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose type is keydown");
    513      synthesizeCompositionChange(
    514        {"composition":
    515          {"string": "b", "clauses": [
    516             {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
    517           ]},
    518          "caret": {"start": 1, "length": 0},
    519          "key": {key: "b", type: "keydown"},
    520        }
    521      );
    522      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    523                           null);
    524 
    525      resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose type is keydown");
    526      synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", type: "keydown"}});
    527      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    528                           null);
    529 
    530      // keydown shouldn't be dispatched automatically if type is specified as keyup
    531      resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose type is keyup");
    532      synthesizeComposition({type: "compositionstart", key: {key: "a", type: "keyup"}});
    533      checkKeyDownAndKeyUp(null,
    534                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_A, key: "a"});
    535 
    536      resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose type is keyup");
    537      synthesizeCompositionChange(
    538        {"composition":
    539          {"string": "b", "clauses": [
    540             {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
    541           ]},
    542          "caret": {"start": 1, "length": 0},
    543          "key": {key: "b", type: "keyup"},
    544        }
    545      );
    546      checkKeyDownAndKeyUp(null,
    547                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"});
    548 
    549      resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose type is keyup");
    550      synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", type: "keyup"}});
    551      checkKeyDownAndKeyUp(null,
    552                           {inComposition: false, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"});
    553 
    554      // keydown event shouldn't be marked as "processed by IME" if doNotMarkKeydownAsProcessed is true
    555      resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose doNotMarkKeydownAsProcessed is true");
    556      synthesizeComposition({type: "compositionstart", key: {key: "a", doNotMarkKeydownAsProcessed: true}});
    557      checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_A, key: "a"},
    558                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_A, key: "a"});
    559 
    560      resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose doNotMarkKeydownAsProcessed is true");
    561      synthesizeCompositionChange(
    562        {"composition":
    563          {"string": "b", "clauses": [
    564             {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
    565           ]},
    566          "caret": {"start": 1, "length": 0},
    567          "key": {key: "b", doNotMarkKeydownAsProcessed: true},
    568        }
    569      );
    570      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"},
    571                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"});
    572 
    573      resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose doNotMarkKeydownAsProcessed is true");
    574      synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", doNotMarkKeydownAsProcessed: true}});
    575      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"},
    576                           {inComposition: false, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"});
    577 
    578      // keyup event should be marked as "processed by IME" if markKeyupAsProcessed is true
    579      resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose markKeyupAsProcessed is true");
    580      synthesizeComposition({type: "compositionstart", key: {key: "a", markKeyupAsProcessed: true}});
    581      checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    582                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
    583 
    584      resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose markKeyupAsProcessed is true");
    585      synthesizeCompositionChange(
    586        {"composition":
    587          {"string": "b", "clauses": [
    588             {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
    589           ]},
    590          "caret": {"start": 1, "length": 0},
    591          "key": {key: "b", markKeyupAsProcessed: true},
    592        }
    593      );
    594      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    595                           {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
    596 
    597      resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose markKeyupAsProcessed is true");
    598      synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", markKeyupAsProcessed: true}});
    599      checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
    600                           {inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
    601 
    602      // If key event is explicitly declared with null, keyboard events shouldn't
    603      // be fired for emulating text inputs without keyboard such as voice input or something.
    604      resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event as null");
    605      synthesizeComposition({type: "compositionstart", key: null});
    606      checkKeyDownAndKeyUp(null, null);
    607 
    608      resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event as null");
    609      synthesizeCompositionChange(
    610        {"composition":
    611          {"string": "b", "clauses": [
    612             {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
    613           ]},
    614          "caret": {"start": 1, "length": 0},
    615          "key": null,
    616        }
    617      );
    618      checkKeyDownAndKeyUp(null, null);
    619 
    620      resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event as null");
    621      synthesizeComposition({type: "compositioncommit", data: "c", key: null});
    622      checkKeyDownAndKeyUp(null, null);
    623 
    624      // If key event is explicitly declared with empty object, keyboard events
    625      // shouldn't be fired for emulating text inputs without keyboard such as
    626      // voice input or something.
    627      resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event as empty");
    628      synthesizeComposition({type: "compositionstart", key: {}});
    629      checkKeyDownAndKeyUp(null, null);
    630 
    631      resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event as empty");
    632      synthesizeCompositionChange(
    633        {"composition":
    634          {"string": "b", "clauses": [
    635             {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
    636           ]},
    637          "caret": {"start": 1, "length": 0},
    638          "key": {},
    639        }
    640      );
    641      checkKeyDownAndKeyUp(null, null);
    642 
    643      resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event as empty");
    644      synthesizeComposition({type: "compositioncommit", data: "c", key: {}});
    645      checkKeyDownAndKeyUp(null, null);
    646 
    647      $("textBoxB").removeEventListener("keydown", onKeyDown);
    648      $("textBoxB").removeEventListener("keyup", onKeyUp);
    649 
    650 
    651      // Async event synthesizing.
    652      // On Android this does not work.
    653      if (navigator.userAgent.includes("Android")) {
    654        SimpleTest.finish();
    655        return;
    656      }
    657 
    658      await (async function () {
    659        await SpecialPowers.pushPrefEnv({set: [["test.events.async.enabled", true]]});
    660        try {
    661          disableNonTestMouseEvents(true);
    662          let mouseMoveCount = 0;
    663          let waitForAllSynthesizedMouseMove =
    664            new Promise(resolve => {
    665              window.addEventListener("mousemove", function onMouseMove(aEvent) {
    666                mouseMoveCount++;
    667                is(aEvent.target, $("testMouseEvent"),
    668                  `The mousemove event target of ${
    669                    mouseMoveCount
    670                  } should be the input#testMouseEvent, but ${
    671                    aEvent.target.nodeName
    672                  }`);
    673                if (mouseMoveCount === 30) {
    674                  window.removeEventListener("mousemove", onMouseMove, { capture: true });
    675                  resolve();
    676                }
    677              }, { capture: true });
    678            });
    679          for (let i = 0; i < 30; i++) {
    680            synthesizeMouse($("testMouseEvent"), 3 + i % 2, 3 + i % 2, { type: "mousemove" });
    681          }
    682          await waitForAllSynthesizedMouseMove;
    683        } finally {
    684          disableNonTestMouseEvents(false);
    685        }
    686      })();
    687 
    688      SimpleTest.finish();
    689    }
    690  );
    691 };
    692 </script>
    693 </body>
    694 </html>