Bug 1077345 part.6 Add NS_COMPOSITION event which automatically commits composition with its mData r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 25 Nov 2014 14:02:32 +0900
changeset 217319 d571e279fac484ed3e8e4275ef4f32214d8221de
parent 217318 d2ca3f9615ac97aa74ebd3b6f7046b6fbb3300ec
child 217320 fad93bdcf42e9b85d4d1c13f69beddebf61768d8
push id52267
push usermasayuki@d-toybox.com
push dateTue, 25 Nov 2014 05:02:42 +0000
treeherdermozilla-inbound@4631a7474d8a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1077345
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1077345 part.6 Add NS_COMPOSITION event which automatically commits composition with its mData r=smaug
dom/base/nsDOMWindowUtils.cpp
dom/events/EventStateManager.cpp
dom/events/TextComposition.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
testing/mochitest/tests/SimpleTest/EventUtils.js
widget/BasicEvents.h
widget/TextEvents.h
widget/WidgetEventImpl.cpp
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2123,28 +2123,38 @@ nsDOMWindowUtils::SendCompositionEvent(c
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
   uint32_t msg;
   if (aType.EqualsLiteral("compositionstart")) {
     msg = NS_COMPOSITION_START;
   } else if (aType.EqualsLiteral("compositionend")) {
-    msg = NS_COMPOSITION_END;
+    // Now we don't support manually dispatching composition end with this
+    // API.  A compositionend is dispatched when this is called with
+    // compositioncommitasis or compositioncommit automatically.  For backward
+    // compatibility, this shouldn't return error in this case.
+    NS_WARNING("Don't call nsIDOMWindowUtils.sendCompositionEvent() for "
+               "compositionend.  Instead, use it with compositioncommitasis or "
+               "compositioncommit.  Then, compositionend will be automatically "
+               "dispatched.");
+    return NS_OK;
   } else if (aType.EqualsLiteral("compositionupdate")) {
     // Now we don't support manually dispatching composition update with this
     // API.  A compositionupdate is dispatched when a DOM text event modifies
     // composition string automatically.  For backward compatibility, this
     // shouldn't return error in this case.
     NS_WARNING("Don't call nsIDOMWindowUtils.sendCompositionEvent() for "
                "compositionupdate since it's ignored and the event is "
                "fired automatically when it's necessary");
     return NS_OK;
   } else if (aType.EqualsLiteral("compositioncommitasis")) {
     msg = NS_COMPOSITION_COMMIT_AS_IS;
+  } else if (aType.EqualsLiteral("compositioncommit")) {
+    msg = NS_COMPOSITION_COMMIT;
   } else {
     return NS_ERROR_FAILURE;
   }
 
   WidgetCompositionEvent compositionEvent(true, msg, widget);
   InitEvent(compositionEvent);
   if (msg != NS_COMPOSITION_START && msg != NS_COMPOSITION_COMMIT_AS_IS) {
     compositionEvent.mData = aData;
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -815,16 +815,17 @@ EventStateManager::PreHandleEvent(nsPres
       DoQuerySelectedText(&selectedText);
       NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
       compositionEvent->mData = selectedText.mReply.mString;
     }
     // through to compositionend handling
   case NS_COMPOSITION_END:
   case NS_COMPOSITION_CHANGE:
   case NS_COMPOSITION_COMMIT_AS_IS:
+  case NS_COMPOSITION_COMMIT:
     {
       WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
       if (IsTargetCrossProcess(compositionEvent)) {
         // Will not be handled locally, remote the event
         if (GetCrossProcessTarget()->SendCompositionEvent(*compositionEvent)) {
           // Cancel local dispatching
           aEvent->mFlags.mPropagationStopped = true;
         }
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -149,16 +149,20 @@ TextComposition::DispatchCompositionEven
     if (mLastData == IDEOGRAPHIC_SPACE) {
       // If the last data is an ideographic space (FullWidth space), it must be
       // a placeholder character of some Chinese IME.  So, committing with
       // this data must not be expected by users.  Let's use empty string.
       aCompositionEvent->mData.Truncate();
     } else {
       aCompositionEvent->mData = mLastData;
     }
+  } else if (aCompositionEvent->message == NS_COMPOSITION_COMMIT) {
+    NS_ASSERTION(!aCompositionEvent->mRanges,
+                 "mRanges of NS_COMPOSITION_COMMIT should be null");
+    aCompositionEvent->mRanges = nullptr;
   }
 
   if (!IsValidStateForComposition(aCompositionEvent->widget)) {
     *aStatus = nsEventStatus_eConsumeNoDefault;
     return;
   }
 
   // If this instance has requested to commit or cancel composition but
@@ -184,16 +188,17 @@ TextComposition::DispatchCompositionEven
   //    data isn't IDEOGRAPHIC SPACE.
   // 2. non-empty string is committed at requesting cancel.
   if (!aIsSynthesized && (mIsRequestingCommit || mIsRequestingCancel)) {
     nsString* committingData = nullptr;
     switch (aCompositionEvent->message) {
       case NS_COMPOSITION_END:
       case NS_COMPOSITION_CHANGE:
       case NS_COMPOSITION_COMMIT_AS_IS:
+      case NS_COMPOSITION_COMMIT:
         committingData = &aCompositionEvent->mData;
         break;
       default:
         NS_WARNING("Unexpected event comes during committing or "
                    "canceling composition");
         break;
     }
     if (committingData) {
@@ -349,71 +354,44 @@ TextComposition::RequestToCommit(nsIWidg
         return rv;
       }
     } else {
       // Emulates to commit or cancel the composition
       // FYI: These events may be discarded by PresShell if it's not safe to
       //      dispatch the event.
       nsCOMPtr<nsIWidget> widget(aWidget);
       nsAutoString commitData(aDiscard ? EmptyString() : lastData);
-      if (commitData == mLastData) {
-        WidgetCompositionEvent commitEvent(true, NS_COMPOSITION_COMMIT_AS_IS,
-                                           widget);
-        commitEvent.mFlags.mIsSynthesizedForTests = true;
-        nsEventStatus status = nsEventStatus_eIgnore;
-        widget->DispatchEvent(&commitEvent, status);
-      } else {
-        WidgetCompositionEvent changeEvent(true, NS_COMPOSITION_CHANGE, widget);
-        changeEvent.mData = commitData;
-        changeEvent.mFlags.mIsSynthesizedForTests = true;
-
-        MaybeDispatchCompositionUpdate(&changeEvent);
-
-        // If changing the data or committing string isn't empty, we need to
-        // dispatch compositionchange event for setting the composition string
-        // without IME selection.
-        if (IsValidStateForComposition(widget)) {
-          nsEventStatus status = nsEventStatus_eIgnore;
-          widget->DispatchEvent(&changeEvent, status);
-        }
-
-        if (IsValidStateForComposition(widget)) {
-          nsEventStatus status = nsEventStatus_eIgnore;
-          WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
-          endEvent.mData = commitData;
-          endEvent.mFlags.mIsSynthesizedForTests = true;
-          widget->DispatchEvent(&endEvent, status);
-        }
+      bool isChanging = commitData != mLastData;
+      uint32_t message =
+        isChanging ? NS_COMPOSITION_COMMIT : NS_COMPOSITION_COMMIT_AS_IS;
+      WidgetCompositionEvent commitEvent(true, message, widget);
+      if (commitEvent.message == NS_COMPOSITION_COMMIT) {
+        commitEvent.mData = commitData;
       }
+      commitEvent.mFlags.mIsSynthesizedForTests = true;
+      nsEventStatus status = nsEventStatus_eIgnore;
+      widget->DispatchEvent(&commitEvent, status);
     }
   }
 
   mRequestedToCommitOrCancel = true;
 
   // If the request is performed synchronously, this must be already destroyed.
   if (Destroyed()) {
     return NS_OK;
   }
 
   // Otherwise, synthesize the commit in content.
   nsAutoString data(aDiscard ? EmptyString() : lastData);
   if (data == mLastData) {
     DispatchCompositionEventRunnable(NS_COMPOSITION_COMMIT_AS_IS, EmptyString(),
                                      true);
-    return NS_OK;
+  } else {
+    DispatchCompositionEventRunnable(NS_COMPOSITION_COMMIT, data, true);
   }
-  // If the last composition string and new data are different, we need to
-  // dispatch compositionchange event for removing IME selection.  However, if
-  // the commit string is empty string and it's not changed from the last data,
-  // we don't need to dispatch compositionchange event.
-  if (lastData != data || !data.IsEmpty()) {
-    DispatchCompositionEventRunnable(NS_COMPOSITION_CHANGE, data, true);
-  }
-  DispatchCompositionEventRunnable(NS_COMPOSITION_END, data, true);
-
   return NS_OK;
 }
 
 nsresult
 TextComposition::NotifyIME(IMEMessage aMessage)
 {
   NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
   return IMEStateManager::NotifyIME(aMessage, mPresContext);
@@ -514,19 +492,19 @@ TextComposition::CompositionEventDispatc
       compStart.mData = selectedText.mReply.mString;
       compStart.mFlags.mIsSynthesizedForTests =
         mTextComposition->IsSynthesizedForTests();
       IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext,
                                                 &compStart, &status, nullptr,
                                                 mIsSynthesizedEvent);
       break;
     }
-    case NS_COMPOSITION_END:
     case NS_COMPOSITION_CHANGE:
-    case NS_COMPOSITION_COMMIT_AS_IS: {
+    case NS_COMPOSITION_COMMIT_AS_IS:
+    case NS_COMPOSITION_COMMIT: {
       WidgetCompositionEvent compEvent(true, mEventMessage, widget);
       if (mEventMessage != NS_COMPOSITION_COMMIT_AS_IS) {
         compEvent.mData = mData;
       }
       compEvent.mFlags.mIsSynthesizedForTests =
         mTextComposition->IsSynthesizedForTests();
       IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext,
                                                 &compEvent, &status, nullptr,
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1031,18 +1031,18 @@ interface nsIDOMWindowUtils : nsISupport
                                [optional] in nsITransferable aTransferable);
 
   /**
    * Synthesize a composition event to the window.
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without chrome privileges.
    *
-   * @param aType     The event type: "compositionstart", "compositionend" or
-   *                  "compositioncommitasis".
+   * @param aType     The event type: "compositionstart",
+   *                  "compositioncommitasis", or "compositioncommit".
    * @param aData     The data property value.  Note that this isn't applied
    *                  for compositionstart event because its value is the
    *                  selected text which is automatically computed. And also
    *                  this isn't applied for compositioncommitasis because
    *                  the last data will be set automatically.
    * @param aLocale   The locale property value.
    */
   void sendCompositionEvent(in AString aType,
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -866,18 +866,18 @@ const COMPOSITION_ATTR_SELECTEDRAWTEXT  
 const COMPOSITION_ATTR_CONVERTEDTEXT         = 0x04;
 const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
 
 /**
  * Synthesize a composition event.
  *
  * @param aEvent               The composition event information.  This must
  *                             have |type| member.  The value must be
- *                             "compositionstart", "compositionend" or
- *                             "compositioncommitasis".
+ *                             "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)
  */
 function synthesizeComposition(aEvent, aWindow)
--- a/widget/BasicEvents.h
+++ b/widget/BasicEvents.h
@@ -192,16 +192,22 @@
 #define NS_COMPOSITION_CHANGE         (NS_COMPOSITION_EVENT_START + 3)
 // NS_COMPOSITION_COMMIT_AS_IS is the message for representing a commit of
 // composition string.  TextComposition will commit composition with the
 // last data.  TextComposition will dispatch this event to the DOM tree as
 // NS_COMPOSITION_CHANGE without clause information.  After that,
 // NS_COMPOSITION_END will be dispatched automatically.
 // Its mData and mRanges should be empty and nullptr.
 #define NS_COMPOSITION_COMMIT_AS_IS   (NS_COMPOSITION_EVENT_START + 4)
+// NS_COMPOSITION_COMMIT is the message for representing a commit of
+// composition string with its mData value.  TextComposition will dispatch this
+// event to the DOM tree as NS_COMPOSITION_CHANGE without clause information.
+// After that, NS_COMPOSITION_END will be dispatched automatically.
+// Its mRanges should be nullptr.
+#define NS_COMPOSITION_COMMIT         (NS_COMPOSITION_EVENT_START + 5)
 
 // UI events
 #define NS_UI_EVENT_START          2500
 // this is not to be confused with NS_ACTIVATE!
 #define NS_UI_ACTIVATE             (NS_UI_EVENT_START)
 #define NS_UI_FOCUSIN              (NS_UI_EVENT_START + 1)
 #define NS_UI_FOCUSOUT             (NS_UI_EVENT_START + 2)
 
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -341,22 +341,24 @@ public:
   uint32_t RangeCount() const
   {
     return mRanges ? mRanges->Length() : 0;
   }
 
   bool CausesDOMTextEvent() const
   {
     return message == NS_COMPOSITION_CHANGE ||
+           message == NS_COMPOSITION_COMMIT ||
            message == NS_COMPOSITION_COMMIT_AS_IS;
   }
 
   bool CausesDOMCompositionEndEvent() const
   {
     return message == NS_COMPOSITION_END ||
+           message == NS_COMPOSITION_COMMIT ||
            message == NS_COMPOSITION_COMMIT_AS_IS;
   }
 };
 
 /******************************************************************************
  * mozilla::WidgetQueryContentEvent
  ******************************************************************************/
 
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -136,16 +136,17 @@ bool
 WidgetEvent::HasIMEEventMessage() const
 {
   switch (message) {
     case NS_COMPOSITION_START:
     case NS_COMPOSITION_END:
     case NS_COMPOSITION_UPDATE:
     case NS_COMPOSITION_CHANGE:
     case NS_COMPOSITION_COMMIT_AS_IS:
+    case NS_COMPOSITION_COMMIT:
       return true;
     default:
       return false;
   }
 }
 
 bool
 WidgetEvent::HasPluginActivationEventMessage() const