Bug 917322 part.20 Add tests of async commit at requesting to commit a composition r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 28 Jan 2015 15:27:33 +0900
changeset 239591 6418c75f250bb251daab83e8d2af447abbe4e89d
parent 239590 62716b1991452c571272936eab7dde3f7042408a
child 239592 9b6ed23707175cef3dec02139ace88eb264072c0
push id500
push userjoshua.m.grant@gmail.com
push dateThu, 29 Jan 2015 01:48:36 +0000
reviewerssmaug
bugs917322
milestone38.0a1
Bug 917322 part.20 Add tests of async commit at requesting to commit a composition r=smaug
testing/mochitest/tests/SimpleTest/EventUtils.js
widget/tests/window_composition_text_querycontent.xul
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -869,27 +869,27 @@ const COMPOSITION_ATTR_RAW_CLAUSE =
   _EU_Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE;
 const COMPOSITION_ATTR_SELECTED_RAW_CLAUSE =
   _EU_Ci.nsITextInputProcessor.ATTR_SELECTED_RAW_CLAUSE;
 const COMPOSITION_ATTR_CONVERTED_CLAUSE =
   _EU_Ci.nsITextInputProcessor.ATTR_CONVERTED_CLAUSE;
 const COMPOSITION_ATTR_SELECTED_CLAUSE =
   _EU_Ci.nsITextInputProcessor.ATTR_SELECTED_CLAUSE;
 
-function _getTIP(aWindow)
+function _getTIP(aWindow, aCallback)
 {
   if (!aWindow) {
     aWindow = window;
   }
   if (!aWindow._EU_TIP) {
     aWindow._EU_TIP =
       _EU_Cc["@mozilla.org/text-input-processor;1"].
         createInstance(_EU_Ci.nsITextInputProcessor);
   }
-  if (!aWindow._EU_TIP.initForTests(aWindow)) {
+  if (!aWindow._EU_TIP.initForTests(aWindow, aCallback)) {
     aWindow._EU_TIP = null;
   }
   return aWindow._EU_TIP;
 }
 
 /**
  * Synthesize a composition event.
  *
@@ -898,20 +898,22 @@ function _getTIP(aWindow)
  *                             "compositionstart", "compositionend",
  *                             "compositioncommitasis" or "compositioncommit".
  *                             And also this may have |data| and |locale| which
  *                             would be used for the value of each property of
  *                             the composition event.  Note that the |data| is
  *                             ignored if the event type is "compositionstart"
  *                             or "compositioncommitasis".
  * @param aWindow              Optional (If null, current |window| will be used)
+ * @param aCallback            Optional (If non-null, use the callback for
+ *                             receiving notifications to IME)
  */
-function synthesizeComposition(aEvent, aWindow)
+function synthesizeComposition(aEvent, aWindow, aCallback)
 {
-  var TIP = _getTIP(aWindow);
+  var TIP = _getTIP(aWindow, aCallback);
   if (!TIP) {
     return false;
   }
   switch (aEvent.type) {
     case "compositionstart":
       return TIP.startComposition();
     case "compositioncommitasis":
       return TIP.commitComposition();
@@ -956,20 +958,22 @@ function synthesizeComposition(aEvent, a
  *
  *                 Set caret position to the |caret.start|. It's offset from
  *                 the start of the composition string.  Set caret length to
  *                 |caret.length|.  If it's larger than 0, it should be wide
  *                 caret.  However, current nsEditor doesn't support wide
  *                 caret, therefore, you should always set 0 now.
  *
  * @param aWindow  Optional (If null, current |window| will be used)
+ * @param aCallback     Optional (If non-null, use the callback for receiving
+ *                      notifications to IME)
  */
-function synthesizeCompositionChange(aEvent, aWindow)
+function synthesizeCompositionChange(aEvent, aWindow, aCallback)
 {
-  var TIP = _getTIP(aWindow);
+  var TIP = _getTIP(aWindow, aCallback);
   if (!TIP) {
     return;
   }
 
   if (!aEvent.composition || !aEvent.composition.clauses ||
       !aEvent.composition.clauses[0]) {
     return;
   }
--- a/widget/tests/window_composition_text_querycontent.xul
+++ b/widget/tests/window_composition_text_querycontent.xul
@@ -77,16 +77,17 @@ var textarea = document.getElementById("
 var panel = document.getElementById("panel");
 var textbox = document.getElementById("textbox");
 var iframe = document.getElementById("iframe");
 var iframe2 = document.getElementById("iframe2");
 var iframe3 = document.getElementById("iframe3");
 var input = document.getElementById("input");
 var textareaInFrame;
 
+const nsITextInputProcessorCallback = Components.interfaces.nsITextInputProcessorCallback;
 const nsIDOMNSEditableElement = Components.interfaces.nsIDOMNSEditableElement;
 const nsIEditorIMESupport = Components.interfaces.nsIEditorIMESupport;
 const nsIInterfaceRequestor = Components.interfaces.nsIInterfaceRequestor;
 const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
 const nsIDocShell = Components.interfaces.nsIDocShell;
 
 function hitEventLoop(aFunc, aTimes)
 {
@@ -2773,16 +2774,116 @@ function runForceCommitTest()
 
   window.removeEventListener("compositionstart", eventHandler, true);
   window.removeEventListener("compositionupdate", eventHandler, true);
   window.removeEventListener("compositionend", eventHandler, true);
   window.removeEventListener("input", eventHandler, true);
   window.removeEventListener("text", eventHandler, true);
 }
 
+function runAsyncForceCommitTest(aNextTest)
+{
+  var events;
+  function eventHandler(aEvent)
+  {
+    events.push(aEvent);
+  };
+
+  // If IME commits composition for a request, TextComposition commits
+  // composition automatically because most web apps must expect that active
+  // composition should be committed synchronously.  Therefore, in this case,
+  // a click during composition should cause committing composition
+  // synchronously and delayed commit shouldn't cause composition events.
+  var commitRequested = false;
+  function callback(aTIP, aNotification)
+  {
+    ok(true, aNotification.type);
+    if (aNotification.type != "request-to-commit") {
+      return true;
+    }
+    commitRequested = true;
+    setTimeout(function () {
+      events = [];
+      aTIP.commitComposition();
+
+      is(events.length, 0,
+         "runAsyncForceCommitTest: composition events shouldn't been fired by asynchronous call of nsITextInputProcessor.commitComposition()");
+
+      window.removeEventListener("compositionstart", eventHandler, true);
+      window.removeEventListener("compositionupdate", eventHandler, true);
+      window.removeEventListener("compositionend", eventHandler, true);
+      window.removeEventListener("input", eventHandler, true);
+      window.removeEventListener("text", eventHandler, true);
+
+      SimpleTest.executeSoon(aNextTest);
+    }, 1);
+    return true;
+  };
+
+  window.addEventListener("compositionstart", eventHandler, true);
+  window.addEventListener("compositionupdate", eventHandler, true);
+  window.addEventListener("compositionend", eventHandler, true);
+  window.addEventListener("input", eventHandler, true);
+  window.addEventListener("text", eventHandler, true);
+
+  // Make the composition in textarea commit by click in the textarea
+  textarea.focus();
+  textarea.value = "";
+
+  events = [];
+  synthesizeCompositionChange(
+    { "composition":
+      { "string": "\u306E",
+        "clauses":
+        [
+          { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+        ]
+      },
+      "caret": { "start": 1, "length": 0 }
+    }, window, callback);
+
+  is(events.length, 4,
+     "runAsyncForceCommitTest: wrong event count #1");
+  is(events[0].type, "compositionstart",
+     "runAsyncForceCommitTest: the 1st event must be compositionstart #1");
+  is(events[1].type, "compositionupdate",
+     "runAsyncForceCommitTest: the 2nd event must be compositionupdate #1");
+  is(events[2].type, "text",
+     "runAsyncForceCommitTest: the 3rd event must be text #1");
+  is(events[3].type, "input",
+     "runAsyncForceCommitTest: the 4th event must be input #1");
+
+  events = [];
+  commitRequested = false;
+  synthesizeMouseAtCenter(textarea, {});
+
+  ok(commitRequested,
+     "runAsyncForceCommitTest: \"request-to-commit\" should've been notified");
+  is(events.length, 3,
+     "runAsyncForceCommitTest: wrong event count #2");
+  is(events[0].type, "text",
+     "runAsyncForceCommitTest: the 1st event must be text #2");
+  is(events[1].type, "compositionend",
+     "runAsyncForceCommitTest: the 2nd event must be compositionend #2");
+  is(events[2].type, "input",
+     "runAsyncForceCommitTest: the 3rd event must be input #2");
+  is(events[1].data, "\u306E",
+     "runAsyncForceCommitTest: compositionend has wrong data #2");
+  is(events[0].target, textarea,
+     "runAsyncForceCommitTest: The 1st event was fired on wrong event target #2");
+  is(events[1].target, textarea,
+     "runAsyncForceCommitTest: The 2nd event was fired on wrong event target #2");
+  is(events[2].target, textarea,
+     "runAsyncForceCommitTest: The 3rd event was fired on wrong event target #2");
+  ok(!getEditorIMESupport(textarea).isComposing,
+     "runAsyncForceCommitTest: the textarea still has composition #2");
+  is(textarea.value, "\u306E",
+     "runAsyncForceCommitTest: the textarea doesn't have the committed text #2");
+}
+
 function runBug811755Test()
 {
   iframe2.contentDocument.body.innerHTML = "<div>content<br/></div>";
   iframe2.contentWindow.focus();
   // Query everything
   var textContent = synthesizeQueryTextContent(0, 10);
   if (!checkQueryContentResult(textContent, "runBug811755Test: synthesizeQueryTextContent #1")) {
     return false;
@@ -3444,19 +3545,21 @@ function runTest()
   runCompositionTest();
   runCompositionEventTest();
   runCharAtPointTest(textarea, "textarea in the document");
   runCharAtPointAtOutsideTest();
   runBug722639Test();
   runForceCommitTest();
   runBug811755Test();
   runIsComposingTest();
-  runRemoveContentTest(function () {
-    runFrameTest();
-    runPanelTest();
-    runMaxLengthTest();
+  runAsyncForceCommitTest(function () {
+    runRemoveContentTest(function () {
+      runFrameTest();
+      runPanelTest();
+      runMaxLengthTest();
+    });
   });
 }
 
 ]]>
 </script>
 
 </window>