Bug 962005 Make mozilla::TextComposition ref-countable class r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 28 Jan 2014 17:19:29 +0900
changeset 181596 d56032aebbedf953757e0f2b1e67ffe71df60a66
parent 181466 ff2a3e1b5faff2bd134edbde01e558cefc53059e
child 181597 0c3b24383c8c02cac818649a02522ee4e339ed68
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs962005
milestone29.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 962005 Make mozilla::TextComposition ref-countable class r=smaug
dom/events/TextComposition.cpp
dom/events/TextComposition.h
dom/events/nsIMEStateManager.cpp
dom/events/nsIMEStateManager.h
widget/xpwidgets/PuppetWidget.cpp
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -26,27 +26,16 @@ TextComposition::TextComposition(nsPresC
                                  WidgetGUIEvent* aEvent) :
   mPresContext(aPresContext), mNode(aNode),
   mNativeContext(aEvent->widget->GetInputContext().mNativeIMEContext),
   mCompositionStartOffset(0), mCompositionTargetOffset(0),
   mIsSynthesizedForTests(aEvent->mFlags.mIsSynthesizedForTests)
 {
 }
 
-TextComposition::TextComposition(const TextComposition& aOther)
-{
-  mNativeContext = aOther.mNativeContext;
-  mPresContext = aOther.mPresContext;
-  mNode = aOther.mNode;
-  mLastData = aOther.mLastData;
-  mCompositionStartOffset = aOther.mCompositionStartOffset;
-  mCompositionTargetOffset = aOther.mCompositionTargetOffset;
-  mIsSynthesizedForTests = aOther.mIsSynthesizedForTests;
-}
-
 bool
 TextComposition::MatchesNativeContext(nsIWidget* aWidget) const
 {
   return mNativeContext == aWidget->GetInputContext().mNativeIMEContext;
 }
 
 void
 TextComposition::DispatchEvent(WidgetGUIEvent* aEvent,
@@ -113,25 +102,23 @@ TextComposition::DispatchCompsotionEvent
   nsContentUtils::AddScriptRunner(
     new CompositionEventDispatcher(mPresContext, mNode,
                                    aEventMessage, aData));
 }
 
 void
 TextComposition::SynthesizeCommit(bool aDiscard)
 {
-  // backup this instance and use it since this instance might be destroyed
-  // by nsIMEStateManager if this is managed by it.
-  TextComposition composition = *this;
-  nsAutoString data(aDiscard ? EmptyString() : composition.mLastData);
-  if (composition.mLastData != data) {
-    composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_UPDATE, data);
-    composition.DispatchCompsotionEventRunnable(NS_TEXT_TEXT, data);
+  nsRefPtr<TextComposition> kungFuDeathGrip(this);
+  nsAutoString data(aDiscard ? EmptyString() : mLastData);
+  if (mLastData != data) {
+    DispatchCompsotionEventRunnable(NS_COMPOSITION_UPDATE, data);
+    DispatchCompsotionEventRunnable(NS_TEXT_TEXT, data);
   }
-  composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_END, data);
+  DispatchCompsotionEventRunnable(NS_COMPOSITION_END, data);
 }
 
 nsresult
 TextComposition::NotifyIME(widget::NotificationToIME aNotification)
 {
   NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
   return nsIMEStateManager::NotifyIME(aNotification, mPresContext);
 }
@@ -197,68 +184,68 @@ TextComposition::CompositionEventDispatc
 /******************************************************************************
  * TextCompositionArray
  ******************************************************************************/
 
 TextCompositionArray::index_type
 TextCompositionArray::IndexOf(nsIWidget* aWidget)
 {
   for (index_type i = Length(); i > 0; --i) {
-    if (ElementAt(i - 1).MatchesNativeContext(aWidget)) {
+    if (ElementAt(i - 1)->MatchesNativeContext(aWidget)) {
       return i - 1;
     }
   }
   return NoIndex;
 }
 
 TextCompositionArray::index_type
 TextCompositionArray::IndexOf(nsPresContext* aPresContext)
 {
   for (index_type i = Length(); i > 0; --i) {
-    if (ElementAt(i - 1).GetPresContext() == aPresContext) {
+    if (ElementAt(i - 1)->GetPresContext() == aPresContext) {
       return i - 1;
     }
   }
   return NoIndex;
 }
 
 TextCompositionArray::index_type
 TextCompositionArray::IndexOf(nsPresContext* aPresContext,
                               nsINode* aNode)
 {
   index_type index = IndexOf(aPresContext);
   if (index == NoIndex) {
     return NoIndex;
   }
-  nsINode* node = ElementAt(index).GetEventTargetNode();
+  nsINode* node = ElementAt(index)->GetEventTargetNode();
   return node == aNode ? index : NoIndex;
 }
 
 TextComposition*
 TextCompositionArray::GetCompositionFor(nsIWidget* aWidget)
 {
   index_type i = IndexOf(aWidget);
-  return i != NoIndex ? &ElementAt(i) : nullptr;
+  return i != NoIndex ? ElementAt(i) : nullptr;
 }
 
 TextComposition*
 TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext,
                                            nsINode* aNode)
 {
   index_type i = IndexOf(aPresContext, aNode);
-  return i != NoIndex ? &ElementAt(i) : nullptr;
+  return i != NoIndex ? ElementAt(i) : nullptr;
 }
 
 TextComposition*
 TextCompositionArray::GetCompositionInContent(nsPresContext* aPresContext,
                                               nsIContent* aContent)
 {
   // There should be only one composition per content object.
   for (index_type i = Length(); i > 0; --i) {
-    nsINode* node = ElementAt(i - 1).GetEventTargetNode();
+    nsINode* node = ElementAt(i - 1)->GetEventTargetNode();
     if (node && nsContentUtils::ContentIsDescendantOf(node, aContent)) {
-      return &ElementAt(i - 1);
+      return ElementAt(i - 1);
     }
   }
   return nullptr;
 }
 
 } // namespace mozilla
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -26,23 +26,24 @@ namespace mozilla {
  * TextComposition represents a text composition.  This class stores the
  * composition event target and its presContext.  At dispatching the event via
  * this class, the instances use the stored event target.
  */
 
 class TextComposition MOZ_FINAL
 {
   friend class ::nsIMEStateManager;
+
+  NS_INLINE_DECL_REFCOUNTING(TextComposition)
+
 public:
   TextComposition(nsPresContext* aPresContext,
                   nsINode* aNode,
                   WidgetGUIEvent* aEvent);
 
-  TextComposition(const TextComposition& aOther);
-
   ~TextComposition()
   {
     // WARNING: mPresContext may be destroying, so, be careful if you touch it.
   }
 
   nsPresContext* GetPresContext() const { return mPresContext; }
   nsINode* GetEventTargetNode() const { return mNode; }
   // The latest CompositionEvent.data value except compositionstart event.
@@ -92,18 +93,20 @@ private:
   uint32_t mCompositionStartOffset;
   // Offset of the selected clause of the composition string from start of the
   // editor
   uint32_t mCompositionTargetOffset;
 
   // See the comment for IsSynthesizedForTests().
   bool mIsSynthesizedForTests;
 
-  // Hide the default constructor
+  // Hide the default constructor and copy constructor.
   TextComposition() {}
+  TextComposition(const TextComposition& aOther);
+
 
   /**
    * DispatchEvent() dispatches the aEvent to the mContent synchronously.
    * The caller must ensure that it's safe to dispatch the event.
    */
   void DispatchEvent(WidgetGUIEvent* aEvent,
                      nsEventStatus* aStatus,
                      nsDispatchingCallback* aCallBack);
@@ -158,17 +161,18 @@ private:
  * Managing with array is enough because only one composition is typically
  * there.  Even if user switches native IME context, it's very rare that
  * second or more composition is started.
  * It's assumed that this is used by nsIMEStateManager for storing all active
  * compositions in the process.  If the instance is it, each TextComposition
  * in the array can be destroyed by calling some methods of itself.
  */
 
-class TextCompositionArray MOZ_FINAL : public nsAutoTArray<TextComposition, 2>
+class TextCompositionArray MOZ_FINAL :
+  public nsAutoTArray<nsRefPtr<TextComposition>, 2>
 {
 public:
   index_type IndexOf(nsIWidget* aWidget);
   index_type IndexOf(nsPresContext* aPresContext);
   index_type IndexOf(nsPresContext* aPresContext, nsINode* aNode);
 
   TextComposition* GetCompositionFor(nsIWidget* aWidget);
   TextComposition* GetCompositionFor(nsPresContext* aPresContext,
--- a/dom/events/nsIMEStateManager.cpp
+++ b/dom/events/nsIMEStateManager.cpp
@@ -143,38 +143,36 @@ nsIMEStateManager::OnDestroyPresContext(
 nsresult
 nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
                                    nsIContent* aContent)
 {
   NS_ENSURE_ARG_POINTER(aPresContext);
 
   // First, if there is a composition in the aContent, clean up it.
   if (sTextCompositions) {
-    TextComposition* compositionInContent =
+    nsRefPtr<TextComposition> compositionInContent =
       sTextCompositions->GetCompositionInContent(aPresContext, aContent);
 
     if (compositionInContent) {
-      // Store the composition before accessing the native IME.
-      TextComposition storedComposition = *compositionInContent;
       // Try resetting the native IME state.  Be aware, typically, this method
       // is called during the content being removed.  Then, the native
       // composition events which are caused by following APIs are ignored due
       // to unsafe to run script (in PresShell::HandleEvent()).
       nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
       if (widget) {
         nsresult rv =
-          storedComposition.NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
+          compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
         if (NS_FAILED(rv)) {
-          storedComposition.NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
+          compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
         }
         // By calling the APIs, the composition may have been finished normally.
         compositionInContent =
           sTextCompositions->GetCompositionFor(
-                               storedComposition.GetPresContext(),
-                               storedComposition.GetEventTargetNode());
+                               compositionInContent->GetPresContext(),
+                               compositionInContent->GetEventTargetNode());
       }
     }
 
     // If the compositionInContent is still available, we should finish the
     // composition just on the content forcibly.
     if (compositionInContent) {
       compositionInContent->SynthesizeCommit(true);
     }
@@ -553,22 +551,22 @@ nsIMEStateManager::DispatchCompositionEv
   if (!aEvent->mFlags.mIsTrusted || aEvent->mFlags.mPropagationStopped) {
     return;
   }
 
   EnsureTextCompositionArray();
 
   WidgetGUIEvent* GUIEvent = aEvent->AsGUIEvent();
 
-  TextComposition* composition =
+  nsRefPtr<TextComposition> composition =
     sTextCompositions->GetCompositionFor(GUIEvent->widget);
   if (!composition) {
     MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START);
-    TextComposition newComposition(aPresContext, aEventTargetNode, GUIEvent);
-    composition = sTextCompositions->AppendElement(newComposition);
+    composition = new TextComposition(aPresContext, aEventTargetNode, GUIEvent);
+    sTextCompositions->AppendElement(composition);
   }
 #ifdef DEBUG
   else {
     MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START);
   }
 #endif // #ifdef DEBUG
 
   // Dispatch the event on composing target.
@@ -588,17 +586,17 @@ nsIMEStateManager::DispatchCompositionEv
 
 // static
 nsresult
 nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
                              nsIWidget* aWidget)
 {
   NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);
 
-  TextComposition* composition = nullptr;
+  nsRefPtr<TextComposition> composition;
   if (sTextCompositions) {
     composition = sTextCompositions->GetCompositionFor(aWidget);
   }
   if (!composition || !composition->IsSynthesizedForTests()) {
     switch (aNotification) {
       case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
         return aWidget->NotifyIME(aNotification);
       case REQUEST_TO_COMMIT_COMPOSITION:
@@ -613,64 +611,60 @@ nsIMEStateManager::NotifyIME(Notificatio
   }
 
   // If the composition is synthesized events for automated tests, we should
   // dispatch composition events for emulating the native composition behavior.
   // NOTE: The dispatched events are discarded if it's not safe to run script.
   switch (aNotification) {
     case REQUEST_TO_COMMIT_COMPOSITION: {
       nsCOMPtr<nsIWidget> widget(aWidget);
-      TextComposition backup = *composition;
-
       nsEventStatus status = nsEventStatus_eIgnore;
-      if (!backup.GetLastData().IsEmpty()) {
+      if (!composition->GetLastData().IsEmpty()) {
         WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
-        textEvent.theText = backup.GetLastData();
+        textEvent.theText = composition->GetLastData();
         textEvent.mFlags.mIsSynthesizedForTests = true;
         widget->DispatchEvent(&textEvent, status);
         if (widget->Destroyed()) {
           return NS_OK;
         }
       }
 
       status = nsEventStatus_eIgnore;
       WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
-      endEvent.data = backup.GetLastData();
+      endEvent.data = composition->GetLastData();
       endEvent.mFlags.mIsSynthesizedForTests = true;
       widget->DispatchEvent(&endEvent, status);
 
       return NS_OK;
     }
     case REQUEST_TO_CANCEL_COMPOSITION: {
       nsCOMPtr<nsIWidget> widget(aWidget);
-      TextComposition backup = *composition;
-
       nsEventStatus status = nsEventStatus_eIgnore;
-      if (!backup.GetLastData().IsEmpty()) {
+      if (!composition->GetLastData().IsEmpty()) {
         WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget);
-        updateEvent.data = backup.GetLastData();
+        updateEvent.data = composition->GetLastData();
         updateEvent.mFlags.mIsSynthesizedForTests = true;
         widget->DispatchEvent(&updateEvent, status);
         if (widget->Destroyed()) {
           return NS_OK;
         }
 
         status = nsEventStatus_eIgnore;
         WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
-        textEvent.theText = backup.GetLastData();
+        textEvent.theText = composition->GetLastData();
         textEvent.mFlags.mIsSynthesizedForTests = true;
         widget->DispatchEvent(&textEvent, status);
         if (widget->Destroyed()) {
           return NS_OK;
         }
       }
 
       status = nsEventStatus_eIgnore;
       WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
-      endEvent.data = backup.GetLastData();
+      endEvent.data = composition->GetLastData();
       endEvent.mFlags.mIsSynthesizedForTests = true;
       widget->DispatchEvent(&endEvent, status);
 
       return NS_OK;
     }
     default:
       return NS_OK;
   }
@@ -1128,12 +1122,13 @@ nsIMEStateManager::GetFocusSelectionAndR
   NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent,
                "uninitialized text state observer");
   NS_ADDREF(*aSel = sTextStateObserver->mSel);
   NS_ADDREF(*aRoot = sTextStateObserver->mRootContent);
   return NS_OK;
 }
 
 TextComposition*
-nsIMEStateManager::GetTextComposition(nsIWidget* aWidget)
+nsIMEStateManager::GetTextCompositionFor(nsIWidget* aWidget)
 {
-  return sTextCompositions->GetCompositionFor(aWidget);
+  return sTextCompositions ?
+    sTextCompositions->GetCompositionFor(aWidget) : nullptr;
 }
--- a/dom/events/nsIMEStateManager.h
+++ b/dom/events/nsIMEStateManager.h
@@ -93,17 +93,17 @@ public:
                                        nsPresContext* aPresContext,
                                        mozilla::WidgetEvent* aEvent,
                                        nsEventStatus* aStatus,
                                        nsDispatchingCallback* aCallBack);
 
   /**
    * Get TextComposition from widget.
    */
-  static mozilla::TextComposition* GetTextComposition(nsIWidget* aWidget);
+  static mozilla::TextComposition* GetTextCompositionFor(nsIWidget* aWidget);
 
   /**
    * Send a notification to IME.  It depends on the IME or platform spec what
    * will occur (or not occur).
    */
   static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
                             nsIWidget* aWidget);
   static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
--- a/widget/xpwidgets/PuppetWidget.cpp
+++ b/widget/xpwidgets/PuppetWidget.cpp
@@ -485,18 +485,18 @@ nsresult
 PuppetWidget::NotifyIMEOfUpdateComposition()
 {
 #ifndef MOZ_CROSS_PROCESS_IME
   return NS_OK;
 #endif
 
   NS_ENSURE_TRUE(mTabChild, NS_ERROR_FAILURE);
 
-  mozilla::TextComposition* textComposition =
-    nsIMEStateManager::GetTextComposition(this);
+  nsRefPtr<TextComposition> textComposition =
+    nsIMEStateManager::GetTextCompositionFor(this);
   NS_ENSURE_TRUE(textComposition, NS_ERROR_FAILURE);
 
   nsEventStatus status;
   uint32_t offset = textComposition->OffsetOfTargetClause();
   WidgetQueryContentEvent textRect(true, NS_QUERY_TEXT_RECT, this);
   InitEvent(textRect, nullptr);
   textRect.InitForQueryTextRect(offset, 1);
   DispatchEvent(&textRect, status);