Bug 1053048 Implement nsIEditor.isInEditAction readonly attribute r=ehsan, sr=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 19 Aug 2014 20:54:08 +0900
changeset 200360 77bf46f52906ecd6c09a13539642be83471661a1
parent 200359 99f640f313595fc5ce635439fbeb9571b7f36629
child 200361 eb7712477525ae30ecf1d95569abaf5932522c0f
push id27342
push userryanvm@gmail.com
push dateTue, 19 Aug 2014 20:23:11 +0000
treeherdermozilla-central@149d3ce6e020 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, smaug
bugs1053048
milestone34.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 1053048 Implement nsIEditor.isInEditAction readonly attribute r=ehsan, sr=smaug
content/html/content/src/nsTextEditorState.cpp
dom/events/IMEContentObserver.cpp
dom/events/IMEContentObserver.h
editor/libeditor/nsEditor.cpp
editor/libeditor/nsEditor.h
editor/nsIEditor.idl
editor/nsIHTMLEditor.idl
editor/nsIPlaintextEditor.idl
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -659,18 +659,16 @@ public:
   /** SetEditor gives an address to the editor that will be accessed
    *  @param aEditor the editor this listener calls for editing operations
    */
   void SetFrame(nsTextControlFrame *aFrame){mFrame = aFrame;}
 
   void SettingValue(bool aValue) { mSettingValue = aValue; }
   void SetValueChanged(bool aSetValueChanged) { mSetValueChanged = aSetValueChanged; }
 
-  bool IsInEditAction() const { return mInEditAction; }
-
   NS_DECL_ISUPPORTS
 
   NS_DECL_NSISELECTIONLISTENER
 
   NS_DECL_NSIDOMEVENTLISTENER
 
   NS_DECL_NSIEDITOROBSERVER
 
@@ -705,36 +703,31 @@ protected:
    * refrain from calling OnValueChanged.
    */
   bool mSettingValue;
   /**
    * Whether we are in the process of a SetValue call that doesn't want
    * |SetValueChanged| to be called.
    */
   bool mSetValueChanged;
-  /**
-   * mInEditAction is true while editor handling an edit action.
-   */
-  bool mInEditAction;
 };
 
 
 /*
  * nsTextInputListener implementation
  */
 
 nsTextInputListener::nsTextInputListener(nsITextControlElement* aTxtCtrlElement)
 : mFrame(nullptr)
 , mTxtCtrlElement(aTxtCtrlElement)
 , mSelectionWasCollapsed(true)
 , mHadUndoItems(false)
 , mHadRedoItems(false)
 , mSettingValue(false)
 , mSetValueChanged(true)
-, mInEditAction(false)
 {
 }
 
 nsTextInputListener::~nsTextInputListener() 
 {
 }
 
 NS_IMPL_ISUPPORTS(nsTextInputListener,
@@ -891,18 +884,16 @@ nsTextInputListener::HandleEvent(nsIDOME
   return NS_OK;
 }
 
 // BEGIN nsIEditorObserver
 
 NS_IMETHODIMP
 nsTextInputListener::EditAction()
 {
-  mInEditAction = false;
-
   nsWeakFrame weakFrame = mFrame;
 
   nsITextControlFrame* frameBase = do_QueryFrame(mFrame);
   nsTextControlFrame* frame = static_cast<nsTextControlFrame*> (frameBase);
   NS_ASSERTION(frame, "Where is our frame?");
   //
   // Update the undo / redo menus
   //
@@ -938,24 +929,22 @@ nsTextInputListener::EditAction()
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsTextInputListener::BeforeEditAction()
 {
-  mInEditAction = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsTextInputListener::CancelEditAction()
 {
-  mInEditAction = false;
   return NS_OK;
 }
 
 // END nsIEditorObserver
 
 
 nsresult
 nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate,
@@ -1524,20 +1513,21 @@ void
 nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
 {
   NS_ENSURE_TRUE_VOID(mBoundFrame);
 
   // If it was, however, it should be unbounded from the same frame.
   NS_ASSERTION(!aFrame || aFrame == mBoundFrame, "Unbinding from the wrong frame");
   NS_ENSURE_TRUE_VOID(!aFrame || aFrame == mBoundFrame);
 
-  // If the editor is modified, we need to notify it here because editor may be
-  // destroyed before EditAction() is called if selection listener causes
-  // flushing layout.
-  if (mTextListener && mTextListener->IsInEditAction()) {
+  // If the editor is modified but nsIEditorObserver::EditAction() hasn't been
+  // called yet, we need to notify it here because editor may be destroyed
+  // before EditAction() is called if selection listener causes flushing layout.
+  if (mTextListener && mEditor && mEditorInitialized &&
+      mEditor->GetIsInEditAction()) {
     mTextListener->EditAction();
   }
 
   // We need to start storing the value outside of the editor if we're not
   // going to use it anymore, so retrieve it for now.
   nsAutoString value;
   GetValue(value, true);
 
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -79,17 +79,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
 
 IMEContentObserver::IMEContentObserver()
   : mESM(nullptr)
   , mPreCharacterDataChangeLength(-1)
-  , mIsEditorInTransaction(false)
   , mIsSelectionChangeEventPending(false)
   , mSelectionChangeCausedOnlyByComposition(false)
   , mIsPositionChangeEventPending(false)
   , mIsFlushingPendingNotifications(false)
 {
 #ifdef DEBUG
   TestMergingTextChangeData();
 #endif
@@ -906,36 +905,33 @@ IMEContentObserver::AttributeChanged(nsI
   TextChangeData data(start, start + mPreAttrChangeLength,
                       start + postAttrChangeLength, causedByComposition);
   MaybeNotifyIMEOfTextChange(data);
 }
 
 NS_IMETHODIMP
 IMEContentObserver::EditAction()
 {
-  mIsEditorInTransaction = false;
   mEndOfAddedTextCache.Clear();
   mStartOfRemovingTextRangeCache.Clear();
   FlushMergeableNotifications();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IMEContentObserver::BeforeEditAction()
 {
-  mIsEditorInTransaction = true;
   mEndOfAddedTextCache.Clear();
   mStartOfRemovingTextRangeCache.Clear();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IMEContentObserver::CancelEditAction()
 {
-  mIsEditorInTransaction = false;
   mEndOfAddedTextCache.Clear();
   mStartOfRemovingTextRangeCache.Clear();
   FlushMergeableNotifications();
   return NS_OK;
 }
 
 void
 IMEContentObserver::MaybeNotifyIMEOfTextChange(const TextChangeData& aData)
@@ -983,20 +979,24 @@ public:
 
 private:
   nsRefPtr<IMEContentObserver> mIMEContentObserver;
 };
 
 void
 IMEContentObserver::FlushMergeableNotifications()
 {
-  // If we're in handling an edit action, this method will be called later.
   // If this is already detached from the widget, this doesn't need to notify
   // anything.
-  if (mIsEditorInTransaction || !mWidget) {
+  if (!mWidget) {
+    return;
+  }
+
+  // If we're in handling an edit action, this method will be called later.
+  if (mEditor && mEditor->GetIsInEditAction()) {
     return;
   }
 
   // Notifying something may cause nested call of this method.  For example,
   // when somebody notified one of the notifications may dispatch query content
   // event. Then, it causes flushing layout which may cause another layout
   // change notification.
 
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -212,17 +212,16 @@ private:
   TextChangeData mTextChangeData;
 
   EventStateManager* mESM;
 
   nsIMEUpdatePreference mUpdatePreference;
   uint32_t mPreAttrChangeLength;
   int64_t mPreCharacterDataChangeLength;
 
-  bool mIsEditorInTransaction;
   bool mIsSelectionChangeEventPending;
   bool mSelectionChangeCausedOnlyByComposition;
   bool mIsPositionChangeEventPending;
   bool mIsFlushingPendingNotifications;
 };
 
 } // namespace mozilla
 
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -142,16 +142,17 @@ nsEditor::nsEditor()
 ,  mIMETextOffset(0)
 ,  mDirection(eNone)
 ,  mDocDirtyState(-1)
 ,  mSpellcheckCheckboxState(eTriUnset)
 ,  mShouldTxnSetSelection(true)
 ,  mDidPreDestroy(false)
 ,  mDidPostCreate(false)
 ,  mDispatchInputEvent(true)
+,  mIsInEditAction(false)
 {
 }
 
 nsEditor::~nsEditor()
 {
   NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?");
 
   mTxnMgr = nullptr;
@@ -1827,32 +1828,35 @@ private:
   bool mIsComposing;
 };
 
 void
 nsEditor::NotifyEditorObservers(NotificationForEditorObservers aNotification)
 {
   switch (aNotification) {
     case eNotifyEditorObserversOfEnd:
+      mIsInEditAction = false;
       for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
         mEditorObservers[i]->EditAction();
       }
 
       if (!mDispatchInputEvent) {
         return;
       }
 
       FireInputEvent();
       break;
     case eNotifyEditorObserversOfBefore:
+      mIsInEditAction = true;
       for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
         mEditorObservers[i]->BeforeEditAction();
       }
       break;
     case eNotifyEditorObserversOfCancel:
+      mIsInEditAction = false;
       for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
         mEditorObservers[i]->CancelEditAction();
       }
       break;
     default:
       MOZ_CRASH("Handle all notifications here");
       break;
   }
@@ -5268,8 +5272,16 @@ nsEditor::GetSuppressDispatchingInputEve
 }
 
 NS_IMETHODIMP
 nsEditor::SetSuppressDispatchingInputEvent(bool aSuppress)
 {
   mDispatchInputEvent = !aSuppress;
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsEditor::GetIsInEditAction(bool* aIsInEditAction)
+{
+  MOZ_ASSERT(aIsInEditAction, "aIsInEditAction must not be null");
+  *aIsInEditAction = mIsInEditAction;
+  return NS_OK;
+}
--- a/editor/libeditor/nsEditor.h
+++ b/editor/libeditor/nsEditor.h
@@ -890,16 +890,17 @@ protected:
   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
   bool mDispatchInputEvent;
+  bool mIsInEditAction;   // true while the instance is handling an edit action
 
   friend bool NSCanUnload(nsISupports* serviceMgr);
   friend class nsAutoTxnsConserveSelection;
   friend class nsAutoSelectionReset;
   friend class nsAutoRules;
   friend class nsRangeUpdater;
 };
 
--- a/editor/nsIEditor.idl
+++ b/editor/nsIEditor.idl
@@ -16,17 +16,17 @@ interface nsIDocumentStateListener;
 interface nsIOutputStream;
 interface nsITransactionManager;
 interface nsITransaction;
 interface nsIEditorObserver;
 interface nsIEditActionListener;
 interface nsIInlineSpellChecker;
 interface nsITransferable;
 
-[scriptable, uuid(65523eab-db1f-44aa-893e-dfe57ad306f0)]
+[builtinclass, scriptable, uuid(c3b61bc9-ccdd-4bcd-acd8-1b8dcbe6a247)]
 
 interface nsIEditor  : nsISupports
 {
 %{C++
   typedef short EDirection;
   typedef short EStripWrappers;
 %}
   const short eNone = 0;
@@ -544,9 +544,16 @@ interface nsIEditor  : nsISupports
   /* Run unit tests. Noop in optimized builds */
   void debugUnitTests(out long outNumTests, out long  outNumTestsFailed);
 
   /* checks if a node is read-only or not */
   [notxpcom] boolean isModifiableNode(in nsIDOMNode aNode);
 
   /* Set true if you want to suppress dispatching input event. */
   attribute boolean suppressDispatchingInputEvent;
+
+  /**
+   * True if an edit action is being handled (in other words, between calls of
+   * nsIEditorObserver::BeforeEditAction() and nsIEditorObserver::EditAction()
+   * or nsIEditorObserver::CancelEditAction().  Otherwise, false.
+   */
+  [infallible, noscript] readonly attribute boolean isInEditAction;
 };
--- a/editor/nsIHTMLEditor.idl
+++ b/editor/nsIHTMLEditor.idl
@@ -17,17 +17,17 @@ namespace mozilla {
 namespace dom {
 class Element;
 }
 }
 %}
 
 [ptr] native Element (mozilla::dom::Element);
 
-[scriptable, uuid(833f30de-94c7-4630-a852-2300ef329d7b)]
+[builtinclass, scriptable, uuid(9470bee7-cad3-4382-8fb4-6bdda9f0273a)]
 
 interface nsIHTMLEditor : nsISupports
 {
 %{C++
   typedef short EAlignment;
 %}
 
   // used by GetAlignment()
--- a/editor/nsIPlaintextEditor.idl
+++ b/editor/nsIPlaintextEditor.idl
@@ -1,16 +1,16 @@
 /* -*- 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 "nsISupports.idl"
 
-[scriptable, uuid(07b6d070-ccea-4a00-84b4-f4b94dd9eb52)]
+[builtinclass, scriptable, uuid(da8f244b-6ffc-4be1-8b1a-667abfe1d304)]
 interface nsIPlaintextEditor : nsISupports
 {
 
   // XXX Why aren't these in nsIEditor?
   // only plain text entry is allowed via events
   const long eEditorPlaintextMask       = 0x0001;
   // enter key and CR-LF handled specially
   const long eEditorSingleLineMask      = 0x0002;