Bug 1399626 - Part 4: Allow initializing nsIContentIterator with RangeBoundaries, r=masayuki draft
authorMichael Layzell <michael@thelayzells.com>
Wed, 13 Sep 2017 16:58:13 -0400
changeset 672783 9440e6bef786e6c9562fff8e0fb97205d5b44d9f
parent 672782 41507a50a158951342fc68925c0866df07f2a968
child 672784 b4dcc64f7eb62cff9c8e358cf7a2636c3883c4ba
push id82373
push userbmo:nika@thelayzells.com
push dateFri, 29 Sep 2017 19:23:28 +0000
reviewersmasayuki
bugs1399626
milestone58.0a1
Bug 1399626 - Part 4: Allow initializing nsIContentIterator with RangeBoundaries, r=masayuki This patch adds an overload to nsIContentIterator::Init which accepts RangeBoundary objects, and modifies the codepath to avoid using the offset of the start or end nodes to construct the nsIContentIterator.
dom/base/nsContentIterator.cpp
dom/base/nsIContentIterator.h
editor/txtsvc/nsFilteredContentIterator.cpp
editor/txtsvc/nsFilteredContentIterator.h
toolkit/components/find/nsFind.cpp
--- a/dom/base/nsContentIterator.cpp
+++ b/dom/base/nsContentIterator.cpp
@@ -13,90 +13,72 @@
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsContentUtils.h"
 #include "nsINode.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsElementTable.h"
 
 using mozilla::DebugOnly;
+using mozilla::RawRangeBoundary;
 
 // couple of utility static functs
 
 ///////////////////////////////////////////////////////////////////////////
-// NodeToParentOffset: returns the node's parent and offset.
-//
-
-static nsINode*
-NodeToParentOffset(nsINode* aNode, int32_t* aOffset)
-{
-  *aOffset = 0;
-
-  nsINode* parent = aNode->GetParentNode();
-
-  if (parent) {
-    *aOffset = parent->IndexOf(aNode);
-    NS_WARNING_ASSERTION(*aOffset >= 0, "bad offset");
-  }
-
-  return parent;
-}
-
-///////////////////////////////////////////////////////////////////////////
 // NodeIsInTraversalRange: returns true if content is visited during
 // the traversal of the range in the specified mode.
 //
 static bool
 NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode,
-                       nsINode* aStartContainer, int32_t aStartOffset,
-                       nsINode* aEndContainer, int32_t aEndOffset)
+                       const RawRangeBoundary& aStart,
+                       const RawRangeBoundary& aEnd)
 {
-  if (NS_WARN_IF(!aStartContainer) || NS_WARN_IF(!aEndContainer) ||
+  if (NS_WARN_IF(!aStart.IsSet()) || NS_WARN_IF(!aEnd.IsSet()) ||
       NS_WARN_IF(!aNode)) {
     return false;
   }
 
   // If a leaf node contains an end point of the traversal range, it is
   // always in the traversal range.
-  if (aNode == aStartContainer || aNode == aEndContainer) {
+  if (aNode == aStart.Container() || aNode == aEnd.Container()) {
     if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
       return true; // text node or something
     }
     if (!aNode->HasChildren()) {
-      MOZ_ASSERT(aNode != aStartContainer || !aStartOffset,
-        "aStartContainer doesn't have children and not a data node, "
-        "aStartOffset should be 0");
-      MOZ_ASSERT(aNode != aEndContainer || !aEndOffset,
-        "aEndContainer doesn't have children and not a data node, "
-        "aEndOffset should be 0");
+      MOZ_ASSERT(aNode != aStart.Container() || aStart.IsStartOfContainer(),
+        "aStart.Container() doesn't have children and not a data node, "
+        "aStart should be at the beginning of its container");
+      MOZ_ASSERT(aNode != aEnd.Container() || aEnd.IsStartOfContainer(),
+        "aEnd.Container() doesn't have children and not a data node, "
+        "aEnd should be at the beginning of its container");
       return true;
     }
   }
 
   nsINode* parent = aNode->GetParentNode();
   if (!parent) {
     return false;
   }
 
-  int32_t indx = parent->IndexOf(aNode);
-  NS_WARNING_ASSERTION(indx != -1, "bad indx");
-
   if (!aIsPreMode) {
+    // aNode should always be content, as we have a parent, but let's just be
+    // extra careful and check.
+    nsIContent* content = NS_WARN_IF(!aNode->IsContent())
+      ? nullptr
+      : aNode->AsContent();
     // Post mode: start < node <= end.
-    return nsContentUtils::ComparePoints(aStartContainer, aStartOffset,
-                                         parent, indx + 1) < 0 &&
-           nsContentUtils::ComparePoints(aEndContainer, aEndOffset,
-                                         parent, indx + 1) >= 0;
+    RawRangeBoundary afterNode(parent, content);
+    return nsContentUtils::ComparePoints(aStart, afterNode) < 0 &&
+           nsContentUtils::ComparePoints(aEnd, afterNode) >= 0;
   }
 
   // Pre mode: start <= node < end.
-  return nsContentUtils::ComparePoints(aStartContainer, aStartOffset,
-                                       parent, indx) <= 0 &&
-         nsContentUtils::ComparePoints(aEndContainer, aEndOffset,
-                                       parent, indx) > 0;
+  RawRangeBoundary beforeNode(parent, aNode->GetPreviousSibling());
+  return nsContentUtils::ComparePoints(aStart, beforeNode) <= 0 &&
+         nsContentUtils::ComparePoints(aEnd, beforeNode) > 0;
 }
 
 
 
 /*
  *  A simple iterator class for traversing the content in "close tag" order
  */
 class nsContentIterator : public nsIContentIterator
@@ -111,16 +93,19 @@ public:
 
   virtual nsresult Init(nsINode* aRoot) override;
 
   virtual nsresult Init(nsIDOMRange* aRange) override;
 
   virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
                         nsINode* aEndContainer, uint32_t aEndOffset) override;
 
+  virtual nsresult Init(const RawRangeBoundary& aStart,
+                        const RawRangeBoundary& aEnd) override;
+
   virtual void First() override;
 
   virtual void Last() override;
 
   virtual void Next() override;
 
   virtual void Prev() override;
 
@@ -134,18 +119,18 @@ protected:
   virtual ~nsContentIterator();
 
   /**
    * Callers must guarantee that:
    * - Neither aStartContainer nor aEndContainer is nullptr.
    * - aStartOffset and aEndOffset are valid for its container.
    * - The start point and the end point are in document order.
    */
-  nsresult InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
-                        nsINode* aEndContainer, uint32_t aEndOffset);
+  nsresult InitInternal(const RawRangeBoundary& aStart,
+                        const RawRangeBoundary& aEnd);
 
   // Recursively get the deepest first/last child of aRoot.  This will return
   // aRoot itself if it has no children.
   nsINode* GetDeepFirstChild(nsINode* aRoot,
                              nsTArray<int32_t>* aIndexes = nullptr);
   nsIContent* GetDeepFirstChild(nsIContent* aRoot,
                                 nsTArray<int32_t>* aIndexes = nullptr);
   nsINode* GetDeepLastChild(nsINode* aRoot,
@@ -318,217 +303,216 @@ nsContentIterator::Init(nsIDOMRange* aDO
     return NS_ERROR_INVALID_ARG;
   }
 
   nsRange* range = static_cast<nsRange*>(aDOMRange);
   if (NS_WARN_IF(!range->IsPositioned())) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  return InitInternal(range->GetStartContainer(), range->StartOffset(),
-                      range->GetEndContainer(), range->EndOffset());
+  return InitInternal(range->StartRef().AsRaw(), range->EndRef().AsRaw());
 }
 
 nsresult
 nsContentIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
                         nsINode* aEndContainer, uint32_t aEndOffset)
 {
   mIsDone = false;
 
   if (NS_WARN_IF(!nsRange::IsValidPoints(aStartContainer, aStartOffset,
                                          aEndContainer, aEndOffset))) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  return InitInternal(aStartContainer, aStartOffset,
-                      aEndContainer, aEndOffset);
+  return InitInternal(RawRangeBoundary(aStartContainer, aStartOffset),
+                      RawRangeBoundary(aEndContainer, aEndOffset));
 }
 
-// XXX Argument names will be replaced in the following patch.
 nsresult
-nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
-                                nsINode* aEndContainer, uint32_t aEndOffset)
+nsContentIterator::Init(const RawRangeBoundary& aStart,
+                        const RawRangeBoundary& aEnd)
+{
+  mIsDone = false;
+
+
+  if (NS_WARN_IF(!nsRange::IsValidPoints(aStart.Container(), aStart.Offset(),
+                                         aEnd.Container(), aEnd.Offset()))) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return InitInternal(aStart, aEnd);
+}
+
+nsresult
+nsContentIterator::InitInternal(const RawRangeBoundary& aStart,
+                                const RawRangeBoundary& aEnd)
 {
   // get common content parent
   mCommonParent =
-    nsContentUtils::GetCommonAncestor(aStartContainer, aEndContainer);
+    nsContentUtils::GetCommonAncestor(aStart.Container(), aEnd.Container());
   if (NS_WARN_IF(!mCommonParent)) {
     return NS_ERROR_FAILURE;
   }
 
-  bool startIsData = aStartContainer->IsNodeOfType(nsINode::eDATA_NODE);
+  bool startIsData = aStart.Container()->IsNodeOfType(nsINode::eDATA_NODE);
 
-  // short circuit when start node == end node
-  if (aStartContainer == aEndContainer) {
-    // Check to see if we have a collapsed range, if so, there is nothing to
-    // iterate over.
-    //
-    // XXX: CharacterDataNodes (text nodes) are currently an exception, since
-    //      we always want to be able to iterate text nodes at the end points
-    //      of a range.
+  // Check to see if we have a collapsed range, if so, there is nothing to
+  // iterate over.
+  //
+  // XXX: CharacterDataNodes (text nodes) are currently an exception, since
+  //      we always want to be able to iterate text nodes at the end points
+  //      of a range.
 
-    if (!startIsData && aStartOffset == aEndOffset) {
-      MakeEmpty();
-      return NS_OK;
-    }
+  if (!startIsData && aStart == aEnd) {
+    MakeEmpty();
+    return NS_OK;
+  }
 
-    if (startIsData) {
-      // It's a character data node.
-      mFirst   = aStartContainer->AsContent();
-      mLast    = mFirst;
-      mCurNode = mFirst;
+  // Handle ranges within a single character data node.
+  if (startIsData && aStart.Container() == aEnd.Container()) {
+    mFirst = aStart.Container()->AsContent();
+    mLast = mFirst;
+    mCurNode = mFirst;
 
-      DebugOnly<nsresult> rv = RebuildIndexStack();
-      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RebuildIndexStack failed");
-      return NS_OK;
-    }
+    DebugOnly<nsresult> rv = RebuildIndexStack();
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RebuildIndexStack failed");
+    return NS_OK;
   }
 
   // Find first node in range.
 
   nsIContent* cChild = nullptr;
 
-  // Valid start indices are 0 <= aStartOffset <= childCount. That means if
-  // start node has no children, only offset 0 is valid.
-  if (!startIsData && aStartOffset < aStartContainer->GetChildCount()) {
-    cChild = aStartContainer->GetChildAt(aStartOffset);
-    NS_WARNING_ASSERTION(cChild, "GetChildAt returned null");
+  // Try to get the child at our starting point. This might return null if
+  // aStart is immediately after the last node in aStart.Container().
+  if (!startIsData) {
+    cChild = aStart.GetChildAtOffset();
   }
 
   if (!cChild) {
     // No children (possibly a <br> or text node), or index is after last child.
 
     if (mPre) {
       // XXX: In the future, if start offset is after the last
       //      character in the cdata node, should we set mFirst to
       //      the next sibling?
 
       // Normally we would skip the start node because the start node is outside
       // of the range in pre mode. However, if aStartOffset == 0, and the node
       // is a non-container node (e.g. <br>), we don't skip the node in this
       // case in order to address bug 1215798.
       bool startIsContainer = true;
-      if (aStartContainer->IsHTMLElement()) {
-        nsIAtom* name = aStartContainer->NodeInfo()->NameAtom();
+      if (aStart.Container()->IsHTMLElement()) {
+        nsIAtom* name = aStart.Container()->NodeInfo()->NameAtom();
         startIsContainer =
           nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name));
       }
-      if (!startIsData && (startIsContainer || aStartOffset)) {
-        mFirst = GetNextSibling(aStartContainer);
+      if (!startIsData && (startIsContainer || !aStart.IsStartOfContainer())) {
+        mFirst = GetNextSibling(aStart.Container());
         NS_WARNING_ASSERTION(mFirst, "GetNextSibling returned null");
 
         // Does mFirst node really intersect the range?  The range could be
         // 'degenerate', i.e., not collapsed but still contain no content.
         if (mFirst &&
-            NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre,
-                                               aStartContainer, aStartOffset,
-                                               aEndContainer, aEndOffset))) {
+            NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, aStart, aEnd))) {
           mFirst = nullptr;
         }
       } else {
-        mFirst = aStartContainer->AsContent();
+        mFirst = aStart.Container()->AsContent();
       }
     } else {
       // post-order
-      if (NS_WARN_IF(!aStartContainer->IsContent())) {
+      if (NS_WARN_IF(!aStart.Container()->IsContent())) {
         // What else can we do?
         mFirst = nullptr;
       } else {
-        mFirst = aStartContainer->AsContent();
+        mFirst = aStart.Container()->AsContent();
       }
     }
   } else {
     if (mPre) {
       mFirst = cChild;
     } else {
       // post-order
       mFirst = GetDeepFirstChild(cChild);
       NS_WARNING_ASSERTION(mFirst, "GetDeepFirstChild returned null");
 
       // Does mFirst node really intersect the range?  The range could be
       // 'degenerate', i.e., not collapsed but still contain no content.
 
-      if (mFirst &&
-          !NodeIsInTraversalRange(mFirst, mPre, aStartContainer, aStartOffset,
-                                  aEndContainer, aEndOffset)) {
+      if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, aStart, aEnd)) {
         mFirst = nullptr;
       }
     }
   }
 
 
   // Find last node in range.
 
-  bool endIsData = aEndContainer->IsNodeOfType(nsINode::eDATA_NODE);
+  bool endIsData = aEnd.Container()->IsNodeOfType(nsINode::eDATA_NODE);
 
-  if (endIsData || !aEndContainer->HasChildren() || !aEndOffset) {
+  if (endIsData || !aEnd.Container()->HasChildren() || aEnd.IsStartOfContainer()) {
     if (mPre) {
-      if (NS_WARN_IF(!aEndContainer->IsContent())) {
+      if (NS_WARN_IF(!aEnd.Container()->IsContent())) {
         // Not much else to do here...
         mLast = nullptr;
       } else {
         // If the end node is a non-container element and the end offset is 0,
         // the last element should be the previous node (i.e., shouldn't
         // include the end node in the range).
         bool endIsContainer = true;
-        if (aEndContainer->IsHTMLElement()) {
-          nsIAtom* name = aEndContainer->NodeInfo()->NameAtom();
+        if (aEnd.Container()->IsHTMLElement()) {
+          nsIAtom* name = aEnd.Container()->NodeInfo()->NameAtom();
           endIsContainer =
             nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name));
         }
-        if (!endIsData && !endIsContainer && !aEndOffset) {
-          mLast = PrevNode(aEndContainer);
+        if (!endIsData && !endIsContainer && aEnd.IsStartOfContainer()) {
+          mLast = PrevNode(aEnd.Container());
           NS_WARNING_ASSERTION(mLast, "PrevNode returned null");
           if (mLast && mLast != mFirst &&
               NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
-                                                 mFirst, 0,
-                                                 aEndContainer, aEndOffset))) {
+                                                 RawRangeBoundary(mFirst, 0),
+                                                 aEnd))) {
             mLast = nullptr;
           }
         } else {
-          mLast = aEndContainer->AsContent();
+          mLast = aEnd.Container()->AsContent();
         }
       }
     } else {
       // post-order
       //
       // XXX: In the future, if end offset is before the first character in the
       //      cdata node, should we set mLast to the prev sibling?
 
       if (!endIsData) {
-        mLast = GetPrevSibling(aEndContainer);
+        mLast = GetPrevSibling(aEnd.Container());
         NS_WARNING_ASSERTION(mLast, "GetPrevSibling returned null");
 
-        if (!NodeIsInTraversalRange(mLast, mPre,
-                                    aStartContainer, aStartOffset,
-                                    aEndContainer, aEndOffset)) {
+        if (!NodeIsInTraversalRange(mLast, mPre, aStart, aEnd)) {
           mLast = nullptr;
         }
       } else {
-        mLast = aEndContainer->AsContent();
+        mLast = aEnd.Container()->AsContent();
       }
     }
   } else {
-    int32_t indx = aEndOffset;
-
-    cChild = aEndContainer->GetChildAt(--indx);
+    cChild = aEnd.Ref();
 
     if (NS_WARN_IF(!cChild)) {
       // No child at offset!
       NS_NOTREACHED("nsContentIterator::nsContentIterator");
       return NS_ERROR_FAILURE;
     }
 
     if (mPre) {
       mLast  = GetDeepLastChild(cChild);
       NS_WARNING_ASSERTION(mLast, "GetDeepLastChild returned null");
 
-      if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
-                                             aStartContainer, aStartOffset,
-                                             aEndContainer, aEndOffset))) {
+      if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre, aStart, aEnd))) {
         mLast = nullptr;
       }
     } else {
       // post-order
       mLast = cChild;
     }
   }
 
@@ -1062,61 +1046,52 @@ nsContentIterator::PositionAt(nsINode* a
   // take an early out if this doesn't actually change the position
   if (mCurNode == tempNode) {
     mIsDone = false;  // paranoia
     return NS_OK;
   }
 
   // Check to see if the node falls within the traversal range.
 
-  nsINode* firstNode = mFirst;
-  nsINode* lastNode = mLast;
-  int32_t firstOffset = 0, lastOffset = 0;
+  RawRangeBoundary first(mFirst, 0);
+  RawRangeBoundary last(mLast, 0);
 
-  if (firstNode && lastNode) {
+  if (mFirst && mLast) {
     if (mPre) {
-      firstNode = NodeToParentOffset(mFirst, &firstOffset);
-      NS_WARNING_ASSERTION(firstNode, "NodeToParentOffset returned null");
-      NS_WARNING_ASSERTION(firstOffset >= 0, "bad firstOffset");
+      // In pre we want to record the point immediately before mFirst, which is
+      // the point immediately after mFirst's previous sibling.
+      first.SetAfterRef(mFirst->GetParentNode(), mFirst->GetPreviousSibling());
 
-      if (lastNode->GetChildCount()) {
-        lastOffset = 0;
-      } else {
-        lastNode = NodeToParentOffset(mLast, &lastOffset);
-        NS_WARNING_ASSERTION(lastNode, "NodeToParentOffset returned null");
-        NS_WARNING_ASSERTION(lastOffset >= 0, "bad lastOffset");
-        ++lastOffset;
+      // If mLast has no children, then we want to make sure to include it.
+      if (!mLast->HasChildren()) {
+        last.SetAfterRef(mLast->GetParentNode(), mLast->AsContent());
       }
     } else {
-      uint32_t numChildren = firstNode->GetChildCount();
-
-      if (numChildren) {
-        firstOffset = numChildren;
-        NS_WARNING_ASSERTION(firstOffset >= 0, "bad firstOffset");
+      // If the first node has any children, we want to be immediately after the
+      // last. Otherwise we want to be immediately before mFirst.
+      if (mFirst->HasChildren()) {
+        first.SetAfterRef(mFirst, mFirst->GetLastChild());
       } else {
-        firstNode = NodeToParentOffset(mFirst, &firstOffset);
-        NS_WARNING_ASSERTION(firstNode, "NodeToParentOffset returned null");
-        NS_WARNING_ASSERTION(firstOffset >= 0, "bad firstOffset");
+        first.SetAfterRef(mFirst->GetParentNode(), mFirst->GetPreviousSibling());
       }
 
-      lastNode = NodeToParentOffset(mLast, &lastOffset);
-      NS_WARNING_ASSERTION(lastNode, "NodeToParentOffset returned null");
-      NS_WARNING_ASSERTION(lastOffset >= 0, "bad lastOffset");
-      ++lastOffset;
+      // Set the last point immediately after the final node.
+      last.SetAfterRef(mLast->GetParentNode(), mLast->AsContent());
     }
   }
 
+  NS_WARNING_ASSERTION(first.IsSetAndValid(), "first is not valid");
+  NS_WARNING_ASSERTION(last.IsSetAndValid(), "last is not valid");
+
   // The end positions are always in the range even if it has no parent.  We
   // need to allow that or 'iter->Init(root)' would assert in Last() or First()
   // for example, bug 327694.
   if (mFirst != mCurNode && mLast != mCurNode &&
-      (NS_WARN_IF(!firstNode) || NS_WARN_IF(!lastNode) ||
-       NS_WARN_IF(!NodeIsInTraversalRange(mCurNode, mPre,
-                                          firstNode, firstOffset,
-                                          lastNode, lastOffset)))) {
+      (NS_WARN_IF(!first.IsSet()) || NS_WARN_IF(!last.IsSet()) ||
+       NS_WARN_IF(!NodeIsInTraversalRange(mCurNode, mPre, first, last)))) {
     mIsDone = true;
     return NS_ERROR_FAILURE;
   }
 
   // We can be at ANY node in the sequence.  Need to regenerate the array of
   // indexes back to the root or common parent!
   AutoTArray<nsINode*, 8>     oldParentStack;
   AutoTArray<int32_t, 8>      newIndexes;
@@ -1240,16 +1215,19 @@ public:
 
   virtual nsresult Init(nsINode* aRoot) override;
 
   virtual nsresult Init(nsIDOMRange* aRange) override;
 
   virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
                         nsINode* aEndContainer, uint32_t aEndOffset) override;
 
+  virtual nsresult Init(const RawRangeBoundary& aStart,
+                        const RawRangeBoundary& aEnd) override;
+
   virtual void Next() override;
 
   virtual void Prev() override;
 
   virtual nsresult PositionAt(nsINode* aCurNode) override;
 
   // Must override these because we don't do PositionAt
   virtual void First() override;
@@ -1342,31 +1320,35 @@ nsContentSubtreeIterator::Init(nsIDOMRan
 
   return InitWithRange();
 }
 
 nsresult
 nsContentSubtreeIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
                                nsINode* aEndContainer, uint32_t aEndOffset)
 {
+  return Init(RawRangeBoundary(aStartContainer, aStartOffset),
+              RawRangeBoundary(aEndContainer, aEndOffset));
+}
+
+nsresult
+nsContentSubtreeIterator::Init(const RawRangeBoundary& aStart,
+                               const RawRangeBoundary& aEnd)
+{
   mIsDone = false;
 
   RefPtr<nsRange> range;
-  nsresult rv = nsRange::CreateRange(aStartContainer, aStartOffset,
-                                     aEndContainer, aEndOffset,
-                                     getter_AddRefs(range));
+  nsresult rv = nsRange::CreateRange(aStart, aEnd, getter_AddRefs(range));
   if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!range) ||
       NS_WARN_IF(!range->IsPositioned())) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  if (NS_WARN_IF(range->GetStartContainer() != aStartContainer) ||
-      NS_WARN_IF(range->GetEndContainer() != aEndContainer) ||
-      NS_WARN_IF(range->StartOffset() != aStartOffset) ||
-      NS_WARN_IF(range->EndOffset() != aEndOffset)) {
+  if (NS_WARN_IF(range->StartRef() != aStart) ||
+      NS_WARN_IF(range->EndRef() != aEnd)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   mRange = Move(range);
 
   return InitWithRange();
 }
 
--- a/dom/base/nsIContentIterator.h
+++ b/dom/base/nsIContentIterator.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __nsIContentIterator_h___
 #define __nsIContentIterator_h___
 
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
+#include "mozilla/RangeBoundary.h"
 
 class nsINode;
 class nsIDOMRange;
 
 #define NS_ICONTENTITERATOR_IID \
 { 0x2550078e, 0xae87, 0x4914, \
  { 0xb3, 0x04, 0xe4, 0xd1, 0x46, 0x19, 0x3d, 0x5f } }
 
@@ -34,16 +35,23 @@ public:
   /* Initializes an iterator for the subtree between
      aStartContainer/aStartOffset and aEndContainer/aEndOffset
      Callers should guarantee that the start point and end point are in
      document order.
    */
   virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
                         nsINode* aEndContainer, uint32_t aEndOffset) = 0;
 
+  /* Initializes an iterator for the subtree between aStart and aEnd.
+     Callers should guarantee that the start point and end point are in
+     document order.
+   */
+  virtual nsresult Init(const mozilla::RawRangeBoundary& aStart,
+                        const mozilla::RawRangeBoundary& aEnd) = 0;
+
   /** First will reset the list.
    */
   virtual void First() = 0;
 
   /** Last will reset the list to the end.
    */
   virtual void Last() = 0;
 
--- a/editor/txtsvc/nsFilteredContentIterator.cpp
+++ b/editor/txtsvc/nsFilteredContentIterator.cpp
@@ -95,29 +95,33 @@ nsFilteredContentIterator::Init(nsIDOMRa
   return InitWithRange();
 }
 
 //------------------------------------------------------------
 nsresult
 nsFilteredContentIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
                                 nsINode* aEndContainer, uint32_t aEndOffset)
 {
+  return Init(RawRangeBoundary(aStartContainer, aStartOffset),
+              RawRangeBoundary(aEndContainer, aEndOffset));
+}
+
+nsresult
+nsFilteredContentIterator::Init(const RawRangeBoundary& aStart,
+                                const RawRangeBoundary& aEnd)
+{
   RefPtr<nsRange> range;
-  nsresult rv = nsRange::CreateRange(aStartContainer, aStartOffset,
-                                     aEndContainer, aEndOffset,
-                                     getter_AddRefs(range));
+  nsresult rv = nsRange::CreateRange(aStart, aEnd, getter_AddRefs(range));
   if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!range) ||
       NS_WARN_IF(!range->IsPositioned())) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  MOZ_ASSERT(range->GetStartContainer() == aStartContainer);
-  MOZ_ASSERT(range->GetEndContainer() == aEndContainer);
-  MOZ_ASSERT(range->StartOffset() == aStartOffset);
-  MOZ_ASSERT(range->EndOffset() == aEndOffset);
+  MOZ_ASSERT(range->StartRef() == aStart);
+  MOZ_ASSERT(range->EndRef() == aEnd);
 
   mRange = Move(range);
 
   return InitWithRange();
 }
 
 nsresult
 nsFilteredContentIterator::InitWithRange()
--- a/editor/txtsvc/nsFilteredContentIterator.h
+++ b/editor/txtsvc/nsFilteredContentIterator.h
@@ -29,16 +29,18 @@ public:
 
   explicit nsFilteredContentIterator(nsITextServicesFilter* aFilter);
 
   /* nsIContentIterator */
   virtual nsresult Init(nsINode* aRoot) override;
   virtual nsresult Init(nsIDOMRange* aRange) override;
   virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
                         nsINode* aEndContainer, uint32_t aEndOffset) override;
+  virtual nsresult Init(const mozilla::RawRangeBoundary& aStart,
+                        const mozilla::RawRangeBoundary& aEnd) override;
   virtual void First() override;
   virtual void Last() override;
   virtual void Next() override;
   virtual void Prev() override;
   virtual nsINode *GetCurrentNode() override;
   virtual bool IsDone() override;
   virtual nsresult PositionAt(nsINode* aCurNode) override;
 
--- a/toolkit/components/find/nsFind.cpp
+++ b/toolkit/components/find/nsFind.cpp
@@ -102,16 +102,22 @@ public:
     return NS_ERROR_NOT_IMPLEMENTED;
   }
   virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
                         nsINode* aEndContainer, uint32_t aEndOffset) override
   {
     NS_NOTREACHED("internal error");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
+  virtual nsresult Init(const RawRangeBoundary& aStart,
+                        const RawRangeBoundary& aEnd) override
+  {
+    NS_NOTREACHED("internal error");
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
   // Not a range because one of the endpoints may be anonymous.
   nsresult Init(nsIDOMNode* aStartNode, int32_t aStartOffset,
                 nsIDOMNode* aEndNode, int32_t aEndOffset);
   virtual void First() override;
   virtual void Last() override;
   virtual void Next() override;
   virtual void Prev() override;
   virtual nsINode* GetCurrentNode() override;