Bug 1065835 part.4 Destroy TextComposition instance after handling synthesized compositionend event when synthesized events for a request to commit or cancel is caused by PresShell discarding native compositionend event r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 26 Sep 2014 09:05:12 +0900
changeset 207378 e7875414ae58ede4cf57b453ca6cb089fa41413d
parent 207377 cf9be916ff23cc1b172100ba35dddabcb07416c2
child 207379 22936d79a04443d4515255a0178887d691675730
push id27552
push usercbook@mozilla.com
push dateFri, 26 Sep 2014 11:21:05 +0000
treeherdermozilla-central@31db6c04c40e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1065835
milestone35.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 1065835 part.4 Destroy TextComposition instance after handling synthesized compositionend event when synthesized events for a request to commit or cancel is caused by PresShell discarding native compositionend event r=smaug
dom/events/IMEStateManager.cpp
dom/events/IMEStateManager.h
dom/events/TextComposition.cpp
dom/events/TextComposition.h
layout/base/nsPresShell.cpp
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -940,31 +940,70 @@ IMEStateManager::DispatchCompositionEven
 
   // WARNING: the |composition| might have been destroyed already.
 
   // Remove the ended composition from the array.
   // NOTE: When TextComposition is synthesizing compositionend event for
   //       emulating a commit, the instance shouldn't be removed from the array
   //       because IME may perform it later.  Then, we need to ignore the
   //       following commit events in TextComposition::DispatchEvent().
-  if (!aIsSynthesized && aEvent->message == NS_COMPOSITION_END) {
+  //       However, if commit or cancel for a request is performed synchronously
+  //       during not safe to dispatch events, PresShell must have discarded
+  //       compositionend event.  Then, the synthesized compositionend event is
+  //       the last event for the composition.  In this case, we need to
+  //       destroy the TextComposition with synthesized compositionend event.
+  if ((!aIsSynthesized ||
+       composition->WasNativeCompositionEndEventDiscarded()) &&
+      aEvent->message == NS_COMPOSITION_END) {
     TextCompositionArray::index_type i =
       sTextCompositions->IndexOf(GUIEvent->widget);
     if (i != TextCompositionArray::NoIndex) {
       PR_LOG(sISMLog, PR_LOG_DEBUG,
         ("ISM:   IMEStateManager::DispatchCompositionEvent(), "
          "removing TextComposition from the array since NS_COMPOSTION_END "
          "was dispatched"));
       sTextCompositions->ElementAt(i)->Destroy();
       sTextCompositions->RemoveElementAt(i);
     }
   }
 }
 
 // static
+void
+IMEStateManager::OnCompositionEventDiscarded(WidgetEvent* aEvent)
+{
+  // Note that this method is never called for synthesized events for emulating
+  // commit or cancel composition.
+
+  PR_LOG(sISMLog, PR_LOG_ALWAYS,
+    ("ISM: IMEStateManager::OnCompositionEventDiscarded(aEvent={ mClass=%s, "
+     "message=%s, mFlags={ mIsTrusted=%s } })",
+     GetEventClassIDName(aEvent->mClass),
+     GetEventMessageName(aEvent->message),
+     GetBoolName(aEvent->mFlags.mIsTrusted)));
+
+  MOZ_ASSERT(aEvent->mClass == eCompositionEventClass ||
+             aEvent->mClass == eTextEventClass);
+  if (!aEvent->mFlags.mIsTrusted) {
+    return;
+  }
+
+  // Ignore compositionstart for now because sTextCompositions may not have
+  // been created yet.
+  if (aEvent->message == NS_COMPOSITION_START) {
+    return;
+  }
+
+  WidgetGUIEvent* GUIEvent = aEvent->AsGUIEvent();
+  nsRefPtr<TextComposition> composition =
+    sTextCompositions->GetCompositionFor(GUIEvent->widget);
+  composition->OnCompositionEventDiscarded(GUIEvent);
+}
+
+// static
 nsresult
 IMEStateManager::NotifyIME(IMEMessage aMessage,
                            nsIWidget* aWidget)
 {
   nsRefPtr<TextComposition> composition;
   if (aWidget && sTextCompositions) {
     composition = sTextCompositions->GetCompositionFor(aWidget);
   }
--- a/dom/events/IMEStateManager.h
+++ b/dom/events/IMEStateManager.h
@@ -101,16 +101,22 @@ public:
   static void DispatchCompositionEvent(nsINode* aEventTargetNode,
                                        nsPresContext* aPresContext,
                                        WidgetEvent* aEvent,
                                        nsEventStatus* aStatus,
                                        EventDispatchingCallback* aCallBack,
                                        bool aIsSynthesized = false);
 
   /**
+   * This is called when PresShell ignores composition event or text event due
+   * to not safe to dispatch events.
+   */
+  static void OnCompositionEventDiscarded(WidgetEvent* aEvent);
+
+  /**
    * Get TextComposition from widget.
    */
   static already_AddRefed<TextComposition>
     GetTextCompositionFor(nsIWidget* aWidget);
 
   /**
    * Returns TextComposition instance for the event.
    *
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -36,16 +36,17 @@ TextComposition::TextComposition(nsPresC
   , mCompositionStartOffset(0)
   , mCompositionTargetOffset(0)
   , mIsSynthesizedForTests(aEvent->mFlags.mIsSynthesizedForTests)
   , mIsComposing(false)
   , mIsEditorHandlingEvent(false)
   , mIsRequestingCommit(false)
   , mIsRequestingCancel(false)
   , mRequestedToCommitOrCancel(false)
+  , mWasNativeCompositionEndEventDiscarded(false)
 {
 }
 
 void
 TextComposition::Destroy()
 {
   mPresContext = nullptr;
   mNode = nullptr;
@@ -89,16 +90,38 @@ TextComposition::MaybeDispatchCompositio
     mLastData = compositionUpdate.data;
     EventDispatcher::Dispatch(mNode, mPresContext,
                               &compositionUpdate, nullptr, &status, nullptr);
   }
   return !Destroyed();
 }
 
 void
+TextComposition::OnCompositionEventDiscarded(const WidgetGUIEvent* aEvent)
+{
+  // Note that this method is never called for synthesized events for emulating
+  // commit or cancel composition.
+
+  MOZ_ASSERT(aEvent->mFlags.mIsTrusted,
+             "Shouldn't be called with untrusted event");
+  MOZ_ASSERT(aEvent->mClass == eCompositionEventClass ||
+             aEvent->mClass == eTextEventClass);
+
+  // XXX If composition events are discarded, should we dispatch them with
+  //     runnable event?  However, even if we do so, it might make native IME
+  //     confused due to async modification.  Especially when native IME is
+  //     TSF.
+  if (aEvent->message != NS_COMPOSITION_END) {
+    return;
+  }
+
+  mWasNativeCompositionEndEventDiscarded = true;
+}
+
+void
 TextComposition::DispatchEvent(WidgetGUIEvent* aEvent,
                                nsEventStatus* aStatus,
                                EventDispatchingCallback* aCallBack,
                                bool aIsSynthesized)
 {
   if (Destroyed()) {
     *aStatus = nsEventStatus_eConsumeNoDefault;
     return;
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -206,16 +206,21 @@ private:
 
   // mRequestedToCommitOrCancel is true *after* we requested IME to commit or
   // cancel the composition.  In other words, we already requested of IME that
   // it commits or cancels current composition.
   // NOTE: Before this is set true, both mIsRequestingCommit and
   //       mIsRequestingCancel are set false.
   bool mRequestedToCommitOrCancel;
 
+  // mWasNativeCompositionEndEventDiscarded is true if this composition was
+  // requested commit or cancel itself but native compositionend event is
+  // discarded by PresShell due to not safe to dispatch events.
+  bool mWasNativeCompositionEndEventDiscarded;
+
   // Hide the default constructor and copy constructor.
   TextComposition() {}
   TextComposition(const TextComposition& aOther);
 
   /**
    * GetEditor() returns nsIEditor pointer of mEditorWeak.
    */
   already_AddRefed<nsIEditor> GetEditor() const;
@@ -251,16 +256,32 @@ private:
    * MaybeDispatchCompositionUpdate() may dispatch a compositionupdate event
    * if aEvent changes composition string.
    * @return Returns false if dispatching the compositionupdate event caused
    *         destroying this composition.
    */
   bool MaybeDispatchCompositionUpdate(const WidgetTextEvent* aEvent);
 
   /**
+   * If IME has already dispatched compositionend event but it was discarded
+   * by PresShell due to not safe to dispatch, this returns true.
+   */
+  bool WasNativeCompositionEndEventDiscarded() const
+  {
+    return mWasNativeCompositionEndEventDiscarded;
+  }
+
+  /**
+   * OnCompositionEventDiscarded() is called when PresShell discards
+   * compositionupdate, compositionend or text event due to not safe to
+   * dispatch event.
+   */
+  void OnCompositionEventDiscarded(const WidgetGUIEvent* aEvent);
+
+  /**
    * Calculate composition offset then notify composition update to widget
    */
   void NotityUpdateComposition(WidgetGUIEvent* aEvent);
 
   /**
    * CompositionEventDispatcher dispatches the specified composition (or text)
    * event.
    */
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6937,16 +6937,20 @@ PresShell::HandleEvent(nsIFrame* aFrame,
   }
 
   if (sPointerEventEnabled) {
     UpdateActivePointerState(aEvent);
   }
 
   if (!nsContentUtils::IsSafeToRunScript() &&
       aEvent->IsAllowedToDispatchDOMEvent()) {
+    if (aEvent->mClass == eCompositionEventClass ||
+        aEvent->mClass == eTextEventClass) {
+      IMEStateManager::OnCompositionEventDiscarded(aEvent);
+    }
 #ifdef DEBUG
     if (aEvent->IsIMERelatedEvent()) {
       nsPrintfCString warning("%d event is discarded", aEvent->message);
       NS_WARNING(warning.get());
     }
 #endif
     nsContentUtils::WarnScriptWasIgnored(GetDocument());
     return NS_OK;