Bug 1460509 - part 81: Make HTMLEditRules::WillDeleteSelection() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the editor r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 18 May 2018 00:43:52 +0900
changeset 798799 94bb38460b6c811a94e5e4228fd28bb66e3e114c
parent 798798 bdd5165b5b18b8c6cb73a5b62bf74472e462a58c
child 798800 6c46689d352d82ceeb6ce3f08a2685d8dab7992a
push id110840
push usermasayuki@d-toybox.com
push dateWed, 23 May 2018 13:41:58 +0000
reviewersm_kato
bugs1460509
milestone62.0a1
Bug 1460509 - part 81: Make HTMLEditRules::WillDeleteSelection() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the editor r?m_kato MozReview-Commit-ID: IPN1GMQYGeY
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -2461,16 +2461,19 @@ HTMLEditRules::WillDeleteSelection(nsIEd
   }
 
   // First check for table selection mode.  If so, hand off to table editor.
   RefPtr<Element> cell;
   nsresult rv =
     HTMLEditorRef().GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
   if (NS_SUCCEEDED(rv) && cell) {
     rv = HTMLEditorRef().DeleteTableCellContents();
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     *aHandled = true;
     return rv;
   }
   cell = nullptr;
 
   // origCollapsed is used later to determine whether we should join blocks. We
   // don't really care about bCollapsed because it will be modified by
   // ExtendSelectionForDelete later. TryToJoinBlocksWithTransaction() should
@@ -2566,21 +2569,27 @@ HTMLEditRules::WillDeleteSelection(nsIEd
       return rv;
     }
 
     if (wsType == WSType::normalWS) {
       // We found some visible ws to delete.  Let ws code handle it.
       *aHandled = true;
       if (aAction == nsIEditor::eNext) {
         rv = wsObj.DeleteWSForward();
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       } else {
         rv = wsObj.DeleteWSBackward();
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       rv = InsertBRIfNeeded();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
@@ -2618,23 +2627,29 @@ HTMLEditRules::WillDeleteSelection(nsIEd
                      "selection end not in visNode");
 
         so = range->StartOffset();
         eo = range->EndOffset();
       }
       rv = WSRunObject::PrepareToDeleteRange(&HTMLEditorRef(),
                                              address_of(visNode),
                                              &so, address_of(visNode), &eo);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       *aHandled = true;
       rv = HTMLEditorRef().DeleteTextWithTransaction(nodeAsText,
                                                      std::min(so, eo),
                                                      DeprecatedAbs(eo - so));
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       // XXX When Backspace key is pressed, Chromium removes following empty
       //     text nodes when removing the last character of the non-empty text
       //     node.  However, Edge never removes empty text nodes even if
       //     selection is in the following empty text node(s).  For now, we
@@ -2666,16 +2681,19 @@ HTMLEditRules::WillDeleteSelection(nsIEd
     }
 
     if (wsType == WSType::special || wsType == WSType::br ||
         visNode->IsHTMLElement(nsGkAtoms::hr)) {
       // Short circuit for invisible breaks.  delete them and recurse.
       if (visNode->IsHTMLElement(nsGkAtoms::br) &&
           !HTMLEditorRef().IsVisibleBRElement(visNode)) {
         rv = HTMLEditorRef().DeleteNodeWithTransaction(*visNode);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         rv = WillDeleteSelection(aAction, aStripWrappers, aCancel, aHandled);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         return NS_OK;
@@ -2719,16 +2737,19 @@ HTMLEditRules::WillDeleteSelection(nsIEd
 
         if (moveOnly) {
           // Go to the position after the <hr>, but to the end of the <hr> line
           // by setting the interline position to left.
           ++selOffset;
           IgnoredErrorResult ignoredError;
           SelectionRef().Collapse(RawRangeBoundary(selNode, selOffset),
                                   ignoredError);
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            return NS_ERROR_EDITOR_DESTROYED;
+          }
           NS_WARNING_ASSERTION(!ignoredError.Failed(),
             "Failed to collapse selection at after the <hr>");
           (ErrorResult&)ignoredError = NS_OK;
           SelectionRef().SetInterlinePosition(false, ignoredError);
           NS_WARNING_ASSERTION(!ignoredError.Failed(),
             "Failed to unset interline position");
           mDidExplicitlySetInterline = true;
           *aHandled = true;
@@ -2747,44 +2768,56 @@ HTMLEditRules::WillDeleteSelection(nsIEd
           if (otherWSType == WSType::br) {
             // Delete the <br>
             if (NS_WARN_IF(!otherNode->IsContent())) {
               return NS_ERROR_FAILURE;
             }
             nsIContent* otherContent = otherNode->AsContent();
             rv = WSRunObject::PrepareToDeleteNode(&HTMLEditorRef(),
                                                   otherContent);
+            if (NS_WARN_IF(!CanHandleEditAction())) {
+              return NS_ERROR_EDITOR_DESTROYED;
+            }
             if (NS_WARN_IF(NS_FAILED(rv))) {
               return rv;
             }
             rv = HTMLEditorRef().DeleteNodeWithTransaction(*otherContent);
+            if (NS_WARN_IF(!CanHandleEditAction())) {
+              return NS_ERROR_EDITOR_DESTROYED;
+            }
             if (NS_WARN_IF(NS_FAILED(rv))) {
               return rv;
             }
           }
 
           return NS_OK;
         }
         // Else continue with normal delete code
       }
 
       if (NS_WARN_IF(!visNode->IsContent())) {
         return NS_ERROR_FAILURE;
       }
       // Found break or image, or hr.
       rv = WSRunObject::PrepareToDeleteNode(&HTMLEditorRef(),
                                             visNode->AsContent());
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       // Remember sibling to visnode, if any
       nsCOMPtr<nsIContent> sibling =
         HTMLEditorRef().GetPriorHTMLSibling(visNode);
       // Delete the node, and join like nodes if appropriate
       rv = HTMLEditorRef().DeleteNodeWithTransaction(*visNode);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       // We did something, so let's say so.
       *aHandled = true;
       // Is there a prior node and are they siblings?
       nsCOMPtr<nsINode> stepbrother;
       if (sibling) {
@@ -2801,16 +2834,20 @@ HTMLEditRules::WillDeleteSelection(nsIEd
           return rv;
         }
         if (NS_WARN_IF(!pt.IsSet())) {
           return NS_ERROR_FAILURE;
         }
         // Fix up selection
         ErrorResult error;
         SelectionRef().Collapse(pt, error);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          error.SuppressException();
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(error.Failed())) {
           return error.StealNSResult();
         }
       }
       rv = InsertBRIfNeeded();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
@@ -2854,16 +2891,19 @@ HTMLEditRules::WillDeleteSelection(nsIEd
       } else {
         leafNode = HTMLEditorRef().GetFirstEditableLeaf(*visNode);
         leftNode = startNode;
         rightNode = leafNode;
       }
 
       if (otherNode->IsHTMLElement(nsGkAtoms::br)) {
         rv = HTMLEditorRef().DeleteNodeWithTransaction(*otherNode);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         // XXX Only in this case, setting "handled" to true only when it
         //     succeeds?
         *aHandled = true;
         bDeletedBR = true;
       }
@@ -2880,16 +2920,19 @@ HTMLEditRules::WillDeleteSelection(nsIEd
           return NS_ERROR_FAILURE;
         }
         EditorDOMPoint newSel = GetGoodSelPointForNode(*leafNode, aAction);
         if (NS_WARN_IF(!newSel.IsSet())) {
           return NS_ERROR_FAILURE;
         }
         IgnoredErrorResult error;
         SelectionRef().Collapse(newSel, error);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         NS_WARNING_ASSERTION(!error.Failed(),
           "Failed to collapse selection at edge of the block");
         return NS_OK;
       }
 
       // Else we are joining content to block
 
       nsCOMPtr<nsINode> selPointNode = startNode;
@@ -2916,27 +2959,33 @@ HTMLEditRules::WillDeleteSelection(nsIEd
       // If TryToJoinBlocksWithTransaction() didn't handle it  and it's not
       // canceled, user may want to modify the start leaf node or the last leaf
       // node of the block.
       if (!*aHandled && !*aCancel && leafNode != startNode) {
         int32_t offset =
           aAction == nsIEditor::ePrevious ?
             static_cast<int32_t>(leafNode->Length()) : 0;
         rv = SelectionRef().Collapse(leafNode, offset);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
           "Failed to collapse selection at the leaf node");
         rv = WillDeleteSelection(aAction, aStripWrappers, aCancel, aHandled);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         return NS_OK;
       }
 
       // Otherwise, we must have deleted the selection as user expected.
       rv = SelectionRef().Collapse(selPointNode, selPointOffset);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
         "Failed to selection at deleted point");
       return NS_OK;
     }
 
     if (wsType == WSType::thisBlock) {
       // At edge of our block.  Look beside it and see if we can join to an
       // adjacent block
@@ -2987,16 +3036,19 @@ HTMLEditRules::WillDeleteSelection(nsIEd
         // anymore in this case.
         *aHandled = true;
         *aCancel |= ret.Canceled();
         if (NS_WARN_IF(ret.Failed())) {
           return ret.Rv();
         }
       }
       rv = SelectionRef().Collapse(selPointNode, selPointOffset);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to collapse selection");
       return NS_OK;
     }
   }
 
 
   // Else we have a non-collapsed selection.  First adjust the selection.
   rv = ExpandSelectionForDeletion();
@@ -3025,16 +3077,19 @@ HTMLEditRules::WillDeleteSelection(nsIEd
 
   // Figure out if the endpoints are in nodes that can be merged.  Adjust
   // surrounding whitespace in preparation to delete selection.
   if (!IsPlaintextEditor()) {
     AutoTransactionsConserveSelection dontChangeMySelection(&HTMLEditorRef());
     rv = WSRunObject::PrepareToDeleteRange(&HTMLEditorRef(),
                                            address_of(startNode), &startOffset,
                                            address_of(endNode), &endOffset);
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   {
     // Track location of where we are deleting
     AutoTrackDOMPoint startTracker(HTMLEditorRef().mRangeUpdater,
@@ -3042,16 +3097,19 @@ HTMLEditRules::WillDeleteSelection(nsIEd
     AutoTrackDOMPoint endTracker(HTMLEditorRef().mRangeUpdater,
                                  address_of(endNode), &endOffset);
     // We are handling all ranged deletions directly now.
     *aHandled = true;
 
     if (endNode == startNode) {
       rv = HTMLEditorRef().DeleteSelectionWithTransaction(aAction,
                                                           aStripWrappers);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else {
       // Figure out mailcite ancestors
       nsCOMPtr<Element> startCiteNode = GetTopEnclosingMailCite(*startNode);
       nsCOMPtr<Element> endCiteNode = GetTopEnclosingMailCite(*endNode);
 
@@ -3066,16 +3124,19 @@ HTMLEditRules::WillDeleteSelection(nsIEd
 
       // Figure out block parents
       nsCOMPtr<Element> leftParent = HTMLEditor::GetBlock(*startNode);
       nsCOMPtr<Element> rightParent = HTMLEditor::GetBlock(*endNode);
 
       // Are endpoint block parents the same?  Use default deletion
       if (leftParent && leftParent == rightParent) {
         HTMLEditorRef().DeleteSelectionWithTransaction(aAction, aStripWrappers);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
       } else {
         // Deleting across blocks.  Are the blocks of same type?
         if (NS_WARN_IF(!leftParent) || NS_WARN_IF(!rightParent)) {
           return NS_ERROR_FAILURE;
         }
 
         // Are the blocks siblings?
         nsCOMPtr<nsINode> leftBlockParent = leftParent->GetParentNode();
@@ -3086,29 +3147,39 @@ HTMLEditRules::WillDeleteSelection(nsIEd
             HTMLEditorRef().AreNodesSameType(leftParent, rightParent) &&
             // XXX What's special about these three types of block?
             (leftParent->IsHTMLElement(nsGkAtoms::p) ||
              HTMLEditUtils::IsListItem(leftParent) ||
              HTMLEditUtils::IsHeader(*leftParent))) {
           // First delete the selection
           rv = HTMLEditorRef().DeleteSelectionWithTransaction(aAction,
                                                               aStripWrappers);
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            return NS_ERROR_EDITOR_DESTROYED;
+          }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
           // Join blocks
           EditorDOMPoint pt =
             HTMLEditorRef().JoinNodesDeepWithTransaction(*leftParent,
                                                          *rightParent);
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            return NS_ERROR_EDITOR_DESTROYED;
+          }
           if (NS_WARN_IF(!pt.IsSet())) {
             return NS_ERROR_FAILURE;
           }
           // Fix up selection
           ErrorResult error;
           SelectionRef().Collapse(pt, error);
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            error.SuppressException();
+            return NS_ERROR_EDITOR_DESTROYED;
+          }
           if (NS_WARN_IF(error.Failed())) {
             return error.StealNSResult();
           }
           return NS_OK;
         }
 
         // Else blocks not same type, or not siblings.  Delete everything
         // except table elements.
@@ -3162,26 +3233,32 @@ HTMLEditRules::WillDeleteSelection(nsIEd
         if (startNode->GetAsText() &&
             startNode->Length() > static_cast<uint32_t>(startOffset)) {
           // Delete to last character
           OwningNonNull<CharacterData> dataNode =
             *static_cast<CharacterData*>(startNode.get());
           rv = HTMLEditorRef().DeleteTextWithTransaction(
                                  dataNode, startOffset,
                                  startNode->Length() - startOffset);
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            return NS_ERROR_EDITOR_DESTROYED;
+          }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
         }
         if (endNode->GetAsText() && endOffset) {
           // Delete to first character
           OwningNonNull<CharacterData> dataNode =
             *static_cast<CharacterData*>(endNode.get());
           rv = HTMLEditorRef().DeleteTextWithTransaction(dataNode, 0,
                                                          endOffset);
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            return NS_ERROR_EDITOR_DESTROYED;
+          }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
         }
 
         if (join) {
           EditActionResult ret =
             TryToJoinBlocksWithTransaction(*leftParent, *rightParent);
@@ -3221,21 +3298,27 @@ HTMLEditRules::WillDeleteSelection(nsIEd
   // should be collapsed to the beginning of the selection. But if we're not
   // joining then the selection should collapse to the beginning of the
   // selection if we'redeleting forward, because the end of the selection will
   // still be in the next block. And same thing for deleting backwards
   // (selection should collapse to the end, because the beginning will still be
   // in the first block). See Bug 507936
   if (aAction == (join ? nsIEditor::eNext : nsIEditor::ePrevious)) {
     rv = SelectionRef().Collapse(endNode, endOffset);
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   } else {
     rv = SelectionRef().Collapse(startNode, startOffset);
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   return NS_OK;
 }
 
 nsresult
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -219,19 +219,29 @@ protected:
    * necessary, this inserts <br> node to new left nodes or existing right
    * nodes.
    *
    * @param aHandled            Returns true if succeeded to split mail-cite
    *                            elements.
    */
   MOZ_MUST_USE nsresult SplitMailCites(bool* aHandled);
 
-  nsresult WillDeleteSelection(nsIEditor::EDirection aAction,
-                               nsIEditor::EStripWrappers aStripWrappers,
-                               bool* aCancel, bool* aHandled);
+  /**
+   * Called before deleting selected contents.  This method actually removes
+   * selected contents.
+   *
+   * @param aAction             Direction of the deletion.
+   * @param aStripWrappers      Must be eStrip or eNoStrip.
+   * @param aCancel             Returns true if the operation is canceled.
+   * @param aHandled            Returns true if the edit action is handled.
+   */
+  MOZ_MUST_USE nsresult
+  WillDeleteSelection(nsIEditor::EDirection aAction,
+                      nsIEditor::EStripWrappers aStripWrappers,
+                      bool* aCancel, bool* aHandled);
 
   /**
    * Called after deleting selected content.
    * This method removes unnecessary empty nodes and/or inserts <br> if
    * necessary.
    */
   MOZ_MUST_USE nsresult DidDeleteSelection();