Bug 1574852 - part 67-1: Make `HTMLEditRules::WillDeleteSelection() return `EditActionResult` r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 05 Sep 2019 13:37:42 +0000
changeset 491942 6b5bc43016bdb565c73025b7c2e1576466368f85
parent 491941 64efa070c5e149a2e0b07e7ddd5a06373c600eb8
child 491943 58347c61ae4e5fcb1c4f458c5063fd2b67090958
push id94622
push usermasayuki@d-toybox.com
push dateThu, 05 Sep 2019 22:14:27 +0000
treeherderautoland@09cbd757c16b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1574852
milestone71.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 1574852 - part 67-1: Make `HTMLEditRules::WillDeleteSelection() return `EditActionResult` r=m_kato Before moving the largest method in our tree, we should split it. This is first preparation of that. Differential Revision: https://phabricator.services.mozilla.com/D44451
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -771,18 +771,21 @@ nsresult HTMLEditRules::WillDoAction(Edi
   switch (aInfo.mEditSubAction) {
     case EditSubAction::eInsertText:
     case EditSubAction::eInsertTextComingFromIME:
       TextEditorRef().UndefineCaretBidiLevel();
       return MOZ_KnownLive(HTMLEditorRef())
           .WillInsertText(aInfo.mEditSubAction, aCancel, aHandled,
                           aInfo.inString, aInfo.outString, aInfo.maxLength);
     case EditSubAction::eDeleteSelectedContent:
-      return WillDeleteSelection(aInfo.collapsedAction, aInfo.stripWrappers,
-                                 aCancel, aHandled);
+      result = WillDeleteSelection(aInfo.collapsedAction, aInfo.stripWrappers);
+      *aHandled = result.Handled();
+      *aCancel = result.Canceled();
+      NS_WARNING_ASSERTION(result.Succeeded(), "WillDeleteSelection() failed");
+      return result.Rv();
     case EditSubAction::eIndent:
       return WillIndent(aCancel, aHandled);
     case EditSubAction::eOutdent:
       return WillOutdent(aCancel, aHandled);
     case EditSubAction::eSetPositionToAbsolute:
       return WillAbsolutePosition(aCancel, aHandled);
     case EditSubAction::eSetPositionToStatic:
       return WillRemoveAbsolutePosition(aCancel, aHandled);
@@ -2321,131 +2324,115 @@ EditActionResult HTMLEditor::SplitMailCi
         return EditActionIgnored(rv);
       }
     }
   }
 
   return EditActionHandled();
 }
 
-nsresult HTMLEditRules::WillDeleteSelection(
-    nsIEditor::EDirection aAction, nsIEditor::EStripWrappers aStripWrappers,
-    bool* aCancel, bool* aHandled) {
+EditActionResult HTMLEditRules::WillDeleteSelection(
+    nsIEditor::EDirection aAction, nsIEditor::EStripWrappers aStripWrappers) {
   MOZ_ASSERT(IsEditorDataAvailable());
   MOZ_ASSERT(aStripWrappers == nsIEditor::eStrip ||
              aStripWrappers == nsIEditor::eNoStrip);
 
-  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-  // Initialize out params
-  *aCancel = false;
-  *aHandled = false;
-
   // Remember that we did a selection deletion.  Used by
   // CreateStyleForInsertText()
   HTMLEditorRef().TopLevelEditSubActionDataRef().mDidDeleteSelection = true;
 
-  // If there is only padding <br> element for empty editor, cancel the
+  // If there is only padding `<br>` element for empty editor, cancel the
   // operation.
   if (HTMLEditorRef().HasPaddingBRElementForEmptyEditor()) {
-    *aCancel = true;
-    return NS_OK;
+    return EditActionCanceled();
   }
 
   // First check for table selection mode.  If so, hand off to table editor.
   ErrorResult error;
   RefPtr<Element> cellElement =
       HTMLEditorRef().GetFirstSelectedTableCellElement(error);
   if (cellElement) {
     error.SuppressException();
     nsresult rv =
         MOZ_KnownLive(HTMLEditorRef()).DeleteTableCellContentsWithTransaction();
     if (NS_WARN_IF(!CanHandleEditAction())) {
-      return NS_ERROR_EDITOR_DESTROYED;
-    }
-    *aHandled = true;
-    return rv;
+      return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
+    }
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                         "DeleteTableCellContentsWithTransaction() failed");
+    return EditActionHandled(rv);
   }
   nsresult rv = error.StealNSResult();
   cellElement = 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
   // happen if the original selection is collapsed and the cursor is at the end
   // of a block element, in which case ExtendSelectionForDelete would always
   // make the selection not collapsed.
   bool join = false;
   bool origCollapsed = SelectionRefPtr()->IsCollapsed();
 
   if (origCollapsed) {
     EditorDOMPoint startPoint(EditorBase::GetStartPoint(*SelectionRefPtr()));
     if (NS_WARN_IF(!startPoint.IsSet())) {
-      return NS_ERROR_FAILURE;
+      return EditActionResult(NS_ERROR_FAILURE);
     }
 
     // If we are inside an empty block, delete it.
     RefPtr<Element> editingHost = HTMLEditorRef().GetActiveEditingHost();
     if (NS_WARN_IF(!editingHost)) {
-      return NS_ERROR_FAILURE;
+      return EditActionResult(NS_ERROR_FAILURE);
     }
 
     if (startPoint.GetContainerAsContent()) {
       AutoEditorDOMPointChildInvalidator lockOffset(startPoint);
 
       EditActionResult result =
           MOZ_KnownLive(HTMLEditorRef())
               .MaybeDeleteTopMostEmptyAncestor(
                   MOZ_KnownLive(*startPoint.GetContainerAsContent()),
                   *editingHost, aAction);
-      if (NS_WARN_IF(result.Failed())) {
-        return result.Rv();
-      }
-      if (result.Handled()) {
-        *aHandled = true;
-        return NS_OK;
+      if (NS_WARN_IF(result.Failed()) || result.Handled()) {
+        return result;
       }
     }
 
     // Test for distance between caret and text that will be deleted
     EditActionResult result =
         HTMLEditorRef().SetCaretBidiLevelForDeletion(startPoint, aAction);
-    if (NS_WARN_IF(result.Failed())) {
-      return result.Rv();
-    }
-    if (result.Canceled()) {
-      *aCancel = true;
-      return NS_OK;
+    if (NS_WARN_IF(result.Failed()) || result.Canceled()) {
+      return result;
     }
 
     // ExtendSelectionForDelete will use selection controller to set
     // selection for delete.  But if focus event doesn't receive yet,
     // ancestor isn't set.  So we must set root eleement of editor to
     // ancestor.
     AutoSetTemporaryAncestorLimiter autoSetter(
         HTMLEditorRef(), *SelectionRefPtr(), *startPoint.GetContainer());
 
     rv = HTMLEditorRef().ExtendSelectionForDelete(&aAction);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+      return EditActionResult(rv);
     }
 
     // We should delete nothing.
     if (aAction == nsIEditor::eNone) {
-      return NS_OK;
+      return EditActionIgnored();
     }
   }
 
   if (SelectionRefPtr()->IsCollapsed()) {
     // ExtendSelectionForDelete() won't change the selection.
 
     EditorDOMPoint startPoint(EditorBase::GetStartPoint(*SelectionRefPtr()));
     if (NS_WARN_IF(!startPoint.IsSet())) {
-      return NS_ERROR_FAILURE;
+      return EditActionResult(NS_ERROR_FAILURE);
     }
 
     // What's in the direction we are deleting?
     WSRunObject wsObj(&HTMLEditorRef(), startPoint);
     nsCOMPtr<nsINode> visNode;
     int32_t visOffset;
     WSType wsType;
 
@@ -2454,103 +2441,101 @@ nsresult HTMLEditRules::WillDeleteSelect
       wsObj.NextVisibleNode(startPoint, address_of(visNode), &visOffset,
                             &wsType);
     } else {
       wsObj.PriorVisibleNode(startPoint, address_of(visNode), &visOffset,
                              &wsType);
     }
 
     if (!visNode) {
-      // Can't find anything to delete!
-      *aCancel = true;
-      // XXX This is the result of
-      //     HTMLEditorRef().GetFirstSelectedTableCellElement().
-      //     The value could be both an error and NS_OK.
-      return rv;
+      // XXX This is the result of GetFirstSelectedTableCellElement().
+      //     This must be a bug.
+      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                           "GetFirstSelectedTableCellElement() failed");
+      return EditActionCanceled(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();
+        nsresult rv = wsObj.DeleteWSForward();
         if (NS_WARN_IF(!CanHandleEditAction())) {
-          return NS_ERROR_EDITOR_DESTROYED;
+          return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
+          return EditActionHandled(rv);
         }
       } else {
-        rv = wsObj.DeleteWSBackward();
+        nsresult rv = wsObj.DeleteWSBackward();
         if (NS_WARN_IF(!CanHandleEditAction())) {
-          return NS_ERROR_EDITOR_DESTROYED;
+          return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-      }
-      rv = MOZ_KnownLive(HTMLEditorRef())
-               .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary(
-                   EditorBase::GetStartPoint(*SelectionRefPtr()));
+          return EditActionHandled(rv);
+        }
+      }
+      nsresult rv =
+          MOZ_KnownLive(HTMLEditorRef())
+              .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary(
+                  EditorBase::GetStartPoint(*SelectionRefPtr()));
       NS_WARNING_ASSERTION(
           NS_SUCCEEDED(rv),
           "InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() failed");
-      return rv;
+      return EditActionHandled(rv);
     }
 
     if (wsType == WSType::text) {
       // Found normal text to delete.
       OwningNonNull<Text> nodeAsText = *visNode->GetAsText();
       int32_t so = visOffset;
       int32_t eo = visOffset + 1;
       if (aAction == nsIEditor::ePrevious) {
         if (!so) {
-          return NS_ERROR_UNEXPECTED;
+          return EditActionResult(NS_ERROR_UNEXPECTED);
         }
         so--;
         eo--;
         // Bug 1068979: delete both codepoints if surrogate pair
         if (so > 0) {
           const nsTextFragment* text = &nodeAsText->TextFragment();
           if (text->IsLowSurrogateFollowingHighSurrogateAt(so)) {
             so--;
           }
         }
       } else {
         RefPtr<nsRange> range = SelectionRefPtr()->GetRangeAt(0);
         if (NS_WARN_IF(!range)) {
-          return NS_ERROR_FAILURE;
+          return EditActionResult(NS_ERROR_FAILURE);
         }
 
         NS_ASSERTION(range->GetStartContainer() == visNode,
                      "selection start not in visNode");
         NS_ASSERTION(range->GetEndContainer() == visNode,
                      "selection end not in visNode");
 
         so = range->StartOffset();
         eo = range->EndOffset();
       }
-      rv = WSRunObject::PrepareToDeleteRange(MOZ_KnownLive(&HTMLEditorRef()),
-                                             address_of(visNode), &so,
-                                             address_of(visNode), &eo);
+      nsresult rv = WSRunObject::PrepareToDeleteRange(
+          MOZ_KnownLive(&HTMLEditorRef()), address_of(visNode), &so,
+          address_of(visNode), &eo);
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
+        return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      *aHandled = true;
+        return EditActionResult(rv);
+      }
       rv = MOZ_KnownLive(HTMLEditorRef())
                .DeleteTextWithTransaction(nodeAsText, std::min(so, eo),
                                           DeprecatedAbs(eo - so));
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
+        return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return EditActionHandled(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
       //     should keep our traditional behavior same as Edge for backward
       //     compatibility.
@@ -2559,61 +2544,60 @@ nsresult HTMLEditRules::WillDeleteSelect
       //     text node.  Chromium removes only selected empty text node and
       //     following empty text nodes and the first character of the
       //     non-empty text node.  For now, we should keep our traditional
       //     behavior same as Chromium for backward compatibility.
 
       rv = MOZ_KnownLive(HTMLEditorRef())
                .DeleteNodeIfInvisibleAndEditableTextNode(nodeAsText);
       if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
-        return NS_ERROR_EDITOR_DESTROYED;
+        return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
       }
       NS_WARNING_ASSERTION(
           NS_SUCCEEDED(rv),
           "DeleteNodeIfInvisibleAndEditableTextNode() failed, but ignored");
 
       rv = MOZ_KnownLive(HTMLEditorRef())
                .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary(
                    EditorBase::GetStartPoint(*SelectionRefPtr()));
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return EditActionHandled(rv);
       }
 
       // Remember that we did a ranged delete for the benefit of
       // AfterEditInner().
       HTMLEditorRef()
           .TopLevelEditSubActionDataRef()
           .mDidDeleteNonCollapsedRange = true;
 
-      return NS_OK;
+      return EditActionHandled();
     }
 
     if (wsType == WSType::special || wsType == WSType::br ||
         visNode->IsHTMLElement(nsGkAtoms::hr)) {
       // If the void element is editing host, we should do nothing.
       if (visNode == wsObj.GetEditingHost()) {
-        *aHandled = true;
-        return NS_OK;
+        return EditActionHandled();
       }
 
       // Short circuit for invisible breaks.  delete them and recurse.
       if (visNode->IsHTMLElement(nsGkAtoms::br) &&
           !HTMLEditorRef().IsVisibleBRElement(visNode)) {
-        rv = MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*visNode);
+        nsresult rv =
+            MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*visNode);
         if (NS_WARN_IF(!CanHandleEditAction())) {
-          return NS_ERROR_EDITOR_DESTROYED;
+          return EditActionResult(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;
+          return EditActionResult(rv);
+        }
+        EditActionResult result = WillDeleteSelection(aAction, aStripWrappers);
+        NS_WARNING_ASSERTION(result.Succeeded(),
+                             "Nested WillDeleteSelection() failed");
+        return result;
       }
 
       // 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
         // want to delete the <hr>.
         //
@@ -2633,17 +2617,17 @@ nsresult HTMLEditRules::WillDeleteSelect
         // end-of-hr-line position.
         bool moveOnly = true;
 
         EditorDOMPoint selPoint(visNode);
 
         ErrorResult err;
         bool interLineIsRight = SelectionRefPtr()->GetInterlinePosition(err);
         if (NS_WARN_IF(err.Failed())) {
-          return err.StealNSResult();
+          return EditActionResult(err.StealNSResult());
         }
 
         if (startPoint.GetContainer() == selPoint.GetContainer() &&
             startPoint.Offset() - 1 == selPoint.Offset() && !interLineIsRight) {
           moveOnly = false;
         }
 
         if (moveOnly) {
@@ -2654,141 +2638,137 @@ nsresult HTMLEditRules::WillDeleteSelect
                                "Failed to advance offset after <hr> element");
 
           {
             AutoEditorDOMPointChildInvalidator lockOffset(selPoint);
 
             IgnoredErrorResult ignoredError;
             SelectionRefPtr()->Collapse(selPoint, ignoredError);
             if (NS_WARN_IF(!CanHandleEditAction())) {
-              return NS_ERROR_EDITOR_DESTROYED;
+              return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
             }
             NS_WARNING_ASSERTION(
                 !ignoredError.Failed(),
                 "Failed to collapse selection at after the <hr>");
           }
 
           IgnoredErrorResult ignoredError;
           SelectionRefPtr()->SetInterlinePosition(false, ignoredError);
           NS_WARNING_ASSERTION(!ignoredError.Failed(),
                                "Failed to unset interline position");
           HTMLEditorRef()
               .TopLevelEditSubActionDataRef()
               .mDidExplicitlySetInterLine = true;
-          *aHandled = true;
 
           // There is one exception to the move only case.  If the <hr> is
           // followed by a <br> we want to delete the <br>.
 
           WSType otherWSType;
           nsCOMPtr<nsINode> otherNode;
 
           wsObj.NextVisibleNode(startPoint, address_of(otherNode), nullptr,
                                 &otherWSType);
 
-          if (otherWSType == WSType::br) {
-            // Delete the <br>
-            if (NS_WARN_IF(!otherNode->IsContent())) {
-              return NS_ERROR_FAILURE;
-            }
-            nsIContent* otherContent = otherNode->AsContent();
-            rv = WSRunObject::PrepareToDeleteNode(
-                MOZ_KnownLive(&HTMLEditorRef()), MOZ_KnownLive(otherContent));
-            if (NS_WARN_IF(!CanHandleEditAction())) {
-              return NS_ERROR_EDITOR_DESTROYED;
-            }
-            if (NS_WARN_IF(NS_FAILED(rv))) {
-              return rv;
-            }
-            rv = MOZ_KnownLive(HTMLEditorRef())
-                     .DeleteNodeWithTransaction(MOZ_KnownLive(*otherContent));
-            if (NS_WARN_IF(!CanHandleEditAction())) {
-              return NS_ERROR_EDITOR_DESTROYED;
-            }
-            if (NS_WARN_IF(NS_FAILED(rv))) {
-              return rv;
-            }
+          if (otherWSType != WSType::br) {
+            return EditActionHandled();
+          }
+
+          // Delete the <br>
+          if (NS_WARN_IF(!otherNode->IsContent())) {
+            return EditActionHandled(NS_ERROR_FAILURE);
           }
-
-          return NS_OK;
+          nsIContent* otherContent = otherNode->AsContent();
+          nsresult rv = WSRunObject::PrepareToDeleteNode(
+              MOZ_KnownLive(&HTMLEditorRef()), MOZ_KnownLive(otherContent));
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
+          }
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            return EditActionHandled(rv);
+          }
+          rv = MOZ_KnownLive(HTMLEditorRef())
+                   .DeleteNodeWithTransaction(MOZ_KnownLive(*otherContent));
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
+          }
+          NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                               "DeleteNodeWithTransaction() failed");
+          return EditActionHandled(rv);
         }
         // Else continue with normal delete code
       }
 
       if (NS_WARN_IF(!visNode->IsContent())) {
-        return NS_ERROR_FAILURE;
+        return EditActionResult(NS_ERROR_FAILURE);
       }
       // Found break or image, or hr.
-      rv = WSRunObject::PrepareToDeleteNode(
+      nsresult rv = WSRunObject::PrepareToDeleteNode(
           MOZ_KnownLive(&HTMLEditorRef()), MOZ_KnownLive(visNode->AsContent()));
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
+        return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return EditActionResult(rv);
       }
       // Remember sibling to visnode, if any
       nsCOMPtr<nsIContent> sibling =
           HTMLEditorRef().GetPriorHTMLSibling(visNode);
       // Delete the node, and join like nodes if appropriate
       rv = MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*visNode);
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
+        return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      // We did something, so let's say so.
-      *aHandled = true;
+        return EditActionResult(rv);
+      }
       // Is there a prior node and are they siblings?
       nsCOMPtr<nsINode> stepbrother;
       if (sibling) {
         stepbrother = HTMLEditorRef().GetNextHTMLSibling(sibling);
       }
       // Are they both text nodes?  If so, join them!
       if (startPoint.GetContainer() == stepbrother &&
           startPoint.GetContainerAsText() && sibling->GetAsText()) {
         EditorDOMPoint pt;
         nsresult rv =
             MOZ_KnownLive(HTMLEditorRef())
                 .JoinNearestEditableNodesWithTransaction(
                     *sibling,
                     MOZ_KnownLive(*startPoint.GetContainerAsContent()), &pt);
         if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
+          return EditActionHandled(rv);
         }
         if (NS_WARN_IF(!pt.IsSet())) {
-          return NS_ERROR_FAILURE;
+          return EditActionHandled(NS_ERROR_FAILURE);
         }
         // Fix up selection
         ErrorResult error;
         SelectionRefPtr()->Collapse(pt, error);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           error.SuppressException();
-          return NS_ERROR_EDITOR_DESTROYED;
+          return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
         }
         if (NS_WARN_IF(error.Failed())) {
-          return error.StealNSResult();
+          return EditActionHandled(error.StealNSResult());
         }
       }
       rv = MOZ_KnownLive(HTMLEditorRef())
                .InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary(
                    EditorBase::GetStartPoint(*SelectionRefPtr()));
       NS_WARNING_ASSERTION(
           NS_SUCCEEDED(rv),
           "InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() failed");
-      return rv;
+      return EditActionHandled(rv);
     }
 
     if (wsType == WSType::otherBlock) {
       // Make sure it's not a table element.  If so, cancel the operation
       // (translation: users cannot backspace or delete across table cells)
       if (HTMLEditUtils::IsTableElement(visNode)) {
-        *aCancel = true;
-        return NS_OK;
+        return EditActionCanceled();
       }
 
       // Next to a block.  See if we are between a block and a br.  If so, we
       // really want to delete the br.  Else join content at selection to the
       // block.
       bool bDeletedBR = false;
       WSType otherWSType;
       nsCOMPtr<nsINode> otherNode;
@@ -2811,247 +2791,241 @@ nsresult HTMLEditRules::WillDeleteSelect
         rightNode = startPoint.GetContainer();
       } else {
         leafNode = HTMLEditorRef().GetFirstEditableLeaf(*visNode);
         leftNode = startPoint.GetContainer();
         rightNode = leafNode;
       }
 
       if (otherNode->IsHTMLElement(nsGkAtoms::br)) {
-        rv = MOZ_KnownLive(HTMLEditorRef())
-                 .DeleteNodeWithTransaction(*otherNode);
+        nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                          .DeleteNodeWithTransaction(*otherNode);
         if (NS_WARN_IF(!CanHandleEditAction())) {
-          return NS_ERROR_EDITOR_DESTROYED;
+          return EditActionResult(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;
+          return EditActionResult(rv);
+        }
         bDeletedBR = true;
       }
 
       // Don't cross table boundaries
       if (leftNode && rightNode &&
           HTMLEditor::NodesInDifferentTableElements(*leftNode, *rightNode)) {
-        return NS_OK;
+        return bDeletedBR ? EditActionHandled() : EditActionIgnored();
       }
 
       if (bDeletedBR) {
         // Put selection at edge of block and we are done.
         if (NS_WARN_IF(!leafNode)) {
-          return NS_ERROR_FAILURE;
+          return EditActionHandled(NS_ERROR_FAILURE);
         }
         EditorDOMPoint newSel =
             HTMLEditorRef().GetGoodCaretPointFor(*leafNode, aAction);
         if (NS_WARN_IF(!newSel.IsSet())) {
-          return NS_ERROR_FAILURE;
+          return EditActionHandled(NS_ERROR_FAILURE);
         }
         IgnoredErrorResult error;
         SelectionRefPtr()->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;
+          return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
+        }
+        NS_WARNING_ASSERTION(!error.Failed(),
+                             "Selection::Collapse() failed, but ignored");
+        return EditActionHandled();
       }
 
       // Else we are joining content to block
-
+      EditActionResult result(NS_OK);
       EditorDOMPoint selPoint(startPoint);
       {
         AutoTrackDOMPoint tracker(HTMLEditorRef().RangeUpdaterRef(), &selPoint);
         if (NS_WARN_IF(!leftNode) || NS_WARN_IF(!leftNode->IsContent()) ||
             NS_WARN_IF(!rightNode) || NS_WARN_IF(!rightNode->IsContent())) {
-          return NS_ERROR_FAILURE;
-        }
-        EditActionResult ret = MOZ_KnownLive(HTMLEditorRef())
-                                   .TryToJoinBlocksWithTransaction(
-                                       MOZ_KnownLive(*leftNode->AsContent()),
-                                       MOZ_KnownLive(*rightNode->AsContent()));
-        *aHandled |= ret.Handled();
-        *aCancel |= ret.Canceled();
-        if (NS_WARN_IF(ret.Failed())) {
-          return ret.Rv();
+          return EditActionResult(NS_ERROR_FAILURE);
+        }
+        result |= MOZ_KnownLive(HTMLEditorRef())
+                      .TryToJoinBlocksWithTransaction(
+                          MOZ_KnownLive(*leftNode->AsContent()),
+                          MOZ_KnownLive(*rightNode->AsContent()));
+        if (NS_WARN_IF(result.Failed())) {
+          return result;
         }
       }
 
       // 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 != startPoint.GetContainer()) {
+      if (!result.Handled() && !result.Canceled() &&
+          leafNode != startPoint.GetContainer()) {
         int32_t offset = aAction == nsIEditor::ePrevious
                              ? static_cast<int32_t>(leafNode->Length())
                              : 0;
-        rv = SelectionRefPtr()->Collapse(leafNode, offset);
+        DebugOnly<nsresult> rv = SelectionRefPtr()->Collapse(leafNode, offset);
         if (NS_WARN_IF(!CanHandleEditAction())) {
-          return NS_ERROR_EDITOR_DESTROYED;
+          return result.SetResult(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;
+                             "Selection::Collapse() failed, but ignored");
+        EditActionResult result = WillDeleteSelection(aAction, aStripWrappers);
+        NS_WARNING_ASSERTION(result.Succeeded(),
+                             "Nested WillDeleteSelection() failed");
+        return result;
       }
 
       // Otherwise, we must have deleted the selection as user expected.
-      IgnoredErrorResult ignored;
-      SelectionRefPtr()->Collapse(selPoint, ignored);
+      IgnoredErrorResult ignoredError;
+      SelectionRefPtr()->Collapse(selPoint, ignoredError);
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
-      }
-      NS_WARNING_ASSERTION(!ignored.Failed(),
+        return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
+      }
+      NS_WARNING_ASSERTION(!ignoredError.Failed(),
                            "Failed to selection at deleted point");
-      return NS_OK;
+      return result;
     }
 
     if (wsType == WSType::thisBlock) {
       // At edge of our block.  Look beside it and see if we can join to an
       // adjacent block
 
       // Make sure it's not a table element.  If so, cancel the operation
       // (translation: users cannot backspace or delete across table cells)
       if (HTMLEditUtils::IsTableElement(visNode)) {
-        *aCancel = true;
-        return NS_OK;
+        return EditActionCanceled();
       }
 
       // First find the relevant nodes
       nsCOMPtr<nsINode> leftNode, rightNode;
       if (aAction == nsIEditor::ePrevious) {
         leftNode = HTMLEditorRef().GetPreviousEditableHTMLNode(*visNode);
         rightNode = startPoint.GetContainer();
       } else {
         rightNode = HTMLEditorRef().GetNextEditableHTMLNode(*visNode);
         leftNode = startPoint.GetContainer();
       }
 
       // Nothing to join
       if (!leftNode || !rightNode) {
-        *aCancel = true;
-        return NS_OK;
+        return EditActionCanceled();
       }
 
       // Don't cross table boundaries -- cancel it
       if (HTMLEditor::NodesInDifferentTableElements(*leftNode, *rightNode)) {
-        *aCancel = true;
-        return NS_OK;
-      }
-
+        return EditActionCanceled();
+      }
+
+      EditActionResult result(NS_OK);
       EditorDOMPoint selPoint(startPoint);
       {
         AutoTrackDOMPoint tracker(HTMLEditorRef().RangeUpdaterRef(), &selPoint);
         if (NS_WARN_IF(!leftNode->IsContent()) ||
             NS_WARN_IF(!rightNode->IsContent())) {
-          return NS_ERROR_FAILURE;
-        }
-        EditActionResult ret = MOZ_KnownLive(HTMLEditorRef())
-                                   .TryToJoinBlocksWithTransaction(
-                                       MOZ_KnownLive(*leftNode->AsContent()),
-                                       MOZ_KnownLive(*rightNode->AsContent()));
+          return EditActionResult(NS_ERROR_FAILURE);
+        }
+        result |= MOZ_KnownLive(HTMLEditorRef())
+                      .TryToJoinBlocksWithTransaction(
+                          MOZ_KnownLive(*leftNode->AsContent()),
+                          MOZ_KnownLive(*rightNode->AsContent()));
         // This should claim that trying to join the block means that
         // this handles the action because the caller shouldn't do anything
         // anymore in this case.
-        *aHandled = true;
-        *aCancel |= ret.Canceled();
-        if (NS_WARN_IF(ret.Failed())) {
-          return ret.Rv();
-        }
-      }
-      IgnoredErrorResult ignored;
-      SelectionRefPtr()->Collapse(selPoint, ignored);
+        result.MarkAsHandled();
+        if (NS_WARN_IF(result.Failed())) {
+          return result;
+        }
+      }
+      IgnoredErrorResult ignoredError;
+      SelectionRefPtr()->Collapse(selPoint, ignoredError);
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
-      }
-      NS_WARNING_ASSERTION(!ignored.Failed(), "Failed to collapse selection");
-      return NS_OK;
+        return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
+      }
+      NS_WARNING_ASSERTION(!ignoredError.Failed(),
+                           "Failed to collapse selection");
+      return result;
     }
   }
 
   // Else we have a non-collapsed selection.  First adjust the selection.
   // XXX Why do we extend selection only when there is only one range?
   if (SelectionRefPtr()->RangeCount() == 1) {
     if (nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0)) {
       RefPtr<StaticRange> extendedRange =
           MOZ_KnownLive(HTMLEditorRef())
               .GetExtendedRangeToIncludeInvisibleNodes(*firstRange);
       if (NS_WARN_IF(!extendedRange)) {
-        return NS_ERROR_FAILURE;
+        return EditActionResult(NS_ERROR_FAILURE);
       }
       ErrorResult error;
       MOZ_KnownLive(SelectionRefPtr())
           ->SetStartAndEndInLimiter(extendedRange->StartRef().AsRaw(),
                                     extendedRange->EndRef().AsRaw(), error);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         error.SuppressException();
-        return NS_ERROR_EDITOR_DESTROYED;
+        return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(error.Failed())) {
-        return error.StealNSResult();
+        return EditActionResult(error.StealNSResult());
       }
     }
   }
 
   // Remember that we did a ranged delete for the benefit of AfterEditInner().
   HTMLEditorRef().TopLevelEditSubActionDataRef().mDidDeleteNonCollapsedRange =
       true;
 
   // Refresh start and end points
   nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
   if (NS_WARN_IF(!firstRange)) {
-    return NS_ERROR_FAILURE;
+    return EditActionResult(NS_ERROR_FAILURE);
   }
   nsCOMPtr<nsINode> startNode = firstRange->GetStartContainer();
   if (NS_WARN_IF(!startNode)) {
-    return NS_ERROR_FAILURE;
+    return EditActionResult(NS_ERROR_FAILURE);
   }
   int32_t startOffset = firstRange->StartOffset();
   nsCOMPtr<nsINode> endNode = firstRange->GetEndContainer();
   if (NS_WARN_IF(!endNode)) {
-    return NS_ERROR_FAILURE;
+    return EditActionResult(NS_ERROR_FAILURE);
   }
   int32_t endOffset = firstRange->EndOffset();
 
   // 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(MOZ_KnownLive(&HTMLEditorRef()),
-                                           address_of(startNode), &startOffset,
-                                           address_of(endNode), &endOffset);
+    nsresult rv = WSRunObject::PrepareToDeleteRange(
+        MOZ_KnownLive(&HTMLEditorRef()), address_of(startNode), &startOffset,
+        address_of(endNode), &endOffset);
     if (NS_WARN_IF(!CanHandleEditAction())) {
-      return NS_ERROR_EDITOR_DESTROYED;
+      return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
+      return EditActionResult(rv);
+    }
+  }
+
+  EditActionResult result(NS_OK);
+  result.MarkAsHandled();
   {
     // Track location of where we are deleting
     AutoTrackDOMPoint startTracker(HTMLEditorRef().RangeUpdaterRef(),
                                    address_of(startNode), &startOffset);
     AutoTrackDOMPoint endTracker(HTMLEditorRef().RangeUpdaterRef(),
                                  address_of(endNode), &endOffset);
     // We are handling all ranged deletions directly now.
-    *aHandled = true;
 
     if (endNode == startNode) {
-      rv = MOZ_KnownLive(HTMLEditorRef())
-               .DeleteSelectionWithTransaction(aAction, aStripWrappers);
+      nsresult rv =
+          MOZ_KnownLive(HTMLEditorRef())
+              .DeleteSelectionWithTransaction(aAction, aStripWrappers);
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
+        return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return result.SetResult(rv);
       }
     } else {
       // Figure out mailcite ancestors
       RefPtr<Element> startCiteNode =
           HTMLEditorRef().GetMostAncestorMailCiteElement(*startNode);
       RefPtr<Element> endCiteNode =
           HTMLEditorRef().GetMostAncestorMailCiteElement(*endNode);
 
@@ -3068,90 +3042,89 @@ nsresult HTMLEditRules::WillDeleteSelect
       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) {
         MOZ_KnownLive(HTMLEditorRef())
             .DeleteSelectionWithTransaction(aAction, aStripWrappers);
         if (NS_WARN_IF(!CanHandleEditAction())) {
-          return NS_ERROR_EDITOR_DESTROYED;
+          return result.SetResult(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;
+          return result.SetResult(NS_ERROR_FAILURE);
         }
 
         // Are the blocks siblings?
         nsCOMPtr<nsINode> leftBlockParent = leftParent->GetParentNode();
         nsCOMPtr<nsINode> rightBlockParent = rightParent->GetParentNode();
 
         // MOOSE: this could conceivably screw up a table.. fix me.
         if (leftBlockParent == rightBlockParent &&
             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 = MOZ_KnownLive(HTMLEditorRef())
-                   .DeleteSelectionWithTransaction(aAction, aStripWrappers);
+          nsresult rv =
+              MOZ_KnownLive(HTMLEditorRef())
+                  .DeleteSelectionWithTransaction(aAction, aStripWrappers);
           if (NS_WARN_IF(!CanHandleEditAction())) {
-            return NS_ERROR_EDITOR_DESTROYED;
+            return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
-            return rv;
+            return result.SetResult(rv);
           }
           // Join blocks
           EditorDOMPoint pt =
               MOZ_KnownLive(HTMLEditorRef())
                   .JoinNodesDeepWithTransaction(*leftParent, *rightParent);
           if (NS_WARN_IF(!CanHandleEditAction())) {
-            return NS_ERROR_EDITOR_DESTROYED;
+            return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
           }
           if (NS_WARN_IF(!pt.IsSet())) {
-            return NS_ERROR_FAILURE;
+            return result.SetResult(NS_ERROR_FAILURE);
           }
           // Fix up selection
           ErrorResult error;
           SelectionRefPtr()->Collapse(pt, error);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             error.SuppressException();
-            return NS_ERROR_EDITOR_DESTROYED;
+            return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
           }
-          if (NS_WARN_IF(error.Failed())) {
-            return error.StealNSResult();
-          }
-          return NS_OK;
+          NS_WARNING_ASSERTION(!error.Failed(), "Selection::Collapse() failed");
+          return result.SetResult(error.StealNSResult());
         }
 
         // Else blocks not same type, or not siblings.  Delete everything
         // except table elements.
         join = true;
 
         AutoRangeArray arrayOfRanges(SelectionRefPtr());
         for (auto& range : arrayOfRanges.mRanges) {
           // Build a list of nodes in the range
           nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
           TrivialFunctor functor;
           DOMSubtreeIterator iter;
           nsresult rv = iter.Init(*range);
           if (NS_WARN_IF(NS_FAILED(rv))) {
-            return rv;
+            return result.SetResult(rv);
           }
           iter.AppendList(functor, arrayOfNodes);
 
           // Now that we have the list, delete non-table elements
           int32_t listCount = arrayOfNodes.Length();
           for (int32_t j = 0; j < listCount; j++) {
             OwningNonNull<nsINode> node = arrayOfNodes[0];
             nsresult rv = DeleteElementsExceptTableRelatedElements(node);
             if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
-              return NS_ERROR_EDITOR_DESTROYED;
+              return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
             }
             NS_WARNING_ASSERTION(
                 NS_SUCCEEDED(rv),
                 "Failed to elements except table related elements");
             arrayOfNodes.RemoveElementAt(0);
             // If something visible is deleted, no need to join.  Visible means
             // all nodes except non-visible textnodes and breaks.
             if (join && origCollapsed) {
@@ -3173,48 +3146,46 @@ nsresult HTMLEditRules::WillDeleteSelect
         // Check endpoints for possible text deletion.  We can assume that if
         // text node is found, we can delete to end or to begining as
         // appropriate, since the case where both sel endpoints in same text
         // node was already handled (we wouldn't be here)
         if (startNode->IsText() &&
             startNode->Length() > static_cast<uint32_t>(startOffset)) {
           // Delete to last character
           OwningNonNull<Text> textNode = *startNode->AsText();
-          rv =
+          nsresult rv =
               MOZ_KnownLive(HTMLEditorRef())
                   .DeleteTextWithTransaction(textNode, startOffset,
                                              startNode->Length() - startOffset);
           if (NS_WARN_IF(!CanHandleEditAction())) {
-            return NS_ERROR_EDITOR_DESTROYED;
+            return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
-            return rv;
+            return result.SetResult(rv);
           }
         }
         if (endNode->IsText() && endOffset) {
           // Delete to first character
           OwningNonNull<Text> textNode = *endNode->AsText();
-          rv = MOZ_KnownLive(HTMLEditorRef())
-                   .DeleteTextWithTransaction(textNode, 0, endOffset);
+          nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                            .DeleteTextWithTransaction(textNode, 0, endOffset);
           if (NS_WARN_IF(!CanHandleEditAction())) {
-            return NS_ERROR_EDITOR_DESTROYED;
+            return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
-            return rv;
+            return result.SetResult(rv);
           }
         }
 
         if (join) {
-          EditActionResult ret =
+          result |=
               MOZ_KnownLive(HTMLEditorRef())
                   .TryToJoinBlocksWithTransaction(*leftParent, *rightParent);
-          MOZ_ASSERT(*aHandled);
-          *aCancel |= ret.Canceled();
-          if (NS_WARN_IF(ret.Failed())) {
-            return ret.Rv();
+          if (NS_WARN_IF(result.Failed())) {
+            return result;
           }
         }
       }
     }
   }
 
   // If we're handling D&D, this is called to delete dragging item from the
   // tree.  In this case, we should move parent blocks if it becomes empty.
@@ -3226,89 +3197,84 @@ nsresult HTMLEditRules::WillDeleteSelect
                                      address_of(startNode), &startOffset);
       AutoTrackDOMPoint endTracker(HTMLEditorRef().RangeUpdaterRef(),
                                    address_of(endNode), &endOffset);
 
       EditorDOMPoint atStart(startNode, startOffset);
       nsresult rv = MOZ_KnownLive(HTMLEditorRef())
                         .DeleteParentBlocksWithTransactionIfEmpty(atStart);
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return result.SetResult(rv);
       }
       HTMLEditorRef()
           .TopLevelEditSubActionDataRef()
           .mDidDeleteEmptyParentBlocks = rv == NS_OK;
     }
     // If we removed parent blocks, Selection should be collapsed at where
     // the most ancestor empty block has been.
     if (HTMLEditorRef()
             .TopLevelEditSubActionDataRef()
             .mDidDeleteEmptyParentBlocks) {
-      rv = SelectionRefPtr()->Collapse(startNode, startOffset);
+      nsresult rv = SelectionRefPtr()->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;
+        return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
+      }
+      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Selection::Collapse() failed");
+      return result.SetResult(rv);
     }
   }
 
   // We might have left only collapsed whitespace in the start/end nodes
   {
     AutoTrackDOMPoint startTracker(HTMLEditorRef().RangeUpdaterRef(),
                                    address_of(startNode), &startOffset);
     AutoTrackDOMPoint endTracker(HTMLEditorRef().RangeUpdaterRef(),
                                  address_of(endNode), &endOffset);
 
     nsresult rv = MOZ_KnownLive(HTMLEditorRef())
                       .DeleteNodeIfInvisibleAndEditableTextNode(*startNode);
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
-      return NS_ERROR_EDITOR_DESTROYED;
+      return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
     }
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "DeleteNodeIfInvisibleAndEditableTextNode() failed to "
                          "remove start node, but ignored");
     rv = MOZ_KnownLive(HTMLEditorRef())
              .DeleteNodeIfInvisibleAndEditableTextNode(*endNode);
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
-      return NS_ERROR_EDITOR_DESTROYED;
+      return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
     }
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "DeleteNodeIfInvisibleAndEditableTextNode() failed to "
                          "remove end node, but ignored");
   }
 
   // If we're joining blocks: if deleting forward the selection should be
   // collapsed to the end of the selection, if deleting backward the selection
   // 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 = SelectionRefPtr()->Collapse(endNode, endOffset);
+    nsresult rv = SelectionRefPtr()->Collapse(endNode, endOffset);
     if (NS_WARN_IF(!CanHandleEditAction())) {
-      return NS_ERROR_EDITOR_DESTROYED;
-    }
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  } else {
-    rv = SelectionRefPtr()->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;
+      return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
+    }
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Selection::Collapse() failed");
+    return result.SetResult(rv);
+  }
+
+  rv = SelectionRefPtr()->Collapse(startNode, startOffset);
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return result.SetResult(NS_ERROR_EDITOR_DESTROYED);
+  }
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Selection::Collapse() failed");
+  return result.SetResult(rv);
 }
 
 nsresult HTMLEditor::DeleteNodeIfInvisibleAndEditableTextNode(nsINode& aNode) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   Text* text = aNode.GetAsText();
   if (!text) {
     return NS_OK;
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -119,23 +119,19 @@ class HTMLEditRules : public TextEditRul
   }
 
   /**
    * 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_CAN_RUN_SCRIPT
-  MOZ_MUST_USE nsresult WillDeleteSelection(
-      nsIEditor::EDirection aAction, nsIEditor::EStripWrappers aStripWrappers,
-      bool* aCancel, bool* aHandled);
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE EditActionResult WillDeleteSelection(
+      nsIEditor::EDirection aAction, nsIEditor::EStripWrappers aStripWrappers);
 
   /**
    * Called after deleting selected content.
    * This method removes unnecessary empty nodes and/or inserts <br> if
    * necessary.
    */
   MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult DidDeleteSelection();