Bug 1408227 - part 4: WSRunObject::DeleteChars() should take two |const EditorRawDOMPoint&| arguments to specify a range to remove r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 22 Nov 2017 01:00:35 +0900
changeset 706048 2d87c6f80e5088e7ba056296016abde958d405e7
parent 706047 2e884a311bfb092f494c4ecb09400af5f0e2966e
child 706049 351b6588e59f2842e7eaced02c19395469e0e03f
push id91674
push usermasayuki@d-toybox.com
push dateFri, 01 Dec 2017 02:38:11 +0000
reviewersm_kato
bugs1408227
milestone59.0a1
Bug 1408227 - part 4: WSRunObject::DeleteChars() should take two |const EditorRawDOMPoint&| arguments to specify a range to remove r?m_kato WSRunObject::DeleteChars() should take two |const EditorRawDOMPoint&| arguments to represent a range to remove. Additionally, this renames it to DeleteRange() because it also removes any nodes in the range. So, "Chars" isn't good word for this method's name. MozReview-Commit-ID: 5Dmxnia1JPO
editor/libeditor/WSRunObject.cpp
editor/libeditor/WSRunObject.h
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -191,20 +191,20 @@ WSRunObject::InsertBreak(Selection& aSel
 
     // Handle any changes needed to ws run after inserted br
     if (!afterRun || (afterRun->mType & WSType::trailingWS)) {
       // Don't need to do anything.  Just insert break.  ws won't change.
     } else if (afterRun->mType & WSType::leadingWS) {
       // 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 =
-        DeleteChars(pointToInsert.Container(), pointToInsert.Offset(),
-                    afterRun->mEndNode, afterRun->mEndOffset);
-      NS_ENSURE_SUCCESS(rv, nullptr);
+      nsresult rv = DeleteRange(pointToInsert.AsRaw(), afterRun->EndPoint());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return nullptr;
+      }
     } else if (afterRun->mType == WSType::normalWS) {
       // Need to determine if break at front of non-nbsp run.  If so, convert
       // run to nbsp.
       WSPoint thePoint =
         GetCharAfter(pointToInsert.Container(), pointToInsert.Offset());
       if (thePoint.mTextNode && nsCRT::IsAsciiSpace(thePoint.mChar)) {
         WSPoint prevPoint = GetCharBefore(thePoint);
         if (!prevPoint.mTextNode ||
@@ -217,20 +217,20 @@ WSRunObject::InsertBreak(Selection& aSel
     }
 
     // Handle any changes needed to ws run before inserted br
     if (!beforeRun || (beforeRun->mType & WSType::leadingWS)) {
       // Don't need to do anything.  Just insert break.  ws won't change.
     } else if (beforeRun->mType & WSType::trailingWS) {
       // Need to delete the trailing ws that is before insertion point, because it
       // would become significant after break inserted.
-      nsresult rv =
-        DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset,
-                    pointToInsert.Container(), pointToInsert.Offset());
-      NS_ENSURE_SUCCESS(rv, nullptr);
+      nsresult rv = DeleteRange(beforeRun->StartPoint(), pointToInsert.AsRaw());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return nullptr;
+      }
     } else if (beforeRun->mType == WSType::normalWS) {
       // Try to change an nbsp to a space, just to prevent nbsp proliferation
       nsresult rv =
         CheckTrailingNBSP(beforeRun, pointToInsert.Container(),
                           pointToInsert.Offset());
       NS_ENSURE_SUCCESS(rv, nullptr);
     }
   }
@@ -281,38 +281,38 @@ WSRunObject::InsertText(nsIDocument& aDo
     AutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, &pointToInsert);
 
     // Handle any changes needed to ws run after inserted text
     if (!afterRun || afterRun->mType & WSType::trailingWS) {
       // Don't need to do anything.  Just insert text.  ws won't change.
     } else if (afterRun->mType & WSType::leadingWS) {
       // Delete the leading ws that is after insertion point, because it
       // would become significant after text inserted.
-      nsresult rv =
-        DeleteChars(pointToInsert.Container(), pointToInsert.Offset(),
-                    afterRun->mEndNode, afterRun->mEndOffset);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsresult rv = DeleteRange(pointToInsert.AsRaw(), afterRun->EndPoint());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     } else if (afterRun->mType == WSType::normalWS) {
       // Try to change an nbsp to a space, if possible, just to prevent nbsp
       // proliferation
       nsresult rv = CheckLeadingNBSP(afterRun, pointToInsert.Container(),
                                      pointToInsert.Offset());
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Handle any changes needed to ws run before inserted text
     if (!beforeRun || beforeRun->mType & WSType::leadingWS) {
       // Don't need to do anything.  Just insert text.  ws won't change.
     } else if (beforeRun->mType & WSType::trailingWS) {
       // Need to delete the trailing ws that is before insertion point, because
       // it would become significant after text inserted.
-      nsresult rv =
-        DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset,
-                    pointToInsert.Container(), pointToInsert.Offset());
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsresult rv = DeleteRange(beforeRun->StartPoint(), pointToInsert.AsRaw());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     } else if (beforeRun->mType == WSType::normalWS) {
       // Try to change an nbsp to a space, if possible, just to prevent nbsp
       // proliferation
       nsresult rv = CheckTrailingNBSP(beforeRun, pointToInsert.Container(),
                                       pointToInsert.Offset());
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
@@ -391,18 +391,23 @@ WSRunObject::InsertText(nsIDocument& aDo
 nsresult
 WSRunObject::DeleteWSBackward()
 {
   WSPoint point = GetCharBefore(mNode, mOffset);
   NS_ENSURE_TRUE(point.mTextNode, NS_OK);  // nothing to delete
 
   // Easy case, preformatted ws.
   if (mPRE &&  (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp)) {
-    return DeleteChars(point.mTextNode, point.mOffset,
-                       point.mTextNode, point.mOffset + 1);
+    nsresult rv =
+      DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset),
+                  EditorRawDOMPoint(point.mTextNode, point.mOffset + 1));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
   }
 
   // Caller's job to ensure that previous char is really ws.  If it is normal
   // ws, we need to delete the whole run.
   if (nsCRT::IsAsciiSpace(point.mChar)) {
     RefPtr<Text> startNodeText, endNodeText;
     int32_t startOffset, endOffset;
     GetAsciiWSBounds(eBoth, point.mTextNode, point.mOffset + 1,
@@ -414,47 +419,62 @@ WSRunObject::DeleteWSBackward()
     nsCOMPtr<nsINode> endNode = endNodeText.get();
     nsresult rv =
       WSRunObject::PrepareToDeleteRange(mHTMLEditor,
                                         address_of(startNode), &startOffset,
                                         address_of(endNode), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // finally, delete that ws
-    return DeleteChars(startNode, startOffset, endNode, endOffset);
+    rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset),
+                     EditorRawDOMPoint(endNode, endOffset));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
   }
 
   if (point.mChar == nbsp) {
     nsCOMPtr<nsINode> node(point.mTextNode);
     // adjust surrounding ws
     int32_t startOffset = point.mOffset;
     int32_t endOffset = point.mOffset + 1;
     nsresult rv =
       WSRunObject::PrepareToDeleteRange(mHTMLEditor,
                                         address_of(node), &startOffset,
                                         address_of(node), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // finally, delete that ws
-    return DeleteChars(node, startOffset, node, endOffset);
+    rv = DeleteRange(EditorRawDOMPoint(node, startOffset),
+                     EditorRawDOMPoint(node, endOffset));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
   }
 
   return NS_OK;
 }
 
 nsresult
 WSRunObject::DeleteWSForward()
 {
   WSPoint point = GetCharAfter(mNode, mOffset);
   NS_ENSURE_TRUE(point.mTextNode, NS_OK); // nothing to delete
 
   // Easy case, preformatted ws.
   if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp)) {
-    return DeleteChars(point.mTextNode, point.mOffset,
-                       point.mTextNode, point.mOffset + 1);
+    nsresult rv =
+      DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset),
+                  EditorRawDOMPoint(point.mTextNode, point.mOffset + 1));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
   }
 
   // Caller's job to ensure that next char is really ws.  If it is normal ws,
   // we need to delete the whole run.
   if (nsCRT::IsAsciiSpace(point.mChar)) {
     RefPtr<Text> startNodeText, endNodeText;
     int32_t startOffset, endOffset;
     GetAsciiWSBounds(eBoth, point.mTextNode, point.mOffset + 1,
@@ -465,32 +485,42 @@ WSRunObject::DeleteWSForward()
     nsCOMPtr<nsINode> startNode(startNodeText), endNode(endNodeText);
     nsresult rv =
       WSRunObject::PrepareToDeleteRange(mHTMLEditor,
                                         address_of(startNode), &startOffset,
                                         address_of(endNode), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Finally, delete that ws
-    return DeleteChars(startNode, startOffset, endNode, endOffset);
+    rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset),
+                     EditorRawDOMPoint(endNode, endOffset));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
   }
 
   if (point.mChar == nbsp) {
     nsCOMPtr<nsINode> node(point.mTextNode);
     // Adjust surrounding ws
     int32_t startOffset = point.mOffset;
     int32_t endOffset = point.mOffset+1;
     nsresult rv =
       WSRunObject::PrepareToDeleteRange(mHTMLEditor,
                                         address_of(node), &startOffset,
                                         address_of(node), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Finally, delete that ws
-    return DeleteChars(node, startOffset, node, endOffset);
+    rv = DeleteRange(EditorRawDOMPoint(node, startOffset),
+                     EditorRawDOMPoint(node, endOffset));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
   }
 
   return NS_OK;
 }
 
 void
 WSRunObject::PriorVisibleNode(nsINode* aNode,
                               int32_t aOffset,
@@ -1185,19 +1215,20 @@ WSRunObject::PrepareToDeleteRangePriv(WS
 
   // get the runs before and after selection
   WSFragment* beforeRun = FindNearestRun(Point(), false);
   WSFragment* afterRun = aEndObject->FindNearestRun(aEndObject->Point(), true);
 
   // trim after run of any leading ws
   if (afterRun && (afterRun->mType & WSType::leadingWS)) {
     nsresult rv =
-      aEndObject->DeleteChars(aEndObject->mNode, aEndObject->mOffset,
-                              afterRun->mEndNode, afterRun->mEndOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+      aEndObject->DeleteRange(aEndObject->Point(), afterRun->EndPoint());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
   // adjust normal ws in afterRun if needed
   if (afterRun && afterRun->mType == WSType::normalWS && !aEndObject->mPRE) {
     if ((beforeRun && (beforeRun->mType & WSType::leadingWS)) ||
         (!beforeRun && ((mStartReason & WSType::block) ||
                         mStartReason == WSType::br))) {
       // make sure leading char of following ws is an nbsp, so that it will show up
       WSPoint point = aEndObject->GetCharAfter(aEndObject->mNode,
@@ -1205,19 +1236,20 @@ WSRunObject::PrepareToDeleteRangePriv(WS
       if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) {
         nsresult rv = aEndObject->ConvertToNBSP(point);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
   // trim before run of any trailing ws
   if (beforeRun && (beforeRun->mType & WSType::trailingWS)) {
-    nsresult rv = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset,
-                              mNode, mOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv = DeleteRange(beforeRun->StartPoint(), Point());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   } else if (beforeRun && beforeRun->mType == WSType::normalWS && !mPRE) {
     if ((afterRun && (afterRun->mType & WSType::trailingWS)) ||
         (afterRun && afterRun->mType == WSType::normalWS) ||
         (!afterRun && (aEndObject->mEndReason & WSType::block))) {
       // make sure trailing char of starting ws is an nbsp, so that it will show up
       WSPoint point = GetCharBefore(mNode, mOffset);
       if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) {
         RefPtr<Text> wsStartNode, wsEndNode;
@@ -1271,83 +1303,95 @@ WSRunObject::PrepareToSplitAcrossBlocksP
       nsresult rv = ConvertToNBSP(point);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
   return NS_OK;
 }
 
 nsresult
-WSRunObject::DeleteChars(nsINode* aStartNode,
-                         int32_t aStartOffset,
-                         nsINode* aEndNode,
-                         int32_t aEndOffset)
+WSRunObject::DeleteRange(const EditorRawDOMPoint& aStartPoint,
+                         const EditorRawDOMPoint& 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.
-  NS_ENSURE_TRUE(aStartNode && aEndNode, NS_ERROR_NULL_POINTER);
 
-  if (aStartNode == aEndNode && aStartOffset == aEndOffset) {
+  if (aStartPoint == aEndPoint) {
     // Nothing to delete
     return NS_OK;
   }
 
-  int32_t idx = mNodeArray.IndexOf(aStartNode);
-  if (idx == -1) {
-    // If our strarting point wasn't one of our ws text nodes, then just go
-    // through them from the beginning.
-    idx = 0;
-  }
-
-  if (aStartNode == aEndNode && aStartNode->GetAsText()) {
-    return mHTMLEditor->DeleteText(*aStartNode->GetAsText(),
-        static_cast<uint32_t>(aStartOffset),
-        static_cast<uint32_t>(aEndOffset - aStartOffset));
+  if (aStartPoint.Container() == aEndPoint.Container() &&
+      aStartPoint.Container()->GetAsText()) {
+    return mHTMLEditor->DeleteText(*aStartPoint.Container()->GetAsText(),
+                                   aStartPoint.Offset(),
+                                   aEndPoint.Offset() - aStartPoint.Offset());
   }
 
   RefPtr<nsRange> range;
   int32_t count = mNodeArray.Length();
+  int32_t idx = mNodeArray.IndexOf(aStartPoint.Container());
+  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 == aStartNode) {
-      uint32_t len = node->Length();
-      if (uint32_t(aStartOffset) < len) {
+    if (node == aStartPoint.Container()) {
+      if (!aStartPoint.IsEndOfContainer()) {
         nsresult rv =
-          mHTMLEditor->DeleteText(*node, AssertedCast<uint32_t>(aStartOffset),
-                                  len - aStartOffset);
-        NS_ENSURE_SUCCESS(rv, rv);
+          mHTMLEditor->DeleteText(*node, aStartPoint.Offset(),
+                                  aStartPoint.Container()->Length() -
+                                    aStartPoint.Offset());
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
-    } else if (node == aEndNode) {
-      if (aEndOffset) {
-        nsresult rv =
-          mHTMLEditor->DeleteText(*node, 0, AssertedCast<uint32_t>(aEndOffset));
-        NS_ENSURE_SUCCESS(rv, rv);
+    } else if (node == aEndPoint.Container()) {
+      if (!aEndPoint.IsStartOfContainer()) {
+        nsresult rv = mHTMLEditor->DeleteText(*node, 0, aEndPoint.Offset());
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
       break;
     } else {
       if (!range) {
-        range = new nsRange(aStartNode);
-        nsresult rv =
-          range->SetStartAndEnd(aStartNode, aStartOffset, aEndNode, aEndOffset);
-        NS_ENSURE_SUCCESS(rv, rv);
+        range = new nsRange(aStartPoint.Container());
+        nsresult rv = range->SetStartAndEnd(aStartPoint, aEndPoint);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
       bool nodeBefore, nodeAfter;
       nsresult rv =
         nsRange::CompareNodeToRange(node, range, &nodeBefore, &nodeAfter);
-      NS_ENSURE_SUCCESS(rv, rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       if (nodeAfter) {
         break;
       }
       if (!nodeBefore) {
         rv = mHTMLEditor->DeleteNode(node);
-        NS_ENSURE_SUCCESS(rv, rv);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
         mNodeArray.RemoveElement(node);
         --count;
         --idx;
       }
     }
   }
   return NS_OK;
 }
@@ -1470,18 +1514,21 @@ WSRunObject::ConvertToNBSP(WSPoint aPoin
   int32_t startOffset = 0, endOffset = 0;
 
   GetAsciiWSBounds(eAfter, aPoint.mTextNode, aPoint.mOffset + 1,
                    getter_AddRefs(startNode), &startOffset,
                    getter_AddRefs(endNode), &endOffset);
 
   // Finally, delete that replaced ws, if any
   if (startNode) {
-    rv = DeleteChars(startNode, startOffset, endNode, endOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset),
+                     EditorRawDOMPoint(endNode, endOffset));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   return NS_OK;
 }
 
 void
 WSRunObject::GetAsciiWSBounds(int16_t aDir,
                               nsINode* aNode,
@@ -1773,37 +1820,45 @@ WSRunObject::CheckTrailingNBSPOfRun(WSFr
       AutoTransactionsConserveSelection dontChangeMySelection(mHTMLEditor);
       nsAutoString spaceStr(char16_t(32));
       nsresult rv =
         mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, *thePoint.mTextNode,
                                                 thePoint.mOffset, true);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Finally, delete that nbsp
-      rv = DeleteChars(thePoint.mTextNode, thePoint.mOffset + 1,
-                       thePoint.mTextNode, thePoint.mOffset + 2);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = DeleteRange(EditorRawDOMPoint(thePoint.mTextNode,
+                                         thePoint.mOffset + 1),
+                       EditorRawDOMPoint(thePoint.mTextNode,
+                                         thePoint.mOffset + 2));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        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
       // editor softwraps at this point, the spaces won't be split across lines,
       // which looks ugly and is bad for the moose.
 
       RefPtr<Text> startNode, endNode;
       int32_t startOffset, endOffset;
       GetAsciiWSBounds(eBoth, prevPoint.mTextNode, prevPoint.mOffset + 1,
                        getter_AddRefs(startNode), &startOffset,
                        getter_AddRefs(endNode), &endOffset);
 
       // Delete that nbsp
-      nsresult rv = DeleteChars(thePoint.mTextNode, thePoint.mOffset,
-                                thePoint.mTextNode, thePoint.mOffset + 1);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsresult rv = DeleteRange(EditorRawDOMPoint(thePoint.mTextNode,
+                                                  thePoint.mOffset),
+                                EditorRawDOMPoint(thePoint.mTextNode,
+                                                  thePoint.mOffset + 1));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
 
       // Finally, insert that nbsp before the ASCII ws run
       AutoTransactionsConserveSelection dontChangeMySelection(mHTMLEditor);
       nsAutoString nbspStr(nbsp);
       rv = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, *startNode,
                                                    startOffset, true);
       NS_ENSURE_SUCCESS(rv, rv);
     }
@@ -1840,19 +1895,23 @@ WSRunObject::CheckTrailingNBSP(WSFragmen
     AutoTransactionsConserveSelection dontChangeMySelection(mHTMLEditor);
     nsAutoString spaceStr(char16_t(32));
     nsresult rv =
       mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, *thePoint.mTextNode,
                                               thePoint.mOffset, true);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Finally, delete that nbsp
-    rv = DeleteChars(thePoint.mTextNode, thePoint.mOffset + 1,
-                     thePoint.mTextNode, thePoint.mOffset + 2);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = DeleteRange(EditorRawDOMPoint(thePoint.mTextNode,
+                                       thePoint.mOffset + 1),
+                     EditorRawDOMPoint(thePoint.mTextNode,
+                                       thePoint.mOffset + 2));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
   return NS_OK;
 }
 
 nsresult
 WSRunObject::CheckLeadingNBSP(WSFragment* aRun,
                               nsINode* aNode,
                               int32_t aOffset)
@@ -1883,33 +1942,38 @@ WSRunObject::CheckLeadingNBSP(WSFragment
     AutoTransactionsConserveSelection dontChangeMySelection(mHTMLEditor);
     nsAutoString spaceStr(char16_t(32));
     nsresult rv =
       mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, *thePoint.mTextNode,
                                               thePoint.mOffset, true);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Finally, delete that nbsp
-    rv = DeleteChars(thePoint.mTextNode, thePoint.mOffset + 1,
-                     thePoint.mTextNode, thePoint.mOffset + 2);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = DeleteRange(EditorRawDOMPoint(thePoint.mTextNode,
+                                       thePoint.mOffset + 1),
+                     EditorRawDOMPoint(thePoint.mTextNode,
+                                       thePoint.mOffset + 2));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
   return NS_OK;
 }
 
 
 nsresult
 WSRunObject::Scrub()
 {
   WSFragment *run = mStartRun;
   while (run) {
     if (run->mType & (WSType::leadingWS | WSType::trailingWS)) {
-      nsresult rv = DeleteChars(run->mStartNode, run->mStartOffset,
-                                run->mEndNode, run->mEndOffset);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsresult rv = DeleteRange(run->StartPoint(), run->EndPoint());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
     run = run->mRight;
   }
   return NS_OK;
 }
 
 bool
 WSRunObject::IsBlockNode(nsINode* aNode)
--- a/editor/libeditor/WSRunObject.h
+++ b/editor/libeditor/WSRunObject.h
@@ -366,18 +366,29 @@ protected:
                                      nsINode* aBlockParent);
   nsIContent* GetPreviousWSNode(const EditorDOMPoint& aPoint,
                                 nsINode* aBlockParent);
   nsIContent* GetNextWSNodeInner(nsINode* aStartNode, nsINode* aBlockParent);
   nsIContent* GetNextWSNode(const EditorDOMPoint& aPoint,
                             nsINode* aBlockParent);
   nsresult PrepareToDeleteRangePriv(WSRunObject* aEndObject);
   nsresult PrepareToSplitAcrossBlocksPriv();
-  nsresult DeleteChars(nsINode* aStartNode, int32_t aStartOffset,
-                       nsINode* aEndNode, int32_t aEndOffset);
+
+  /**
+   * 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.
+   */
+  nsresult DeleteRange(const EditorRawDOMPoint& aStartPoint,
+                       const EditorRawDOMPoint& aEndPoint);
+
   WSPoint GetCharAfter(nsINode* aNode, int32_t aOffset);
   WSPoint GetCharBefore(nsINode* aNode, int32_t aOffset);
   WSPoint GetCharAfter(const WSPoint& aPoint);
   WSPoint GetCharBefore(const WSPoint& aPoint);
   nsresult ConvertToNBSP(WSPoint aPoint);
   void GetAsciiWSBounds(int16_t aDir, nsINode* aNode, int32_t aOffset,
                         dom::Text** outStartNode, int32_t* outStartOffset,
                         dom::Text** outEndNode, int32_t* outEndOffset);