Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/base/test/chrome/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 257 kB image not shown  

Quelle  window_nsITextInputProcessor.xhtml   Sprache: unbekannt

 
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                 type="text/css"?>
<window title="Testing nsITextInputProcessor behavior"
  xmlns:html="http://www.w3.org/1999/xhtml"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  onunload="onunload();">
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<body  xmlns="http://www.w3.org/1999/xhtml">
<div id="display">
<input id="input" type="text"/><input id="anotherInput" type="text"/><br/>
<textarea></textarea>
<iframe id="iframe" width="300" height="150"
        src="data:text/html,<textarea id='textarea' cols='20' rows='4'></textarea>"></iframe><br/>
<div contenteditable=""><br/></div>
</div>
<div id="content" style="display: none">

</div>
<pre id="test">
</pre>
</body>

<script class="testbody" type="application/javascript">
<![CDATA[

var SimpleTest = window.arguments[0].SimpleTest;

SimpleTest.waitForFocus(runTests, window);

function getHTMLEditor(aWindow) {
  return SpecialPowers.wrap(aWindow).docShell.editingSession?.getEditorForWindow(aWindow);
}

function ok(aCondition, aMessage)
{
  SimpleTest.ok(aCondition, aMessage);
}

function is(aLeft, aRight, aMessage)
{
  SimpleTest.is(aLeft, aRight, aMessage);
}

function isnot(aLeft, aRight, aMessage)
{
  SimpleTest.isnot(aLeft, aRight, aMessage);
}

function todo_is(aLeft, aRight, aMessage)
{
  SimpleTest.todo_is(aLeft, aRight, aMessage);
}

function info(aMessage) {
  SimpleTest.info(aMessage);
}

function finish()
{
  window.close();
}

function onunload()
{
  SimpleTest.finish();
}

function checkInputEvent(aEvent, aCancelable, aIsComposing, aInputType, aData, aDescription) {
  if (aEvent.type !== "input" && aEvent.type !== "beforeinput") {
    console.trace();
    throw new Error(`${aDescription}"${aEvent.type}" is not InputEvent`);
  }
  ok(InputEvent.isInstance(aEvent), `${aDescription}"${aEvent.type}" event should be dispatched with InputEvent interface`);
  is(aEvent.cancelable, aCancelable, `${aDescription}"${aEvent.type}" event should ${aCancelable ? "be" : "not be"} cancelable`);
  is(aEvent.bubbles, true, `${aDescription}"${aEvent.type}" event should always bubble`);
  is(aEvent.isComposing, aIsComposing, `${aDescription}isComposing of "${aEvent.type}" event should be ${aIsComposing}`);
  is(aEvent.inputType, aInputType, `${aDescription}inputType of "${aEvent.type}" event should be "${aInputType}"`);
  is(aEvent.data, aData, `${aDescription}data of "${aEvent.type}" event should be "${aData}"`);
  is(aEvent.dataTransfer, null, `${aDescription}dataTransfer of "${aEvent.type}" event should be null`);
  is(aEvent.getTargetRanges().length, 0, `${aDescription}getTargetRanges() of "${aEvent.type}" event should return empty array`);
}

const kIsMac = (navigator.platform.indexOf("Mac") == 0);

const iframe = document.getElementById("iframe");
let childWindow = iframe.contentWindow;
let textareaInFrame;
let input = document.getElementById("input");
const textarea = document.querySelector("textarea");
const otherWindow = window.arguments[0];
const otherDocument = otherWindow.document;
const inputInChildWindow = otherDocument.getElementById("input");
const contenteditable = document.querySelector("div[contenteditable]");
const { AppConstants } = ChromeUtils.importESModule(
  "resource://gre/modules/AppConstants.sys.mjs"
);
const kLF = "\n";
const kExpectInputBeforeCompositionEnd = SpecialPowers.getBoolPref("dom.input_events.dispatch_before_compositionend");

function getNativeText(aXPText)
{
  if (kLF == "\n") {
    return aXPText;
  }
  return aXPText.replace(/\n/g, kLF);
}

function createTIP()
{
  return Cc["@mozilla.org/text-input-processor;1"].
           createInstance(Ci.nsITextInputProcessor);
}

function runBeginInputTransactionMethodTests()
{
  var description = "runBeginInputTransactionMethodTests: ";
  input.value = "";
  input.focus();

  var simpleCallback = function (aTIP, aNotification)
  {
    switch (aNotification.type) {
      case "request-to-commit":
        aTIP.commitComposition();
        break;
      case "request-to-cancel":
        aTIP.cancelComposition();
        break;
    }
    return true;
  };

  var TIP1 = createTIP();
  var TIP2 = createTIP();
  isnot(TIP1, TIP2,
        description + "TIP instances should be different");

  // beginInputTransaction() and beginInputTransactionForTests() can take ownership if there is no composition.
  ok(TIP1.beginInputTransaction(window, simpleCallback),
     description + "TIP1.beginInputTransaction(window) should succeed because there is no composition");
  ok(TIP1.beginInputTransactionForTests(window),
     description + "TIP1.beginInputTransactionForTests(window) should succeed because there is no composition");
  ok(TIP2.beginInputTransaction(window, simpleCallback),
     description + "TIP2.beginInputTransaction(window) should succeed because there is no composition");
  ok(TIP2.beginInputTransactionForTests(window),
     description + "TIP2.beginInputTransactionForTests(window) should succeed because there is no composition");

  // Start composition with TIP1, then, other TIPs cannot take ownership during a composition.
  ok(TIP1.beginInputTransactionForTests(window),
     description + "TIP1.beginInputTransactionForTests() should succeed because there is no composition");
  var composingStr = "foo";
  TIP1.setPendingCompositionString(composingStr);
  TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
  ok(TIP1.flushPendingComposition(),
     description + "TIP1.flushPendingComposition() should return true becuase it should be valid composition");
  is(input.value, composingStr,
     description + "The input element should have composing string");

  // Composing nsITextInputProcessor instance shouldn't allow initialize it again.
  try {
    TIP1.beginInputTransaction(window, simpleCallback);
    ok(false,
       "TIP1.beginInputTransaction(window) should cause throwing an exception because it's composing with different purpose");
  } catch (e) {
    ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
       description + "TIP1.beginInputTransaction(window) should cause throwing an exception including NS_ERROR_ALREADY_INITIALIZED because it's composing for tests");
  }
  try {
    TIP1.beginInputTransactionForTests(otherWindow);
    ok(false,
       "TIP1.beginInputTransactionForTests(otherWindow) should cause throwing an exception because it's composing on different window");
  } catch (e) {
    ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
       description + "TIP1.beginInputTransaction(otherWindow) should cause throwing an exception including NS_ERROR_ALREADY_INITIALIZED because it's composing on this window");
  }
  ok(TIP1.beginInputTransactionForTests(window),
     description + "TIP1.beginInputTransactionForTests(window) should succeed because TextEventDispatcher was initialized with same purpose");
  ok(TIP1.beginInputTransactionForTests(childWindow),
     description + "TIP1.beginInputTransactionForTests(childWindow) should succeed because TextEventDispatcher was initialized with same purpose and is shared by window and childWindow");
  ok(!TIP2.beginInputTransaction(window, simpleCallback),
     description + "TIP2.beginInputTransaction(window) should not succeed because there is composition synthesized by TIP1");
  ok(!TIP2.beginInputTransactionForTests(window),
     description + "TIP2.beginInputTransactionForTests(window) should not succeed because there is composition synthesized by TIP1");
  ok(!TIP2.beginInputTransaction(childWindow, simpleCallback),
     description + "TIP2.beginInputTransaction(childWindow) should not succeed because there is composition synthesized by TIP1");
  ok(!TIP2.beginInputTransactionForTests(childWindow),
     description + "TIP2.beginInputTransactionForTests(childWindow) should not succeed because there is composition synthesized by TIP1");
  ok(TIP2.beginInputTransaction(otherWindow, simpleCallback),
     description + "TIP2.beginInputTransaction(otherWindow) should succeed because there is composition synthesized by TIP1 but it's in other window");
  ok(TIP2.beginInputTransactionForTests(otherWindow),
     description + "TIP2.beginInputTransactionForTests(otherWindow) should succeed because there is composition synthesized by TIP1 but it's in other window");

  // Let's confirm that the composing string is NOT committed by above tests.
  TIP1.commitComposition();
  is(input.value, composingStr,
     description + "TIP1.commitString() without specifying commit string should commit current composition with the last composing string");
  ok(!TIP1.hasComposition,
     description + "TIP1.commitString() without specifying commit string should've end composition");

  ok(TIP1.beginInputTransaction(window, simpleCallback),
     description + "TIP1.beginInputTransaction() should succeed because there is no composition #2");
  ok(TIP1.beginInputTransactionForTests(window),
     description + "TIP1.beginInputTransactionForTests() should succeed because there is no composition #2");
  ok(TIP2.beginInputTransactionForTests(window),
     description + "TIP2.beginInputTransactionForTests() should succeed because the composition was already committed #2");

  // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during startComposition().
  var events = [];
  input.addEventListener("compositionstart", function (aEvent) {
    events.push(aEvent);
    // eslint-disable-next-line no-caller
    input.removeEventListener(aEvent.type, arguments.callee);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from compositionstart event handler during TIP1.startComposition();");
  });
  TIP1.beginInputTransaction(window, simpleCallback);
  TIP1.startComposition();
  is(events.length, 1,
     description + "compositionstart event should be fired by TIP1.startComposition()");
  TIP1.cancelComposition();

  // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during flushPendingComposition().
  events = [];
  input.addEventListener("compositionstart", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from compositionstart event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  input.addEventListener("compositionupdate", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from compositionupdate event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  input.addEventListener("text", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from text event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  input.addEventListener("beforeinput", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from beforeinput event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  input.addEventListener("input", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from input event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  TIP1.beginInputTransaction(window, simpleCallback);
  TIP1.setPendingCompositionString(composingStr);
  TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
  TIP1.flushPendingComposition();
  is(events.length, 5,
     description + "compositionstart, compositionupdate, text, beforeinput and input events should be fired by TIP1.flushPendingComposition()");
  is(events[0].type, "compositionstart",
     description + "events[0] should be compositionstart");
  is(events[1].type, "compositionupdate",
     description + "events[1] should be compositionupdate");
  is(events[2].type, "text",
     description + "events[2] should be text");
  is(events[3].type, "beforeinput",
     description + "events[3] should be beforeinput");
  checkInputEvent(events[3], false, true, "insertCompositionText", composingStr, description);
  is(events[4].type, "input",
     description + "events[4] should be input");
  checkInputEvent(events[4], false, true, "insertCompositionText", composingStr, description);
  TIP1.cancelComposition();

  // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during commitComposition().
  (() => {
    events = [];
    TIP1.beginInputTransaction(window, simpleCallback);
    TIP1.setPendingCompositionString(composingStr);
    TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
    TIP1.flushPendingComposition();
    input.addEventListener("text", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from text event handler during a call of TIP1.commitComposition();");
    }, {once: true});
    input.addEventListener("compositionend", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from compositionend event handler during a call of TIP1.commitComposition();");
    }, {once: true});
    input.addEventListener("beforeinput", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from beforeinput event handler during a call of TIP1.commitComposition();");
    }, {once: true});
    function onInput(aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from input event handler during a call of TIP1.commitComposition();");
    }
    input.addEventListener("input", onInput);
    TIP1.commitComposition();
    is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
      description + "text, beforeinput, compositionend and input events should be fired by TIP1.commitComposition()");
    let index = -1;
    is(events[++index].type, "text",
      `${description}events[${index}] should be text`);
    is(events[++index].type, "beforeinput",
      `${description}events[${index}] should be beforeinput`);
    checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description);
    if (kExpectInputBeforeCompositionEnd) {
      is(events[++index].type, "input",
        `${description}events[${index}] should be input`);
      checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description);
    }
    is(events[++index].type, "compositionend",
      `${description}events[${index}] should be compositionend`);
    is(events[++index].type, "input",
      `${description}events[${index}] should be input`);
    checkInputEvent(events[index], false, false, "insertCompositionText", composingStr, description);
    input.removeEventListener("input", onInput);
  })();

  // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during commitCompositionWith("bar").
  (() => {
    events = [];
    input.addEventListener("compositionstart", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from compositionstart event handler during TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    input.addEventListener("compositionupdate", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction during compositionupdate event handler TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    input.addEventListener("text", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction during text event handler TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    input.addEventListener("compositionend", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction during compositionend event handler TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    input.addEventListener("beforeinput", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction during beforeinput event handler TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    function onInput(aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction during input event handler TIP1.commitCompositionWith(\"bar\");");
    }
    input.addEventListener("input", onInput);
    TIP1.beginInputTransaction(window, simpleCallback);
    TIP1.commitCompositionWith("bar");
    is(events.length, kExpectInputBeforeCompositionEnd ? 7 : 6,
      description + "compositionstart, compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.commitCompositionWith(\"bar\")");
    let index = -1;
    is(events[++index].type, "compositionstart",
      `${description}events[${index}] should be compositionstart`);
    is(events[++index].type, "compositionupdate",
      `${description}events[${index}] should be compositionupdate`);
    is(events[++index].type, "text",
      `${description}events[${index}] should be text`);
    is(events[++index].type, "beforeinput",
      `${description}events[${index}] should be beforeinput`);
    checkInputEvent(events[3], false, true, "insertCompositionText", "bar", description);
    if (kExpectInputBeforeCompositionEnd) {
      is(events[++index].type, "input",
        `${description}events[${index}] should be input`);
      checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description);
    }
    is(events[++index].type, "compositionend",
      `${description}events[${index}] should be compositionend`);
    is(events[++index].type, "input",
      `${description}events[${index}] should be input`);
    checkInputEvent(events[index], false, false, "insertCompositionText", "bar", description);
    input.removeEventListener("input", onInput);
  })();

  // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during cancelComposition().
  (() => {
    events = [];
    TIP1.beginInputTransaction(window, simpleCallback);
    TIP1.setPendingCompositionString(composingStr);
    TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
    TIP1.flushPendingComposition();
    input.addEventListener("compositionupdate", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from compositionupdate event handler during a call of TIP1.cancelComposition();");
    }, {once: true});
    input.addEventListener("text", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from text event handler during a call of TIP1.cancelComposition();");
    }, {once: true});
    input.addEventListener("compositionend", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from compositionend event handler during a call of TIP1.cancelComposition();");
    }, {once: true});
    input.addEventListener("beforeinput", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from beforeinput event handler during a call of TIP1.cancelComposition();");
    }, {once: true});
    function onInput(aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransaction(window, simpleCallback),
        description + "TIP2 shouldn't be able to begin input transaction from input event handler during a call of TIP1.cancelComposition();");
    }
    input.addEventListener("input", onInput);
    TIP1.cancelComposition();
    is(events.length, kExpectInputBeforeCompositionEnd ? 6 : 5,
      description + "compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.cancelComposition()");
    let index = -1;
    is(events[++index].type, "compositionupdate",
      `${description}events[${index}] should be compositionupdate`);
    is(events[++index].type, "text",
      `${description}events[${index}] should be text`);
    is(events[++index].type, "beforeinput",
      `${description}events[${index}] should be beforeinput`);
    checkInputEvent(events[index], false, true, "insertCompositionText", "", description);
    if (kExpectInputBeforeCompositionEnd) {
      is(events[++index].type, "input",
        `${description}events[${index}] should be input`);
      checkInputEvent(events[index], false, true, "insertCompositionText", "", description);
    }
    is(events[++index].type, "compositionend",
      `${description}events[${index}] should be compositionend`);
    is(events[++index].type, "input",
      `${description}events[${index}] should be input`);
    checkInputEvent(events[index], false, false, "insertCompositionText", "", description);
    input.removeEventListener("input", onInput);
  })();

  // Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during keydown() and keyup().
  events = [];
  TIP1.beginInputTransaction(window, simpleCallback);
  input.addEventListener("keydown", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from keydown event handler during a call of TIP1.keydown();");
  }, {once: true});
  input.addEventListener("keypress", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from keypress event handler during a call of TIP1.keydown();");
  }, {once: true});
  input.addEventListener("beforeinput", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from beforeinput event handler during a call of TIP1.keydown();");
  }, {once: true});
  input.addEventListener("input", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from input event handler during a call of TIP1.keydown();");
  }, {once: true});
  input.addEventListener("keyup", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransaction(window, simpleCallback),
       description + "TIP2 shouldn't be able to begin input transaction from keyup event handler during a call of TIP1.keyup();");
  }, {once: true});
  var keyA = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A });
  TIP1.keydown(keyA);
  TIP1.keyup(keyA);
  is(events.length, 5,
     description + "keydown, keypress, beforeinput, input, keyup events should be fired by TIP1.keydown() and TIP1.keyup()");
  is(events[0].type, "keydown",
     description + "events[0] should be keydown");
  is(events[1].type, "keypress",
     description + "events[1] should be keypress");
  is(events[2].type, "beforeinput",
     description + "events[2] should be beforeinput");
  checkInputEvent(events[2], true, false, "insertText", "a", description);
  is(events[3].type, "input",
     description + "events[3] should be input");
  checkInputEvent(events[3], false, false, "insertText", "a", description);
  is(events[4].type, "keyup",
     description + "events[4] should be keyup");

  // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during startComposition().
  events = [];
  input.addEventListener("compositionstart", function (aEvent) {
    events.push(aEvent);
    // eslint-disable-next-line no-caller
    input.removeEventListener(aEvent.type, arguments.callee);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from compositionstart event handler during TIP1.startComposition();");
  });
  TIP1.beginInputTransactionForTests(window);
  TIP1.startComposition();
  is(events.length, 1,
     description + "compositionstart event should be fired by TIP1.startComposition()");
  TIP1.cancelComposition();

  // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during flushPendingComposition().
  events = [];
  input.addEventListener("compositionstart", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from compositionstart event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  input.addEventListener("compositionupdate", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from compositionupdate event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  input.addEventListener("text", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from text event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  input.addEventListener("beforeinput", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from beforeinput event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  input.addEventListener("input", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from input event handler during a call of TIP1.flushPendingComposition();");
  }, {once: true});
  TIP1.beginInputTransactionForTests(window);
  TIP1.setPendingCompositionString(composingStr);
  TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
  TIP1.flushPendingComposition();
  is(events.length, 5,
     description + "compositionstart, compositionupdate, text, beforeinput and input events should be fired by TIP1.flushPendingComposition()");
  is(events[0].type, "compositionstart",
     description + "events[0] should be compositionstart");
  is(events[1].type, "compositionupdate",
     description + "events[1] should be compositionupdate");
  is(events[2].type, "text",
     description + "events[2] should be text");
  is(events[3].type, "beforeinput",
     description + "events[3] should be beforeinput");
  checkInputEvent(events[3], false, true, "insertCompositionText", composingStr, description);
  is(events[4].type, "input",
     description + "events[4] should be input");
  checkInputEvent(events[4], false, true, "insertCompositionText", composingStr, description);
  TIP1.cancelComposition();

  // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during commitComposition().
  (function () {
    events = [];
    TIP1.beginInputTransactionForTests(window, simpleCallback);
    TIP1.setPendingCompositionString(composingStr);
    TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
    TIP1.flushPendingComposition();
    input.addEventListener("text", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from text event handler during a call of TIP1.commitComposition();");
    }, {once: true});
    input.addEventListener("compositionend", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from compositionend event handler during a call of TIP1.commitComposition();");
    }, {once: true});
    input.addEventListener("beforeinput", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from beforeinput event handler during a call of TIP1.commitComposition();");
    }, {once: true});
    function onInput(aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from input event handler during a call of TIP1.commitComposition();");
    }
    input.addEventListener("input", onInput);
    TIP1.commitComposition();
    is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
      description + "text, beforeinput, compositionend and input events should be fired by TIP1.commitComposition()");
    let index = -1;
    is(events[++index].type, "text",
      `${description}events[${index}] should be text`);
    is(events[++index].type, "beforeinput",
      `${description}events[${index}] should be beforeinput`);
    checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description);
    if (kExpectInputBeforeCompositionEnd) {
      is(events[++index].type, "input",
        `${description}events[${index}] should be input`);
      checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description);
    }
    is(events[++index].type, "compositionend",
      `${description}events[${index}] should be compositionend`);
    is(events[++index].type, "input",
      `${description}events[${index}] should be input`);
    checkInputEvent(events[index], false, false, "insertCompositionText", composingStr, description);
    input.removeEventListener("input", onInput);
  })();

  // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during commitCompositionWith("bar").
  (() => {
    events = [];
    input.addEventListener("compositionstart", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from compositionstart event handler during TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    input.addEventListener("compositionupdate", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests during compositionupdate event handler TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    input.addEventListener("text", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests during text event handler TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    input.addEventListener("compositionend", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests during compositionend event handler TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    input.addEventListener("beforeinput", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests during beforeinput event handler TIP1.commitCompositionWith(\"bar\");");
    }, {once: true});
    function onInput(aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests during input event handler TIP1.commitCompositionWith(\"bar\");");
    }
    input.addEventListener("input", onInput);
    TIP1.beginInputTransactionForTests(window);
    TIP1.commitCompositionWith("bar");
    is(events.length, kExpectInputBeforeCompositionEnd ? 7 : 6,
      description + "compositionstart, compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.commitCompositionWith(\"bar\")");
    let index = -1;
    is(events[++index].type, "compositionstart",
      `${description}events[${index}] should be compositionstart`);
    is(events[++index].type, "compositionupdate",
      `${description}events[${index}] should be compositionupdate`);
    is(events[++index].type, "text",
      `${description}events[${index}] should be text`);
    is(events[++index].type, "beforeinput",
      `${description}events[${index}] should be beforeinput`);
    checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description);
    if (kExpectInputBeforeCompositionEnd) {
      is(events[++index].type, "input",
        `${description}events[${index}] should be input`);
      checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description);
    }
    is(events[++index].type, "compositionend",
      `${description}events[${index}] should be compositionend`);
    is(events[++index].type, "input",
      `${description}events[${index}] should be input`);
    checkInputEvent(events[index], false, false, "insertCompositionText", "bar", description);
    input.removeEventListener("input", onInput);
  })();

  // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during cancelComposition().
  (() => {
    events = [];
    TIP1.beginInputTransactionForTests(window, simpleCallback);
    TIP1.setPendingCompositionString(composingStr);
    TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
    TIP1.flushPendingComposition();
    input.addEventListener("compositionupdate", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from compositionupdate event handler during a call of TIP1.cancelComposition();");
    }, {once: true});
    input.addEventListener("text", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from text event handler during a call of TIP1.cancelComposition();");
    }, {once: true});
    input.addEventListener("compositionend", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from compositionend event handler during a call of TIP1.cancelComposition();");
    }, {once: true});
    input.addEventListener("beforeinput", function (aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from beforeinput event handler during a call of TIP1.cancelComposition();");
    }, {once: true});
    function onInput(aEvent) {
      events.push(aEvent);
      ok(!TIP2.beginInputTransactionForTests(window),
        description + "TIP2 shouldn't be able to begin input transaction for tests from input event handler during a call of TIP1.cancelComposition();");
    }
    input.addEventListener("input", onInput);
    TIP1.cancelComposition();
    is(events.length, kExpectInputBeforeCompositionEnd ? 6 : 5,
      description + "compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.cancelComposition()");
    let index = -1;
    is(events[++index].type, "compositionupdate",
      `${description}events[${index}] should be compositionupdate`);
    is(events[++index].type, "text",
      `${description}events[${index}] should be text`);
    is(events[++index].type, "beforeinput",
      `${description}events[${index}] should be beforeinput`);
    checkInputEvent(events[index], false, true, "insertCompositionText", "", description);
    if (kExpectInputBeforeCompositionEnd) {
      is(events[++index].type, "input",
        `${description}events[${index}] should be input`);
      checkInputEvent(events[index], false, true, "insertCompositionText", "", description);
    }
    is(events[++index].type, "compositionend",
      `${description}events[${index}] should be compositionend`);
    is(events[++index].type, "input",
      `${description}events[${index}] should be input`);
    checkInputEvent(events[index], false, false, "insertCompositionText", "", description);
    input.removeEventListener("input", onInput);
  })();

  // Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during keydown() and keyup().
  events = [];
  TIP1.beginInputTransactionForTests(window);
  input.addEventListener("keydown", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests for tests from keydown event handler during a call of TIP1.keydown();");
  }, {once: true});
  input.addEventListener("keypress", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from keypress event handler during a call of TIP1.keydown();");
  }, {once: true});
  input.addEventListener("beforeinput", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from beforeinput event handler during a call of TIP1.keydown();");
  }, {once: true});
  input.addEventListener("input", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from input event handler during a call of TIP1.keydown();");
  }, {once: true});
  input.addEventListener("keyup", function (aEvent) {
    events.push(aEvent);
    ok(!TIP2.beginInputTransactionForTests(window),
       description + "TIP2 shouldn't be able to begin input transaction for tests from keyup event handler during a call of TIP1.keyup();");
  }, {once: true});
  keyA = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A });
  TIP1.keydown(keyA);
  TIP1.keyup(keyA);
  is(events.length, 5,
     description + "keydown, keypress, beforeinput, input, keyup events should be fired by TIP1.keydown() and TIP1.keyup()");
  is(events[0].type, "keydown",
     description + "events[0] should be keydown");
  is(events[1].type, "keypress",
     description + "events[1] should be keypress");
  is(events[2].type, "beforeinput",
     description + "events[2] should be beforeinput");
  checkInputEvent(events[2], true, false, "insertText", "a", description);
  is(events[3].type, "input",
     description + "events[3] should be input");
  checkInputEvent(events[3], false, false, "insertText", "a", description);
  is(events[4].type, "keyup",
     description + "events[4] should be keyup");

  // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during startComposition().
  events = [];
  input.addEventListener("compositionstart", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should throw an exception during startComposition()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should cause NS_ERROR_ALREADY_INITIALIZED during startComposition()");
    }
  }, {once: true});
  TIP1.beginInputTransaction(window, simpleCallback);
  TIP1.startComposition();
  is(events.length, 1,
     description + "compositionstart event should be fired by TIP1.startComposition()");
  TIP1.cancelComposition();

  // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during flushPendingComposition().
  events = [];
  input.addEventListener("compositionstart", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should throw an exception during flushPendingComposition()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()");
    }
  }, {once: true});
  input.addEventListener("compositionupdate", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should throw an exception during flushPendingComposition()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()");
    }
  }, {once: true});
  input.addEventListener("text", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should throw an exception during flushPendingComposition()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()");
    }
  }, {once: true});
  input.addEventListener("beforeinput", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during flushPendingComposition()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()");
    }
  }, {once: true});
  input.addEventListener("input", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during flushPendingComposition()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during flushPendingComposition()");
    }
  }, {once: true});
  TIP1.beginInputTransaction(window, simpleCallback);
  TIP1.setPendingCompositionString(composingStr);
  TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
  TIP1.flushPendingComposition();
  is(events.length, 5,
     description + "compositionstart, compositionupdate, text, beforeinput and input events should be fired by TIP1.flushPendingComposition()");
  is(events[0].type, "compositionstart",
     description + "events[0] should be compositionstart");
  is(events[1].type, "compositionupdate",
     description + "events[1] should be compositionupdate");
  is(events[2].type, "text",
     description + "events[2] should be text");
  is(events[3].type, "beforeinput",
     description + "events[3] should be beforeinput");
  checkInputEvent(events[3], false, true, "insertCompositionText", composingStr, description);
  is(events[4].type, "input",
     description + "events[4] should be input");
  checkInputEvent(events[4], false, true, "insertCompositionText", composingStr, description);
  TIP1.cancelComposition();

  // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during commitComposition().
  (function () {
    events = [];
    TIP1.beginInputTransaction(window, simpleCallback);
    TIP1.setPendingCompositionString(composingStr);
    TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
    TIP1.flushPendingComposition();
    input.addEventListener("text", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should throw an exception during commitComposition()");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()");
      }
    }, {once: true});
    input.addEventListener("compositionend", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should throw an exception during commitComposition()");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()");
      }
    }, {once: true});
    input.addEventListener("beforeinput", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during commitComposition()");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()");
      }
    }, {once: true});
    function onInput(aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during commitComposition()");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during commitComposition()");
      }
    }
    input.addEventListener("input", onInput);
    TIP1.commitComposition();
    is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
      description + "text, beforeinput, compositionend and input events should be fired by TIP1.commitComposition()");
    let index = -1;
    is(events[++index].type, "text",
      `${description}events[${index}] should be text`);
    is(events[++index].type, "beforeinput",
      `${description}events[${index}] should be beforeinput`);
    checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description);
    if (kExpectInputBeforeCompositionEnd) {
      is(events[++index].type, "input",
        `${description}events[${index}] should be input`);
      checkInputEvent(events[index], false, true, "insertCompositionText", composingStr, description);
    }
    is(events[++index].type, "compositionend",
      `${description}events[${index}] should be compositionend`);
    is(events[++index].type, "input",
      `${description}events[${index}] should be input`);
    checkInputEvent(events[index], false, false, "insertCompositionText", composingStr, description);
    input.removeEventListener("input", onInput);
  })();

  // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during commitCompositionWith("bar");.
  (() => {
    events = [];
    input.addEventListener("compositionstart", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should throw an exception during commitCompositionWith(\"bar\")");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionstart\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")");
      }
    }, {once: true});
    input.addEventListener("compositionupdate", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should throw an exception during commitCompositionWith(\"bar\")");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")");
      }
    }, {once: true});
    input.addEventListener("text", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should throw an exception during commitCompositionWith(\"bar\")");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")");
      }
    }, {once: true});
    input.addEventListener("compositionend", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should throw an exception during commitCompositionWith(\"bar\")");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")");
      }
    }, {once: true});
    input.addEventListener("beforeinput", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during commitCompositionWith(\"bar\")");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")");
      }
    }, {once: true});
    function onInput(aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during commitCompositionWith(\"bar\")");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during commitCompositionWith(\"bar\")");
      }
    }
    input.addEventListener("input", onInput);
    TIP1.beginInputTransaction(window, simpleCallback);
    TIP1.commitCompositionWith("bar");
    is(events.length, kExpectInputBeforeCompositionEnd ? 7 : 6,
      description + "compositionstart, compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.commitCompositionWith(\"bar\")");
    let index = -1;
    is(events[++index].type, "compositionstart",
      `${description}events[${index}] should be compositionstart`);
    is(events[++index].type, "compositionupdate",
      `${description}events[${index}] should be compositionupdate`);
    is(events[++index].type, "text",
      `${description}events[${index}] should be text`);
    is(events[++index].type, "beforeinput",
      `${description}events[${index}] should be beforeinput`);
    checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description);
    if (kExpectInputBeforeCompositionEnd) {
      is(events[++index].type, "input",
        `${description}events[${index}] should be input`);
      checkInputEvent(events[index], false, true, "insertCompositionText", "bar", description);
    }
    is(events[++index].type, "compositionend",
      `${description}events[${index}] should be compositionend`);
    is(events[++index].type, "input",
      `${description}events[${index}] should be input`);
    checkInputEvent(events[index], false, false, "insertCompositionText", "bar", description);
    input.removeEventListener("input", onInput);
  })();

  // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during cancelComposition();.
  (() => {
    events = [];
    TIP1.beginInputTransaction(window, simpleCallback);
    TIP1.setPendingCompositionString(composingStr);
    TIP1.appendClauseToPendingComposition(composingStr.length, TIP1.ATTR_RAW_CLAUSE);
    TIP1.flushPendingComposition();
    input.addEventListener("compositionupdate", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should throw an exception during cancelComposition()");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionupdate\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()");
      }
    }, {once: true});
    input.addEventListener("text", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should throw an exception during cancelComposition()");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"text\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()");
      }
    }, {once: true});
    input.addEventListener("compositionend", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should throw an exception during cancelComposition()");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"compositionend\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()");
      }
    }, {once: true});
    input.addEventListener("beforeinput", function (aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during cancelComposition()");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()");
      }
    }, {once: true});
    function onInput(aEvent) {
      events.push(aEvent);
      try {
        TIP1.beginInputTransaction(otherWindow, simpleCallback);
        ok(false,
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during cancelComposition()");
      } catch (e) {
        ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
          description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during cancelComposition()");
      }
    }
    input.addEventListener("input", onInput);
    TIP1.cancelComposition();
    is(events.length, kExpectInputBeforeCompositionEnd ? 6 : 5,
      description + "compositionupdate, text, beforeinput, compositionend and input events should be fired by TIP1.cancelComposition()");
    let index = -1;
    is(events[++index].type, "compositionupdate",
      `${description}events[${index}] should be compositionupdate`);
    is(events[++index].type, "text",
      `${description}events[${index}] should be text`);
    is(events[++index].type, "beforeinput",
      `${description}events[${index}] should be beforeinput`);
    checkInputEvent(events[index], false, true, "insertCompositionText", "", description);
    if (kExpectInputBeforeCompositionEnd) {
      is(events[++index].type, "input",
        `${description}events[${index}] should be input`);
      checkInputEvent(events[index], false, true, "insertCompositionText", "", description);
    }
    is(events[++index].type, "compositionend",
      `${description}events[${index}] should be compositionend`);
    is(events[++index].type, "input",
      `${description}events[${index}] should be input`);
    checkInputEvent(events[index], false, false, "insertCompositionText", "", description);
    input.removeEventListener("input", onInput);
  })();

  // Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during keydown() and keyup();.
  events = [];
  TIP1.beginInputTransaction(window, simpleCallback);
  input.addEventListener("keydown", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keydown\" should throw an exception during keydown()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keydown\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()");
    }
  }, {once: true});
  input.addEventListener("keypress", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keypress\" should throw an exception during keydown()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keypress\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()");
    }
  }, {once: true});
  input.addEventListener("beforeinput", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should throw an exception during keydown()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"beforeinput\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()");
    }
  }, {once: true});
  input.addEventListener("input", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should throw an exception during keydown()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"input\" should cause NS_ERROR_ALREADY_INITIALIZED during keydown()");
    }
  }, {once: true});
  input.addEventListener("keyup", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransaction(otherWindow, simpleCallback);
      ok(false,
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keyup\" should throw an exception during keyup()");
    } catch (e) {
      ok(e.message.includes("NS_ERROR_ALREADY_INITIALIZED"),
         description + "TIP1.beginInputTransaction(otherWindow, simpleCallback) called from \"keyup\" should cause NS_ERROR_ALREADY_INITIALIZED during keyup()");
    }
  }, {once: true});
  keyA = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A });
  TIP1.keydown(keyA);
  TIP1.keyup(keyA);
  is(events.length, 5,
     description + "keydown, keypress, beforeinput, input, keyup events should be fired by TIP1.keydown() and TIP1.keyup()");
  is(events[0].type, "keydown",
     description + "events[0] should be keydown");
  is(events[1].type, "keypress",
     description + "events[1] should be keypress");
  is(events[2].type, "beforeinput",
     description + "events[2] should be beforeinput");
  checkInputEvent(events[2], true, false, "insertText", "a", description);
  is(events[3].type, "input",
     description + "events[3] should be input");
  checkInputEvent(events[3], false, false, "insertText", "a", description);
  is(events[4].type, "keyup",
     description + "events[4] should be keyup");

  // Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during startComposition().
  events = [];
  input.addEventListener("compositionstart", function (aEvent) {
    events.push(aEvent);
    try {
      TIP1.beginInputTransactionForTests(otherWindow, simpleCallback);
      ok(false,
--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.6 Sekunden  (vorverarbeitet)  ]