Bug 1408227 - part 5: Redesign GetCharAfter(), GetCharBefore(), GetWSPointAfter() and GetWSPointBefore() of WSRunObject r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 22 Nov 2017 02:12:27 +0900
changeset 706049 351b6588e59f2842e7eaced02c19395469e0e03f
parent 706048 2d87c6f80e5088e7ba056296016abde958d405e7
child 706050 e0b5868929733077ae57bc84748f65901928a982
push id91674
push usermasayuki@d-toybox.com
push dateFri, 01 Dec 2017 02:38:11 +0000
reviewersm_kato
bugs1408227
milestone59.0a1
Bug 1408227 - part 5: Redesign GetCharAfter(), GetCharBefore(), GetWSPointAfter() and GetWSPointBefore() of WSRunObject r?m_kato WSRunObject::GetChar(After|Before)(nsINode*, int32_t) returns next/previous character's DOM point as WSPoint. If the container node is a text node in mNodeArray, it calls WSRunObject::GetChar(After|Before)(const WSPoint&) which returns next/previous offset if the point isn't end/start of the text node. If the point is at end/start of the text node, it returns start/end of next/previous text node in mNodeArray. If the container node is not a text node in mNodeArray, it calls WSRunObject::GetWSPoint(After|Before)(). It looks for next/previous text node in mNodeArray from the point. Then, it calls WSRunObject::GetChar(After|Before)(const WSPoint&) and returns the result. So, we should rename GetCharAfter() to GetNextCharPoint(), GetCharBefore() to GetPreviousCharPoint(), GetWSPointAfter() to GetNextCharPointInternal() and GetWSPointBefore() to GetPreviousCharPointInternal(). Then, they should take |const EditorRawDOMPoint&| instead of a set of container node and offset in it. So, looks like that "WS"RunObject is not good name for this class, perhaps, AutoTextRunManager or something? But I'm still not sure. MozReview-Commit-ID: 85cX3MdlFwz
editor/libeditor/WSRunObject.cpp
editor/libeditor/WSRunObject.h
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -198,20 +198,19 @@ WSRunObject::InsertBreak(Selection& aSel
       // just more aesthetically pleasing to.
       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());
+      WSPoint thePoint = GetNextCharPoint(pointToInsert.AsRaw());
       if (thePoint.mTextNode && nsCRT::IsAsciiSpace(thePoint.mChar)) {
-        WSPoint prevPoint = GetCharBefore(thePoint);
+        WSPoint prevPoint = GetPreviousCharPoint(thePoint);
         if (!prevPoint.mTextNode ||
             (prevPoint.mTextNode && !nsCRT::IsAsciiSpace(prevPoint.mChar))) {
           // We are at start of non-nbsps.  Convert to a single nbsp.
           nsresult rv = ConvertToNBSP(thePoint);
           NS_ENSURE_SUCCESS(rv, nullptr);
         }
       }
     }
@@ -324,18 +323,17 @@ WSRunObject::InsertText(nsIDocument& aDo
   // ws char into an nbsp:
 
   if (nsCRT::IsAsciiSpace(theString[0])) {
     // We have a leading space
     if (beforeRun) {
       if (beforeRun->mType & WSType::leadingWS) {
         theString.SetCharAt(nbsp, 0);
       } else if (beforeRun->mType & WSType::normalWS) {
-        WSPoint wspoint =
-          GetCharBefore(pointToInsert.Container(), pointToInsert.Offset());
+        WSPoint wspoint = GetPreviousCharPoint(pointToInsert.AsRaw());
         if (wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar)) {
           theString.SetCharAt(nbsp, 0);
         }
       }
     } else if (mStartReason & WSType::block || mStartReason == WSType::br) {
       theString.SetCharAt(nbsp, 0);
     }
   }
@@ -344,18 +342,17 @@ WSRunObject::InsertText(nsIDocument& aDo
   uint32_t lastCharIndex = theString.Length() - 1;
 
   if (nsCRT::IsAsciiSpace(theString[lastCharIndex])) {
     // We have a leading space
     if (afterRun) {
       if (afterRun->mType & WSType::trailingWS) {
         theString.SetCharAt(nbsp, lastCharIndex);
       } else if (afterRun->mType & WSType::normalWS) {
-        WSPoint wspoint =
-          GetCharAfter(pointToInsert.Container(), pointToInsert.Offset());
+        WSPoint wspoint = GetNextCharPoint(pointToInsert.AsRaw());
         if (wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar)) {
           theString.SetCharAt(nbsp, lastCharIndex);
         }
       }
     } else if (mEndReason & WSType::block) {
       theString.SetCharAt(nbsp, lastCharIndex);
     }
   }
@@ -386,17 +383,17 @@ WSRunObject::InsertText(nsIDocument& aDo
     return NS_OK;
   }
   return NS_OK;
 }
 
 nsresult
 WSRunObject::DeleteWSBackward()
 {
-  WSPoint point = GetCharBefore(mNode, mOffset);
+  WSPoint point = GetPreviousCharPoint(Point());
   NS_ENSURE_TRUE(point.mTextNode, NS_OK);  // nothing to delete
 
   // Easy case, preformatted ws.
   if (mPRE &&  (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp)) {
     nsresult rv =
       DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset),
                   EditorRawDOMPoint(point.mTextNode, point.mOffset + 1));
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -453,17 +450,17 @@ WSRunObject::DeleteWSBackward()
   }
 
   return NS_OK;
 }
 
 nsresult
 WSRunObject::DeleteWSForward()
 {
-  WSPoint point = GetCharAfter(mNode, mOffset);
+  WSPoint point = GetNextCharPoint(Point());
   NS_ENSURE_TRUE(point.mTextNode, NS_OK); // nothing to delete
 
   // Easy case, preformatted ws.
   if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp)) {
     nsresult rv =
       DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset),
                   EditorRawDOMPoint(point.mTextNode, point.mOffset + 1));
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -533,17 +530,17 @@ WSRunObject::PriorVisibleNode(nsINode* a
   // anything return start of ws.
   MOZ_ASSERT(aNode && outVisNode && outVisOffset && outType);
 
   WSFragment* run = FindNearestRun(EditorRawDOMPoint(aNode, aOffset), false);
 
   // Is there a visible run there or earlier?
   for (; run; run = run->mLeft) {
     if (run->mType == WSType::normalWS) {
-      WSPoint point = GetCharBefore(aNode, aOffset);
+      WSPoint point = GetPreviousCharPoint(EditorRawDOMPoint(aNode, aOffset));
       // When it's a non-empty text node, return it.
       if (point.mTextNode && point.mTextNode->Length()) {
         *outVisNode = point.mTextNode;
         *outVisOffset = point.mOffset + 1;
         if (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp) {
           *outType = WSType::normalWS;
         } else {
           *outType = WSType::text;
@@ -574,17 +571,17 @@ WSRunObject::NextVisibleNode(nsINode* aN
   // anything return end of ws.
   MOZ_ASSERT(aNode && outVisNode && outVisOffset && outType);
 
   WSFragment* run = FindNearestRun(EditorRawDOMPoint(aNode, aOffset), true);
 
   // Is there a visible run there or later?
   for (; run; run = run->mRight) {
     if (run->mType == WSType::normalWS) {
-      WSPoint point = GetCharAfter(aNode, aOffset);
+      WSPoint point = GetNextCharPoint(EditorRawDOMPoint(aNode, aOffset));
       // When it's a non-empty text node, return it.
       if (point.mTextNode && point.mTextNode->Length()) {
         *outVisNode = point.mTextNode;
         *outVisOffset = point.mOffset;
         if (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == nbsp) {
           *outType = WSType::normalWS;
         } else {
           *outType = WSType::text;
@@ -1226,18 +1223,17 @@ WSRunObject::PrepareToDeleteRangePriv(WS
     }
   }
   // 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,
-                                               aEndObject->mOffset);
+      WSPoint point = aEndObject->GetNextCharPoint(aEndObject->Point());
       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)) {
@@ -1245,17 +1241,17 @@ WSRunObject::PrepareToDeleteRangePriv(WS
     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);
+      WSPoint point = GetPreviousCharPoint(Point());
       if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) {
         RefPtr<Text> wsStartNode, wsEndNode;
         int32_t wsStartOffset, wsEndOffset;
         GetAsciiWSBounds(eBoth, mNode, mOffset,
                          getter_AddRefs(wsStartNode), &wsStartOffset,
                          getter_AddRefs(wsEndNode), &wsEndOffset);
         point.mTextNode = wsStartNode;
         point.mOffset = wsStartOffset;
@@ -1276,27 +1272,27 @@ WSRunObject::PrepareToSplitAcrossBlocksP
 
   // get the runs before and after selection
   WSFragment* beforeRun = FindNearestRun(Point(), false);
   WSFragment* afterRun = FindNearestRun(Point(), true);
 
   // adjust normal ws in afterRun if needed
   if (afterRun && afterRun->mType == WSType::normalWS) {
     // make sure leading char of following ws is an nbsp, so that it will show up
-    WSPoint point = GetCharAfter(mNode, mOffset);
+    WSPoint point = GetNextCharPoint(Point());
     if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) {
       nsresult rv = ConvertToNBSP(point);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // adjust normal ws in beforeRun if needed
   if (beforeRun && beforeRun->mType == WSType::normalWS) {
     // make sure trailing char of starting ws is an nbsp, so that it will show up
-    WSPoint point = GetCharBefore(mNode, mOffset);
+    WSPoint point = GetPreviousCharPoint(Point());
     if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) {
       RefPtr<Text> wsStartNode, wsEndNode;
       int32_t wsStartOffset, wsEndOffset;
       GetAsciiWSBounds(eBoth, mNode, mOffset,
                        getter_AddRefs(wsStartNode), &wsStartOffset,
                        getter_AddRefs(wsEndNode), &wsEndOffset);
       point.mTextNode = wsStartNode;
       point.mOffset = wsStartOffset;
@@ -1392,47 +1388,45 @@ WSRunObject::DeleteRange(const EditorRaw
         --idx;
       }
     }
   }
   return NS_OK;
 }
 
 WSRunObject::WSPoint
-WSRunObject::GetCharAfter(nsINode* aNode,
-                          int32_t aOffset)
+WSRunObject::GetNextCharPoint(const EditorRawDOMPoint& aPoint)
 {
-  MOZ_ASSERT(aNode);
+  MOZ_ASSERT(aPoint.IsSetAndValid());
 
-  int32_t idx = mNodeArray.IndexOf(aNode);
+  int32_t idx = mNodeArray.IndexOf(aPoint.Container());
   if (idx == -1) {
-    // Use range comparisons to get right ws node
-    return GetWSPointAfter(aNode, aOffset);
+    // Use range comparisons to get next text node which is in mNodeArray.
+    return GetNextCharPointInternal(aPoint);
   }
-  // Use WSPoint version of GetCharAfter()
-  return GetCharAfter(WSPoint(mNodeArray[idx], aOffset, 0));
+  // Use WSPoint version of GetNextCharPoint()
+  return GetNextCharPoint(WSPoint(mNodeArray[idx], aPoint.Offset(), 0));
 }
 
 WSRunObject::WSPoint
-WSRunObject::GetCharBefore(nsINode* aNode,
-                           int32_t aOffset)
+WSRunObject::GetPreviousCharPoint(const EditorRawDOMPoint& aPoint)
 {
-  MOZ_ASSERT(aNode);
+  MOZ_ASSERT(aPoint.IsSetAndValid());
 
-  int32_t idx = mNodeArray.IndexOf(aNode);
+  int32_t idx = mNodeArray.IndexOf(aPoint.Container());
   if (idx == -1) {
-    // Use range comparisons to get right ws node
-    return GetWSPointBefore(aNode, aOffset);
+    // Use range comparisons to get previous text node which is in mNodeArray.
+    return GetPreviousCharPointInternal(aPoint);
   }
-  // Use WSPoint version of GetCharBefore()
-  return GetCharBefore(WSPoint(mNodeArray[idx], aOffset, 0));
+  // Use WSPoint version of GetPreviousCharPoint()
+  return GetPreviousCharPoint(WSPoint(mNodeArray[idx], aPoint.Offset(), 0));
 }
 
 WSRunObject::WSPoint
-WSRunObject::GetCharAfter(const WSPoint &aPoint)
+WSRunObject::GetNextCharPoint(const WSPoint &aPoint)
 {
   MOZ_ASSERT(aPoint.mTextNode);
 
   WSPoint outPoint;
   outPoint.mTextNode = nullptr;
   outPoint.mOffset = 0;
   outPoint.mChar = 0;
 
@@ -1455,17 +1449,17 @@ WSRunObject::GetCharAfter(const WSPoint 
     outPoint.mOffset = 0;
     outPoint.mChar = GetCharAt(outPoint.mTextNode, 0);
   }
 
   return outPoint;
 }
 
 WSRunObject::WSPoint
-WSRunObject::GetCharBefore(const WSPoint &aPoint)
+WSRunObject::GetPreviousCharPoint(const WSPoint &aPoint)
 {
   MOZ_ASSERT(aPoint.mTextNode);
 
   WSPoint outPoint;
   outPoint.mTextNode = nullptr;
   outPoint.mOffset = 0;
   outPoint.mChar = 0;
 
@@ -1540,47 +1534,47 @@ WSRunObject::GetAsciiWSBounds(int16_t aD
 {
   MOZ_ASSERT(aNode && outStartNode && outStartOffset && outEndNode &&
              outEndOffset);
 
   RefPtr<Text> startNode, endNode;
   int32_t startOffset = 0, endOffset = 0;
 
   if (aDir & eAfter) {
-    WSPoint point = GetCharAfter(aNode, aOffset);
+    WSPoint point = GetNextCharPoint(EditorRawDOMPoint(aNode, aOffset));
     if (point.mTextNode) {
       // We found a text node, at least
       startNode = endNode = point.mTextNode;
       startOffset = endOffset = point.mOffset;
 
       // Scan ahead to end of ASCII ws
       for (; nsCRT::IsAsciiSpace(point.mChar) && point.mTextNode;
-           point = GetCharAfter(point)) {
+           point = GetNextCharPoint(point)) {
         endNode = point.mTextNode;
         // endOffset is _after_ ws
         point.mOffset++;
         endOffset = point.mOffset;
       }
     }
   }
 
   if (aDir & eBefore) {
-    WSPoint point = GetCharBefore(aNode, aOffset);
+    WSPoint point = GetPreviousCharPoint(EditorRawDOMPoint(aNode, aOffset));
     if (point.mTextNode) {
       // We found a text node, at least
       startNode = point.mTextNode;
       startOffset = point.mOffset + 1;
       if (!endNode) {
         endNode = startNode;
         endOffset = startOffset;
       }
 
       // Scan back to start of ASCII ws
       for (; nsCRT::IsAsciiSpace(point.mChar) && point.mTextNode;
-           point = GetCharBefore(point)) {
+           point = GetPreviousCharPoint(point)) {
         startNode = point.mTextNode;
         startOffset = point.mOffset;
       }
     }
   }
 
   startNode.forget(outStartNode);
   *outStartOffset = startOffset;
@@ -1637,68 +1631,65 @@ WSRunObject::GetCharAt(Text* aTextNode,
   int32_t len = int32_t(aTextNode->TextLength());
   if (aOffset < 0 || aOffset >= len) {
     return 0;
   }
   return aTextNode->GetText()->CharAt(aOffset);
 }
 
 WSRunObject::WSPoint
-WSRunObject::GetWSPointAfter(nsINode* aNode,
-                             int32_t aOffset)
+WSRunObject::GetNextCharPointInternal(const EditorRawDOMPoint& aPoint)
 {
-  // Note: only to be called if aNode is not a ws node.
+  // Note: only to be called if aPoint.Container() is not a ws node.
 
   // Binary search on wsnodes
   uint32_t numNodes = mNodeArray.Length();
 
   if (!numNodes) {
     // Do nothing if there are no nodes to search
     WSPoint outPoint;
     return outPoint;
   }
 
-  uint32_t firstNum = 0, curNum = numNodes/2, lastNum = numNodes;
-  int16_t cmp = 0;
-  RefPtr<Text> curNode;
-
   // Begin binary search.  We do this because we need to minimize calls to
   // ComparePoints(), which is expensive.
+  uint32_t firstNum = 0, curNum = numNodes / 2, lastNum = numNodes;
   while (curNum != lastNum) {
-    curNode = mNodeArray[curNum];
-    cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0);
+    RefPtr<Text> curNode = mNodeArray[curNum];
+    int16_t cmp =
+      nsContentUtils::ComparePoints(aPoint, EditorRawDOMPoint(curNode, 0));
     if (cmp < 0) {
       lastNum = curNum;
     } else {
       firstNum = curNum + 1;
     }
-    curNum = (lastNum - firstNum)/2 + firstNum;
+    curNum = (lastNum - firstNum) / 2 + firstNum;
     MOZ_ASSERT(firstNum <= curNum && curNum <= lastNum, "Bad binary search");
   }
 
   // When the binary search is complete, we always know that the current node
-  // is the same as the end node, which is always past our range. Therefore,
+  // is the same as the end node, which is always past our range.  Therefore,
   // we've found the node immediately after the point of interest.
   if (curNum == mNodeArray.Length()) {
-    // hey asked for past our range (it's after the last node). GetCharAfter
-    // will do the work for us when we pass it the last index of the last node.
+    // hey asked for past our range (it's after the last node).
+    // GetNextCharPoint() will do the work for us when we pass it the last
+    // index of the last node.
     RefPtr<Text> textNode(mNodeArray[curNum - 1]);
     WSPoint point(textNode, textNode->TextLength(), 0);
-    return GetCharAfter(point);
-  } else {
-    // The char after the point is the first character of our range.
-    RefPtr<Text> textNode(mNodeArray[curNum]);
-    WSPoint point(textNode, 0, 0);
-    return GetCharAfter(point);
+    return GetNextCharPoint(point);
   }
+
+  // The char after the point is the first character of our range.
+  RefPtr<Text> textNode(mNodeArray[curNum]);
+  WSPoint point(textNode, 0, 0);
+  return GetNextCharPoint(point);
 }
 
 WSRunObject::WSPoint
-WSRunObject::GetWSPointBefore(nsINode* aNode,
-                              int32_t aOffset)
+WSRunObject::GetPreviousCharPointInternal(const EditorRawDOMPoint& aPoint)
 {
   // Note: only to be called if aNode is not a ws node.
 
   // Binary search on wsnodes
   uint32_t numNodes = mNodeArray.Length();
 
   if (!numNodes) {
     // Do nothing if there are no nodes to search
@@ -1709,43 +1700,44 @@ WSRunObject::GetWSPointBefore(nsINode* a
   uint32_t firstNum = 0, curNum = numNodes/2, lastNum = numNodes;
   int16_t cmp = 0;
   RefPtr<Text>  curNode;
 
   // Begin binary search.  We do this because we need to minimize calls to
   // ComparePoints(), which is expensive.
   while (curNum != lastNum) {
     curNode = mNodeArray[curNum];
-    cmp = nsContentUtils::ComparePoints(aNode, aOffset, curNode, 0);
+    cmp = nsContentUtils::ComparePoints(aPoint, EditorRawDOMPoint(curNode, 0));
     if (cmp < 0) {
       lastNum = curNum;
     } else {
       firstNum = curNum + 1;
     }
     curNum = (lastNum - firstNum)/2 + firstNum;
     MOZ_ASSERT(firstNum <= curNum && curNum <= lastNum, "Bad binary search");
   }
 
   // When the binary search is complete, we always know that the current node
   // is the same as the end node, which is always past our range. Therefore,
   // we've found the node immediately after the point of interest.
   if (curNum == mNodeArray.Length()) {
     // Get the point before the end of the last node, we can pass the length of
-    // the node into GetCharBefore, and it will return the last character.
+    // the node into GetPreviousCharPoint(), and it will return the last
+    // character.
     RefPtr<Text> textNode(mNodeArray[curNum - 1]);
     WSPoint point(textNode, textNode->TextLength(), 0);
-    return GetCharBefore(point);
-  } else {
-    // We can just ask the current node for the point immediately before it,
-    // it will handle moving to the previous node (if any) and returning the
-    // appropriate character
-    RefPtr<Text> textNode(mNodeArray[curNum]);
-    WSPoint point(textNode, 0, 0);
-    return GetCharBefore(point);
+    return GetPreviousCharPoint(point);
   }
+
+  // We can just ask the current node for the point immediately before it,
+  // it will handle moving to the previous node (if any) and returning the
+  // appropriate character
+  RefPtr<Text> textNode(mNodeArray[curNum]);
+  WSPoint point(textNode, 0, 0);
+  return GetPreviousCharPoint(point);
 }
 
 nsresult
 WSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun)
 {
   // Try to change an nbsp to a space, if possible, just to prevent nbsp
   // proliferation.  Examine what is before and after the trailing nbsp, if
   // any.
@@ -1755,20 +1747,20 @@ WSRunObject::CheckTrailingNBSPOfRun(WSFr
   bool rightCheck = false;
 
   // confirm run is normalWS
   if (aRun->mType != WSType::normalWS) {
     return NS_ERROR_FAILURE;
   }
 
   // first check for trailing nbsp
-  WSPoint thePoint = GetCharBefore(aRun->mEndNode, aRun->mEndOffset);
+  WSPoint thePoint = GetPreviousCharPoint(aRun->EndPoint());
   if (thePoint.mTextNode && thePoint.mChar == nbsp) {
     // now check that what is to the left of it is compatible with replacing nbsp with space
-    WSPoint prevPoint = GetCharBefore(thePoint);
+    WSPoint prevPoint = GetPreviousCharPoint(thePoint);
     if (prevPoint.mTextNode) {
       if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) {
         leftCheck = true;
       } else {
         spaceNBSP = true;
       }
     } else if (aRun->mLeftType == WSType::text ||
                aRun->mLeftType == WSType::special) {
@@ -1805,18 +1797,18 @@ WSRunObject::CheckTrailingNBSPOfRun(WSFr
         // beginning of soft wrapped lines, and lets the user see 2 spaces when
         // they type 2 spaces.
 
         nsCOMPtr<Element> brNode =
           mHTMLEditor->CreateBR(aRun->mEndNode, aRun->mEndOffset);
         NS_ENSURE_TRUE(brNode, NS_ERROR_FAILURE);
 
         // Refresh thePoint, prevPoint
-        thePoint = GetCharBefore(aRun->mEndNode, aRun->mEndOffset);
-        prevPoint = GetCharBefore(thePoint);
+        thePoint = GetPreviousCharPoint(aRun->EndPoint());
+        prevPoint = GetPreviousCharPoint(thePoint);
         rightCheck = true;
       }
     }
     if (leftCheck && rightCheck) {
       // Now replace nbsp with space.  First, insert a space
       AutoTransactionsConserveSelection dontChangeMySelection(mHTMLEditor);
       nsAutoString spaceStr(char16_t(32));
       nsresult rv =
@@ -1873,19 +1865,19 @@ WSRunObject::CheckTrailingNBSP(WSFragmen
 {
   // Try to change an nbsp to a space, if possible, just to prevent nbsp
   // proliferation.  This routine is called when we are about to make this
   // point in the ws abut an inserted break or text, so we don't have to worry
   // about what is after it.  What is after it now will end up after the
   // inserted object.
   NS_ENSURE_TRUE(aRun && aNode, NS_ERROR_NULL_POINTER);
   bool canConvert = false;
-  WSPoint thePoint = GetCharBefore(aNode, aOffset);
+  WSPoint thePoint = GetPreviousCharPoint(EditorRawDOMPoint(aNode, aOffset));
   if (thePoint.mTextNode && thePoint.mChar == nbsp) {
-    WSPoint prevPoint = GetCharBefore(thePoint);
+    WSPoint prevPoint = GetPreviousCharPoint(thePoint);
     if (prevPoint.mTextNode) {
       if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) {
         canConvert = true;
       }
     } else if (aRun->mLeftType == WSType::text ||
                aRun->mLeftType == WSType::special) {
       canConvert = true;
     }
@@ -1916,22 +1908,22 @@ WSRunObject::CheckLeadingNBSP(WSFragment
                               nsINode* aNode,
                               int32_t aOffset)
 {
   // Try to change an nbsp to a space, if possible, just to prevent nbsp
   // proliferation This routine is called when we are about to make this point
   // in the ws abut an inserted text, so we don't have to worry about what is
   // before it.  What is before it now will end up before the inserted text.
   bool canConvert = false;
-  WSPoint thePoint = GetCharAfter(aNode, aOffset);
+  WSPoint thePoint = GetNextCharPoint(EditorRawDOMPoint(aNode, aOffset));
   if (thePoint.mChar == nbsp) {
     WSPoint tmp = thePoint;
     // we want to be after thePoint
     tmp.mOffset++;
-    WSPoint nextPoint = GetCharAfter(tmp);
+    WSPoint nextPoint = GetNextCharPoint(tmp);
     if (nextPoint.mTextNode) {
       if (!nsCRT::IsAsciiSpace(nextPoint.mChar)) {
         canConvert = true;
       }
     } else if (aRun->mRightType == WSType::text ||
                aRun->mRightType == WSType::special ||
                aRun->mRightType == WSType::br) {
       canConvert = true;
--- a/editor/libeditor/WSRunObject.h
+++ b/editor/libeditor/WSRunObject.h
@@ -379,20 +379,42 @@ protected:
    * 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);
+  /**
+   * GetNextCharPoint() returns next character's point of aPoint.  If there is
+   * no character after aPoint, mTextNode is set to nullptr.
+   */
+  WSPoint GetNextCharPoint(const EditorRawDOMPoint& aPoint);
+  WSPoint GetNextCharPoint(const WSPoint& aPoint);
+
+  /**
+   * GetPreviousCharPoint() returns previous character's point of of aPoint.
+   * If there is no character before aPoint, mTextNode is set to nullptr.
+   */
+  WSPoint GetPreviousCharPoint(const EditorRawDOMPoint& aPoint);
+  WSPoint GetPreviousCharPoint(const WSPoint& aPoint);
+
+  /**
+   * GetNextCharPointInternal() and GetPreviousCharPointInternal() are
+   * helper methods of GetNextCharPoint(const EditorRawDOMPoint&) and
+   * GetPreviousCharPoint(const EditorRawDOMPoint&).  When the container
+   * isn't in mNodeArray, they call one of these methods.  Then, these
+   * methods look for nearest text node in mNodeArray from aPoint.
+   * Then, will call GetNextCharPoint(const WSPoint&) or
+   * GetPreviousCharPoint(const WSPoint&) and returns its result.
+   */
+  WSPoint GetNextCharPointInternal(const EditorRawDOMPoint& aPoint);
+  WSPoint GetPreviousCharPointInternal(const EditorRawDOMPoint& 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);
 
   /**
    * FindNearestRun() looks for a WSFragment which is closest to specified
    * direction from aPoint.
@@ -412,18 +434,16 @@ protected:
    *                      if aPoint is end of a run, returns the run.
    *                      if aPoint is start of a run, returns its next run.
    *                      if aPoint is before the first run, returns nullptr.
    *                      if aPoint is after the last run, returns the last run.
    */
   WSFragment* FindNearestRun(const EditorRawDOMPoint& aPoint, bool aForward);
 
   char16_t GetCharAt(dom::Text* aTextNode, int32_t aOffset);
-  WSPoint GetWSPointAfter(nsINode* aNode, int32_t aOffset);
-  WSPoint GetWSPointBefore(nsINode* aNode, int32_t aOffset);
   nsresult CheckTrailingNBSPOfRun(WSFragment *aRun);
   nsresult CheckTrailingNBSP(WSFragment* aRun, nsINode* aNode,
                              int32_t aOffset);
   nsresult CheckLeadingNBSP(WSFragment* aRun, nsINode* aNode,
                             int32_t aOffset);
 
   nsresult Scrub();
   bool IsBlockNode(nsINode* aNode);