Bug 1375502 - part2: Add nsIContentIterator::Init(nsINode*, uint32_t, nsINode*, uint32_t) r?mats draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 26 Jun 2017 17:26:27 +0900
changeset 652769 235a229c203d125fafe56d9ca2aa8e7f1545fb56
parent 652768 11166f68a01fdaf6d34479410988f9d664411268
child 652770 f8a34bcf1d2984f50e088783fa2c1b65d7f2807c
push id76147
push usermasayuki@d-toybox.com
push dateFri, 25 Aug 2017 07:24:02 +0000
reviewersmats
bugs1375502
milestone57.0a1
Bug 1375502 - part2: Add nsIContentIterator::Init(nsINode*, uint32_t, nsINode*, uint32_t) r?mats nsIContentIterator::Init() takes nsRange but it's too expensive for some users. So, there should be another Init() which can be specified a range in DOM tree with 2 pairs of nsINode* and uint32_t. MozReview-Commit-ID: 6JXic0KOM2d
dom/base/nsContentIterator.cpp
dom/base/nsIContentIterator.h
dom/base/nsRange.cpp
dom/base/nsRange.h
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
editor/txtsvc/nsFilteredContentIterator.cpp
editor/txtsvc/nsFilteredContentIterator.h
toolkit/components/find/nsFind.cpp
--- a/dom/base/nsContentIterator.cpp
+++ b/dom/base/nsContentIterator.cpp
@@ -108,16 +108,19 @@ public:
   explicit nsContentIterator(bool aPre);
 
   // nsIContentIterator interface methods ------------------------------
 
   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 void First() override;
 
   virtual void Last() override;
 
   virtual void Next() override;
 
   virtual void Prev() override;
 
@@ -125,16 +128,25 @@ public:
 
   virtual bool IsDone() override;
 
   virtual nsresult PositionAt(nsINode* aCurNode) override;
 
 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);
+
   // 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,
                             nsTArray<int32_t>* aIndexes = nullptr);
@@ -295,42 +307,55 @@ nsContentIterator::Init(nsINode* aRoot)
   mCurNode = mFirst;
   RebuildIndexStack();
   return NS_OK;
 }
 
 nsresult
 nsContentIterator::Init(nsIDOMRange* aDOMRange)
 {
+  mIsDone = false;
+
   if (NS_WARN_IF(!aDOMRange)) {
     return NS_ERROR_INVALID_ARG;
   }
+
   nsRange* range = static_cast<nsRange*>(aDOMRange);
-
-  mIsDone = false;
-
-  // get common content parent
-  mCommonParent = range->GetCommonAncestor();
-  if (NS_WARN_IF(!mCommonParent)) {
-    return NS_ERROR_FAILURE;
+  if (NS_WARN_IF(!range->IsPositioned())) {
+    return NS_ERROR_INVALID_ARG;
   }
 
-  // get the start node and offset
-  int32_t startIndx = range->StartOffset();
-  NS_WARNING_ASSERTION(startIndx >= 0, "bad startIndx");
-  nsINode* startNode = range->GetStartContainer();
-  if (NS_WARN_IF(!startNode)) {
-    return NS_ERROR_FAILURE;
+  return InitInternal(range->GetStartContainer(), range->StartOffset(),
+                      range->GetEndContainer(), range->EndOffset());
+}
+
+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;
   }
 
-  // get the end node and offset
-  int32_t endIndx = range->EndOffset();
-  NS_WARNING_ASSERTION(endIndx >= 0, "bad endIndx");
-  nsINode* endNode = range->GetEndContainer();
-  if (NS_WARN_IF(!endNode)) {
+  return InitInternal(aStartContainer, aStartOffset,
+                      aEndContainer, aEndOffset);
+}
+
+// XXX Argument names will be replaced in the following patch.
+nsresult
+nsContentIterator::InitInternal(nsINode* startNode, uint32_t startIndx,
+                                nsINode* endNode, uint32_t endIndx)
+{
+  // get common content parent
+  mCommonParent =
+    nsContentUtils::GetCommonAncestor(startNode, endNode);
+  if (NS_WARN_IF(!mCommonParent)) {
     return NS_ERROR_FAILURE;
   }
 
   bool startIsData = startNode->IsNodeOfType(nsINode::eDATA_NODE);
 
   // short circuit when start node == end node
   if (startNode == endNode) {
     // Check to see if we have a collapsed range, if so, there is nothing to
@@ -1213,31 +1238,39 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsContentSubtreeIterator, nsContentIterator)
 
   // nsContentIterator overrides ------------------------------
 
   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 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;
 
   // Must override these because we don't do PositionAt
   virtual void Last() override;
 
 protected:
   virtual ~nsContentSubtreeIterator() {}
 
+  /**
+   * Callers must guarantee that mRange isn't nullptr and is positioned.
+   */
+  nsresult InitWithRange();
+
   // Returns the highest inclusive ancestor of aNode that's in the range
   // (possibly aNode itself).  Returns null if aNode is null, or is not itself
   // in the range.  A node is in the range if (node, 0) comes strictly after
   // the range endpoint, and (node, node.length) comes strictly before it, so
   // the range's start and end nodes will never be considered "in" it.
   nsIContent* GetTopAncestorInRange(nsINode* aNode);
 
   // no copy's or assigns  FIX ME
@@ -1296,17 +1329,58 @@ nsContentSubtreeIterator::Init(nsINode* 
 
 nsresult
 nsContentSubtreeIterator::Init(nsIDOMRange* aRange)
 {
   MOZ_ASSERT(aRange);
 
   mIsDone = false;
 
-  mRange = static_cast<nsRange*>(aRange);
+  nsRange* range = static_cast<nsRange*>(aRange);
+  if (NS_WARN_IF(!range->IsPositioned())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mRange = range;
+
+  return InitWithRange();
+}
+
+nsresult
+nsContentSubtreeIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
+                               nsINode* aEndContainer, uint32_t aEndOffset)
+{
+  mIsDone = false;
+
+  RefPtr<nsRange> range;
+  nsresult rv = nsRange::CreateRange(aStartContainer, aStartOffset,
+                                     aEndContainer, aEndOffset,
+                                     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)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mRange = Move(range);
+
+  return InitWithRange();
+}
+
+nsresult
+nsContentSubtreeIterator::InitWithRange()
+{
+  MOZ_ASSERT(mRange);
+  MOZ_ASSERT(mRange->IsPositioned());
 
   // get the start node and offset, convert to nsINode
   mCommonParent = mRange->GetCommonAncestor();
   nsINode* startContainer = mRange->GetStartContainer();
   int32_t startOffset = mRange->StartOffset();
   nsINode* endContainer = mRange->GetEndContainer();
   int32_t endOffset = mRange->EndOffset();
   MOZ_ASSERT(mCommonParent && startContainer && endContainer);
--- a/dom/base/nsIContentIterator.h
+++ b/dom/base/nsIContentIterator.h
@@ -26,16 +26,24 @@ public:
    */
   virtual nsresult Init(nsINode* aRoot) = 0;
 
   /* Initializes an iterator for the subtree defined by the range aRange
      Subclasses should make sure they implement both of these!
    */
   virtual nsresult Init(nsIDOMRange* aRange) = 0;
 
+  /* 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;
+
   /** First will reset the list.
    */
   virtual void First() = 0;
 
   /** Last will reset the list to the end.
    */
   virtual void Last() = 0;
 
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -1258,16 +1258,51 @@ nsRange::ComputeRootNode(nsINode* aNode,
 
   NS_ASSERTION(!root->IsNodeOfType(nsINode::eDOCUMENT),
                "GetUncomposedDoc should have returned a doc");
 
   // We allow this because of backward compatibility.
   return root;
 }
 
+/* static */
+bool
+nsRange::IsValidPoints(nsINode* aStartContainer, uint32_t aStartOffset,
+                       nsINode* aEndContainer, uint32_t aEndOffset)
+{
+  // Use NS_WARN_IF() only for the cases where the arguments are unexpected.
+  if (NS_WARN_IF(!aStartContainer) || NS_WARN_IF(!aEndContainer) ||
+      NS_WARN_IF(!IsValidOffset(aStartContainer, aStartOffset)) ||
+      NS_WARN_IF(!IsValidOffset(aEndContainer, aEndOffset))) {
+    return false;
+  }
+
+  // Otherwise, don't use NS_WARN_IF() for preventing to make console messy.
+  // Instead, check one by one since it is easier to catch the error reason
+  // with debugger.
+
+  if (ComputeRootNode(aStartContainer) != ComputeRootNode(aEndContainer)) {
+    return false;
+  }
+
+  bool disconnected = false;
+  int32_t order =
+    nsContentUtils::ComparePoints(aStartContainer,
+                                    static_cast<int32_t>(aStartOffset),
+                                    aEndContainer,
+                                    static_cast<int32_t>(aEndOffset),
+                                    &disconnected);
+  // FYI: disconnected should be false unless |order| is 1.
+  if (order == 1 || NS_WARN_IF(disconnected)) {
+    return false;
+  }
+
+  return true;
+}
+
 void
 nsRange::SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
 {
   AutoCalledByJSRestore calledByJSRestorer(*this);
   mCalledByJS = true;
   SetStart(aNode, aOffset, aErr);
 }
 
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -323,16 +323,23 @@ public:
    * fragment.  If this returns nullptr, that means aNode can be neither the
    * start container nor end container of any range.
    */
   static nsINode* ComputeRootNode(nsINode* aNode)
   {
     return ComputeRootNode(aNode, false);
   }
 
+  /**
+   * Return true if aStartContainer/aStartOffset and aEndContainer/aEndOffset
+   * are valid start and end points for a range.  Otherwise, return false.
+   */
+  static bool IsValidPoints(nsINode* aStartContainer, uint32_t aStartOffset,
+                            nsINode* aEndContainer, uint32_t aEndOffset);
+
 /******************************************************************************
  *  Utility routine to detect if a content node starts before a range and/or
  *  ends after a range.  If neither it is contained inside the range.
  *
  *  XXX - callers responsibility to ensure node in same doc as range!
  *
  *****************************************************************************/
   static nsresult CompareNodeToRange(nsINode* aNode, nsRange* aRange,
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -195,25 +195,16 @@ ContentEventHandler::RawRange::SelectNod
   }
   mRoot = newRoot;
   mStartContainer = mEndContainer = aNodeToSelectContents;
   mStartOffset = 0;
   mEndOffset = aNodeToSelectContents->Length();
   return NS_OK;
 }
 
-already_AddRefed<nsRange>
-ContentEventHandler::RawRange::CreateRange() const
-{
-  RefPtr<nsRange> range = new nsRange(mRoot);
-  range->SetStartAndEnd(mStartContainer, mStartOffset,
-                        mEndContainer, mEndOffset);
-  return range.forget();
-}
-
 /******************************************************************/
 /* ContentEventHandler                                            */
 /******************************************************************/
 
 // NOTE
 //
 // ContentEventHandler *creates* ranges as following rules:
 // 1. Start of range:
@@ -863,18 +854,19 @@ ContentEventHandler::GenerateFlatTextCon
     nsIContent* content = startNode->AsContent();
     AppendSubString(aString, content, aRawRange.StartOffset(),
                     aRawRange.EndOffset() - aRawRange.StartOffset());
     ConvertToNativeNewlines(aString);
     return NS_OK;
   }
 
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
-  RefPtr<nsRange> range = aRawRange.CreateRange();
-  nsresult rv = iter->Init(range);
+  nsresult rv =
+    iter->Init(aRawRange.GetStartContainer(), aRawRange.StartOffset(),
+               aRawRange.GetEndContainer(), aRawRange.EndOffset());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   for (; !iter->IsDone(); iter->Next()) {
     nsINode* node = iter->GetCurrentNode();
     if (NS_WARN_IF(!node)) {
       break;
     }
@@ -1035,18 +1027,19 @@ ContentEventHandler::GenerateFlatFontRan
   nsINode* endNode = aRawRange.GetEndContainer();
   if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
     return NS_ERROR_FAILURE;
   }
 
   // baseOffset is the flattened offset of each content node.
   int32_t baseOffset = 0;
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
-  RefPtr<nsRange> range = aRawRange.CreateRange();
-  nsresult rv = iter->Init(range);
+  nsresult rv =
+    iter->Init(aRawRange.GetStartContainer(), aRawRange.StartOffset(),
+               aRawRange.GetEndContainer(), aRawRange.EndOffset());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   for (; !iter->IsDone(); iter->Next()) {
     nsINode* node = iter->GetCurrentNode();
     if (NS_WARN_IF(!node)) {
       break;
     }
@@ -1659,18 +1652,23 @@ ContentEventHandler::GetNodePositionHavi
   return NodePosition();
 }
 
 ContentEventHandler::FrameAndNodeOffset
 ContentEventHandler::GetFirstFrameInRangeForTextRect(const RawRange& aRawRange)
 {
   NodePosition nodePosition;
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
-  RefPtr<nsRange> range = aRawRange.CreateRange();
-  for (iter->Init(range); !iter->IsDone(); iter->Next()) {
+  nsresult rv =
+    iter->Init(aRawRange.GetStartContainer(), aRawRange.StartOffset(),
+               aRawRange.GetEndContainer(), aRawRange.EndOffset());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return FrameAndNodeOffset();
+  }
+  for (; !iter->IsDone(); iter->Next()) {
     nsINode* node = iter->GetCurrentNode();
     if (NS_WARN_IF(!node)) {
       break;
     }
 
     if (!node->IsContent()) {
       continue;
     }
@@ -1707,18 +1705,22 @@ ContentEventHandler::GetFirstFrameInRang
   return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
 }
 
 ContentEventHandler::FrameAndNodeOffset
 ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange)
 {
   NodePosition nodePosition;
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
-  RefPtr<nsRange> range = aRawRange.CreateRange();
-  iter->Init(range);
+  nsresult rv =
+    iter->Init(aRawRange.GetStartContainer(), aRawRange.StartOffset(),
+               aRawRange.GetEndContainer(), aRawRange.EndOffset());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return FrameAndNodeOffset();
+  }
 
   nsINode* endNode = aRawRange.GetEndContainer();
   uint32_t endOffset = aRawRange.EndOffset();
   // If the end point is start of a text node or specified by its parent and
   // index, the node shouldn't be included into the range.  For example,
   // with this case, |<p>abc[<br>]def</p>|, the range ends at 3rd children of
   // <p> (see the range creation rules, "2.4. Cases: <element/>]"). This causes
   // following frames:
@@ -2316,18 +2318,21 @@ ContentEventHandler::OnQueryTextRect(Wid
                                      true, &aEvent->mReply.mOffset,
                                      getter_AddRefs(lastTextContent));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = GenerateFlatTextContent(rawRange, aEvent->mReply.mString, lineBreakType);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // used to iterate over all contents and their frames
   nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
-  RefPtr<nsRange> range = rawRange.CreateRange();
-  iter->Init(range);
+  rv = iter->Init(rawRange.GetStartContainer(), rawRange.StartOffset(),
+                  rawRange.GetEndContainer(), rawRange.EndOffset());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_ERROR_FAILURE;
+  }
 
   // Get the first frame which causes some text after the offset.
   FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(rawRange);
 
   // If GetFirstFrameInRangeForTextRect() does not return valid frame, that
   // means that there are no visible frames having text or the offset reached
   // the end of contents.
   if (!firstFrame.IsValid()) {
@@ -2941,30 +2946,32 @@ ContentEventHandler::GetFlatTextLengthIn
 
     if (endPosition.OffsetIsValid()) {
       // Offset is within node's length; set end of range to that offset
       rv = prevRawRange.SetEnd(endPosition.mNode, endPosition.mOffset);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       iter = NS_NewPreContentIterator();
-      RefPtr<nsRange> prevRange = prevRawRange.CreateRange();
-      rv = iter->Init(prevRange);
+      rv =
+        iter->Init(prevRawRange.GetStartContainer(), prevRawRange.StartOffset(),
+                   prevRawRange.GetEndContainer(), prevRawRange.EndOffset());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else if (endPosition.mNode != aRootContent) {
       // Offset is past node's length; set end of range to end of node
       rv = prevRawRange.SetEndAfter(endPosition.mNode);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       iter = NS_NewPreContentIterator();
-      RefPtr<nsRange> prevRange = prevRawRange.CreateRange();
-      rv = iter->Init(prevRange);
+      rv =
+        iter->Init(prevRawRange.GetStartContainer(), prevRawRange.StartOffset(),
+                   prevRawRange.GetEndContainer(), prevRawRange.EndOffset());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else {
       // Offset is past the root node; set end of range to end of root node
       iter = NS_NewPreContentIterator();
       rv = iter->Init(aRootContent);
       if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -81,18 +81,16 @@ private:
     nsresult SetEnd(nsINode* aEndContainer, uint32_t aEndOffset);
     nsresult SetEndAfter(nsINode* aEndContainer);
     void SetStartAndEnd(const nsRange* aRange);
     nsresult SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset,
                             nsINode* aEndContainer, uint32_t aEndOffset);
 
     nsresult SelectNodeContents(nsINode* aNodeToSelectContents);
 
-    already_AddRefed<nsRange> CreateRange() const;
-
   private:
     bool IsValidOffset(nsINode* aContainer, uint32_t aOffset) const;
     nsINode* IsValidBoundary(nsINode* aNode) const;
     inline void AssertStartIsBeforeOrEqualToEnd();
 
     nsCOMPtr<nsINode> mRoot;
     nsCOMPtr<nsINode> mStartContainer;
     nsCOMPtr<nsINode> mEndContainer;
--- a/editor/txtsvc/nsFilteredContentIterator.cpp
+++ b/editor/txtsvc/nsFilteredContentIterator.cpp
@@ -1,29 +1,32 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 #include "mozilla/mozalloc.h"
+#include "mozilla/Move.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsFilteredContentIterator.h"
 #include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "nsIDOMNode.h"
 #include "nsINode.h"
 #include "nsISupportsBase.h"
 #include "nsISupportsUtils.h"
 #include "nsITextServicesFilter.h"
 #include "nsRange.h"
 
+using namespace mozilla;
+
 //------------------------------------------------------------
 nsFilteredContentIterator::nsFilteredContentIterator(nsITextServicesFilter* aFilter) :
   mFilter(aFilter),
   mDidSkip(false),
   mIsOutOfRange(false),
   mDirection(eDirNotSet)
 {
   mIterator = do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
@@ -73,27 +76,72 @@ nsFilteredContentIterator::Init(nsINode*
   NS_ENSURE_SUCCESS(rv, rv);
   return mIterator->Init(mRange);
 }
 
 //------------------------------------------------------------
 nsresult
 nsFilteredContentIterator::Init(nsIDOMRange* aRange)
 {
-  NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
-  NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
-  NS_ENSURE_ARG_POINTER(aRange);
-  mIsOutOfRange    = false;
-  mDirection       = eForward;
+  if (NS_WARN_IF(!aRange)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsRange* range = static_cast<nsRange*>(aRange);
+  if (NS_WARN_IF(!range->IsPositioned())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mRange = range->CloneRange();
+
+  return InitWithRange();
+}
+
+//------------------------------------------------------------
+nsresult
+nsFilteredContentIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
+                                nsINode* aEndContainer, uint32_t aEndOffset)
+{
+  RefPtr<nsRange> range;
+  nsresult rv = nsRange::CreateRange(aStartContainer, aStartOffset,
+                                     aEndContainer, aEndOffset,
+                                     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);
+
+  mRange = Move(range);
+
+  return InitWithRange();
+}
+
+nsresult
+nsFilteredContentIterator::InitWithRange()
+{
+  MOZ_ASSERT(mRange);
+  MOZ_ASSERT(mRange->IsPositioned());
+
+  if (NS_WARN_IF(!mPreIterator) || NS_WARN_IF(!mIterator)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mIsOutOfRange = false;
+  mDirection = eForward;
   mCurrentIterator = mPreIterator;
 
-  mRange = static_cast<nsRange*>(aRange)->CloneRange();
-
   nsresult rv = mPreIterator->Init(mRange);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   return mIterator->Init(mRange);
 }
 
 //------------------------------------------------------------
 nsresult
 nsFilteredContentIterator::SwitchDirections(bool aChangeToForward)
 {
   nsINode *node = mCurrentIterator->GetCurrentNode();
--- a/editor/txtsvc/nsFilteredContentIterator.h
+++ b/editor/txtsvc/nsFilteredContentIterator.h
@@ -27,16 +27,18 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsFilteredContentIterator)
 
   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 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;
 
@@ -44,16 +46,21 @@ public:
   bool DidSkip()      { return mDidSkip; }
   void         ClearDidSkip() {  mDidSkip = false; }
 
 protected:
   nsFilteredContentIterator() : mDidSkip(false), mIsOutOfRange(false) { }
 
   virtual ~nsFilteredContentIterator();
 
+  /**
+   * Callers must guarantee that mRange isn't nullptr and it's positioned.
+   */
+  nsresult InitWithRange();
+
   // enum to give us the direction
   typedef enum {eDirNotSet, eForward, eBackward} eDirectionType;
   nsresult AdvanceNode(nsIDOMNode* aNode, nsIDOMNode*& aNewNode, eDirectionType aDir);
   void CheckAdvNode(nsIDOMNode* aNode, bool& aDidSkip, eDirectionType aDir);
   nsresult SwitchDirections(bool aChangeToForward);
 
   nsCOMPtr<nsIContentIterator> mCurrentIterator;
   nsCOMPtr<nsIContentIterator> mIterator;
--- a/toolkit/components/find/nsFind.cpp
+++ b/toolkit/components/find/nsFind.cpp
@@ -96,16 +96,22 @@ public:
     NS_NOTREACHED("internal error");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
   virtual nsresult Init(nsIDOMRange* aRange) override
   {
     NS_NOTREACHED("internal error");
     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;
+  }
   // 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;