Bug 960866 part.4 Remove nsEditor::mIMEBufferLength r=ehsan+smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 12 Feb 2014 22:02:56 +0900
changeset 168377 5159f90e44eff593513507f74fc496648ced101b
parent 168376 984a2e65a52f7c22056381683e3b103644d1e137
child 168378 48191ef36ffc171afb33043a561fd627dc6b5975
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersehsan
bugs960866
milestone30.0a1
Bug 960866 part.4 Remove nsEditor::mIMEBufferLength r=ehsan+smaug
dom/events/TextComposition.cpp
dom/events/TextComposition.h
dom/events/nsIMEStateManager.cpp
editor/libeditor/base/nsEditor.cpp
editor/libeditor/base/nsEditor.h
editor/libeditor/text/nsPlaintextEditor.cpp
editor/libeditor/text/nsTextEditRules.cpp
toolkit/content/tests/chrome/file_autocomplete_with_composition.js
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -125,16 +125,26 @@ TextComposition::NotifyIME(widget::Notif
   NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
   return nsIMEStateManager::NotifyIME(aNotification, mPresContext);
 }
 
 void
 TextComposition::EditorWillHandleTextEvent(const WidgetTextEvent* aTextEvent)
 {
   mIsComposing = aTextEvent->IsComposing();
+
+  MOZ_ASSERT(mLastData == aTextEvent->theText,
+    "The text of a text event must be same as previous data attribute value "
+    "of the latest compositionupdate event");
+}
+
+void
+TextComposition::EditorDidHandleTextEvent()
+{
+  mString = mLastData;
 }
 
 /******************************************************************************
  * TextComposition::CompositionEventDispatcher
  ******************************************************************************/
 
 TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
                                                nsPresContext* aPresContext,
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -42,17 +42,24 @@ public:
   ~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.
-  const nsString& GetLastData() const { return mLastData; }
+  // This value is modified at dispatching compositionupdate.
+  const nsString& LastData() const { return mLastData; }
+  // The composition string which is already handled by the focused editor.
+  // I.e., this value must be same as the composition string on the focused
+  // editor.  This value is modified at a call of EditorDidHandleTextEvent().
+  // Note that mString and mLastData are different between dispatcing
+  // compositionupdate and text event handled by focused editor.
+  const nsString& String() const { return mString; }
   // Returns true if the composition is started with synthesized event which
   // came from nsDOMWindowUtils.
   bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; }
 
   bool MatchesNativeContext(nsIWidget* aWidget) const;
 
   /**
    * SynthesizeCommit() dispatches compositionupdate, text and compositionend
@@ -80,32 +87,42 @@ public:
   bool IsComposing() const { return mIsComposing; }
 
   /**
    * EditorWillHandleTextEvent() must be called before the focused editor
    * handles the text event.
    */
   void EditorWillHandleTextEvent(const WidgetTextEvent* aTextEvent);
 
+  /**
+   * EditorDidHandleTextEvent() must be called after the focused editor handles
+   * a text event.
+   */
+  void EditorDidHandleTextEvent();
+
 private:
   // This class holds nsPresContext weak.  This instance shouldn't block
   // destroying it.  When the presContext is being destroyed, it's notified to
   // nsIMEStateManager::OnDestroyPresContext(), and then, it destroy
   // this instance.
   nsPresContext* mPresContext;
   nsCOMPtr<nsINode> mNode;
 
   // mNativeContext stores a opaque pointer.  This works as the "ID" for this
   // composition.  Don't access the instance, it may not be available.
   void* mNativeContext;
 
   // mLastData stores the data attribute of the latest composition event (except
   // the compositionstart event).
   nsString mLastData;
 
+  // mString stores the composition text which has been handled by the focused
+  // editor.
+  nsString mString;
+
   // Offset of the composition string from start of the editor
   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;
--- a/dom/events/nsIMEStateManager.cpp
+++ b/dom/events/nsIMEStateManager.cpp
@@ -612,59 +612,59 @@ 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);
       nsEventStatus status = nsEventStatus_eIgnore;
-      if (!composition->GetLastData().IsEmpty()) {
+      if (!composition->LastData().IsEmpty()) {
         WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
-        textEvent.theText = composition->GetLastData();
+        textEvent.theText = composition->LastData();
         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 = composition->GetLastData();
+      endEvent.data = composition->LastData();
       endEvent.mFlags.mIsSynthesizedForTests = true;
       widget->DispatchEvent(&endEvent, status);
 
       return NS_OK;
     }
     case REQUEST_TO_CANCEL_COMPOSITION: {
       nsCOMPtr<nsIWidget> widget(aWidget);
       nsEventStatus status = nsEventStatus_eIgnore;
-      if (!composition->GetLastData().IsEmpty()) {
+      if (!composition->LastData().IsEmpty()) {
         WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget);
-        updateEvent.data = composition->GetLastData();
+        updateEvent.data = composition->LastData();
         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 = composition->GetLastData();
+        textEvent.theText = composition->LastData();
         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 = composition->GetLastData();
+      endEvent.data = composition->LastData();
       endEvent.mFlags.mIsSynthesizedForTests = true;
       widget->DispatchEvent(&endEvent, status);
 
       return NS_OK;
     }
     default:
       return NS_OK;
   }
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -135,17 +135,16 @@ nsEditor::nsEditor()
 ,  mSelState(nullptr)
 ,  mPhonetic(nullptr)
 ,  mModCount(0)
 ,  mFlags(0)
 ,  mUpdateCount(0)
 ,  mPlaceHolderBatch(0)
 ,  mAction(EditAction::none)
 ,  mIMETextOffset(0)
-,  mIMEBufferLength(0)
 ,  mDirection(eNone)
 ,  mDocDirtyState(-1)
 ,  mSpellcheckCheckboxState(eTriUnset)
 ,  mShouldTxnSetSelection(true)
 ,  mDidPreDestroy(false)
 ,  mDidPostCreate(false)
 ,  mDispatchInputEvent(true)
 {
@@ -242,18 +241,16 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsI
   if (aRoot)
     mRootElement = do_QueryInterface(aRoot);
 
   mUpdateCount=0;
 
   /* initialize IME stuff */
   mIMETextNode = nullptr;
   mIMETextOffset = 0;
-  mIMEBufferLength = 0;
-  
   /* Show the caret */
   selCon->SetCaretReadOnly(false);
   selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
 
   selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);//we want to see all the selection reflected to user
 
   NS_POSTCONDITION(mDocWeak, "bad state");
 
@@ -2051,17 +2048,16 @@ nsEditor::EndIMEComposition()
       NS_ASSERTION(NS_SUCCEEDED(rv),
                    "nsIAbsorbingTransaction::Commit() failed");
     }
   }
 
   /* reset the data we need to construct a transaction */
   mIMETextNode = nullptr;
   mIMETextOffset = 0;
-  mIMEBufferLength = 0;
   mComposition = nullptr;
 
   // notify editor observers of action
   NotifyEditorObservers();
 }
 
 
 NS_IMETHODIMP
@@ -4188,20 +4184,20 @@ nsEditor::DeleteSelectionAndCreateNode(c
 
   // we want the selection to be just after the new node
   return selection->Collapse(node, offset + 1);
 }
 
 
 /* Non-interface, protected methods */
 
-int32_t
-nsEditor::GetIMEBufferLength()
-{
-  return mIMEBufferLength;
+TextComposition*
+nsEditor::GetComposition() const
+{
+  return mComposition;
 }
 
 bool
 nsEditor::IsIMEComposing() const
 {
   return mComposition && mComposition->IsComposing();
 }
 
@@ -4394,17 +4390,19 @@ nsEditor::CreateTxnForDeleteNode(nsINode
 NS_IMETHODIMP 
 nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert,
                               IMETextTxn ** aTxn)
 {
   NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn");
      
   nsRefPtr<IMETextTxn> txn = new IMETextTxn();
 
-  nsresult rv = txn->Init(mIMETextNode, mIMETextOffset, mIMEBufferLength,
+  // During handling IME composition, mComposition must have been initialized.
+  nsresult rv = txn->Init(mIMETextNode, mIMETextOffset,
+                          mComposition->String().Length(),
                           mIMETextRangeList, aStringToInsert, this);
   if (NS_SUCCEEDED(rv))
   {
     txn.forget(aTxn);
   }
 
   return rv;
 }
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -594,17 +594,20 @@ public:
   bool IsMozEditorBogusNode(nsIContent *aNode);
 
   /** counts number of editable child nodes */
   uint32_t CountEditableChildren(nsINode* aNode);
   
   /** Find the deep first and last children. */
   nsINode* GetFirstEditableNode(nsINode* aRoot);
 
-  int32_t GetIMEBufferLength();
+  /**
+   * Returns current composition.
+   */
+  mozilla::TextComposition* GetComposition() const;
   /**
    * Returns true if there is composition string and not fixed.
    */
   bool IsIMEComposing() const;
 
   /** from html rules code - migration in progress */
   static nsresult GetTagString(nsIDOMNode *aNode, nsAString& outString);
   static nsIAtom *GetTag(nsIDOMNode *aNode);
@@ -855,17 +858,16 @@ protected:
   uint32_t          mFlags;        // behavior flags. See nsIPlaintextEditor.idl for the flags we use.
 
   int32_t           mUpdateCount;
 
   int32_t           mPlaceHolderBatch;   // nesting count for batching
   EditAction        mAction;             // the current editor action
 
   uint32_t          mIMETextOffset;    // offset in text node where IME comp string begins
-  uint32_t          mIMEBufferLength;  // current length of IME comp string
 
   EDirection        mDirection;          // the current direction of editor action
   int8_t            mDocDirtyState;      // -1 = not initialized
   uint8_t           mSpellcheckCheckboxState; // a Tristate value
 
   bool mShouldTxnSetSelection;  // turn off for conservative selection adjustment by txns
   bool mDidPreDestroy;    // whether PreDestroy has been called
   bool mDidPostCreate;    // whether PostCreate has been called
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -861,17 +861,18 @@ nsPlaintextEditor::UpdateIMEComposition(
   mIMETextRangeList = privateTextEvent->GetInputRange();
   NS_ABORT_IF_FALSE(mIMETextRangeList, "mIMETextRangeList must not be nullptr");
 
   {
     nsAutoPlaceHolderBatch batch(this, nsGkAtoms::IMETxnName);
 
     rv = InsertText(widgetTextEvent->theText);
 
-    mIMEBufferLength = widgetTextEvent->theText.Length();
+    // XXX This approach is ugly, we should sort out the text event handling.
+    mComposition->EditorDidHandleTextEvent();
 
     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/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "TextComposition.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Selection.h"
 #include "mozilla/dom/Element.h"
 #include "nsAString.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
@@ -1203,17 +1204,18 @@ nsTextEditRules::TruncateInsertionIfNeed
     int32_t docLength;
     res = mEditor->GetTextLength(&docLength);
     if (NS_FAILED(res)) { return res; }
 
     int32_t start, end;
     nsContentUtils::GetSelectionInTextControl(aSelection, mEditor->GetRoot(),
                                               start, end);
 
-    int32_t oldCompStrLength = mEditor->GetIMEBufferLength();
+    TextComposition* composition = mEditor->GetComposition();
+    int32_t oldCompStrLength = composition ? composition->String().Length() : 0;
 
     const int32_t selectionLength = end - start;
     const int32_t resultingDocLength = docLength - selectionLength - oldCompStrLength;
     if (resultingDocLength >= aMaxLength)
     {
       aOutString->Truncate();
       if (aTruncated) {
         *aTruncated = true;
--- a/toolkit/content/tests/chrome/file_autocomplete_with_composition.js
+++ b/toolkit/content/tests/chrome/file_autocomplete_with_composition.js
@@ -275,17 +275,17 @@ nsDoTestsForAutoCompleteWithComposition.
             },
             "caret": { "start": 1, "length": 0 }
           }, aWindow);
       }, popup: false, value: "Moz", searchString: "Mozi"
     },
     { description: "compositionupdate shouldn't reopen the popup",
       completeDefaultIndex: false,
       execute: function (aWindow) {
-        synthesizeComposition({ type: "compositionupdate", data: "ll" }, aWindow);
+        synthesizeComposition({ type: "compositionupdate", data: "zi" }, aWindow);
         synthesizeText(
           { "composition":
             { "string": "zi",
               "clauses":
               [
                 { "length": 2, "attr": COMPOSITION_ATTR_RAWINPUT }
               ]
             },