Bug 1460509 - part 1: Declare NS_ERROR_EDITOR_DESTROYED error code and add a check method to TextEditRules whether it can keep handling edit action at a moment r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 24 Apr 2018 15:23:01 +0900
changeset 476185 d5afcbdbea9d47129110b4c9b0e1be956e8fa9da
parent 476184 dfe87a1c9bd2a6cb9f151c0686c182f7203bca57
child 476186 0899d038b7f35c5103e9b8f56347327734c78fcf
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1460509
milestone62.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 1460509 - part 1: Declare NS_ERROR_EDITOR_DESTROYED error code and add a check method to TextEditRules whether it can keep handling edit action at a moment r=m_kato This patch defines NS_ERROR_EDITOR_DESTROYED error code as an editor module specific error code. And creates TextEditRules::CanHandleEditAction() to check if the instance can keep handling edit action. MozReview-Commit-ID: 4qECwNBO0yz
editor/libeditor/EditorUtils.h
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/TextEditRules.cpp
editor/libeditor/TextEditRules.h
xpcom/base/ErrorList.py
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -38,16 +38,17 @@ template <class T> class OwningNonNull;
 class MOZ_STACK_CLASS EditActionResult final
 {
 public:
   bool Succeeded() const { return NS_SUCCEEDED(mRv); }
   bool Failed() const { return NS_FAILED(mRv); }
   nsresult Rv() const { return mRv; }
   bool Canceled() const { return mCanceled; }
   bool Handled() const { return mHandled; }
+  bool EditorDestroyed() const { return mRv == NS_ERROR_EDITOR_DESTROYED; }
 
   EditActionResult SetResult(nsresult aRv)
   {
     mRv = aRv;
     return *this;
   }
   EditActionResult MarkAsCanceled()
   {
@@ -70,18 +71,23 @@ public:
   EditActionResult& operator|=(const EditActionResult& aOther)
   {
     mCanceled |= aOther.mCanceled;
     mHandled |= aOther.mHandled;
     // When both result are same, keep the result.
     if (mRv == aOther.mRv) {
       return *this;
     }
+    // If one of the result is NS_ERROR_EDITOR_DESTROYED, use it since it's
+    // the most important error code for editor.
+    if (EditorDestroyed() || aOther.EditorDestroyed()) {
+      mRv = NS_ERROR_EDITOR_DESTROYED;
+    }
     // If one of the results is error, use NS_ERROR_FAILURE.
-    if (Failed() || aOther.Failed()) {
+    else if (Failed() || aOther.Failed()) {
       mRv = NS_ERROR_FAILURE;
     } else {
       // Otherwise, use generic success code, NS_OK.
       mRv = NS_OK;
     }
     return *this;
   }
 
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -321,24 +321,24 @@ HTMLEditRules::DetachEditor()
   mHTMLEditor = nullptr;
   return TextEditRules::DetachEditor();
 }
 
 nsresult
 HTMLEditRules::BeforeEdit(EditAction aAction,
                           nsIEditor::EDirection aDirection)
 {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
   AutoLockRulesSniffing lockIt(this);
   mDidExplicitlySetInterline = false;
 
   if (!mActionNesting) {
     mActionNesting++;
 
     // Clear our flag about if just deleted a range
     mDidRangedDelete = false;
@@ -407,23 +407,24 @@ HTMLEditRules::BeforeEdit(EditAction aAc
   return NS_OK;
 }
 
 
 nsresult
 HTMLEditRules::AfterEdit(EditAction aAction,
                          nsIEditor::EDirection aDirection)
 {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
   AutoLockRulesSniffing lockIt(this);
 
   MOZ_ASSERT(mActionNesting > 0);
   nsresult rv = NS_OK;
   mActionNesting--;
   if (!mActionNesting) {
     Selection* selection = mHTMLEditor->GetSelection();
     if (NS_WARN_IF(!selection)) {
@@ -612,18 +613,18 @@ nsresult
 HTMLEditRules::WillDoAction(Selection* aSelection,
                             RulesInfo* aInfo,
                             bool* aCancel,
                             bool* aHandled)
 {
   if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
     return NS_ERROR_INVALID_ARG;
   }
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
   }
 
   MOZ_ASSERT(aCancel);
   MOZ_ASSERT(aHandled);
 
   *aCancel = false;
   *aHandled = false;
 
@@ -716,18 +717,18 @@ HTMLEditRules::WillDoAction(Selection* a
 nsresult
 HTMLEditRules::DidDoAction(Selection* aSelection,
                            RulesInfo* aInfo,
                            nsresult aResult)
 {
   if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
     return NS_ERROR_INVALID_ARG;
   }
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *aSelection);
 
   switch (aInfo->action) {
     case EditAction::insertText:
     case EditAction::insertBreak:
     case EditAction::insertIMEText:
@@ -765,19 +766,20 @@ HTMLEditRules::GetListState(bool* aMixed
 {
   NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
   *aMixed = false;
   *aOL = false;
   *aUL = false;
   *aDL = false;
   bool bNonList = false;
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
   Selection* selection = mHTMLEditor->GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
@@ -828,19 +830,20 @@ HTMLEditRules::GetListItemState(bool* aM
 {
   NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER);
   *aMixed = false;
   *aLI = false;
   *aDT = false;
   *aDD = false;
   bool bNonList = false;
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
   Selection* selection = mHTMLEditor->GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
@@ -882,19 +885,20 @@ HTMLEditRules::GetListItemState(bool* aM
 }
 
 nsresult
 HTMLEditRules::GetAlignment(bool* aMixed,
                             nsIHTMLEditor::EAlignment* aAlign)
 {
   MOZ_ASSERT(aMixed && aAlign);
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
   Selection* selection = mHTMLEditor->GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
   // For now, just return first alignment.  We'll lie about if it's mixed.
@@ -1070,25 +1074,26 @@ MarginPropertyAtomForIndent(nsINode& aNo
 nsresult
 HTMLEditRules::GetIndentState(bool* aCanIndent,
                               bool* aCanOutdent)
 {
   if (NS_WARN_IF(!aCanIndent) || NS_WARN_IF(!aCanOutdent)) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
   // XXX Looks like that this is implementation of
   //     nsIHTMLEditor::getIndentState() however nobody calls this method
   //     even with the interface method.
   *aCanIndent = true;
   *aCanOutdent = false;
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
   Selection* selection = mHTMLEditor->GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
   // contruct a list of nodes to act on.
@@ -1173,24 +1178,25 @@ HTMLEditRules::GetIndentState(bool* aCan
 nsresult
 HTMLEditRules::GetParagraphState(bool* aMixed,
                                  nsAString& outFormat)
 {
   if (NS_WARN_IF(!aMixed)) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
   // This routine is *heavily* tied to our ui choices in the paragraph
   // style popup.  I can't see a way around that.
   *aMixed = true;
   outFormat.Truncate(0);
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
   Selection* selection = mHTMLEditor->GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
   bool bMixed = false;
@@ -9455,17 +9461,17 @@ HTMLEditRules::InsertBRIfNeededInternal(
 void
 HTMLEditRules::DidCreateNode(Selection& aSelection,
                              Element& aNewElement)
 {
   if (!mListenerEnabled) {
     return;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
 
   // assumption that Join keeps the righthand node
   IgnoredErrorResult ignoredError;
   mUtilRange->SelectNode(aNewElement, ignoredError);
@@ -9478,17 +9484,17 @@ HTMLEditRules::DidCreateNode(Selection& 
 void
 HTMLEditRules::DidInsertNode(Selection& aSelection,
                              nsIContent& aContent)
 {
   if (!mListenerEnabled) {
     return;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
 
   IgnoredErrorResult ignoredError;
   mUtilRange->SelectNode(aContent, ignoredError);
   if (NS_WARN_IF(ignoredError.Failed())) {
@@ -9500,17 +9506,17 @@ HTMLEditRules::DidInsertNode(Selection& 
 void
 HTMLEditRules::WillDeleteNode(Selection& aSelection,
                               nsINode& aChild)
 {
   if (!mListenerEnabled) {
     return;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
 
   IgnoredErrorResult ignoredError;
   mUtilRange->SelectNode(aChild, ignoredError);
   if (NS_WARN_IF(ignoredError.Failed())) {
@@ -9523,17 +9529,17 @@ void
 HTMLEditRules::DidSplitNode(Selection& aSelection,
                             nsINode& aExistingRightNode,
                             nsINode& aNewLeftNode)
 {
   if (!mListenerEnabled) {
     return;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
 
   nsresult rv = mUtilRange->SetStartAndEnd(&aNewLeftNode, 0,
                                            &aExistingRightNode, 0);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -9544,30 +9550,35 @@ HTMLEditRules::DidSplitNode(Selection& a
 
 void
 HTMLEditRules::WillJoinNodes(nsINode& aLeftNode,
                              nsINode& aRightNode)
 {
   if (!mListenerEnabled) {
     return;
   }
+
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return;
+  }
+
   // remember split point
   mJoinOffset = aLeftNode.Length();
 }
 
 void
 HTMLEditRules::DidJoinNodes(Selection& aSelection,
                             nsINode& aLeftNode,
                             nsINode& aRightNode)
 {
   if (!mListenerEnabled) {
     return;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
 
   // assumption that Join keeps the righthand node
   nsresult rv = mUtilRange->CollapseTo(&aRightNode, mJoinOffset);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -9581,17 +9592,17 @@ HTMLEditRules::DidInsertText(Selection& 
                              nsINode& aTextNode,
                              int32_t aOffset,
                              const nsAString& aString)
 {
   if (!mListenerEnabled) {
     return;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
 
   int32_t length = aString.Length();
   nsresult rv = mUtilRange->SetStartAndEnd(&aTextNode, aOffset,
                                            &aTextNode, aOffset + length);
@@ -9606,17 +9617,17 @@ HTMLEditRules::DidDeleteText(Selection& 
                              nsINode& aTextNode,
                              int32_t aOffset,
                              int32_t aLength)
 {
   if (!mListenerEnabled) {
     return;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
 
   nsresult rv = mUtilRange->CollapseTo(&aTextNode, aOffset);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
@@ -9626,17 +9637,17 @@ HTMLEditRules::DidDeleteText(Selection& 
 
 void
 HTMLEditRules::WillDeleteSelection(Selection& aSelection)
 {
   if (!mListenerEnabled) {
     return;
   }
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
 
   EditorRawDOMPoint startPoint = EditorBase::GetStartPoint(&SelectionRef());
   if (NS_WARN_IF(!startPoint.IsSet())) {
     return;
@@ -9795,18 +9806,18 @@ HTMLEditRules::MakeSureElemStartsOrEndsO
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::MakeSureElemStartsAndEndsOnCR(nsINode& aNode)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
   }
 
   Selection* selection = mHTMLEditor->GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
@@ -10264,17 +10275,17 @@ HTMLEditRules::DocumentModified()
                       this,
                       &HTMLEditRules::DocumentModifiedWorker));
   return NS_OK;
 }
 
 void
 HTMLEditRules::DocumentModifiedWorker()
 {
-  if (!mHTMLEditor) {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
   Selection* selection = mHTMLEditor->GetSelection();
   if (NS_WARN_IF(!selection)) {
     return;
   }
 
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -192,33 +192,32 @@ TextEditRules::DetachEditor()
   mTextEditor = nullptr;
   return NS_OK;
 }
 
 nsresult
 TextEditRules::BeforeEdit(EditAction aAction,
                           nsIEditor::EDirection aDirection)
 {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
   AutoLockRulesSniffing lockIt(this);
   mDidExplicitlySetInterline = false;
   if (!mActionNesting) {
     // let rules remember the top level action
     mTheAction = aAction;
   }
   mActionNesting++;
 
-  // get the selection and cache the position before editing
-  if (NS_WARN_IF(!mTextEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
   if (aAction == EditAction::setText) {
     // setText replaces all text, so mCachedSelectionNode might be invalid on
     // AfterEdit.
     // Since this will be used as start position of spellchecker, we should
     // use root instead.
     mCachedSelectionNode = mTextEditor->GetRoot();
     mCachedSelectionOffset = 0;
   } else {
@@ -232,27 +231,28 @@ TextEditRules::BeforeEdit(EditAction aAc
 
   return NS_OK;
 }
 
 nsresult
 TextEditRules::AfterEdit(EditAction aAction,
                          nsIEditor::EDirection aDirection)
 {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
   AutoLockRulesSniffing lockIt(this);
 
   MOZ_ASSERT(mActionNesting>0, "bad action nesting!");
   if (!--mActionNesting) {
-    if (NS_WARN_IF(!mTextEditor)) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
     Selection* selection = mTextEditor->GetSelection();
     if (NS_WARN_IF(!selection)) {
       return NS_ERROR_FAILURE;
     }
 
     AutoSafeEditorData setData(*this, *mTextEditor, *selection);
 
     nsresult rv =
@@ -295,18 +295,18 @@ nsresult
 TextEditRules::WillDoAction(Selection* aSelection,
                             RulesInfo* aInfo,
                             bool* aCancel,
                             bool* aHandled)
 {
   if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
     return NS_ERROR_INVALID_ARG;
   }
-  if (NS_WARN_IF(!mTextEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
   }
 
   MOZ_ASSERT(aCancel);
   MOZ_ASSERT(aHandled);
 
   *aCancel = false;
   *aHandled = false;
 
@@ -353,18 +353,18 @@ TextEditRules::WillDoAction(Selection* a
 nsresult
 TextEditRules::DidDoAction(Selection* aSelection,
                            RulesInfo* aInfo,
                            nsresult aResult)
 {
   if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
     return NS_ERROR_INVALID_ARG;
   }
-  if (NS_WARN_IF(!mTextEditor)) {
-    return NS_ERROR_NOT_AVAILABLE;
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
   }
 
   AutoSafeEditorData setData(*this, *mTextEditor, *aSelection);
 
   // don't let any txns in here move the selection around behind our back.
   // Note that this won't prevent explicit selection setting from working.
   AutoTransactionsConserveSelection dontChangeMySelection(&TextEditorRef());
 
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -329,16 +329,27 @@ protected:
     MOZ_ASSERT(mData);
     return mData->TextEditorRef();
   }
   Selection& SelectionRef() const
   {
     MOZ_ASSERT(mData);
     return mData->SelectionRef();
   }
+  bool CanHandleEditAction() const
+  {
+    if (!mTextEditor) {
+      return false;
+    }
+    if (mTextEditor->Destroyed()) {
+      return false;
+    }
+    MOZ_ASSERT(mTextEditor->IsInitialized());
+    return true;
+  }
 
 #ifdef DEBUG
   bool IsEditorDataAvailable() const { return !!mData; }
 #endif // #ifdef DEBUG
 
   // A buffer we use to store the real value of password editors.
   nsString mPasswordText;
   // A buffer we use to track the IME composition string.
--- a/xpcom/base/ErrorList.py
+++ b/xpcom/base/ErrorList.py
@@ -697,16 +697,18 @@ with modules["IMGLIB"]:
     errors["NS_IMAGELIB_ERROR_NO_ENCODER"] = FAILURE(9)
 
 
 
 # =======================================================================
 # 17: NS_ERROR_MODULE_EDITOR
 # =======================================================================
 with modules["EDITOR"]:
+    errors["NS_ERROR_EDITOR_DESTROYED"] = FAILURE(1)
+
     errors["NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND"] = SUCCESS(1)
     errors["NS_SUCCESS_EDITOR_FOUND_TARGET"] = SUCCESS(2)
 
 
 
 # =======================================================================
 # 18: NS_ERROR_MODULE_XPCONNECT
 # =======================================================================