Bug 1451672 - part 7: Rename EditorBase::DeleteNode() to EditorBase::DeleteNodeWithTransaction() r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 10 Apr 2018 16:23:54 +0900
changeset 786090 b2e1130f2189f3a6ff33b4bc26746d1ca729e9b6
parent 786089 afb58e92d761391baa96c4c985261c16a6eabff0
child 786091 0bc2bc3bda9841271322ff01cc24b4e5c3073396
push id107393
push usermasayuki@d-toybox.com
push dateSat, 21 Apr 2018 04:40:12 +0000
reviewersm_kato
bugs1451672
milestone61.0a1
Bug 1451672 - part 7: Rename EditorBase::DeleteNode() to EditorBase::DeleteNodeWithTransaction() r?m_kato MozReview-Commit-ID: AQVVTjfXJv
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorBase.h
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
editor/libeditor/HTMLEditorDataTransfer.cpp
editor/libeditor/HTMLStyleEditor.cpp
editor/libeditor/HTMLTableEditor.cpp
editor/libeditor/TextEditRules.cpp
editor/libeditor/WSRunObject.cpp
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -1604,49 +1604,49 @@ EditorBase::JoinNodesWithTransaction(nsI
 
   return rv;
 }
 
 NS_IMETHODIMP
 EditorBase::DeleteNode(nsIDOMNode* aNode)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
-  NS_ENSURE_STATE(node);
-  return DeleteNode(node);
+  if (NS_WARN_IF(!node)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  return DeleteNodeWithTransaction(*node);
 }
 
 nsresult
-EditorBase::DeleteNode(nsINode* aNode)
-{
-  if (NS_WARN_IF(!aNode)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
+EditorBase::DeleteNodeWithTransaction(nsINode& aNode)
+{
   AutoRules beginRulesSniffing(this, EditAction::createNode,
                                nsIEditor::ePrevious);
 
   if (mRules && mRules->AsHTMLEditRules()) {
     RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
-    htmlEditRules->WillDeleteNode(aNode);
-  }
-
+    htmlEditRules->WillDeleteNode(&aNode);
+  }
+
+  // FYI: DeleteNodeTransaction grabs aNode while it's alive.  So, it's safe
+  //      to refer aNode even after calling DoTransaction().
   RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
-    DeleteNodeTransaction::MaybeCreate(*this, *aNode);
+    DeleteNodeTransaction::MaybeCreate(*this, aNode);
   nsresult rv = deleteNodeTransaction ? DoTransaction(deleteNodeTransaction) :
                                         NS_ERROR_FAILURE;
 
   if (mTextServicesDocument && NS_SUCCEEDED(rv)) {
     RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
-    textServicesDocument->DidDeleteNode(aNode);
+    textServicesDocument->DidDeleteNode(&aNode);
   }
 
   if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
-      listener->DidDeleteNode(aNode->AsDOMNode(), rv);
+      listener->DidDeleteNode(aNode.AsDOMNode(), rv);
     }
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 /**
@@ -1690,18 +1690,20 @@ EditorBase::ReplaceContainer(Element* aO
   // to initialize mRangeUpdater.
   AutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, aOldContainer,
                                                newContainer);
   {
     AutoTransactionsConserveSelection conserveSelection(this);
     // Move all children from the old container to the new container.
     while (aOldContainer->HasChildren()) {
       nsCOMPtr<nsIContent> child = aOldContainer->GetFirstChild();
-
-      nsresult rv = DeleteNode(child);
+      if (NS_WARN_IF(!child)) {
+        return nullptr;
+      }
+      nsresult rv = DeleteNodeWithTransaction(*child);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return nullptr;
       }
 
       rv = InsertNodeWithTransaction(*child,
                                      EditorRawDOMPoint(newContainer,
                                                        newContainer->Length()));
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1714,17 +1716,17 @@ EditorBase::ReplaceContainer(Element* aO
   NS_WARNING_ASSERTION(atOldContainer.IsSetAndValid(),
     "The old container might be moved by mutation observer");
   nsresult rv = InsertNodeWithTransaction(*newContainer, atOldContainer);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   // Delete old container.
-  rv = DeleteNode(aOldContainer);
+  rv = DeleteNodeWithTransaction(*aOldContainer);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   return newContainer.forget();
 }
 
 /**
@@ -1745,34 +1747,41 @@ EditorBase::RemoveContainer(nsIContent* 
   AutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode,
                                          pointToInsertChildren.GetContainer(),
                                          pointToInsertChildren.Offset(),
                                          aNode->GetChildCount());
 
   // Move all children from aNode to its parent.
   while (aNode->HasChildren()) {
     nsCOMPtr<nsIContent> child = aNode->GetLastChild();
-    nsresult rv = DeleteNode(child);
+    if (NS_WARN_IF(!child)) {
+      return NS_ERROR_FAILURE;
+    }
+    nsresult rv = DeleteNodeWithTransaction(*child);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // Insert the last child before the previous last child.  So, we need to
     // use offset here because previous child might have been moved to
     // container.
     rv = InsertNodeWithTransaction(*child,
                                    EditorRawDOMPoint(
                                      pointToInsertChildren.GetContainer(),
                                      pointToInsertChildren.Offset()));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  return DeleteNode(aNode);
+  nsresult rv = DeleteNodeWithTransaction(*aNode);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
 }
 
 /**
  * InsertContainerAbove() inserts a new parent for inNode, which is contructed
  * to be of type aNodeType.  outNode becomes a child of inNode's earlier
  * parent.  Caller's responsibility to make sure inNode's can be child of
  * outNode, and outNode can be child of old parent.
  */
@@ -1809,17 +1818,17 @@ EditorBase::InsertContainerAbove(nsICont
       return nullptr;
     }
   }
 
   // Notify our internal selection state listener
   AutoInsertContainerSelNotify selNotify(mRangeUpdater);
 
   // Put aNode in the new container, first.
-  nsresult rv = DeleteNode(aNode);
+  nsresult rv = DeleteNodeWithTransaction(*aNode);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   {
     AutoTransactionsConserveSelection conserveSelection(this);
     rv = InsertNodeWithTransaction(*aNode, EditorRawDOMPoint(newContainer, 0));
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1872,24 +1881,24 @@ EditorBase::MoveNode(nsIContent* aNode,
 
   // Need to adjust aOffset if we're moving aNode later in its current parent
   if (aParent == oldParent && oldOffset < aOffset) {
     // When we delete aNode, it will make the offsets after it off by one
     aOffset--;
   }
 
   // Hold a reference so aNode doesn't go away when we remove it (bug 772282)
-  nsCOMPtr<nsINode> kungFuDeathGrip = aNode;
-
-  nsresult rv = DeleteNode(aNode);
+  nsCOMPtr<nsIContent> nodeToBeMoved(aNode);
+  nsresult rv = DeleteNodeWithTransaction(*nodeToBeMoved);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = InsertNodeWithTransaction(*aNode, EditorRawDOMPoint(aParent, aOffset));
+  rv = InsertNodeWithTransaction(*nodeToBeMoved,
+                                 EditorRawDOMPoint(aParent, aOffset));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 void
 EditorBase::MoveAllChildren(nsINode& aContainer,
@@ -2870,17 +2879,17 @@ EditorBase::InsertTextIntoTextNodeImpl(c
   // longer in the document.  This does not break undo/redo, because all these
   // txns are wrapped in a parent PlaceHolder txn, and placeholder txns are
   // already savvy to having multiple ime txns inside them.
 
   // Delete empty IME text node if there is one
   if (isIMETransaction && mComposition) {
     Text* textNode = mComposition->GetContainerTextNode();
     if (textNode && !textNode->Length()) {
-      DeleteNode(textNode);
+      DeleteNodeWithTransaction(*textNode);
       mComposition->OnTextNodeRemoved();
       static_cast<CompositionTransaction*>(transaction.get())->MarkFixed();
     }
   }
 
   return rv;
 }
 
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -330,19 +330,21 @@ public:
                        Text& aTextNode);
 
   NS_IMETHOD DeleteSelectionImpl(EDirection aAction,
                                  EStripWrappers aStripWrappers);
 
   already_AddRefed<Element> DeleteSelectionAndCreateElement(nsAtom& aTag);
 
   /**
-   * Helper routines for node/parent manipulations.
+   * DeleteNodeWithTransaction() removes aNode from the DOM tree.
+   *
+   * @param aNode       The node which will be removed form the DOM tree.
    */
-  nsresult DeleteNode(nsINode* aNode);
+  nsresult DeleteNodeWithTransaction(nsINode& aNode);
 
   /**
    * InsertNodeWithTransaction() inserts aContentToInsert before the child
    * specified by aPointToInsert.
    *
    * @param aContentToInsert    The node to be inserted.
    * @param aPointToInsert      The insertion point of aContentToInsert.
    *                            If this refers end of the container, the
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1569,17 +1569,20 @@ HTMLEditRules::WillLoadHTML(Selection* a
 
   // Delete mBogusNode if it exists. If we really need one,
   // it will be added during post-processing in AfterEditInner().
 
   if (mBogusNode) {
     if (NS_WARN_IF(!mHTMLEditor)) {
       return NS_ERROR_UNEXPECTED;
     }
-    mHTMLEditor->DeleteNode(mBogusNode);
+    RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+    DebugOnly<nsresult> rv = htmlEditor->DeleteNodeWithTransaction(*mBogusNode);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+      "Failed to remove the bogus node");
     mBogusNode = nullptr;
   }
 
   return NS_OK;
 }
 
 bool
 HTMLEditRules::CanContainParagraph(Element& aElement) const
@@ -2127,30 +2130,30 @@ HTMLEditRules::SplitMailCites(Selection*
   if (previousNodeOfSplitPoint) {
     nsresult rv =
       htmlEditor->IsEmptyNode(previousNodeOfSplitPoint, &bEmptyCite,
                               true, false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     if (bEmptyCite) {
-      rv = htmlEditor->DeleteNode(previousNodeOfSplitPoint);
+      rv = htmlEditor->DeleteNodeWithTransaction(*previousNodeOfSplitPoint);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
 
   if (citeNode) {
     nsresult rv = htmlEditor->IsEmptyNode(citeNode, &bEmptyCite, true, false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     if (bEmptyCite) {
-      rv = htmlEditor->DeleteNode(citeNode);
+      rv = htmlEditor->DeleteNodeWithTransaction(*citeNode);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
 
   *aHandled = true;
   return NS_OK;
@@ -2362,19 +2365,24 @@ HTMLEditRules::WillDeleteSelection(Selec
       return NS_OK;
     }
 
     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) &&
           (!mHTMLEditor || !mHTMLEditor->IsVisibleBRElement(visNode))) {
-        NS_ENSURE_STATE(mHTMLEditor);
-        rv = mHTMLEditor->DeleteNode(visNode);
-        NS_ENSURE_SUCCESS(rv, rv);
+        if (NS_WARN_IF(!mHTMLEditor)) {
+          return NS_ERROR_FAILURE;
+        }
+        RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+        rv = htmlEditor->DeleteNodeWithTransaction(*visNode);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
         return WillDeleteSelection(aSelection, aAction, aStripWrappers,
                                    aCancel, aHandled);
       }
 
       // Special handling for backspace when positioned after <hr>
       if (aAction == nsIEditor::ePrevious &&
           visNode->IsHTMLElement(nsGkAtoms::hr)) {
         // Only if the caret is positioned at the end-of-hr-line position, we
@@ -2425,50 +2433,60 @@ HTMLEditRules::WillDeleteSelection(Selec
           int32_t otherOffset;
 
           wsObj.NextVisibleNode(EditorRawDOMPoint(startNode, startOffset),
                                 address_of(otherNode),
                                 &otherOffset, &otherWSType);
 
           if (otherWSType == WSType::br) {
             // Delete the <br>
-
-            NS_ENSURE_STATE(mHTMLEditor);
-            nsCOMPtr<nsIContent> otherContent(do_QueryInterface(otherNode));
-            rv = WSRunObject::PrepareToDeleteNode(mHTMLEditor, otherContent);
-            NS_ENSURE_SUCCESS(rv, rv);
-            NS_ENSURE_STATE(mHTMLEditor);
-            rv = mHTMLEditor->DeleteNode(otherNode);
-            NS_ENSURE_SUCCESS(rv, rv);
+            if (NS_WARN_IF(!mHTMLEditor) ||
+                NS_WARN_IF(!otherNode->IsContent())) {
+              return NS_ERROR_FAILURE;
+            }
+            nsIContent* otherContent = otherNode->AsContent();
+            RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+            rv = WSRunObject::PrepareToDeleteNode(htmlEditor, otherContent);
+            if (NS_WARN_IF(NS_FAILED(rv))) {
+              return rv;
+            }
+            rv = htmlEditor->DeleteNodeWithTransaction(*otherContent);
+            if (NS_WARN_IF(NS_FAILED(rv))) {
+              return rv;
+            }
           }
 
           return NS_OK;
         }
         // Else continue with normal delete code
       }
 
+      if (NS_WARN_IF(!mHTMLEditor) ||
+          NS_WARN_IF(!visNode->IsContent())) {
+        return NS_ERROR_FAILURE;
+      }
+      RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
       // Found break or image, or hr.
-      NS_ENSURE_STATE(mHTMLEditor);
-      NS_ENSURE_STATE(visNode->IsContent());
-      rv = WSRunObject::PrepareToDeleteNode(mHTMLEditor, visNode->AsContent());
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = WSRunObject::PrepareToDeleteNode(htmlEditor, visNode->AsContent());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       // Remember sibling to visnode, if any
-      NS_ENSURE_STATE(mHTMLEditor);
-      nsCOMPtr<nsIContent> sibling = mHTMLEditor->GetPriorHTMLSibling(visNode);
+      nsCOMPtr<nsIContent> sibling = htmlEditor->GetPriorHTMLSibling(visNode);
       // Delete the node, and join like nodes if appropriate
-      NS_ENSURE_STATE(mHTMLEditor);
-      rv = mHTMLEditor->DeleteNode(visNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->DeleteNodeWithTransaction(*visNode);
+      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) {
-        NS_ENSURE_STATE(mHTMLEditor);
-        stepbrother = mHTMLEditor->GetNextHTMLSibling(sibling);
+        stepbrother = htmlEditor->GetNextHTMLSibling(sibling);
       }
       // Are they both text nodes?  If so, join them!
       if (startNode == stepbrother && startNode->GetAsText() &&
           sibling->GetAsText()) {
         EditorDOMPoint pt =
           JoinNearestEditableNodesWithTransaction(*sibling,
                                                   *startNode->AsContent());
         if (NS_WARN_IF(!pt.IsSet())) {
@@ -2524,19 +2542,24 @@ HTMLEditRules::WillDeleteSelection(Selec
       } else {
         NS_ENSURE_STATE(mHTMLEditor);
         leafNode = mHTMLEditor->GetFirstEditableLeaf(*visNode);
         leftNode = startNode;
         rightNode = leafNode;
       }
 
       if (otherNode->IsHTMLElement(nsGkAtoms::br)) {
-        NS_ENSURE_STATE(mHTMLEditor);
-        rv = mHTMLEditor->DeleteNode(otherNode);
-        NS_ENSURE_SUCCESS(rv, rv);
+        if (NS_WARN_IF(!mHTMLEditor)) {
+          return NS_ERROR_FAILURE;
+        }
+        RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+        rv = htmlEditor->DeleteNodeWithTransaction(*otherNode);
+        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;
       }
 
       // Don't cross table boundaries
       if (leftNode && rightNode &&
@@ -2880,17 +2903,18 @@ HTMLEditRules::DeleteNodeIfCollapsedText
   }
 
   if (NS_WARN_IF(!mHTMLEditor)) {
     return;
   }
 
   if (!mHTMLEditor->IsVisibleTextNode(*text)) {
     RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
-    htmlEditor->DeleteNode(&aNode);
+    DebugOnly<nsresult> rv = htmlEditor->DeleteNodeWithTransaction(aNode);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove aNode");
   }
 }
 
 
 /**
  * InsertBRIfNeeded() determines if a br is needed for current selection to not
  * be spastic.  If so, it inserts one.  Callers responsibility to only call
  * with collapsed selection.
@@ -3104,17 +3128,17 @@ HTMLEditRules::TryToJoinBlocksWithTransa
         if (NS_WARN_IF(!atRightBlockChild.GetContainer()->GetParentElement())) {
           return EditActionIgnored(NS_ERROR_UNEXPECTED);
         }
         rightBlock = atRightBlockChild.GetContainer()->GetParentElement();
       }
     }
 
     // Do br adjustment.
-    nsCOMPtr<Element> brNode =
+    RefPtr<Element> brNode =
       CheckForInvisibleBR(*leftBlock, BRLocation::blockEnd);
     EditActionResult ret(NS_OK);
     if (NS_WARN_IF(mergeLists)) {
       // Since 2002, here was the following comment:
       // > The idea here is to take all children in rightList that are past
       // > offset, and pull them into leftlist.
       // However, this has never been performed because we are here only when
       // neither left list nor right list is a descendant of the other but
@@ -3136,17 +3160,18 @@ HTMLEditRules::TryToJoinBlocksWithTransa
                   -1, atRightBlockChild.Offset());
       if (retMoveBlock.Handled()) {
         ret.MarkAsHandled();
       }
       // Now, all children of rightBlock were moved to leftBlock.  So,
       // atRightBlockChild is now invalid.
       atRightBlockChild.Clear();
     }
-    if (brNode && NS_SUCCEEDED(htmlEditor->DeleteNode(brNode))) {
+    if (brNode &&
+        NS_SUCCEEDED(htmlEditor->DeleteNodeWithTransaction(*brNode))) {
       ret.MarkAsHandled();
     }
     return ret;
   }
 
   MOZ_DIAGNOSTIC_ASSERT(!atRightBlockChild.IsSet());
 
   // Offset below is where you find yourself in leftBlock when you traverse
@@ -3180,17 +3205,17 @@ HTMLEditRules::TryToJoinBlocksWithTransa
       } else {
         if (NS_WARN_IF(!leftBlockChild.GetContainer()->GetParentElement())) {
           return EditActionIgnored(NS_ERROR_UNEXPECTED);
         }
         leftBlock = leftBlockChild.GetContainer()->GetParentElement();
       }
     }
     // Do br adjustment.
-    nsCOMPtr<Element> brNode =
+    RefPtr<Element> brNode =
       CheckForInvisibleBR(*leftBlock, BRLocation::beforeBlock,
                           leftBlockChild.Offset());
     EditActionResult ret(NS_OK);
     if (mergeLists) {
       // XXX Why do we ignore the result of MoveContents()?
       int32_t offset = leftBlockChild.Offset();
       EditActionResult retMoveContents =
         MoveContents(*rightList, *leftList, &offset);
@@ -3256,17 +3281,18 @@ HTMLEditRules::TryToJoinBlocksWithTransa
       }
 
       ret |= MoveBlock(*previousContent.GetContainerAsElement(), *rightBlock,
                        previousContent.Offset(), 0);
       if (NS_WARN_IF(ret.Failed())) {
         return ret;
       }
     }
-    if (brNode && NS_SUCCEEDED(htmlEditor->DeleteNode(brNode))) {
+    if (brNode &&
+        NS_SUCCEEDED(htmlEditor->DeleteNodeWithTransaction(*brNode))) {
       ret.MarkAsHandled();
     }
     return ret;
   }
 
   MOZ_DIAGNOSTIC_ASSERT(!atRightBlockChild.IsSet());
   MOZ_DIAGNOSTIC_ASSERT(!leftBlockChild.IsSet());
 
@@ -3298,19 +3324,20 @@ HTMLEditRules::TryToJoinBlocksWithTransa
   } else {
     // Nodes are dissimilar types.
     ret |= MoveBlock(*leftBlock, *rightBlock, -1, 0);
     if (NS_WARN_IF(ret.Failed())) {
       return ret;
     }
   }
   if (brNode) {
-    rv = htmlEditor->DeleteNode(brNode);
-    // XXX In other top level if blocks, the result of DeleteNode()
-    //     is ignored.  Why does only this result is respected?
+    rv = htmlEditor->DeleteNodeWithTransaction(*brNode);
+    // XXX In other top level if blocks, the result of
+    //     DeleteNodeWithTransaction() is ignored.  Why does only this result
+    //     is respected?
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return ret.SetResult(rv);
     }
     ret.MarkAsHandled();
   }
   return ret;
 }
 
@@ -3324,30 +3351,36 @@ HTMLEditRules::MoveBlock(Element& aLeftB
   // GetNodesFromPoint is the workhorse that figures out what we wnat to move.
   nsresult rv = GetNodesFromPoint(EditorDOMPoint(&aRightBlock, aRightOffset),
                                   EditAction::makeList, arrayOfNodes,
                                   TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditActionIgnored(rv);
   }
 
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return EditActionIgnored(NS_ERROR_NOT_AVAILABLE);
+  }
+  RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
   EditActionResult ret(NS_OK);
   for (uint32_t i = 0; i < arrayOfNodes.Length(); i++) {
     // get the node to act on
     if (IsBlockNode(arrayOfNodes[i])) {
       // For block nodes, move their contents only, then delete block.
       ret |=
         MoveContents(*arrayOfNodes[i]->AsElement(), aLeftBlock, &aLeftOffset);
       if (NS_WARN_IF(ret.Failed())) {
         return ret;
       }
       if (NS_WARN_IF(!mHTMLEditor)) {
         return ret.SetResult(NS_ERROR_UNEXPECTED);
       }
-      rv = mHTMLEditor->DeleteNode(arrayOfNodes[i]);
+      rv = htmlEditor->DeleteNodeWithTransaction(*arrayOfNodes[i]);
+      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+        "Failed to remove a block node");
       ret.MarkAsHandled();
     } else {
       // Otherwise move the content as is, checking against the DTD.
       ret |=
         MoveNodeSmart(*arrayOfNodes[i]->AsContent(), aLeftBlock, &aLeftOffset);
     }
   }
 
@@ -3391,17 +3424,17 @@ HTMLEditRules::MoveNodeSmart(nsIContent&
   EditActionResult ret(NS_OK);
   if (aNode.IsElement()) {
     ret = MoveContents(*aNode.AsElement(), aDestElement, aInOutDestOffset);
     if (NS_WARN_IF(ret.Failed())) {
       return ret;
     }
   }
 
-  nsresult rv = htmlEditor->DeleteNode(&aNode);
+  nsresult rv = htmlEditor->DeleteNodeWithTransaction(aNode);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return ret.SetResult(rv);
   }
   return ret.MarkAsHandled();
 }
 
 EditActionResult
 HTMLEditRules::MoveContents(Element& aElement,
@@ -3426,18 +3459,25 @@ HTMLEditRules::MoveContents(Element& aEl
 }
 
 
 nsresult
 HTMLEditRules::DeleteNonTableElements(nsINode* aNode)
 {
   MOZ_ASSERT(aNode);
   if (!HTMLEditUtils::IsTableElementButNotTable(aNode)) {
-    NS_ENSURE_STATE(mHTMLEditor);
-    return mHTMLEditor->DeleteNode(aNode);
+    if (NS_WARN_IF(!mHTMLEditor)) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
+    RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+    nsresult rv = htmlEditor->DeleteNodeWithTransaction(*aNode);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
   }
 
   AutoTArray<nsCOMPtr<nsIContent>, 10> childList;
   for (nsIContent* child = aNode->GetFirstChild();
        child; child = child->GetNextSibling()) {
     childList.AppendElement(child);
   }
 
@@ -3464,27 +3504,27 @@ HTMLEditRules::DidDeleteSelection(Select
 
   // find where we are
   EditorDOMPoint atStartOfSelection(EditorBase::GetStartPoint(aSelection));
   if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
     return NS_ERROR_FAILURE;
   }
 
   // find any enclosing mailcite
-  nsCOMPtr<Element> citeNode =
+  RefPtr<Element> citeNode =
     GetTopEnclosingMailCite(*atStartOfSelection.GetContainer());
   if (citeNode) {
     bool isEmpty = true, seenBR = false;
     htmlEditor->IsEmptyNodeImpl(citeNode, &isEmpty, true, true, false,
                                 &seenBR);
     if (isEmpty) {
       EditorDOMPoint atCiteNode(citeNode);
       {
         AutoEditorDOMPointChildInvalidator lockOffset(atCiteNode);
-        nsresult rv = htmlEditor->DeleteNode(citeNode);
+        nsresult rv = htmlEditor->DeleteNodeWithTransaction(*citeNode);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       if (atCiteNode.IsSet() && seenBR) {
         RefPtr<Element> brNode = htmlEditor->CreateBR(atCiteNode);
         if (NS_WARN_IF(!brNode)) {
           return NS_ERROR_FAILURE;
@@ -3567,18 +3607,20 @@ HTMLEditRules::WillMakeList(Selection* a
   }
 
   // if no nodes, we make empty list.  Ditto if the user tried to make a list
   // of some # of breaks.
   if (arrayOfNodes.IsEmpty() || bOnlyBreaks) {
     // if only breaks, delete them
     if (bOnlyBreaks) {
       for (auto& node : arrayOfNodes) {
-        rv = htmlEditor->DeleteNode(node);
-        NS_ENSURE_SUCCESS(rv, rv);
+        rv = htmlEditor->DeleteNodeWithTransaction(*node);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
     }
 
     nsRange* firstRange = aSelection->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
     }
 
@@ -3652,17 +3694,17 @@ HTMLEditRules::WillMakeList(Selection* a
       curList = nullptr;
     }
 
     // If curNode is a break, delete it, and quit remembering prev list item.
     // If an empty inline container, delete it, but still remember the previous
     // item.
     if (htmlEditor->IsEditable(curNode) && (TextEditUtils::IsBreak(curNode) ||
                                             IsEmptyInline(curNode))) {
-      rv = htmlEditor->DeleteNode(curNode);
+      rv = htmlEditor->DeleteNodeWithTransaction(*curNode);
       NS_ENSURE_SUCCESS(rv, rv);
       if (TextEditUtils::IsBreak(curNode)) {
         prevListItem = nullptr;
       }
       continue;
     }
 
     if (HTMLEditUtils::IsList(curNode)) {
@@ -3960,18 +4002,20 @@ HTMLEditRules::MakeBasicBlock(Selection&
 
       // If the first editable node after selection is a br, consume it.
       // Otherwise it gets pushed into a following block after the split,
       // which is visually bad.
       nsCOMPtr<nsIContent> brNode =
         htmlEditor->GetNextEditableHTMLNode(pointToInsertBlock);
       if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
         AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertBlock);
-        rv = htmlEditor->DeleteNode(brNode);
-        NS_ENSURE_SUCCESS(rv, rv);
+        rv = htmlEditor->DeleteNodeWithTransaction(*brNode);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
       // Do the splits!
       SplitNodeResult splitNodeResult =
         htmlEditor->SplitNodeDeepWithTransaction(
                       *curBlock, pointToInsertBlock,
                       SplitAtEdges::eDoNotCreateEmptyContainer);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
@@ -3992,18 +4036,20 @@ HTMLEditRules::MakeBasicBlock(Selection&
       return NS_OK;
     }
 
     // We are making a block.  Consume a br, if needed.
     nsCOMPtr<nsIContent> brNode =
       htmlEditor->GetNextEditableHTMLNodeInBlock(pointToInsertBlock);
     if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
       AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertBlock);
-      rv = htmlEditor->DeleteNode(brNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->DeleteNodeWithTransaction(*brNode);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       // We don't need to act on this node any more
       arrayOfNodes.RemoveElement(brNode);
     }
     // Make sure we can put a block here.
     SplitNodeResult splitNodeResult =
       MaybeSplitAncestorsForInsertWithTransaction(blockType,
                                                   pointToInsertBlock);
     if (NS_WARN_IF(splitNodeResult.Failed())) {
@@ -4015,18 +4061,20 @@ HTMLEditRules::MakeBasicBlock(Selection&
     if (NS_WARN_IF(!block)) {
       return NS_ERROR_FAILURE;
     }
     // Remember our new block for postprocessing
     mNewBlock = block;
     // Delete anything that was in the list of nodes
     while (!arrayOfNodes.IsEmpty()) {
       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
-      rv = htmlEditor->DeleteNode(curNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->DeleteNodeWithTransaction(*curNode);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       arrayOfNodes.RemoveElementAt(0);
     }
     // Put selection in new block
     rv = aSelection.Collapse(block, 0);
     // Don't restore the selection
     selectionRestorer.Abort();
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
@@ -4169,18 +4217,20 @@ HTMLEditRules::WillCSSIndent(Selection* 
       return NS_ERROR_FAILURE;
     }
     // remember our new block for postprocessing
     mNewBlock = theBlock;
     ChangeIndentation(*theBlock, Change::plus);
     // delete anything that was in the list of nodes
     while (!arrayOfNodes.IsEmpty()) {
       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
-      rv = htmlEditor->DeleteNode(curNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->DeleteNodeWithTransaction(*curNode);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       arrayOfNodes.RemoveElementAt(0);
     }
     // put selection in new block
     *aHandled = true;
     EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
     ErrorResult error;
     aSelection->Collapse(atStartOfTheBlock, error);
     // Don't restore the selection
@@ -4376,18 +4426,20 @@ HTMLEditRules::WillHTMLIndent(Selection*
     if (NS_WARN_IF(!theBlock)) {
       return NS_ERROR_FAILURE;
     }
     // remember our new block for postprocessing
     mNewBlock = theBlock;
     // delete anything that was in the list of nodes
     while (!arrayOfNodes.IsEmpty()) {
       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
-      rv = htmlEditor->DeleteNode(curNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->DeleteNodeWithTransaction(*curNode);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       arrayOfNodes.RemoveElementAt(0);
     }
     // put selection in new block
     *aHandled = true;
     EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
     ErrorResult error;
     aSelection->Collapse(atStartOfTheBlock, error);
     // Don't restore the selection
@@ -4754,18 +4806,20 @@ HTMLEditRules::WillOutdent(Selection& aS
               // We have an embedded list, so move it out from under the parent
               // list. Be sure to put it after the parent list because this
               // loop iterates backwards through the parent's list of children.
 
               rv = htmlEditor->MoveNode(child, curParent, offset + 1);
               NS_ENSURE_SUCCESS(rv, rv);
             } else {
               // Delete any non-list items for now
-              rv = htmlEditor->DeleteNode(child);
-              NS_ENSURE_SUCCESS(rv, rv);
+              rv = htmlEditor->DeleteNodeWithTransaction(*child);
+              if (NS_WARN_IF(NS_FAILED(rv))) {
+                return rv;
+              }
             }
             child = curNode->GetLastChild();
           }
           // Delete the now-empty list
           rv = htmlEditor->RemoveBlockContainer(curNode);
           NS_ENSURE_SUCCESS(rv, rv);
         } else if (useCSS) {
           nsCOMPtr<Element> element;
@@ -5180,18 +5234,20 @@ HTMLEditRules::WillAlign(Selection& aSel
       // putting our div is not a block, then the br we found is in same block
       // we are, so it's safe to consume it.
       nsCOMPtr<nsIContent> sibling;
       if (pointToInsertDiv.GetChild()) {
         sibling = htmlEditor->GetNextHTMLSibling(pointToInsertDiv.GetChild());
       }
       if (sibling && !IsBlockNode(*sibling)) {
         AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertDiv);
-        rv = htmlEditor->DeleteNode(brContent);
-        NS_ENSURE_SUCCESS(rv, rv);
+        rv = htmlEditor->DeleteNodeWithTransaction(*brContent);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
     }
     RefPtr<Element> div =
       htmlEditor->CreateNodeWithTransaction(*nsGkAtoms::div, pointToInsertDiv);
     if (NS_WARN_IF(!div)) {
       return NS_ERROR_FAILURE;
     }
     // Remember our new block for postprocessing
@@ -5422,19 +5478,19 @@ HTMLEditRules::CheckForEmptyBlock(nsINod
 
   if (NS_WARN_IF(!mHTMLEditor)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
 
   // If we are inside an empty block, delete it.  Note: do NOT delete table
   // elements this way.
-  nsCOMPtr<Element> block = htmlEditor->GetBlock(*aStartNode);
+  RefPtr<Element> block = htmlEditor->GetBlock(*aStartNode);
   bool bIsEmptyNode;
-  nsCOMPtr<Element> emptyBlock;
+  RefPtr<Element> emptyBlock;
   if (block && block != aBodyNode) {
     // Efficiency hack, avoiding IsEmptyNode() call when in body
     nsresult rv = htmlEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
     NS_ENSURE_SUCCESS(rv, rv);
     while (block && bIsEmptyNode && !HTMLEditUtils::IsTableElement(block) &&
            block != aBodyNode) {
       emptyBlock = block;
       block = htmlEditor->GetBlockNodeParent(emptyBlock);
@@ -5521,18 +5577,20 @@ HTMLEditRules::CheckForEmptyBlock(nsINod
           nsresult rv = aSelection->Collapse(afterEmptyBlock);
           NS_ENSURE_SUCCESS(rv, rv);
         }
       } else if (aAction != nsIEditor::eNone) {
         MOZ_CRASH("CheckForEmptyBlock doesn't support this action yet");
       }
     }
     *aHandled = true;
-    nsresult rv = htmlEditor->DeleteNode(emptyBlock);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv = htmlEditor->DeleteNodeWithTransaction(*emptyBlock);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
   return NS_OK;
 }
 
 Element*
 HTMLEditRules::CheckForInvisibleBR(Element& aBlock,
                                    BRLocation aWhere,
                                    int32_t aOffset)
@@ -6919,18 +6977,20 @@ HTMLEditRules::ReturnInHeader(Selection&
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
     }
   }
 
   // If the new (righthand) header node is empty, delete it
   if (IsEmptyBlockElement(aHeader, IgnoreSingleBR::eYes)) {
-    rv = htmlEditor->DeleteNode(&aHeader);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = htmlEditor->DeleteNodeWithTransaction(aHeader);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
     // Layout tells the caret to blink in a weird place if we don't place a
     // break after the header.
     nsCOMPtr<nsIContent> sibling;
     if (aHeader.GetNextSibling()) {
       sibling = htmlEditor->GetNextHTMLSibling(aHeader.GetNextSibling());
     }
     if (!sibling || !sibling->IsHTMLElement(nsGkAtoms::br)) {
       ClearCachedStyles();
@@ -7208,18 +7268,20 @@ HTMLEditRules::SplitParagraph(
   }
   if (NS_WARN_IF(!splitDivOrPResult.DidSplit())) {
     return NS_ERROR_FAILURE;
   }
 
   // Get rid of the break, if it is visible (otherwise it may be needed to
   // prevent an empty p).
   if (aNextBRNode && htmlEditor->IsVisibleBRElement(aNextBRNode)) {
-    rv = htmlEditor->DeleteNode(aNextBRNode);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = htmlEditor->DeleteNodeWithTransaction(*aNextBRNode);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   // Remove ID attribute on the paragraph from the existing right node.
   rv = htmlEditor->RemoveAttribute(aParentDivOrP.AsElement(), nsGkAtoms::id);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We need to ensure to both paragraphs visible even if they are empty.
   // However, moz-<br> element isn't useful in this case because moz-<br>
@@ -7299,18 +7361,20 @@ HTMLEditRules::ReturnInListItem(Selectio
         htmlEditor->MoveNode(&aListItem,
                              atNextSiblingOfLeftList.GetContainer(),
                              atNextSiblingOfLeftList.Offset());
       NS_ENSURE_SUCCESS(rv, rv);
       rv = aSelection.Collapse(&aListItem, 0);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       // Otherwise kill this item
-      nsresult rv = htmlEditor->DeleteNode(&aListItem);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsresult rv = htmlEditor->DeleteNodeWithTransaction(aListItem);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
 
       // Time to insert a paragraph
       nsAtom& paraAtom = DefaultParagraphSeparator();
       // We want a wrapper even if we separate with <br>
       RefPtr<Element> pNode =
         htmlEditor->CreateNodeWithTransaction(&paraAtom == nsGkAtoms::br ?
                                                 *nsGkAtoms::p : paraAtom,
                                               atNextSiblingOfLeftList);
@@ -7381,21 +7445,25 @@ HTMLEditRules::ReturnInListItem(Selectio
           MOZ_DIAGNOSTIC_ASSERT(itemOffset != -1);
           EditorRawDOMPoint atNextListItem(list, aListItem.GetNextSibling(),
                                            itemOffset + 1);
           RefPtr<Element> newListItem =
             htmlEditor->CreateNodeWithTransaction(*listAtom, atNextListItem);
           if (NS_WARN_IF(!newListItem)) {
             return NS_ERROR_FAILURE;
           }
-          rv = htmlEditor->DeleteNode(&aListItem);
-          NS_ENSURE_SUCCESS(rv, rv);
-          rv = aSelection.Collapse(newListItem, 0);
-          NS_ENSURE_SUCCESS(rv, rv);
-
+          rv = htmlEditor->DeleteNodeWithTransaction(aListItem);
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            return rv;
+          }
+          ErrorResult error;
+          aSelection.Collapse(EditorRawDOMPoint(newListItem, 0), error);
+          if (NS_WARN_IF(error.Failed())) {
+            return error.StealNSResult();
+          }
           return NS_OK;
         }
 
         nsCOMPtr<Element> brNode;
         rv = htmlEditor->CopyLastEditableChildStyles(prevItem,
                                                      &aListItem,
                                                      getter_AddRefs(brNode));
         NS_ENSURE_SUCCESS(rv, rv);
@@ -7694,18 +7762,20 @@ HTMLEditRules::ApplyBlockStyle(nsTArray<
     }
 
     if (curNode->IsHTMLElement(nsGkAtoms::br)) {
       // If the node is a break, we honor it by putting further nodes in a new
       // parent
       if (curBlock) {
         // Forget any previous block used for previous inline nodes
         curBlock = nullptr;
-        nsresult rv = htmlEditor->DeleteNode(curNode);
-        NS_ENSURE_SUCCESS(rv, rv);
+        nsresult rv = htmlEditor->DeleteNodeWithTransaction(*curNode);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
         continue;
       }
 
       // The break is the first (or even only) node we encountered.  Create a
       // block for it.
       SplitNodeResult splitNodeResult =
         MaybeSplitAncestorsForInsertWithTransaction(aBlockTag, atCurNode);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
@@ -8573,18 +8643,20 @@ HTMLEditRules::RemoveEmptyNodes()
     }
 
     iter->Next();
   }
 
   // now delete the empty nodes
   for (OwningNonNull<nsINode>& delNode : arrayOfEmptyNodes) {
     if (htmlEditor->IsModifiableNode(delNode)) {
-      rv = htmlEditor->DeleteNode(delNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->DeleteNodeWithTransaction(*delNode);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
   }
 
   // Now delete the empty mailcites.  This is a separate step because we want
   // to pull out any br's and preserve them.
   for (OwningNonNull<nsINode>& delNode : arrayOfEmptyCites) {
     bool bIsEmptyNode;
     rv = htmlEditor->IsEmptyNode(delNode, &bIsEmptyNode, false, true);
@@ -8592,18 +8664,20 @@ HTMLEditRules::RemoveEmptyNodes()
     if (!bIsEmptyNode) {
       // We are deleting a cite that has just a br.  We want to delete cite,
       // but preserve br.
       RefPtr<Element> br = htmlEditor->CreateBR(EditorRawDOMPoint(delNode));
       if (NS_WARN_IF(!br)) {
         return NS_ERROR_FAILURE;
       }
     }
-    rv = htmlEditor->DeleteNode(delNode);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = htmlEditor->DeleteNodeWithTransaction(*delNode);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::SelectionEndpointInNode(nsINode* aNode,
                                        bool* aResult)
@@ -8798,18 +8872,20 @@ HTMLEditRules::RemoveListStructure(Eleme
         nsresult rv = PopListItem(child, &isOutOfList);
         NS_ENSURE_SUCCESS(rv, rv);
       } while (!isOutOfList);
     } else if (HTMLEditUtils::IsList(child)) {
       nsresult rv = RemoveListStructure(*child->AsElement());
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       // Delete any non-list items for now
-      nsresult rv = htmlEditor->DeleteNode(child);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsresult rv = htmlEditor->DeleteNodeWithTransaction(*child);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
   }
 
   // Delete the now-empty list
   nsresult rv = htmlEditor->RemoveBlockContainer(aList);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
@@ -9428,18 +9504,20 @@ HTMLEditRules::WillAbsolutePosition(Sele
     if (NS_WARN_IF(!positionedDiv)) {
       return NS_ERROR_FAILURE;
     }
     // Remember our new block for postprocessing
     mNewBlock = positionedDiv;
     // Delete anything that was in the list of nodes
     while (!arrayOfNodes.IsEmpty()) {
       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
-      rv = htmlEditor->DeleteNode(curNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->DeleteNodeWithTransaction(*curNode);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       arrayOfNodes.RemoveElementAt(0);
     }
     // Put selection in new block
     *aHandled = true;
     rv = aSelection.Collapse(positionedDiv, 0);
     // Don't restore the selection
     selectionRestorer.Abort();
     NS_ENSURE_SUCCESS(rv, rv);
@@ -9687,29 +9765,32 @@ HTMLEditRules::DocumentModified()
 
 void
 HTMLEditRules::DocumentModifiedWorker()
 {
   if (!mHTMLEditor) {
     return;
   }
 
-  // DeleteNode below may cause a flush, which could destroy the editor
+  // DeleteNodeWithTransaction() below may cause a flush, which could destroy
+  // the editor
   nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
   RefPtr<Selection> selection = htmlEditor->GetSelection();
   if (!selection) {
     return;
   }
 
   // Delete our bogus node, if we have one, since the document might not be
   // empty any more.
   if (mBogusNode) {
-    htmlEditor->DeleteNode(mBogusNode);
+    DebugOnly<nsresult> rv = htmlEditor->DeleteNodeWithTransaction(*mBogusNode);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+      "Failed to remove the bogus node");
     mBogusNode = nullptr;
   }
 
   // Try to recreate the bogus node if needed.
   CreateBogusNodeIfNeeded(selection);
 }
 
 } // namespace mozilla
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1251,17 +1251,17 @@ HTMLEditor::ReplaceHeadContentsWithHTML(
            err.ErrorCodeAsInt());
 #endif
     return err.StealNSResult();
   }
   NS_ENSURE_TRUE(docfrag, NS_ERROR_NULL_POINTER);
 
   // First delete all children in head
   while (nsCOMPtr<nsIContent> child = headNode->GetFirstChild()) {
-    nsresult rv = DeleteNode(child);
+    nsresult rv = DeleteNodeWithTransaction(*child);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Now insert the new nodes
   int32_t offsetOfNewNode = 0;
 
   // Loop over the contents of the fragment and move into the document
   while (nsCOMPtr<nsIContent> child = docfrag->GetFirstChild()) {
@@ -3184,40 +3184,55 @@ HTMLEditor::DeleteSelectionImpl(EDirecti
   if (content && !IsBlockNode(content) && !content->Length() &&
       content->IsEditable() && content != content->GetEditingHost()) {
     while (content->GetParent() && !IsBlockNode(content->GetParent()) &&
            content->GetParent()->Length() == 1 &&
            content->GetParent()->IsEditable() &&
            content->GetParent() != content->GetEditingHost()) {
       content = content->GetParent();
     }
-    rv = DeleteNode(content);
+    rv = DeleteNodeWithTransaction(*content);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
-HTMLEditor::DeleteNode(nsINode* aNode)
+HTMLEditor::DeleteNodeWithTransaction(nsINode& aNode)
 {
-  return DeleteNode(aNode->AsDOMNode());
+  if (NS_WARN_IF(!aNode.IsContent())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  // Do nothing if the node is read-only.
+  // XXX This is not a override method of EditorBase's method.  This might
+  //     cause not called accidentally.  We need to investigate this issue.
+  if (NS_WARN_IF(!IsModifiableNode(aNode.AsContent()) &&
+                 !IsMozEditorBogusNode(aNode.AsContent()))) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = EditorBase::DeleteNodeWithTransaction(aNode);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
 }
 
 NS_IMETHODIMP
-HTMLEditor::DeleteNode(nsIDOMNode* aNode)
+HTMLEditor::DeleteNode(nsIDOMNode* aDOMNode)
 {
-  // do nothing if the node is read-only
-  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
-  if (NS_WARN_IF(!IsModifiableNode(content) &&
-                 !IsMozEditorBogusNode(content))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return EditorBase::DeleteNode(aNode);
+  nsCOMPtr<nsINode> node = do_QueryInterface(aDOMNode);
+  if (NS_WARN_IF(!node)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  nsresult rv = DeleteNodeWithTransaction(*node);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
 }
 
 nsresult
 HTMLEditor::DeleteText(CharacterData& aCharData,
                        uint32_t aOffset,
                        uint32_t aLength)
 {
   // Do nothing if the node is read-only
@@ -4463,30 +4478,34 @@ HTMLEditor::AreNodesSameType(nsIContent*
                                          aNode2->AsElement());
 }
 
 nsresult
 HTMLEditor::CopyLastEditableChildStyles(nsINode* aPreviousBlock,
                                         nsINode* aNewBlock,
                                         Element** aOutBrNode)
 {
-  nsCOMPtr<nsINode> newBlock = do_QueryInterface(aNewBlock);
-  NS_ENSURE_STATE(newBlock || !aNewBlock);
+  if (NS_WARN_IF(!aNewBlock)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  nsCOMPtr<nsINode> newBlock(aNewBlock);
   *aOutBrNode = nullptr;
-  nsCOMPtr<nsINode> child, tmp;
-  // first, clear out aNewBlock.  Contract is that we want only the styles from previousBlock.
-  child = aNewBlock->GetFirstChild();
-  while (child) {
-    nsresult rv = DeleteNode(child);
-    NS_ENSURE_SUCCESS(rv, rv);
-    child = aNewBlock->GetFirstChild();
+  // First, clear out aNewBlock.  Contract is that we want only the styles
+  // from aPreviousBlock.
+  for (nsCOMPtr<nsINode> child = aNewBlock->GetFirstChild();
+       child;
+       child = aNewBlock->GetFirstChild()) {
+    nsresult rv = DeleteNodeWithTransaction(*child);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
   // now find and clone the styles
-  child = aPreviousBlock;
-  tmp = aPreviousBlock;
+  nsCOMPtr<nsINode> child = aPreviousBlock;
+  nsCOMPtr<nsINode> tmp = aPreviousBlock;
   while (tmp) {
     child = tmp;
     tmp = GetLastEditableChild(*child);
   }
   while (child && TextEditUtils::IsBreak(child)) {
     child = GetPreviousEditableHTMLNode(*child);
   }
   nsCOMPtr<Element> newStyles, deepestStyle;
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -380,18 +380,28 @@ public:
    */
   nsresult CollapseAdjacentTextNodes(nsRange* aRange);
 
   virtual bool AreNodesSameType(nsIContent* aNode1,
                                 nsIContent* aNode2) override;
 
   NS_IMETHOD DeleteSelectionImpl(EDirection aAction,
                                  EStripWrappers aStripWrappers) override;
-  nsresult DeleteNode(nsINode* aNode);
+
+  /**
+   * DeleteNodeWithTransaction() removes aNode from the DOM tree if it's
+   * modifiable.  Note that this is not an override of same method of
+   * EditorBase.
+   *
+   * @param aNode       The node to be removed from the DOM tree.
+   */
+  nsresult DeleteNodeWithTransaction(nsINode& aNode);
+
   NS_IMETHOD DeleteNode(nsIDOMNode* aNode) override;
+
   nsresult DeleteText(dom::CharacterData& aTextNode, uint32_t aOffset,
                       uint32_t aLength);
   virtual nsresult
   InsertTextImpl(nsIDocument& aDocument,
                  const nsAString& aStringToInsert,
                  const EditorRawDOMPoint& aPointToInsert,
                  EditorRawDOMPoint* aPointAfterInsertedString =
                    nullptr) override;
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -351,18 +351,20 @@ HTMLEditor::DoInsertHTMLWithContext(cons
     // if there are any invisible br's after our insertion point, remove them.
     // this is because if there is a br at end of what we paste, it will make
     // the invisible br visible.
     WSRunObject wsObj(this, pointToInsert);
     if (wsObj.mEndReasonNode &&
         TextEditUtils::IsBreak(wsObj.mEndReasonNode) &&
         !IsVisibleBRElement(wsObj.mEndReasonNode)) {
       AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
-      rv = DeleteNode(wsObj.mEndReasonNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = DeleteNodeWithTransaction(*wsObj.mEndReasonNode);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
 
     // Remember if we are in a link.
     bool bStartedInLink = IsInLink(pointToInsert.GetContainer());
 
     // Are we in a text node? If so, split it.
     if (pointToInsert.IsInTextNode()) {
       SplitNodeResult splitNodeResult =
@@ -486,17 +488,17 @@ HTMLEditor::DoInsertHTMLWithContext(cons
             if (HTMLEditUtils::IsListItem(pointToInsert.GetContainer())) {
               bool isEmpty;
               rv = IsEmptyNode(pointToInsert.GetContainer(), &isEmpty, true);
               if (NS_SUCCEEDED(rv) && isEmpty) {
                 if (NS_WARN_IF(!pointToInsert.GetContainer()->
                                                 GetParentNode())) {
                   // Is it an orphan node?
                 } else {
-                  DeleteNode(pointToInsert.GetContainer());
+                  DeleteNodeWithTransaction(*pointToInsert.GetContainer());
                   pointToInsert.Set(pointToInsert.GetContainer());
                 }
               }
             }
             EditorDOMPoint insertedPoint =
               InsertNodeIntoProperAncestor(
                 *firstChild, pointToInsert,
                 SplitAtEdges::eDoNotCreateEmptyContainer);
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -591,18 +591,20 @@ HTMLEditor::ClearStyle(nsCOMPtr<nsINode>
                                      getter_AddRefs(rightNode));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (leftNode) {
     bool bIsEmptyNode;
     IsEmptyNode(leftNode, &bIsEmptyNode, false, true);
     if (bIsEmptyNode) {
       // delete leftNode if it became empty
-      rv = DeleteNode(leftNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = DeleteNodeWithTransaction(*leftNode);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
   }
   if (rightNode) {
     nsCOMPtr<nsINode> secondSplitParent = GetLeftmostChild(rightNode);
     // don't try to split non-containers (br's, images, hr's, etc.)
     if (!secondSplitParent) {
       secondSplitParent = rightNode;
     }
@@ -622,18 +624,20 @@ HTMLEditor::ClearStyle(nsCOMPtr<nsINode>
                               getter_AddRefs(rightNode));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (rightNode) {
       bool bIsEmptyNode;
       IsEmptyNode(rightNode, &bIsEmptyNode, false, true);
       if (bIsEmptyNode) {
         // delete rightNode if it became empty
-        rv = DeleteNode(rightNode);
-        NS_ENSURE_SUCCESS(rv, rv);
+        rv = DeleteNodeWithTransaction(*rightNode);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
     }
 
     if (!leftNode) {
       return NS_OK;
     }
 
     // should be impossible to not get a new leftnode here
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -833,18 +833,24 @@ HTMLEditor::DeleteTableCell(int32_t aNum
         }
         if (!deleteCol) {
           // First get the next cell to delete
           nsCOMPtr<nsIDOMElement> nextCell;
           rv = GetNextSelectedCell(nullptr, getter_AddRefs(nextCell));
           NS_ENSURE_SUCCESS(rv, rv);
 
           // Then delete the cell
-          rv = DeleteNode(cell);
-          NS_ENSURE_SUCCESS(rv, rv);
+          nsCOMPtr<nsINode> cellToBeRemoved = do_QueryInterface(cell);
+          if (NS_WARN_IF(!cellToBeRemoved)) {
+            return NS_ERROR_FAILURE;
+          }
+          rv = DeleteNodeWithTransaction(*cellToBeRemoved);
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            return rv;
+          }
 
           // The next cell to delete
           cell = nextCell;
           if (cell) {
             rv = GetCellIndexes(cell, &startRowIndex, &startColIndex);
             NS_ENSURE_SUCCESS(rv, rv);
           }
         }
@@ -886,20 +892,25 @@ HTMLEditor::DeleteTableCell(int32_t aNum
         // More than 1 cell in the row
 
         // The setCaret object will call AutoSelectionSetterAfterTableEdit in its
         // destructor
         AutoSelectionSetterAfterTableEdit setCaret(*this, table, startRowIndex,
                                                    startColIndex, ePreviousColumn,
                                                    false);
         AutoTransactionsConserveSelection dontChangeSelection(this);
-
-        rv = DeleteNode(cell);
+        nsCOMPtr<nsINode> cellToBeRemoved = do_QueryInterface(cell);
+        if (NS_WARN_IF(!cellToBeRemoved)) {
+          return NS_ERROR_FAILURE;
+        }
+        rv = DeleteNodeWithTransaction(*cellToBeRemoved);
         // If we fail, don't try to delete any more cells???
-        NS_ENSURE_SUCCESS(rv, rv);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::DeleteTableCellContents()
@@ -958,18 +969,20 @@ HTMLEditor::DeleteCellContents(nsIDOMEle
 {
   nsCOMPtr<Element> cell = do_QueryInterface(aCell);
   NS_ENSURE_TRUE(cell, NS_ERROR_NULL_POINTER);
 
   // Prevent rules testing until we're done
   AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
 
   while (nsCOMPtr<nsINode> child = cell->GetLastChild()) {
-    nsresult rv = DeleteNode(child);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv = DeleteNodeWithTransaction(*child);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::DeleteTableColumn(int32_t aNumber)
 {
   RefPtr<Selection> selection;
@@ -1116,18 +1129,24 @@ HTMLEditor::DeleteColumn(nsIDOMElement* 
           rv = DeleteRow(aTable, startRowIndex);
           NS_ENSURE_SUCCESS(rv, rv);
 
           // Note that we don't incremenet rowIndex
           // since a row was deleted and "next"
           // row now has current rowIndex
         } else {
           // A more "normal" deletion
-          rv = DeleteNode(cell);
-          NS_ENSURE_SUCCESS(rv, rv);
+          nsCOMPtr<nsINode> cellToBeRemoved = do_QueryInterface(cell);
+          if (NS_WARN_IF(!cellToBeRemoved)) {
+            return NS_ERROR_FAILURE;
+          }
+          rv = DeleteNodeWithTransaction(*cellToBeRemoved);
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            return rv;
+          }
 
           //Skip over any rows spanned by this cell
           rowIndex += actualRowSpan;
         }
       }
     }
   } while (cell);
 
@@ -1307,18 +1326,24 @@ HTMLEditor::DeleteRow(nsIDOMElement* aTa
 
   // Delete the entire row
   nsCOMPtr<nsIDOMElement> parentRow;
   rv = GetElementOrParentByTagName(NS_LITERAL_STRING("tr"), cellInDeleteRow,
                                    getter_AddRefs(parentRow));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (parentRow) {
-    rv = DeleteNode(parentRow);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsINode> rowToBeRemoved = do_QueryInterface(parentRow);
+    if (NS_WARN_IF(!rowToBeRemoved)) {
+      return NS_ERROR_FAILURE;
+    }
+    rv = DeleteNodeWithTransaction(*rowToBeRemoved);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   // Now we can set new rowspans for cells stored above
   for (uint32_t i = 0, n = spanCellList.Length(); i < n; i++) {
     nsIDOMElement *cellPtr = spanCellList[i];
     if (cellPtr) {
       rv = SetRowSpan(cellPtr, newSpanList[i]);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -2164,19 +2189,24 @@ HTMLEditor::JoinTableCells(bool aMergeNo
     // All cell contents are merged. Delete the empty cells we accumulated
     // Prevent rules testing until we're done
     AutoRules beginRulesSniffing(this, EditAction::deleteNode,
                                  nsIEditor::eNext);
 
     for (uint32_t i = 0, n = deleteList.Length(); i < n; i++) {
       nsIDOMElement *elementPtr = deleteList[i];
       if (elementPtr) {
-        nsCOMPtr<nsIDOMNode> node = do_QueryInterface(elementPtr);
-        rv = DeleteNode(node);
-        NS_ENSURE_SUCCESS(rv, rv);
+        nsCOMPtr<nsINode> nodeToBeRemoved = do_QueryInterface(elementPtr);
+        if (NS_WARN_IF(!nodeToBeRemoved)) {
+          return NS_ERROR_FAILURE;
+        }
+        rv = DeleteNodeWithTransaction(*nodeToBeRemoved);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
     }
     // Cleanup selection: remove ranges where cells were deleted
     RefPtr<Selection> selection = GetSelection();
     NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
 
     uint32_t rangeCount = selection->RangeCount();
 
@@ -2288,44 +2318,60 @@ HTMLEditor::MergeCells(nsCOMPtr<nsIDOMEl
     //  we insert at index 0
     int32_t insertIndex = 0;
 
     // Start inserting just after last child
     uint32_t len = targetCell->GetChildCount();
     if (len == 1 && IsEmptyCell(targetCell)) {
       // Delete the empty node
       nsIContent* cellChild = targetCell->GetFirstChild();
-      nsresult rv = DeleteNode(cellChild->AsDOMNode());
-      NS_ENSURE_SUCCESS(rv, rv);
+      if (NS_WARN_IF(!cellChild)) {
+        return NS_ERROR_FAILURE;
+      }
+      nsresult rv = DeleteNodeWithTransaction(*cellChild);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       insertIndex = 0;
     } else {
       insertIndex = (int32_t)len;
     }
 
     // Move the contents
     while (cellToMerge->HasChildren()) {
       nsCOMPtr<nsIContent> cellChild = cellToMerge->GetLastChild();
-      // XXX We need HTMLEditor::DeleteNode(nsINode&).
-      nsresult rv = DeleteNode(cellChild->AsDOMNode());
-      NS_ENSURE_SUCCESS(rv, rv);
-
+      if (NS_WARN_IF(!cellChild)) {
+        return NS_ERROR_FAILURE;
+      }
+      nsresult rv = DeleteNodeWithTransaction(*cellChild);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       rv = InsertNodeWithTransaction(*cellChild,
                                      EditorRawDOMPoint(aTargetCell,
                                                        insertIndex));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
 
-  // Delete cells whose contents were moved
-  if (aDeleteCellToMerge) {
-    return DeleteNode(aCellToMerge);
+  if (!aDeleteCellToMerge) {
+    return NS_OK;
   }
 
+  // Delete cells whose contents were moved.
+  nsCOMPtr<nsIContent> cellToBeRemoved = do_QueryInterface(aCellToMerge);
+  if (NS_WARN_IF(!cellToBeRemoved)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  nsresult rv = DeleteNodeWithTransaction(*cellToBeRemoved);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   return NS_OK;
 }
 
 
 nsresult
 HTMLEditor::FixBadRowSpan(nsIDOMElement* aTable,
                           int32_t aRowIndex,
                           int32_t& aNewRowCount)
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -382,18 +382,23 @@ TextEditRules::WillInsert(Selection& aSe
     return;
   }
 
   // initialize out param
   *aCancel = false;
 
   // check for the magic content node and delete it if it exists
   if (mBogusNode) {
-    NS_ENSURE_TRUE_VOID(mTextEditor);
-    mTextEditor->DeleteNode(mBogusNode);
+    if (NS_WARN_IF(!mTextEditor)) {
+      return; // XXX Shouldn't we release mBogusNode now?
+    }
+    RefPtr<TextEditor> textEditor(mTextEditor);
+    DebugOnly<nsresult> rv = textEditor->DeleteNodeWithTransaction(*mBogusNode);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+      "Failed to remove the bogus node");
     mBogusNode = nullptr;
   }
 }
 
 nsresult
 TextEditRules::DidInsert(Selection* aSelection,
                          nsresult aResult)
 {
@@ -1083,17 +1088,19 @@ TextEditRules::DidDeleteSelection(Select
 
   // Delete empty text nodes at selection.
   if (selectionStartPoint.IsInTextNode() &&
       !selectionStartPoint.GetContainer()->Length()) {
     if (NS_WARN_IF(!mTextEditor)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
     RefPtr<TextEditor> textEditor(mTextEditor);
-    nsresult rv = textEditor->DeleteNode(selectionStartPoint.GetContainer());
+    nsresult rv =
+      textEditor->DeleteNodeWithTransaction(
+                    *selectionStartPoint.GetContainer());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     // Be aware, mTextEditor may be nullptr here.
   }
 
   if (mDidExplicitlySetInterline) {
     return NS_OK;
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -1357,21 +1357,24 @@ WSRunObject::DeleteRange(const EditorDOM
   // MOOSE: this routine needs to be modified to preserve the integrity of the
   // wsFragment info.
 
   if (aStartPoint == aEndPoint) {
     // Nothing to delete
     return NS_OK;
   }
 
+  MOZ_ASSERT(mHTMLEditor);
+  RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+
   if (aStartPoint.GetContainer() == aEndPoint.GetContainer() &&
       aStartPoint.IsInTextNode()) {
-    return mHTMLEditor->DeleteText(*aStartPoint.GetContainerAsText(),
-                                   aStartPoint.Offset(),
-                                   aEndPoint.Offset() - aStartPoint.Offset());
+    return htmlEditor->DeleteText(*aStartPoint.GetContainerAsText(),
+                                  aStartPoint.Offset(),
+                                  aEndPoint.Offset() - aStartPoint.Offset());
   }
 
   RefPtr<nsRange> range;
   int32_t count = mNodeArray.Length();
   int32_t idx = mNodeArray.IndexOf(aStartPoint.GetContainer());
   if (idx == -1) {
     // If our starting point wasn't one of our ws text nodes, then just go
     // through them from the beginning.
@@ -1381,26 +1384,26 @@ WSRunObject::DeleteRange(const EditorDOM
     RefPtr<Text> node = mNodeArray[idx];
     if (!node) {
       // We ran out of ws nodes; must have been deleting to end
       return NS_OK;
     }
     if (node == aStartPoint.GetContainer()) {
       if (!aStartPoint.IsEndOfContainer()) {
         nsresult rv =
-          mHTMLEditor->DeleteText(*node, aStartPoint.Offset(),
-                                  aStartPoint.GetContainer()->Length() -
-                                    aStartPoint.Offset());
+          htmlEditor->DeleteText(*node, aStartPoint.Offset(),
+                                 aStartPoint.GetContainer()->Length() -
+                                   aStartPoint.Offset());
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     } else if (node == aEndPoint.GetContainer()) {
       if (!aEndPoint.IsStartOfContainer()) {
-        nsresult rv = mHTMLEditor->DeleteText(*node, 0, aEndPoint.Offset());
+        nsresult rv = htmlEditor->DeleteText(*node, 0, aEndPoint.Offset());
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       break;
     } else {
       if (!range) {
         range = new nsRange(aStartPoint.GetContainer());
@@ -1414,17 +1417,17 @@ WSRunObject::DeleteRange(const EditorDOM
         nsRange::CompareNodeToRange(node, range, &nodeBefore, &nodeAfter);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (nodeAfter) {
         break;
       }
       if (!nodeBefore) {
-        rv = mHTMLEditor->DeleteNode(node);
+        rv = htmlEditor->DeleteNodeWithTransaction(*node);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         mNodeArray.RemoveElement(node);
         --count;
         --idx;
       }
     }