Bug 1637856 - part 1: Move `WSRunObject::DeleteRange()` to `HTMLEditor` r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 18 May 2020 05:12:16 +0000
changeset 530550 3ead0007c8fb0f39dd9953b751a014de834fe87c
parent 530549 734c4f395eba16391772a817a2cccd4a078853af
child 530551 ac9f8166fa65f84f404575292dcbae15affb2827
push id116231
push usermasayuki@d-toybox.com
push dateMon, 18 May 2020 06:50:56 +0000
treeherderautoland@ac9f8166fa65 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1637856
milestone78.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 1637856 - part 1: Move `WSRunObject::DeleteRange()` to `HTMLEditor` r=m_kato `WSRunObject::DeleteRange()` removes only text nodes which are stored when `WSRunObject` is created. Although it removes text nodes if it's removed, this patch does not take care about it in the new method. The reason is the following patch will remove `mNodeArray` and anyway DOM tree modifiers can check whether they are in proper position before access if it's needed. Differential Revision: https://phabricator.services.mozilla.com/D75470
editor/libeditor/HTMLEditSubActionHandler.cpp
editor/libeditor/HTMLEditor.h
editor/libeditor/WSRunObject.cpp
editor/libeditor/WSRunObject.h
--- a/editor/libeditor/HTMLEditSubActionHandler.cpp
+++ b/editor/libeditor/HTMLEditSubActionHandler.cpp
@@ -191,16 +191,21 @@ template EditorDOMPoint HTMLEditor::GetC
 template Element* HTMLEditor::GetInvisibleBRElementAt(
     const EditorDOMPoint& aPoint);
 template Element* HTMLEditor::GetInvisibleBRElementAt(
     const EditorRawDOMPoint& aPoint);
 template nsIContent* HTMLEditor::FindNearEditableContent(
     const EditorDOMPoint& aPoint, nsIEditor::EDirection aDirection);
 template nsIContent* HTMLEditor::FindNearEditableContent(
     const EditorRawDOMPoint& aPoint, nsIEditor::EDirection aDirection);
+template nsresult HTMLEditor::DeleteTextAndTextNodesWithTransaction(
+    const EditorDOMPoint& aStartPoint, const EditorDOMPoint& aEndPoint);
+template nsresult HTMLEditor::DeleteTextAndTextNodesWithTransaction(
+    const EditorDOMPointInText& aStartPoint,
+    const EditorDOMPointInText& aEndPoint);
 
 nsresult HTMLEditor::InitEditorContentAndSelection() {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   nsresult rv = TextEditor::InitEditorContentAndSelection();
   if (NS_FAILED(rv)) {
     NS_WARNING("TextEditor::InitEditorContentAndSelection() failed");
     return rv;
@@ -3561,16 +3566,110 @@ nsresult HTMLEditor::DeleteNodeIfInvisib
   if (NS_WARN_IF(Destroyed())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                        "HTMLEditor::DeleteNodeWithTransaction() failed");
   return rv;
 }
 
+template <typename EditorDOMPointType>
+nsresult HTMLEditor::DeleteTextAndTextNodesWithTransaction(
+    const EditorDOMPointType& aStartPoint,
+    const EditorDOMPointType& aEndPoint) {
+  if (NS_WARN_IF(!aStartPoint.IsSet()) || NS_WARN_IF(!aEndPoint.IsSet())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  // MOOSE: this routine needs to be modified to preserve the integrity of the
+  // wsFragment info.
+
+  if (aStartPoint == aEndPoint) {
+    // Nothing to delete
+    return NS_OK;
+  }
+
+  if (aStartPoint.GetContainer() == aEndPoint.GetContainer() &&
+      aStartPoint.IsInTextNode()) {
+    RefPtr<Text> textNode = aStartPoint.ContainerAsText();
+    nsresult rv =
+        DeleteTextWithTransaction(*textNode, aStartPoint.Offset(),
+                                  aEndPoint.Offset() - aStartPoint.Offset());
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                         "HTMLEditor::DeleteTextWithTransaction() failed");
+    return rv;
+  }
+
+  RefPtr<nsRange> range =
+      nsRange::Create(aStartPoint.ToRawRangeBoundary(),
+                      aEndPoint.ToRawRangeBoundary(), IgnoreErrors());
+  if (!range) {
+    NS_WARNING("nsRange::Create() failed");
+    return NS_ERROR_FAILURE;
+  }
+
+  // Collect editable text nodes in the given range.
+  AutoTArray<OwningNonNull<Text>, 16> arrayOfTextNodes;
+  DOMIterator iter;
+  if (NS_FAILED(iter.Init(*range))) {
+    return NS_OK;  // Nothing to delete in the range.
+  }
+  iter.AppendNodesToArray(
+      +[](nsINode& aNode, void*) {
+        MOZ_ASSERT(aNode.IsText());
+        return HTMLEditUtils::IsSimplyEditableNode(aNode);
+      },
+      arrayOfTextNodes);
+  for (OwningNonNull<Text>& textNode : arrayOfTextNodes) {
+    if (textNode == aStartPoint.GetContainer()) {
+      if (aStartPoint.IsEndOfContainer()) {
+        continue;
+      }
+      nsresult rv = DeleteTextWithTransaction(
+          MOZ_KnownLive(textNode), aStartPoint.Offset(),
+          textNode->Length() - aStartPoint.Offset());
+      if (NS_WARN_IF(Destroyed())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
+      if (NS_FAILED(rv)) {
+        NS_WARNING("HTMLEditor::DeleteTextWithTransaction() failed");
+        return rv;
+      }
+      continue;
+    }
+
+    if (textNode == aEndPoint.GetContainer()) {
+      if (aEndPoint.IsStartOfContainer()) {
+        break;
+      }
+      nsresult rv = DeleteTextWithTransaction(MOZ_KnownLive(textNode), 0,
+                                              aEndPoint.Offset());
+      if (NS_WARN_IF(Destroyed())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
+      if (NS_FAILED(rv)) {
+        NS_WARNING("HTMLEditor::DeleteTextWithTransaction() failed");
+        return rv;
+      }
+      return NS_OK;
+    }
+
+    nsresult rv = DeleteNodeWithTransaction(MOZ_KnownLive(textNode));
+    if (NS_WARN_IF(Destroyed())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
+    if (NS_FAILED(rv)) {
+      NS_WARNING("HTMLEditor::DeleteNodeWithTransaction() failed");
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
 nsresult HTMLEditor::InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary(
     const EditorDOMPoint& aPointToInsert) {
   MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(aPointToInsert.IsSet());
 
   if (!aPointToInsert.GetContainerAsContent()) {
     return NS_OK;
   }
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -2008,16 +2008,26 @@ class HTMLEditor final : public TextEdit
   /**
    * If aContent is a text node that contains only collapsed whitespace or empty
    * and editable.
    */
   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
   DeleteNodeIfInvisibleAndEditableTextNode(nsIContent& aContent);
 
   /**
+   * DeleteTextAndTextNodesWithTransaction() removes text nodes which are in
+   * the given range and delete some characters in start and/or end of
+   * the range.
+   */
+  template <typename EditorDOMPointType>
+  [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
+  DeleteTextAndTextNodesWithTransaction(const EditorDOMPointType& aStartPoint,
+                                        const EditorDOMPointType& aEndPoint);
+
+  /**
    * If aPoint follows invisible `<br>` element, returns the invisible `<br>`
    * element.  Otherwise, nullptr.
    */
   template <typename PT, typename CT>
   Element* GetInvisibleBRElementAt(const EditorDOMPointBase<PT, CT>& aPoint);
 
   /**
    * JoinNodesWithTransaction() joins aLeftNode and aRightNode.  Content of
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -209,19 +209,22 @@ already_AddRefed<Element> WSRunObject::I
 
     // Handle any changes needed to ws run after inserted br
     if (!afterRun || afterRun->IsEndOfHardLine()) {
       // Don't need to do anything.  Just insert break.  ws won't change.
     } else if (afterRun->IsStartOfHardLine()) {
       // Delete the leading ws that is after insertion point.  We don't
       // have to (it would still not be significant after br), but it's
       // just more aesthetically pleasing to.
-      nsresult rv = DeleteRange(pointToInsert, afterRun->EndPoint());
+      nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                        .DeleteTextAndTextNodesWithTransaction(
+                            pointToInsert, afterRun->EndPoint());
       if (NS_FAILED(rv)) {
-        NS_WARNING("WSRunObject::DeleteRange() failed");
+        NS_WARNING(
+            "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
         return nullptr;
       }
     } else if (afterRun->IsVisibleAndMiddleOfHardLine()) {
       // Need to determine if break at front of non-nbsp run.  If so, convert
       // run to nbsp.
       EditorDOMPointInText atNextCharOfInsertionPoint =
           GetNextCharPoint(pointToInsert);
       if (atNextCharOfInsertionPoint.IsSet() &&
@@ -246,19 +249,22 @@ already_AddRefed<Element> WSRunObject::I
     }
 
     // Handle any changes needed to ws run before inserted br
     if (!beforeRun || beforeRun->IsStartOfHardLine()) {
       // Don't need to do anything.  Just insert break.  ws won't change.
     } else if (beforeRun->IsEndOfHardLine()) {
       // Need to delete the trailing ws that is before insertion point, because
       // it would become significant after break inserted.
-      nsresult rv = DeleteRange(beforeRun->StartPoint(), pointToInsert);
+      nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                        .DeleteTextAndTextNodesWithTransaction(
+                            beforeRun->StartPoint(), pointToInsert);
       if (NS_FAILED(rv)) {
-        NS_WARNING("WSRunObject::DeleteRange() failed");
+        NS_WARNING(
+            "WSRunObject::DeleteTextAndTextNodesWithTransaction() failed");
         return nullptr;
       }
     } else if (beforeRun->IsVisibleAndMiddleOfHardLine()) {
       // Try to change an nbsp to a space, just to prevent nbsp proliferation
       nsresult rv = ReplacePreviousNBSPIfUnnecessary(beforeRun, pointToInsert);
       if (NS_FAILED(rv)) {
         NS_WARNING("WSRunObject::ReplacePreviousNBSPIfUnnecessary() failed");
         return nullptr;
@@ -308,19 +314,22 @@ nsresult WSRunObject::InsertText(Documen
     AutoTrackDOMPoint tracker(mHTMLEditor.RangeUpdaterRef(), &pointToInsert);
 
     // Handle any changes needed to ws run after inserted text
     if (!afterRun || afterRun->IsEndOfHardLine()) {
       // Don't need to do anything.  Just insert text.  ws won't change.
     } else if (afterRun->IsStartOfHardLine()) {
       // Delete the leading ws that is after insertion point, because it
       // would become significant after text inserted.
-      nsresult rv = DeleteRange(pointToInsert, afterRun->EndPoint());
+      nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                        .DeleteTextAndTextNodesWithTransaction(
+                            pointToInsert, afterRun->EndPoint());
       if (NS_FAILED(rv)) {
-        NS_WARNING("WSRunObject::DeleteRange() failed");
+        NS_WARNING(
+            "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
         return rv;
       }
     } else if (afterRun->IsVisibleAndMiddleOfHardLine()) {
       // Try to change an nbsp to a space, if possible, just to prevent nbsp
       // proliferation
       nsresult rv = CheckLeadingNBSP(
           afterRun, MOZ_KnownLive(pointToInsert.GetContainer()),
           pointToInsert.Offset());
@@ -331,19 +340,22 @@ nsresult WSRunObject::InsertText(Documen
     }
 
     // Handle any changes needed to ws run before inserted text
     if (!beforeRun || beforeRun->IsStartOfHardLine()) {
       // Don't need to do anything.  Just insert text.  ws won't change.
     } else if (beforeRun->IsEndOfHardLine()) {
       // Need to delete the trailing ws that is before insertion point, because
       // it would become significant after text inserted.
-      nsresult rv = DeleteRange(beforeRun->StartPoint(), pointToInsert);
+      nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                        .DeleteTextAndTextNodesWithTransaction(
+                            beforeRun->StartPoint(), pointToInsert);
       if (NS_FAILED(rv)) {
-        NS_WARNING("WSRunObject::DeleteRange() failed");
+        NS_WARNING(
+            "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
         return rv;
       }
     } else if (beforeRun->IsVisibleAndMiddleOfHardLine()) {
       // Try to change an nbsp to a space, if possible, just to prevent nbsp
       // proliferation
       nsresult rv = ReplacePreviousNBSPIfUnnecessary(beforeRun, pointToInsert);
       if (NS_FAILED(rv)) {
         NS_WARNING("WSRunObject::ReplacePreviousNBSPIfUnnecessary() failed");
@@ -451,18 +463,22 @@ nsresult WSRunObject::DeleteWSBackward()
 
   // Easy case, preformatted ws.
   if (mPRE) {
     if (!atPreviousCharOfStart.IsCharASCIISpace() &&
         !atPreviousCharOfStart.IsCharNBSP()) {
       return NS_OK;
     }
     nsresult rv =
-        DeleteRange(atPreviousCharOfStart, atPreviousCharOfStart.NextPoint());
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WSRunObject::DeleteRange() failed");
+        MOZ_KnownLive(mHTMLEditor)
+            .DeleteTextAndTextNodesWithTransaction(
+                atPreviousCharOfStart, atPreviousCharOfStart.NextPoint());
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
     return rv;
   }
 
   // Caller's job to ensure that previous char is really ws.  If it is normal
   // ws, we need to delete the whole run.
   if (atPreviousCharOfStart.IsCharASCIISpace()) {
     EditorDOMPointInText start, end;
     Tie(start, end) =
@@ -479,34 +495,40 @@ nsresult WSRunObject::DeleteWSBackward()
     nsresult rv = WSRunObject::PrepareToDeleteRange(
         MOZ_KnownLive(mHTMLEditor), &startToDelete, &endToDelete);
     if (NS_FAILED(rv)) {
       NS_WARNING("WSRunObject::PrepareToDeleteRange() failed");
       return rv;
     }
 
     // finally, delete that ws
-    rv = DeleteRange(startToDelete, endToDelete);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WSRunObject::DeleteRange() failed");
+    rv = MOZ_KnownLive(mHTMLEditor)
+             .DeleteTextAndTextNodesWithTransaction(startToDelete, endToDelete);
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
     return rv;
   }
 
   if (atPreviousCharOfStart.IsCharNBSP()) {
     EditorDOMPoint startToDelete(atPreviousCharOfStart);
     EditorDOMPoint endToDelete(startToDelete.NextPoint());
     nsresult rv = WSRunObject::PrepareToDeleteRange(
         MOZ_KnownLive(mHTMLEditor), &startToDelete, &endToDelete);
     if (NS_FAILED(rv)) {
       NS_WARNING("WSRunObject::PrepareToDeleteRange() failed");
       return rv;
     }
 
     // finally, delete that ws
-    rv = DeleteRange(startToDelete, endToDelete);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WSRunObject::DeleteRange() failed");
+    rv = MOZ_KnownLive(mHTMLEditor)
+             .DeleteTextAndTextNodesWithTransaction(startToDelete, endToDelete);
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult WSRunObject::DeleteWSForward() {
   EditorDOMPointInText atNextCharOfStart = GetNextCharPoint(mScanStartPoint);
@@ -515,18 +537,22 @@ nsresult WSRunObject::DeleteWSForward() 
   }
 
   // Easy case, preformatted ws.
   if (mPRE) {
     if (!atNextCharOfStart.IsCharASCIISpace() &&
         !atNextCharOfStart.IsCharNBSP()) {
       return NS_OK;
     }
-    nsresult rv = DeleteRange(atNextCharOfStart, atNextCharOfStart.NextPoint());
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WSRunObject::DeleteRange() failed");
+    nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                      .DeleteTextAndTextNodesWithTransaction(
+                          atNextCharOfStart, atNextCharOfStart.NextPoint());
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
     return rv;
   }
 
   // Caller's job to ensure that next char is really ws.  If it is normal ws,
   // we need to delete the whole run.
   if (atNextCharOfStart.IsCharASCIISpace()) {
     EditorDOMPointInText start, end;
     Tie(start, end) =
@@ -542,34 +568,40 @@ nsresult WSRunObject::DeleteWSForward() 
     nsresult rv = WSRunObject::PrepareToDeleteRange(
         MOZ_KnownLive(mHTMLEditor), &startToDelete, &endToDelete);
     if (NS_FAILED(rv)) {
       NS_WARNING("WSRunObject::PrepareToDeleteRange() failed");
       return rv;
     }
 
     // Finally, delete that ws
-    rv = DeleteRange(startToDelete, endToDelete);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WSRunObject::DeleteRange() failed");
+    rv = MOZ_KnownLive(mHTMLEditor)
+             .DeleteTextAndTextNodesWithTransaction(startToDelete, endToDelete);
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
     return rv;
   }
 
   if (atNextCharOfStart.IsCharNBSP()) {
     EditorDOMPoint startToDelete(atNextCharOfStart);
     EditorDOMPoint endToDelete(startToDelete.NextPoint());
     nsresult rv = WSRunObject::PrepareToDeleteRange(
         MOZ_KnownLive(mHTMLEditor), &startToDelete, &endToDelete);
     if (NS_FAILED(rv)) {
       NS_WARNING("WSRunObject::PrepareToDeleteRange() failed");
       return rv;
     }
 
     // Finally, delete that ws
-    rv = DeleteRange(startToDelete, endToDelete);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WSRunObject::DeleteRange() failed");
+    rv = MOZ_KnownLive(mHTMLEditor)
+             .DeleteTextAndTextNodesWithTransaction(startToDelete, endToDelete);
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
     return rv;
   }
 
   return NS_OK;
 }
 
 template <typename PT, typename CT>
 WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
@@ -1116,20 +1148,22 @@ nsresult WSRunObject::PrepareToDeleteRan
   }
 
   if (afterRun) {
     // trim after run of any leading ws
     if (afterRun->IsStartOfHardLine()) {
       // mScanStartPoint will be referred bellow so that we need to keep
       // it a valid point.
       AutoEditorDOMPointChildInvalidator forgetChild(mScanStartPoint);
-      nsresult rv = aEndObject->DeleteRange(aEndObject->mScanStartPoint,
-                                            afterRun->EndPoint());
+      nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                        .DeleteTextAndTextNodesWithTransaction(
+                            aEndObject->mScanStartPoint, afterRun->EndPoint());
       if (NS_FAILED(rv)) {
-        NS_WARNING("WSRunObject::DeleteRange() failed");
+        NS_WARNING(
+            "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
         return rv;
       }
     }
     // adjust normal ws in afterRun if needed
     else if (afterRun->IsVisibleAndMiddleOfHardLine() && !aEndObject->mPRE) {
       if ((beforeRun && beforeRun->IsStartOfHardLine()) ||
           (!beforeRun && StartsFromHardLineBreak())) {
         // make sure leading char of following ws is an nbsp, so that it will
@@ -1157,22 +1191,23 @@ nsresult WSRunObject::PrepareToDeleteRan
   }
 
   if (!beforeRun) {
     return NS_OK;
   }
 
   // trim before run of any trailing ws
   if (beforeRun->IsEndOfHardLine()) {
-    nsresult rv = DeleteRange(beforeRun->StartPoint(), mScanStartPoint);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("WSRunObject::DeleteRange() failed");
-      return rv;
-    }
-    return NS_OK;
+    nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                      .DeleteTextAndTextNodesWithTransaction(
+                          beforeRun->StartPoint(), mScanStartPoint);
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
+    return rv;
   }
 
   if (beforeRun->IsVisibleAndMiddleOfHardLine() && !mPRE) {
     if ((afterRun && (afterRun->IsEndOfHardLine() || afterRun->IsVisible())) ||
         (!afterRun && aEndObject->EndsByBlockBoundary())) {
       // make sure trailing char of starting ws is an nbsp, so that it will show
       // up
       EditorDOMPointInText atPreviousCharOfStart =
@@ -1255,117 +1290,16 @@ nsresult WSRunObject::PrepareToSplitAcro
             "failed");
         return rv;
       }
     }
   }
   return NS_OK;
 }
 
-nsresult WSRunObject::DeleteRange(const EditorDOMPoint& aStartPoint,
-                                  const EditorDOMPoint& aEndPoint) {
-  if (NS_WARN_IF(!aStartPoint.IsSet()) || NS_WARN_IF(!aEndPoint.IsSet())) {
-    return NS_ERROR_INVALID_ARG;
-  }
-  MOZ_ASSERT(aStartPoint.IsSetAndValid());
-  MOZ_ASSERT(aEndPoint.IsSetAndValid());
-
-  // MOOSE: this routine needs to be modified to preserve the integrity of the
-  // wsFragment info.
-
-  if (aStartPoint == aEndPoint) {
-    // Nothing to delete
-    return NS_OK;
-  }
-
-  if (aStartPoint.GetContainer() == aEndPoint.GetContainer() &&
-      aStartPoint.IsInTextNode()) {
-    RefPtr<Text> textNode = aStartPoint.ContainerAsText();
-    nsresult rv = MOZ_KnownLive(mHTMLEditor)
-                      .DeleteTextWithTransaction(
-                          *textNode, aStartPoint.Offset(),
-                          aEndPoint.Offset() - aStartPoint.Offset());
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
-                         "HTMLEditor::DeleteTextWithTransaction() failed");
-    return rv;
-  }
-
-  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.
-    idx = 0;
-  }
-  for (; idx < count; idx++) {
-    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 = MOZ_KnownLive(mHTMLEditor)
-                          .DeleteTextWithTransaction(
-                              *node, aStartPoint.Offset(),
-                              aStartPoint.GetContainer()->Length() -
-                                  aStartPoint.Offset());
-        if (NS_FAILED(rv)) {
-          NS_WARNING("HTMLEditor::DeleteTextWithTransaction() failed");
-          return rv;
-        }
-      }
-    } else if (node == aEndPoint.GetContainer()) {
-      if (!aEndPoint.IsStartOfContainer()) {
-        nsresult rv =
-            MOZ_KnownLive(mHTMLEditor)
-                .DeleteTextWithTransaction(*node, 0, aEndPoint.Offset());
-        if (NS_FAILED(rv)) {
-          NS_WARNING("HTMLEditor::DeleteTextWithTransaction() failed");
-          return rv;
-        }
-      }
-      break;
-    } else {
-      if (!range) {
-        ErrorResult error;
-        range = nsRange::Create(aStartPoint.ToRawRangeBoundary(),
-                                aEndPoint.ToRawRangeBoundary(), error);
-        if (!range) {
-          NS_WARNING("nsRange::Create() failed");
-          return error.StealNSResult();
-        }
-      }
-      bool nodeBefore, nodeAfter;
-      nsresult rv =
-          RangeUtils::CompareNodeToRange(node, range, &nodeBefore, &nodeAfter);
-      if (NS_FAILED(rv)) {
-        NS_WARNING("RangeUtils::CompareNodeToRange() failed");
-        return rv;
-      }
-      if (nodeAfter) {
-        break;
-      }
-      if (!nodeBefore) {
-        nsresult rv =
-            MOZ_KnownLive(mHTMLEditor).DeleteNodeWithTransaction(*node);
-        if (NS_FAILED(rv)) {
-          NS_WARNING("HTMLEditor::DeleteNodeWithTransaction() failed");
-          return rv;
-        }
-        mNodeArray.RemoveElement(node);
-        --count;
-        --idx;
-      }
-    }
-  }
-  return NS_OK;
-}
-
 template <typename PT, typename CT>
 EditorDOMPointInText WSRunScanner::GetNextCharPoint(
     const EditorDOMPointBase<PT, CT>& aPoint) const {
   MOZ_ASSERT(aPoint.IsSetAndValid());
 
   size_t index = aPoint.IsInTextNode()
                      ? mNodeArray.IndexOf(aPoint.GetContainer())
                      : decltype(mNodeArray)::NoIndex;
@@ -1476,18 +1410,21 @@ nsresult WSRunObject::InsertNBSPAndRemov
   if (!start.IsSet()) {
     return NS_OK;
   }
   NS_WARNING_ASSERTION(end.IsSet(),
                        "WSRunObject::GetASCIIWhitespacesBounds() didn't return "
                        "end point, but ignored");
 
   // Finally, delete that replaced ws, if any
-  rv = DeleteRange(start, end);
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WSRunObject::DeleteRange() failed");
+  rv = MOZ_KnownLive(mHTMLEditor)
+           .DeleteTextAndTextNodesWithTransaction(start, end);
+  NS_WARNING_ASSERTION(
+      NS_SUCCEEDED(rv),
+      "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
   return rv;
 }
 
 template <typename PT, typename CT>
 Tuple<EditorDOMPointInText, EditorDOMPointInText>
 WSRunObject::GetASCIIWhitespacesBounds(
     int16_t aDir, const EditorDOMPointBase<PT, CT>& aPoint) const {
   MOZ_ASSERT(aPoint.IsSet());
@@ -1803,21 +1740,23 @@ nsresult WSRunObject::CheckTrailingNBSPO
                    "The text node was modified by mutation event listener");
       if (!atPreviousCharOfEndOfRun.IsEndOfContainer() &&
           !atPreviousCharOfEndOfRun.IsAtLastContent()) {
         NS_ASSERTION(atPreviousCharOfEndOfRun.IsNextCharNBSP(),
                      "Trying to remove an NBSP, but it's gone from the "
                      "expected position");
         EditorDOMPointInText atNextCharOfPreviousCharOfEndOfRun =
             atPreviousCharOfEndOfRun.NextPoint();
-        nsresult rv =
-            DeleteRange(atNextCharOfPreviousCharOfEndOfRun,
-                        atNextCharOfPreviousCharOfEndOfRun.NextPoint());
+        nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                          .DeleteTextAndTextNodesWithTransaction(
+                              atNextCharOfPreviousCharOfEndOfRun,
+                              atNextCharOfPreviousCharOfEndOfRun.NextPoint());
         if (NS_FAILED(rv)) {
-          NS_WARNING("WSRunObject::DeleteRange() failed");
+          NS_WARNING(
+              "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
           return rv;
         }
       }
     } else if (!mPRE && spaceNBSP && rightCheck) {
       // Don't mess with this preformatted for now.  We have a run of ASCII
       // whitespace (which will render as one space) followed by an nbsp (which
       // is at the end of the whitespace run).  Let's switch their order.  This
       // will ensure that if someone types two spaces after a sentence, and the
@@ -1837,20 +1776,23 @@ nsresult WSRunObject::CheckTrailingNBSPO
 
       // Delete that nbsp
       NS_ASSERTION(!atPreviousCharOfEndOfRun.IsEndOfContainer(),
                    "The text node was modified by mutation event listener");
       if (!atPreviousCharOfEndOfRun.IsEndOfContainer()) {
         NS_ASSERTION(atPreviousCharOfEndOfRun.IsCharNBSP(),
                      "Trying to remove an NBSP, but it's gone from the "
                      "expected position");
-        nsresult rv = DeleteRange(atPreviousCharOfEndOfRun,
-                                  atPreviousCharOfEndOfRun.NextPoint());
+        nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                          .DeleteTextAndTextNodesWithTransaction(
+                              atPreviousCharOfEndOfRun,
+                              atPreviousCharOfEndOfRun.NextPoint());
         if (NS_FAILED(rv)) {
-          NS_WARNING("WSRunObject::DeleteRange() failed");
+          NS_WARNING(
+              "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
           return rv;
         }
       }
 
       // Finally, insert that nbsp before the ASCII ws run
       NS_ASSERTION(start.IsSetAndValid(),
                    "The text node was modified by mutation event listener");
       if (start.IsSetAndValid()) {
@@ -1922,19 +1864,23 @@ nsresult WSRunObject::ReplacePreviousNBS
   NS_ASSERTION(
       !atPreviousChar.IsEndOfContainer() && !atPreviousChar.IsAtLastContent(),
       "The text node was modified by mutation event listener");
   if (!atPreviousChar.IsEndOfContainer() && !atPreviousChar.IsAtLastContent()) {
     NS_ASSERTION(
         atPreviousChar.IsNextCharNBSP(),
         "Trying to remove an NBSP, but it's gone from the expected position");
     EditorDOMPointInText atNextCharOfPreviousChar = atPreviousChar.NextPoint();
-    nsresult rv = DeleteRange(atNextCharOfPreviousChar,
-                              atNextCharOfPreviousChar.NextPoint());
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WSRunObject::DeleteRange() failed");
+    nsresult rv =
+        MOZ_KnownLive(mHTMLEditor)
+            .DeleteTextAndTextNodesWithTransaction(
+                atNextCharOfPreviousChar, atNextCharOfPreviousChar.NextPoint());
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult WSRunObject::CheckLeadingNBSP(WSFragment* aRun, nsINode* aNode,
                                        int32_t aOffset) {
@@ -1977,32 +1923,37 @@ nsresult WSRunObject::CheckLeadingNBSP(W
     NS_ASSERTION(
         !atNextChar.IsEndOfContainer() && !atNextChar.IsAtLastContent(),
         "The text node was modified by mutation event listener");
     if (!atNextChar.IsEndOfContainer() && !atNextChar.IsAtLastContent()) {
       NS_ASSERTION(
           atNextChar.IsNextCharNBSP(),
           "Trying to remove an NBSP, but it's gone from the expected position");
       EditorDOMPointInText atNextCharOfNextChar = atNextChar.NextPoint();
-      rv = DeleteRange(atNextCharOfNextChar, atNextCharOfNextChar.NextPoint());
-      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
-                           "WSRunObject::DeleteRange() failed");
+      rv = MOZ_KnownLive(mHTMLEditor)
+               .DeleteTextAndTextNodesWithTransaction(
+                   atNextCharOfNextChar, atNextCharOfNextChar.NextPoint());
+      NS_WARNING_ASSERTION(
+          NS_SUCCEEDED(rv),
+          "HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
       return rv;
     }
   }
   return NS_OK;
 }
 
 nsresult WSRunObject::Scrub() {
   for (WSFragment* run = mStartRun; run; run = run->mRight) {
     if (run->IsMiddleOfHardLine()) {
       continue;
     }
-    nsresult rv = DeleteRange(run->StartPoint(), run->EndPoint());
+    nsresult rv = MOZ_KnownLive(mHTMLEditor)
+                      .DeleteTextAndTextNodesWithTransaction(run->StartPoint(),
+                                                             run->EndPoint());
     if (NS_FAILED(rv)) {
-      NS_WARNING("WSRunObject::DeleteRange() failed");
+      NS_WARNING("HTMLEditor::DeleteTextAndTextNodesWithTransaction() failed");
       return rv;
     }
   }
   return NS_OK;
 }
 
 }  // namespace mozilla
--- a/editor/libeditor/WSRunObject.h
+++ b/editor/libeditor/WSRunObject.h
@@ -431,20 +431,26 @@ class MOZ_STACK_CLASS WSRunScanner {
           mLeft(nullptr),
           mRight(nullptr),
           mLeftWSType(WSType::NotInitialized),
           mRightWSType(WSType::NotInitialized),
           mIsVisible(Visible::No),
           mIsStartOfHardLine(StartOfHardLine::No),
           mIsEndOfHardLine(EndOfHardLine::No) {}
 
-    EditorRawDOMPoint StartPoint() const {
+    EditorDOMPoint StartPoint() const {
+      return EditorDOMPoint(mStartNode, mStartOffset);
+    }
+    EditorDOMPoint EndPoint() const {
+      return EditorDOMPoint(mEndNode, mEndOffset);
+    }
+    EditorRawDOMPoint RawStartPoint() const {
       return EditorRawDOMPoint(mStartNode, mStartOffset);
     }
-    EditorRawDOMPoint EndPoint() const {
+    EditorRawDOMPoint RawEndPoint() const {
       return EditorRawDOMPoint(mEndNode, mEndOffset);
     }
 
     enum class Visible : bool { No, Yes };
     enum class StartOfHardLine : bool { No, Yes };
     enum class EndOfHardLine : bool { No, Yes };
 
     /**
@@ -794,28 +800,16 @@ class MOZ_STACK_CLASS WSRunObject final 
   // be safely converted to regular ascii space and converts them.
   MOZ_CAN_RUN_SCRIPT nsresult AdjustWhitespace();
 
  protected:
   MOZ_CAN_RUN_SCRIPT nsresult PrepareToDeleteRangePriv(WSRunObject* aEndObject);
   MOZ_CAN_RUN_SCRIPT nsresult PrepareToSplitAcrossBlocksPriv();
 
   /**
-   * DeleteRange() removes the range between aStartPoint and aEndPoint.
-   * When aStartPoint and aEndPoint are same point, does nothing.
-   * When aStartPoint and aEndPoint are in same text node, removes characters
-   * between them.
-   * When aStartPoint is in a text node, removes the text data after the point.
-   * When aEndPoint is in a text node, removes the text data before the point.
-   * Removes any nodes between them.
-   */
-  MOZ_CAN_RUN_SCRIPT nsresult DeleteRange(const EditorDOMPoint& aStartPoint,
-                                          const EditorDOMPoint& aEndPoint);
-
-  /**
    * InsertNBSPAndRemoveFollowingASCIIWhitespaces() inserts an NBSP first.
    * Then, if following characters are ASCII whitespaces, will remove them.
    */
   MOZ_CAN_RUN_SCRIPT nsresult InsertNBSPAndRemoveFollowingASCIIWhitespaces(
       const EditorDOMPointInText& aPoint);
 
   /**
    * GetASCIIWhitespacesBounds() returns a range from start of whitespaces