Bug 960866 part.1 nsEditor should store a pointer of TextComposition while composition r=ehsan
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 12 Feb 2014 22:02:55 +0900
changeset 168374 184e8ed6ca48a07b9136cfccba9be86c4f2a6dae
parent 168373 0a13fa6115e9dd0b8613b1a551205c77f144e448
child 168375 41d17ebf8454c296b26991963887db13ae0758c5
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersehsan
bugs960866
milestone30.0a1
Bug 960866 part.1 nsEditor should store a pointer of TextComposition while composition r=ehsan
dom/events/nsIMEStateManager.cpp
dom/events/nsIMEStateManager.h
editor/libeditor/base/nsEditor.cpp
editor/libeditor/base/nsEditor.h
editor/libeditor/base/nsEditorEventListener.cpp
editor/libeditor/text/nsPlaintextEditor.cpp
editor/libeditor/text/nsPlaintextEditor.h
--- a/dom/events/nsIMEStateManager.cpp
+++ b/dom/events/nsIMEStateManager.cpp
@@ -1121,14 +1121,28 @@ 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*
+// static
+already_AddRefed<TextComposition>
 nsIMEStateManager::GetTextCompositionFor(nsIWidget* aWidget)
 {
-  return sTextCompositions ?
-    sTextCompositions->GetCompositionFor(aWidget) : nullptr;
+  if (!sTextCompositions) {
+    return nullptr;
+  }
+  nsRefPtr<TextComposition> textComposition =
+    sTextCompositions->GetCompositionFor(aWidget);
+  return textComposition.forget();
 }
+
+// static
+already_AddRefed<TextComposition>
+nsIMEStateManager::GetTextCompositionFor(WidgetGUIEvent* aEvent)
+{
+  MOZ_ASSERT(aEvent->AsCompositionEvent() || aEvent->AsTextEvent(),
+             "aEvent has to be WidgetCompositionEvent or WidgetTextEvent");
+  return GetTextCompositionFor(aEvent->widget);
+}
--- a/dom/events/nsIMEStateManager.h
+++ b/dom/events/nsIMEStateManager.h
@@ -93,17 +93,27 @@ public:
                                        nsPresContext* aPresContext,
                                        mozilla::WidgetEvent* aEvent,
                                        nsEventStatus* aStatus,
                                        nsDispatchingCallback* aCallBack);
 
   /**
    * Get TextComposition from widget.
    */
-  static mozilla::TextComposition* GetTextCompositionFor(nsIWidget* aWidget);
+  static already_AddRefed<mozilla::TextComposition>
+    GetTextCompositionFor(nsIWidget* aWidget);
+
+  /**
+   * Returns TextComposition instance for the event.
+   *
+   * @param aEvent      Should be a composition event or a text event which is
+   *                    being dispatched.
+   */
+  static already_AddRefed<mozilla::TextComposition>
+    GetTextCompositionFor(mozilla::WidgetGUIEvent* aEvent);
 
   /**
    * 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/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -16,16 +16,17 @@
 #include "EditAggregateTxn.h"           // for EditAggregateTxn
 #include "EditTxn.h"                    // for EditTxn
 #include "IMETextTxn.h"                 // for IMETextTxn
 #include "InsertElementTxn.h"           // for InsertElementTxn
 #include "InsertTextTxn.h"              // for InsertTextTxn
 #include "JoinElementTxn.h"             // for JoinElementTxn
 #include "PlaceholderTxn.h"             // for PlaceholderTxn
 #include "SplitElementTxn.h"            // for SplitElementTxn
+#include "TextComposition.h"            // for TextComposition
 #include "mozFlushType.h"               // for mozFlushType::Flush_Frames
 #include "mozISpellCheckingEngine.h"
 #include "mozInlineSpellChecker.h"      // for mozInlineSpellChecker
 #include "mozilla/Preferences.h"        // for Preferences
 #include "mozilla/Selection.h"          // for Selection, etc
 #include "mozilla/Services.h"           // for GetObserverService
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/Element.h"        // for Element, nsINode::AsElement
@@ -2005,20 +2006,35 @@ nsEditor::RestorePreservedSelection(nsIS
 
 void     
 nsEditor::StopPreservingSelection()
 {
   mRangeUpdater.DropSelectionState(mSavedSel);
   mSavedSel.MakeEmpty();
 }
 
+void
+nsEditor::EnsureComposition(mozilla::WidgetGUIEvent* aEvent)
+{
+  if (mComposition) {
+    return;
+  }
+  // The compositionstart event must cause creating new TextComposition
+  // instance at being dispatched by nsIMEStateManager.
+  mComposition = nsIMEStateManager::GetTextCompositionFor(aEvent);
+  if (!mComposition) {
+    MOZ_CRASH("nsIMEStateManager doesn't return proper composition");
+  }
+}
 
 nsresult
-nsEditor::BeginIMEComposition()
-{
+nsEditor::BeginIMEComposition(WidgetCompositionEvent* aCompositionEvent)
+{
+  MOZ_ASSERT(!mComposition, "There is composition already");
+  EnsureComposition(aCompositionEvent);
   mInIMEMode = true;
   if (mPhonetic) {
     mPhonetic->Truncate(0);
   }
   return NS_OK;
 }
 
 void
@@ -2039,16 +2055,17 @@ nsEditor::EndIMEComposition()
   }
 
   /* reset the data we need to construct a transaction */
   mIMETextNode = nullptr;
   mIMETextOffset = 0;
   mIMEBufferLength = 0;
   mInIMEMode = false;
   mIsIMEComposing = false;
+  mComposition = nullptr;
 
   // notify editor observers of action
   NotifyEditorObservers();
 }
 
 
 NS_IMETHODIMP
 nsEditor::GetPhonetic(nsAString& aPhonetic)
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -64,16 +64,17 @@ class nsISupports;
 class nsITransaction;
 class nsIWidget;
 class nsRange;
 class nsString;
 class nsTransactionManager;
 
 namespace mozilla {
 class Selection;
+class TextComposition;
 
 namespace dom {
 class Element;
 class EventTarget;
 }  // namespace dom
 }  // namespace mozilla
 
 namespace mozilla {
@@ -237,19 +238,18 @@ public:
      Arguments:
       nsString& aTag          - tag you want
       nsIContent** aContent   - returned Content that was created with above namespace.
   */
   nsresult CreateHTMLContent(const nsAString& aTag,
                              mozilla::dom::Element** aContent);
 
   // IME event handlers
-  virtual nsresult BeginIMEComposition();
-  virtual nsresult UpdateIMEComposition(const nsAString &aCompositionString,
-                                        nsIPrivateTextRangeList *aTextRange)=0;
+  virtual nsresult BeginIMEComposition(mozilla::WidgetCompositionEvent* aEvent);
+  virtual nsresult UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent) = 0;
   void EndIMEComposition();
 
   void SwitchTextDirectionTo(uint32_t aDirection);
 
 protected:
   nsresult DetermineCurrentDirection();
   void FireInputEvent();
 
@@ -408,16 +408,23 @@ protected:
 
   bool CanEnableSpellCheck()
   {
     // Check for password/readonly/disabled, which are not spellchecked
     // regardless of DOM. Also, check to see if spell check should be skipped or not.
     return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() && !ShouldSkipSpellCheck();
   }
 
+  /**
+   * EnsureComposition() should be composition event handlers or text event
+   * handler.  This tries to get the composition for the event and set it to
+   * mComposition.
+   */
+  void EnsureComposition(mozilla::WidgetGUIEvent* aEvent);
+
 public:
 
   /** All editor operations which alter the doc should be prefaced
    *  with a call to StartOperation, naming the action and direction */
   NS_IMETHOD StartOperation(EditAction opID,
                             nsIEditor::EDirection aDirection);
 
   /** All editor operations which alter the doc should be followed
@@ -825,16 +832,19 @@ protected:
   nsCOMPtr<mozilla::dom::EventTarget> mEventTarget; // The form field as an event receiver
   nsCOMPtr<nsIDOMEventListener> mEventListener;
   nsWeakPtr        mSelConWeak;          // weak reference to the nsISelectionController
   nsWeakPtr        mPlaceHolderTxn;      // weak reference to placeholder for begin/end batch purposes
   nsWeakPtr        mDocWeak;             // weak reference to the nsIDOMDocument
   nsIAtom          *mPlaceHolderName;    // name of placeholder transaction
   nsSelectionState *mSelState;           // saved selection state for placeholder txn batching
   nsString         *mPhonetic;
+  // IME composition this is not null between compositionstart and
+  // compositionend.
+  nsRefPtr<mozilla::TextComposition> mComposition;
 
   // various listeners
   nsCOMArray<nsIEditActionListener> mActionListeners;  // listens to all low level actions on the doc
   nsCOMArray<nsIEditorObserver> mEditorObservers;  // just notify once per high level change
   nsCOMArray<nsIDocumentStateListener> mDocStateListeners;// listen to overall doc state (dirty or not, just created, etc)
 
   nsSelectionState  mSavedSel;           // cached selection for nsAutoSelectionReset
   nsRangeUpdater    mRangeUpdater;       // utility class object for maintaining preserved ranges
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=4 sw=2 et tw=78: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Preferences.h"        // for Preferences
+#include "mozilla/TextEvents.h"         // for WidgetCompositionEvent
 #include "mozilla/dom/Element.h"        // for Element
 #include "mozilla/dom/EventTarget.h"    // for EventTarget
 #include "nsAString.h"
 #include "nsCaret.h"                    // for nsCaret
 #include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
 #include "nsEditor.h"                   // for nsEditor, etc
 #include "nsEditorEventListener.h"
 #include "nsEventListenerManager.h"     // for nsEventListenerManager
@@ -37,18 +38,16 @@
 #include "nsIFocusManager.h"            // for nsIFocusManager
 #include "nsIFormControl.h"             // for nsIFormControl, etc
 #include "nsIHTMLEditor.h"              // for nsIHTMLEditor
 #include "nsIMEStateManager.h"          // for nsIMEStateManager
 #include "nsINativeKeyBindings.h"       // for nsINativeKeyBindings
 #include "nsINode.h"                    // for nsINode, ::NODE_IS_EDITABLE, etc
 #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor, etc
 #include "nsIPresShell.h"               // for nsIPresShell
-#include "nsIPrivateTextEvent.h"        // for nsIPrivateTextEvent
-#include "nsIPrivateTextRange.h"        // for nsIPrivateTextRangeList
 #include "nsISelection.h"               // for nsISelection
 #include "nsISelectionController.h"     // for nsISelectionController, etc
 #include "nsISelectionPrivate.h"        // for nsISelectionPrivate
 #include "nsITransferable.h"            // for kFileMime, kHTMLMime, etc
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
 #include "nsPIWindowRoot.h"             // for nsPIWindowRoot
 #include "nsServiceManagerUtils.h"      // for do_GetService
 #include "nsString.h"                   // for nsAutoString
@@ -656,34 +655,22 @@ NS_IMETHODIMP
 nsEditorEventListener::HandleText(nsIDOMEvent* aTextEvent)
 {
   NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
 
   if (!mEditor->IsAcceptableInputEvent(aTextEvent)) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIPrivateTextEvent> textEvent = do_QueryInterface(aTextEvent);
-  if (!textEvent) {
-     //non-ui event passed in.  bad things.
-     return NS_OK;
-  }
-
-  nsAutoString                      composedText;
-  nsCOMPtr<nsIPrivateTextRangeList> textRangeList;
-
-  textEvent->GetText(composedText);
-  textRangeList = textEvent->GetInputRange();
-
   // if we are readonly or disabled, then do nothing.
   if (mEditor->IsReadonly() || mEditor->IsDisabled()) {
     return NS_OK;
   }
 
-  return mEditor->UpdateIMEComposition(composedText, textRangeList);
+  return mEditor->UpdateIMEComposition(aTextEvent);
 }
 
 /**
  * Drag event implementation
  */
 
 nsresult
 nsEditorEventListener::DragEnter(nsIDOMDragEvent* aDragEvent)
@@ -900,17 +887,19 @@ nsEditorEventListener::CanDrop(nsIDOMDra
 
 NS_IMETHODIMP
 nsEditorEventListener::HandleStartComposition(nsIDOMEvent* aCompositionEvent)
 {
   NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
   if (!mEditor->IsAcceptableInputEvent(aCompositionEvent)) {
     return NS_OK;
   }
-  return mEditor->BeginIMEComposition();
+  WidgetCompositionEvent* compositionStart =
+    aCompositionEvent->GetInternalNSEvent()->AsCompositionEvent();
+  return mEditor->BeginIMEComposition(compositionStart);
 }
 
 void
 nsEditorEventListener::HandleEndComposition(nsIDOMEvent* aCompositionEvent)
 {
   MOZ_ASSERT(mEditor);
   if (!mEditor->IsAcceptableInputEvent(aCompositionEvent)) {
     return;
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -34,16 +34,17 @@
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDocumentEncoder.h"
 #include "nsIEditorIMESupport.h"
 #include "nsINameSpaceManager.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
+#include "nsIPrivateTextEvent.h"
 #include "nsIPrivateTextRange.h"
 #include "nsISelection.h"
 #include "nsISelectionController.h"
 #include "nsISelectionPrivate.h"
 #include "nsISupportsPrimitives.h"
 #include "nsITransferable.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsInternetCiter.h"
@@ -806,61 +807,71 @@ NS_IMETHODIMP nsPlaintextEditor::InsertL
     // post-process, always called if WillInsertBreak didn't return cancel==true
     res = mRules->DidDoAction(selection, &ruleInfo, res);
   }
 
   return res;
 }
 
 nsresult
-nsPlaintextEditor::BeginIMEComposition()
+nsPlaintextEditor::BeginIMEComposition(WidgetCompositionEvent* aEvent)
 {
   NS_ENSURE_TRUE(!mInIMEMode, NS_OK);
 
   if (IsPasswordEditor()) {
     NS_ENSURE_TRUE(mRules, NS_ERROR_NULL_POINTER);
     // Protect the edit rules object from dying
     nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
     nsTextEditRules *textEditRules =
       static_cast<nsTextEditRules*>(mRules.get());
     textEditRules->ResetIMETextPWBuf();
   }
 
-  return nsEditor::BeginIMEComposition();
+  return nsEditor::BeginIMEComposition(aEvent);
 }
 
 nsresult
-nsPlaintextEditor::UpdateIMEComposition(const nsAString& aCompositionString,
-                                        nsIPrivateTextRangeList* aTextRangeList)
+nsPlaintextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
 {
-  NS_ABORT_IF_FALSE(aTextRangeList, "aTextRangeList must not be nullptr");
+  NS_ABORT_IF_FALSE(aDOMTextEvent, "aDOMTextEvent must not be nullptr");
+
+  WidgetTextEvent* widgetTextEvent =
+    aDOMTextEvent->GetInternalNSEvent()->AsTextEvent();
+  NS_ENSURE_TRUE(widgetTextEvent, NS_ERROR_INVALID_ARG);
+
+  EnsureComposition(widgetTextEvent);
+  mInIMEMode = true;
 
   nsCOMPtr<nsIPresShell> ps = GetPresShell();
   NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
 
   nsCOMPtr<nsISelection> selection;
   nsresult rv = GetSelection(getter_AddRefs(selection));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<nsCaret> caretP = ps->GetCaret();
 
   // Update information of clauses in the new composition string.
   // This will be refered by followed methods.
-  mIMETextRangeList = aTextRangeList;
+  nsCOMPtr<nsIPrivateTextEvent> privateTextEvent =
+    do_QueryInterface(aDOMTextEvent);
+  NS_ENSURE_TRUE(privateTextEvent, NS_ERROR_INVALID_ARG);
+  mIMETextRangeList = privateTextEvent->GetInputRange();
+  NS_ABORT_IF_FALSE(mIMETextRangeList, "mIMETextRangeList must not be nullptr");
 
   // We set mIsIMEComposing properly.
   SetIsIMEComposing();
 
   {
     nsAutoPlaceHolderBatch batch(this, nsGkAtoms::IMETxnName);
 
-    rv = InsertText(aCompositionString);
+    rv = InsertText(widgetTextEvent->theText);
 
-    mIMEBufferLength = aCompositionString.Length();
+    mIMEBufferLength = widgetTextEvent->theText.Length();
 
     if (caretP) {
       caretP->SetCaretDOMSelection(selection);
     }
   }
 
   // If still composing, we should fire input event via observer.
   // Note that if committed, we don't need to notify it since it will be
--- a/editor/libeditor/text/nsPlaintextEditor.h
+++ b/editor/libeditor/text/nsPlaintextEditor.h
@@ -117,19 +117,18 @@ public:
 
   /** make the given selection span the entire document */
   NS_IMETHOD SelectEntireDocument(nsISelection *aSelection);
 
   virtual nsresult HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent);
 
   virtual already_AddRefed<mozilla::dom::EventTarget> GetDOMEventTarget();
 
-  virtual nsresult BeginIMEComposition();
-  virtual nsresult UpdateIMEComposition(const nsAString &aCompositionString,
-                                        nsIPrivateTextRangeList *aTextRange);
+  virtual nsresult BeginIMEComposition(mozilla::WidgetCompositionEvent* aEvent);
+  virtual nsresult UpdateIMEComposition(nsIDOMEvent* aTextEvent) MOZ_OVERRIDE;
 
   virtual already_AddRefed<nsIContent> GetInputEventTargetContent();
 
   /* ------------ Utility Routines, not part of public API -------------- */
   NS_IMETHOD TypedText(const nsAString& aString, ETypingAction aAction);
 
   nsresult InsertTextAt(const nsAString &aStringToInsert,
                         nsIDOMNode *aDestinationNode,