diff --git a/dom/base/nsContentIterator.cpp b/dom/base/nsContentIterator.cpp index 82962efbeb0b..38b747bcec40 100644 --- a/dom/base/nsContentIterator.cpp +++ b/dom/base/nsContentIterator.cpp @@ -113,6 +113,9 @@ public: 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; @@ -130,6 +133,15 @@ public: 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, @@ -300,35 +312,48 @@ nsContentIterator::Init(nsINode* aRoot) nsresult nsContentIterator::Init(nsIDOMRange* aDOMRange) { + mIsDone = false; + if (NS_WARN_IF(!aDOMRange)) { return NS_ERROR_INVALID_ARG; } - nsRange* range = static_cast(aDOMRange); + nsRange* range = static_cast(aDOMRange); + if (NS_WARN_IF(!range->IsPositioned())) { + return NS_ERROR_INVALID_ARG; + } + + 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; + } + + 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 = range->GetCommonAncestor(); + mCommonParent = + nsContentUtils::GetCommonAncestor(startNode, endNode); if (NS_WARN_IF(!mCommonParent)) { return NS_ERROR_FAILURE; } - // 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; - } - - // 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 NS_ERROR_FAILURE; - } - bool startIsData = startNode->IsNodeOfType(nsINode::eDATA_NODE); // short circuit when start node == end node @@ -1218,6 +1243,9 @@ public: 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; @@ -1233,6 +1261,11 @@ public: 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 @@ -1301,7 +1334,48 @@ nsContentSubtreeIterator::Init(nsIDOMRange* aRange) mIsDone = false; - mRange = static_cast(aRange); + nsRange* range = static_cast(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 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(); diff --git a/dom/base/nsIContentIterator.h b/dom/base/nsIContentIterator.h index 0c41ad4854d0..c9ebd4885944 100644 --- a/dom/base/nsIContentIterator.h +++ b/dom/base/nsIContentIterator.h @@ -31,6 +31,14 @@ public: */ 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; diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index 4be7e623aa0e..33657f396eb0 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -1263,6 +1263,41 @@ nsRange::ComputeRootNode(nsINode* aNode, bool aMaySpanAnonymousSubtrees) 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(aStartOffset), + aEndContainer, + static_cast(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) { diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index 51757bbffc67..024c3cf71755 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -328,6 +328,13 @@ public: 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. diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index 95a4386fec0a..86448f18b4ac 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -200,15 +200,6 @@ ContentEventHandler::RawRange::SelectNodeContents( return NS_OK; } -already_AddRefed -ContentEventHandler::RawRange::CreateRange() const -{ - RefPtr range = new nsRange(mRoot); - range->SetStartAndEnd(mStartContainer, mStartOffset, - mEndContainer, mEndOffset); - return range.forget(); -} - /******************************************************************/ /* ContentEventHandler */ /******************************************************************/ @@ -868,8 +859,9 @@ ContentEventHandler::GenerateFlatTextContent(const RawRange& aRawRange, } nsCOMPtr iter = NS_NewPreContentIterator(); - RefPtr 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; } @@ -1040,8 +1032,9 @@ ContentEventHandler::GenerateFlatFontRanges(const RawRange& aRawRange, // baseOffset is the flattened offset of each content node. int32_t baseOffset = 0; nsCOMPtr iter = NS_NewPreContentIterator(); - RefPtr 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; } @@ -1664,8 +1657,13 @@ ContentEventHandler::GetFirstFrameInRangeForTextRect(const RawRange& aRawRange) { NodePosition nodePosition; nsCOMPtr iter = NS_NewPreContentIterator(); - RefPtr 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; @@ -1712,8 +1710,12 @@ ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange) { NodePosition nodePosition; nsCOMPtr iter = NS_NewPreContentIterator(); - RefPtr 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(); @@ -2321,8 +2323,11 @@ ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent) // used to iterate over all contents and their frames nsCOMPtr iter = NS_NewContentIterator(); - RefPtr 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); @@ -2946,8 +2951,9 @@ ContentEventHandler::GetFlatTextLengthInRange( return rv; } iter = NS_NewPreContentIterator(); - RefPtr 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; } @@ -2958,8 +2964,9 @@ ContentEventHandler::GetFlatTextLengthInRange( return rv; } iter = NS_NewPreContentIterator(); - RefPtr 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; } diff --git a/dom/events/ContentEventHandler.h b/dom/events/ContentEventHandler.h index eca0fca42006..5ad54439c4ac 100644 --- a/dom/events/ContentEventHandler.h +++ b/dom/events/ContentEventHandler.h @@ -86,8 +86,6 @@ private: nsresult SelectNodeContents(nsINode* aNodeToSelectContents); - already_AddRefed CreateRange() const; - private: bool IsValidOffset(nsINode* aContainer, uint32_t aOffset) const; nsINode* IsValidBoundary(nsINode* aNode) const; diff --git a/editor/txtsvc/nsFilteredContentIterator.cpp b/editor/txtsvc/nsFilteredContentIterator.cpp index 7ee567f1d953..c77171da3af5 100644 --- a/editor/txtsvc/nsFilteredContentIterator.cpp +++ b/editor/txtsvc/nsFilteredContentIterator.cpp @@ -4,6 +4,7 @@ * 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" @@ -19,6 +20,8 @@ #include "nsITextServicesFilter.h" #include "nsRange.h" +using namespace mozilla; + //------------------------------------------------------------ nsFilteredContentIterator::nsFilteredContentIterator(nsITextServicesFilter* aFilter) : mFilter(aFilter), @@ -78,17 +81,62 @@ nsFilteredContentIterator::Init(nsINode* aRoot) 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(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 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(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); } diff --git a/editor/txtsvc/nsFilteredContentIterator.h b/editor/txtsvc/nsFilteredContentIterator.h index 9dde8132fb2e..574b0da19700 100644 --- a/editor/txtsvc/nsFilteredContentIterator.h +++ b/editor/txtsvc/nsFilteredContentIterator.h @@ -32,6 +32,8 @@ public: /* 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; @@ -49,6 +51,11 @@ protected: 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); diff --git a/toolkit/components/find/nsFind.cpp b/toolkit/components/find/nsFind.cpp index 8f7ad9a8c8e1..e01452217fb7 100644 --- a/toolkit/components/find/nsFind.cpp +++ b/toolkit/components/find/nsFind.cpp @@ -101,6 +101,12 @@ public: 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);