1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ContentIterator.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/RangeBoundary.h"
12 #include "mozilla/RangeUtils.h"
13 #include "mozilla/Result.h"
15 #include "nsContentUtils.h"
16 #include "nsElementTable.h"
17 #include "nsIContent.h"
24 #define NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(aResultType, aMethodName, ...) \
25 template aResultType ContentIteratorBase<RefPtr<nsINode>>::aMethodName( \
27 template aResultType ContentIteratorBase<nsINode*>::aMethodName(__VA_ARGS__)
29 static bool ComparePostMode(const RawRangeBoundary
& aStart
,
30 const RawRangeBoundary
& aEnd
, nsINode
& aNode
) {
31 nsINode
* parent
= aNode
.GetParentNode();
36 // aNode should always be content, as we have a parent, but let's just be
37 // extra careful and check.
39 NS_WARN_IF(!aNode
.IsContent()) ? nullptr : aNode
.AsContent();
41 // Post mode: start < node <= end.
42 RawRangeBoundary
afterNode(parent
, content
);
43 const auto isStartLessThanAfterNode
= [&]() {
44 const Maybe
<int32_t> startComparedToAfterNode
=
45 nsContentUtils::ComparePoints(aStart
, afterNode
);
46 return !NS_WARN_IF(!startComparedToAfterNode
) &&
47 (*startComparedToAfterNode
< 0);
50 const auto isAfterNodeLessOrEqualToEnd
= [&]() {
51 const Maybe
<int32_t> afterNodeComparedToEnd
=
52 nsContentUtils::ComparePoints(afterNode
, aEnd
);
53 return !NS_WARN_IF(!afterNodeComparedToEnd
) &&
54 (*afterNodeComparedToEnd
<= 0);
57 return isStartLessThanAfterNode() && isAfterNodeLessOrEqualToEnd();
60 static bool ComparePreMode(const RawRangeBoundary
& aStart
,
61 const RawRangeBoundary
& aEnd
, nsINode
& aNode
) {
62 nsINode
* parent
= aNode
.GetParentNode();
67 // Pre mode: start <= node < end.
68 RawRangeBoundary
beforeNode(parent
, aNode
.GetPreviousSibling());
70 const auto isStartLessOrEqualToBeforeNode
= [&]() {
71 const Maybe
<int32_t> startComparedToBeforeNode
=
72 nsContentUtils::ComparePoints(aStart
, beforeNode
);
73 return !NS_WARN_IF(!startComparedToBeforeNode
) &&
74 (*startComparedToBeforeNode
<= 0);
77 const auto isBeforeNodeLessThanEndNode
= [&]() {
78 const Maybe
<int32_t> beforeNodeComparedToEnd
=
79 nsContentUtils::ComparePoints(beforeNode
, aEnd
);
80 return !NS_WARN_IF(!beforeNodeComparedToEnd
) &&
81 (*beforeNodeComparedToEnd
< 0);
84 return isStartLessOrEqualToBeforeNode() && isBeforeNodeLessThanEndNode();
87 ///////////////////////////////////////////////////////////////////////////
88 // NodeIsInTraversalRange: returns true if content is visited during
89 // the traversal of the range in the specified mode.
91 static bool NodeIsInTraversalRange(nsINode
* aNode
, bool aIsPreMode
,
92 const RawRangeBoundary
& aStart
,
93 const RawRangeBoundary
& aEnd
) {
94 if (NS_WARN_IF(!aStart
.IsSet()) || NS_WARN_IF(!aEnd
.IsSet()) ||
99 // If a leaf node contains an end point of the traversal range, it is
100 // always in the traversal range.
101 if (aNode
== aStart
.Container() || aNode
== aEnd
.Container()) {
102 if (aNode
->IsCharacterData()) {
103 return true; // text node or something
105 if (!aNode
->HasChildren()) {
107 aNode
!= aStart
.Container() || aStart
.IsStartOfContainer(),
108 "aStart.Container() doesn't have children and not a data node, "
109 "aStart should be at the beginning of its container");
110 MOZ_ASSERT(aNode
!= aEnd
.Container() || aEnd
.IsStartOfContainer(),
111 "aEnd.Container() doesn't have children and not a data node, "
112 "aEnd should be at the beginning of its container");
118 return ComparePreMode(aStart
, aEnd
, *aNode
);
121 return ComparePostMode(aStart
, aEnd
, *aNode
);
124 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
125 PostContentIterator
& aField
, const char* aName
,
126 uint32_t aFlags
= 0) {
127 ImplCycleCollectionTraverse(
128 aCallback
, static_cast<SafeContentIteratorBase
&>(aField
), aName
, aFlags
);
131 void ImplCycleCollectionUnlink(PostContentIterator
& aField
) {
132 ImplCycleCollectionUnlink(static_cast<SafeContentIteratorBase
&>(aField
));
135 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
136 PreContentIterator
& aField
, const char* aName
,
137 uint32_t aFlags
= 0) {
138 ImplCycleCollectionTraverse(
139 aCallback
, static_cast<SafeContentIteratorBase
&>(aField
), aName
, aFlags
);
142 void ImplCycleCollectionUnlink(PreContentIterator
& aField
) {
143 ImplCycleCollectionUnlink(static_cast<SafeContentIteratorBase
&>(aField
));
146 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
147 ContentSubtreeIterator
& aField
,
148 const char* aName
, uint32_t aFlags
= 0) {
149 ImplCycleCollectionTraverse(aCallback
, aField
.mRange
, aName
, aFlags
);
150 ImplCycleCollectionTraverse(
151 aCallback
, static_cast<SafeContentIteratorBase
&>(aField
), aName
, aFlags
);
154 void ImplCycleCollectionUnlink(ContentSubtreeIterator
& aField
) {
155 ImplCycleCollectionUnlink(aField
.mRange
);
156 ImplCycleCollectionUnlink(static_cast<SafeContentIteratorBase
&>(aField
));
159 /******************************************************
160 * ContentIteratorBase
161 ******************************************************/
163 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(, ContentIteratorBase
, Order
);
165 template <typename NodeType
>
166 ContentIteratorBase
<NodeType
>::ContentIteratorBase(Order aOrder
)
169 template ContentIteratorBase
<RefPtr
<nsINode
>>::~ContentIteratorBase();
170 template ContentIteratorBase
<nsINode
*>::~ContentIteratorBase();
172 template <typename NodeType
>
173 ContentIteratorBase
<NodeType
>::~ContentIteratorBase() {
174 MOZ_DIAGNOSTIC_ASSERT_IF(mMutationGuard
.isSome(),
175 !mMutationGuard
->Mutated(0));
178 /******************************************************
180 ******************************************************/
182 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, Init
, nsINode
*);
184 template <typename NodeType
>
185 nsresult ContentIteratorBase
<NodeType
>::Init(nsINode
* aRoot
) {
186 if (NS_WARN_IF(!aRoot
)) {
187 return NS_ERROR_NULL_POINTER
;
190 if (mOrder
== Order::Pre
) {
192 mLast
= ContentIteratorBase::GetDeepLastChild(aRoot
);
193 NS_WARNING_ASSERTION(mLast
, "GetDeepLastChild returned null");
195 mFirst
= ContentIteratorBase::GetDeepFirstChild(aRoot
);
196 NS_WARNING_ASSERTION(mFirst
, "GetDeepFirstChild returned null");
200 mClosestCommonInclusiveAncestor
= aRoot
;
205 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, Init
, AbstractRange
*);
207 template <typename NodeType
>
208 nsresult ContentIteratorBase
<NodeType
>::Init(AbstractRange
* aRange
) {
209 if (NS_WARN_IF(!aRange
)) {
210 return NS_ERROR_INVALID_ARG
;
213 if (NS_WARN_IF(!aRange
->IsPositioned())) {
214 return NS_ERROR_INVALID_ARG
;
217 return InitInternal(aRange
->StartRef().AsRaw(), aRange
->EndRef().AsRaw());
220 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, Init
, nsINode
*, uint32_t,
223 template <typename NodeType
>
224 nsresult ContentIteratorBase
<NodeType
>::Init(nsINode
* aStartContainer
,
225 uint32_t aStartOffset
,
226 nsINode
* aEndContainer
,
227 uint32_t aEndOffset
) {
228 if (NS_WARN_IF(!RangeUtils::IsValidPoints(aStartContainer
, aStartOffset
,
229 aEndContainer
, aEndOffset
))) {
230 return NS_ERROR_INVALID_ARG
;
233 return InitInternal(RawRangeBoundary(aStartContainer
, aStartOffset
),
234 RawRangeBoundary(aEndContainer
, aEndOffset
));
237 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, Init
, const RawRangeBoundary
&,
238 const RawRangeBoundary
&);
240 template <typename NodeType
>
241 nsresult ContentIteratorBase
<NodeType
>::Init(const RawRangeBoundary
& aStart
,
242 const RawRangeBoundary
& aEnd
) {
243 if (NS_WARN_IF(!RangeUtils::IsValidPoints(aStart
, aEnd
))) {
244 return NS_ERROR_INVALID_ARG
;
247 return InitInternal(aStart
, aEnd
);
250 template <typename NodeType
>
251 class MOZ_STACK_CLASS ContentIteratorBase
<NodeType
>::Initializer final
{
253 Initializer(ContentIteratorBase
<NodeType
>& aIterator
,
254 const RawRangeBoundary
& aStart
, const RawRangeBoundary
& aEnd
)
255 : mIterator
{aIterator
},
258 mStartIsCharacterData
{mStart
.Container()->IsCharacterData()} {
259 MOZ_ASSERT(mStart
.IsSetAndValid());
260 MOZ_ASSERT(mEnd
.IsSetAndValid());
267 * @return may be nullptr.
269 nsINode
* DetermineFirstNode() const;
272 * @return may be nullptr.
274 [[nodiscard
]] Result
<nsINode
*, nsresult
> DetermineLastNode() const;
276 bool IsCollapsedNonCharacterRange() const;
277 bool IsSingleNodeCharacterRange() const;
279 ContentIteratorBase
& mIterator
;
280 const RawRangeBoundary
& mStart
;
281 const RawRangeBoundary
& mEnd
;
282 const bool mStartIsCharacterData
;
286 nsresult ContentIteratorBase
<RefPtr
<nsINode
>>::InitInternal(
287 const RawRangeBoundary
& aStart
, const RawRangeBoundary
& aEnd
) {
288 Initializer initializer
{*this, aStart
, aEnd
};
289 return initializer
.Run();
293 nsresult ContentIteratorBase
<nsINode
*>::InitInternal(
294 const RawRangeBoundary
& aStart
, const RawRangeBoundary
& aEnd
) {
295 Initializer initializer
{*this, aStart
, aEnd
};
296 nsresult rv
= initializer
.Run();
300 mMutationGuard
.emplace();
301 mAssertNoGC
.emplace();
305 template <typename NodeType
>
306 bool ContentIteratorBase
<NodeType
>::Initializer::IsCollapsedNonCharacterRange()
308 return !mStartIsCharacterData
&& mStart
== mEnd
;
311 template <typename NodeType
>
312 bool ContentIteratorBase
<NodeType
>::Initializer::IsSingleNodeCharacterRange()
314 return mStartIsCharacterData
&& mStart
.Container() == mEnd
.Container();
317 template <typename NodeType
>
318 nsresult ContentIteratorBase
<NodeType
>::Initializer::Run() {
319 // get common content parent
320 mIterator
.mClosestCommonInclusiveAncestor
=
321 nsContentUtils::GetClosestCommonInclusiveAncestor(mStart
.Container(),
323 if (NS_WARN_IF(!mIterator
.mClosestCommonInclusiveAncestor
)) {
324 return NS_ERROR_FAILURE
;
327 // Check to see if we have a collapsed range, if so, there is nothing to
330 // XXX: CharacterDataNodes (text nodes) are currently an exception, since
331 // we always want to be able to iterate text nodes at the end points
334 if (IsCollapsedNonCharacterRange()) {
335 mIterator
.SetEmpty();
339 if (IsSingleNodeCharacterRange()) {
340 mIterator
.mFirst
= mStart
.Container()->AsContent();
341 mIterator
.mLast
= mIterator
.mFirst
;
342 mIterator
.mCurNode
= mIterator
.mFirst
;
347 mIterator
.mFirst
= DetermineFirstNode();
349 if (Result
<nsINode
*, nsresult
> lastNode
= DetermineLastNode();
350 NS_WARN_IF(lastNode
.isErr())) {
351 return lastNode
.unwrapErr();
353 mIterator
.mLast
= lastNode
.unwrap();
356 // If either first or last is null, they both have to be null!
357 if (!mIterator
.mFirst
|| !mIterator
.mLast
) {
358 mIterator
.SetEmpty();
361 mIterator
.mCurNode
= mIterator
.mFirst
;
366 template <typename NodeType
>
367 nsINode
* ContentIteratorBase
<NodeType
>::Initializer::DetermineFirstNode()
369 nsIContent
* cChild
= nullptr;
371 // Try to get the child at our starting point. This might return null if
372 // mStart is immediately after the last node in mStart.Container().
373 if (!mStartIsCharacterData
) {
374 cChild
= mStart
.GetChildAtOffset();
378 // No children (possibly a <br> or text node), or index is after last child.
380 if (mIterator
.mOrder
== Order::Pre
) {
381 // XXX: In the future, if start offset is after the last
382 // character in the cdata node, should we set mFirst to
385 // Normally we would skip the start node because the start node is outside
386 // of the range in pre mode. However, if aStartOffset == 0, and the node
387 // is a non-container node (e.g. <br>), we don't skip the node in this
388 // case in order to address bug 1215798.
389 bool startIsContainer
= true;
390 if (mStart
.Container()->IsHTMLElement()) {
391 nsAtom
* name
= mStart
.Container()->NodeInfo()->NameAtom();
393 nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name
));
395 if (!mStartIsCharacterData
&&
396 (startIsContainer
|| !mStart
.IsStartOfContainer())) {
397 nsINode
* const result
=
398 ContentIteratorBase::GetNextSibling(mStart
.Container());
399 NS_WARNING_ASSERTION(result
, "GetNextSibling returned null");
401 // Does mFirst node really intersect the range? The range could be
402 // 'degenerate', i.e., not collapsed but still contain no content.
404 NS_WARN_IF(!NodeIsInTraversalRange(
405 result
, mIterator
.mOrder
== Order::Pre
, mStart
, mEnd
))) {
411 return mStart
.Container()->AsContent();
415 if (NS_WARN_IF(!mStart
.Container()->IsContent())) {
416 // What else can we do?
419 return mStart
.Container()->AsContent();
422 if (mIterator
.mOrder
== Order::Pre
) {
427 nsINode
* const result
= ContentIteratorBase::GetDeepFirstChild(cChild
);
428 NS_WARNING_ASSERTION(result
, "GetDeepFirstChild returned null");
430 // Does mFirst node really intersect the range? The range could be
431 // 'degenerate', i.e., not collapsed but still contain no content.
432 if (result
&& !NodeIsInTraversalRange(result
, mIterator
.mOrder
== Order::Pre
,
440 template <typename NodeType
>
441 Result
<nsINode
*, nsresult
>
442 ContentIteratorBase
<NodeType
>::Initializer::DetermineLastNode() const {
443 const bool endIsCharacterData
= mEnd
.Container()->IsCharacterData();
445 if (endIsCharacterData
|| !mEnd
.Container()->HasChildren() ||
446 mEnd
.IsStartOfContainer()) {
447 if (mIterator
.mOrder
== Order::Pre
) {
448 if (NS_WARN_IF(!mEnd
.Container()->IsContent())) {
449 // Not much else to do here...
453 // If the end node is a non-container element and the end offset is 0,
454 // the last element should be the previous node (i.e., shouldn't
455 // include the end node in the range).
456 bool endIsContainer
= true;
457 if (mEnd
.Container()->IsHTMLElement()) {
458 nsAtom
* name
= mEnd
.Container()->NodeInfo()->NameAtom();
460 nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name
));
462 if (!endIsCharacterData
&& !endIsContainer
&& mEnd
.IsStartOfContainer()) {
463 nsINode
* const result
= mIterator
.PrevNode(mEnd
.Container());
464 NS_WARNING_ASSERTION(result
, "PrevNode returned null");
465 if (result
&& result
!= mIterator
.mFirst
&&
466 NS_WARN_IF(!NodeIsInTraversalRange(
467 result
, mIterator
.mOrder
== Order::Pre
,
468 RawRangeBoundary(mIterator
.mFirst
, 0u), mEnd
))) {
475 return mEnd
.Container()->AsContent();
480 // XXX: In the future, if end offset is before the first character in the
481 // cdata node, should we set mLast to the prev sibling?
483 if (!endIsCharacterData
) {
484 nsINode
* const result
=
485 ContentIteratorBase::GetPrevSibling(mEnd
.Container());
486 NS_WARNING_ASSERTION(result
, "GetPrevSibling returned null");
488 if (!NodeIsInTraversalRange(result
, mIterator
.mOrder
== Order::Pre
,
494 return mEnd
.Container()->AsContent();
497 nsIContent
* cChild
= mEnd
.Ref();
499 if (NS_WARN_IF(!cChild
)) {
500 // No child at offset!
501 MOZ_ASSERT_UNREACHABLE("ContentIterator::ContentIterator");
502 return Err(NS_ERROR_FAILURE
);
505 if (mIterator
.mOrder
== Order::Pre
) {
506 nsINode
* const result
= ContentIteratorBase::GetDeepLastChild(cChild
);
507 NS_WARNING_ASSERTION(result
, "GetDeepLastChild returned null");
509 if (NS_WARN_IF(!NodeIsInTraversalRange(
510 result
, mIterator
.mOrder
== Order::Pre
, mStart
, mEnd
))) {
521 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, SetEmpty
);
523 template <typename NodeType
>
524 void ContentIteratorBase
<NodeType
>::SetEmpty() {
528 mClosestCommonInclusiveAncestor
= nullptr;
532 template <typename NodeType
>
533 nsINode
* ContentIteratorBase
<NodeType
>::GetDeepFirstChild(nsINode
* aRoot
) {
534 if (NS_WARN_IF(!aRoot
) || !aRoot
->HasChildren()) {
538 return ContentIteratorBase::GetDeepFirstChild(aRoot
->GetFirstChild());
542 template <typename NodeType
>
543 nsIContent
* ContentIteratorBase
<NodeType
>::GetDeepFirstChild(
545 if (NS_WARN_IF(!aRoot
)) {
549 nsIContent
* node
= aRoot
;
550 nsIContent
* child
= node
->GetFirstChild();
554 child
= node
->GetFirstChild();
561 template <typename NodeType
>
562 nsINode
* ContentIteratorBase
<NodeType
>::GetDeepLastChild(nsINode
* aRoot
) {
563 if (NS_WARN_IF(!aRoot
) || !aRoot
->HasChildren()) {
567 return ContentIteratorBase::GetDeepLastChild(aRoot
->GetLastChild());
571 template <typename NodeType
>
572 nsIContent
* ContentIteratorBase
<NodeType
>::GetDeepLastChild(nsIContent
* aRoot
) {
573 if (NS_WARN_IF(!aRoot
)) {
577 nsIContent
* node
= aRoot
;
578 while (node
->HasChildren()) {
579 nsIContent
* child
= node
->GetLastChild();
585 // Get the next sibling, or parent's next sibling, or grandpa's next sibling...
587 template <typename NodeType
>
588 nsIContent
* ContentIteratorBase
<NodeType
>::GetNextSibling(nsINode
* aNode
) {
589 if (NS_WARN_IF(!aNode
)) {
593 if (nsIContent
* next
= aNode
->GetNextSibling()) {
597 nsINode
* parent
= aNode
->GetParentNode();
598 if (NS_WARN_IF(!parent
)) {
602 return ContentIteratorBase::GetNextSibling(parent
);
605 // Get the prev sibling, or parent's prev sibling, or grandpa's prev sibling...
607 template <typename NodeType
>
608 nsIContent
* ContentIteratorBase
<NodeType
>::GetPrevSibling(nsINode
* aNode
) {
609 if (NS_WARN_IF(!aNode
)) {
613 if (nsIContent
* prev
= aNode
->GetPreviousSibling()) {
617 nsINode
* parent
= aNode
->GetParentNode();
618 if (NS_WARN_IF(!parent
)) {
622 return ContentIteratorBase::GetPrevSibling(parent
);
625 template <typename NodeType
>
626 nsINode
* ContentIteratorBase
<NodeType
>::NextNode(nsINode
* aNode
) {
627 nsINode
* node
= aNode
;
629 // if we are a Pre-order iterator, use pre-order
630 if (mOrder
== Order::Pre
) {
631 // if it has children then next node is first child
632 if (node
->HasChildren()) {
633 nsIContent
* firstChild
= node
->GetFirstChild();
634 MOZ_ASSERT(firstChild
);
639 // else next sibling is next
640 return ContentIteratorBase::GetNextSibling(node
);
644 nsINode
* parent
= node
->GetParentNode();
645 if (NS_WARN_IF(!parent
)) {
646 MOZ_ASSERT(parent
, "The node is the root node but not the last node");
651 if (nsIContent
* sibling
= node
->GetNextSibling()) {
652 // next node is sibling's "deep left" child
653 return ContentIteratorBase::GetDeepFirstChild(sibling
);
659 template <typename NodeType
>
660 nsINode
* ContentIteratorBase
<NodeType
>::PrevNode(nsINode
* aNode
) {
661 nsINode
* node
= aNode
;
663 // if we are a Pre-order iterator, use pre-order
664 if (mOrder
== Order::Pre
) {
665 nsINode
* parent
= node
->GetParentNode();
666 if (NS_WARN_IF(!parent
)) {
667 MOZ_ASSERT(parent
, "The node is the root node but not the first node");
672 nsIContent
* sibling
= node
->GetPreviousSibling();
674 return ContentIteratorBase::GetDeepLastChild(sibling
);
681 if (node
->HasChildren()) {
682 return node
->GetLastChild();
685 // else prev sibling is previous
686 return ContentIteratorBase::GetPrevSibling(node
);
689 /******************************************************
690 * ContentIteratorBase routines
691 ******************************************************/
693 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, First
);
695 template <typename NodeType
>
696 void ContentIteratorBase
<NodeType
>::First() {
698 MOZ_ASSERT(IsDone());
703 mozilla::DebugOnly
<nsresult
> rv
= PositionAt(mFirst
);
704 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to position iterator!");
707 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, Last
);
709 template <typename NodeType
>
710 void ContentIteratorBase
<NodeType
>::Last() {
711 // Note that mLast can be nullptr if SetEmpty() is called in Init()
712 // since at that time, Init() returns NS_OK.
714 MOZ_ASSERT(IsDone());
719 mozilla::DebugOnly
<nsresult
> rv
= PositionAt(mLast
);
720 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to position iterator!");
723 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, Next
);
725 template <typename NodeType
>
726 void ContentIteratorBase
<NodeType
>::Next() {
731 if (mCurNode
== mLast
) {
736 mCurNode
= NextNode(mCurNode
);
739 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, Prev
);
741 template <typename NodeType
>
742 void ContentIteratorBase
<NodeType
>::Prev() {
747 if (mCurNode
== mFirst
) {
752 mCurNode
= PrevNode(mCurNode
);
755 // Keeping arrays of indexes for the stack of nodes makes PositionAt
757 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, PositionAt
, nsINode
*);
759 template <typename NodeType
>
760 nsresult ContentIteratorBase
<NodeType
>::PositionAt(nsINode
* aCurNode
) {
761 if (NS_WARN_IF(!aCurNode
)) {
762 return NS_ERROR_NULL_POINTER
;
765 // take an early out if this doesn't actually change the position
766 if (mCurNode
== aCurNode
) {
771 // Check to see if the node falls within the traversal range.
773 RawRangeBoundary
first(mFirst
, 0u);
774 RawRangeBoundary
last(mLast
, 0u);
776 if (mFirst
&& mLast
) {
777 if (mOrder
== Order::Pre
) {
778 // In pre we want to record the point immediately before mFirst, which is
779 // the point immediately after mFirst's previous sibling.
780 first
= {mFirst
->GetParentNode(), mFirst
->GetPreviousSibling()};
782 // If mLast has no children, then we want to make sure to include it.
783 if (!mLast
->HasChildren()) {
784 last
= {mLast
->GetParentNode(), mLast
->AsContent()};
787 // If the first node has any children, we want to be immediately after the
788 // last. Otherwise we want to be immediately before mFirst.
789 if (mFirst
->HasChildren()) {
790 first
= {mFirst
, mFirst
->GetLastChild()};
792 first
= {mFirst
->GetParentNode(), mFirst
->GetPreviousSibling()};
795 // Set the last point immediately after the final node.
796 last
= {mLast
->GetParentNode(), mLast
->AsContent()};
800 NS_WARNING_ASSERTION(first
.IsSetAndValid(), "first is not valid");
801 NS_WARNING_ASSERTION(last
.IsSetAndValid(), "last is not valid");
803 // The end positions are always in the range even if it has no parent. We
804 // need to allow that or 'iter->Init(root)' would assert in Last() or First()
805 // for example, bug 327694.
806 if (mFirst
!= mCurNode
&& mLast
!= mCurNode
&&
807 (NS_WARN_IF(!first
.IsSet()) || NS_WARN_IF(!last
.IsSet()) ||
808 NS_WARN_IF(!NodeIsInTraversalRange(mCurNode
, mOrder
== Order::Pre
, first
,
811 return NS_ERROR_FAILURE
;
817 /******************************************************
818 * ContentSubtreeIterator init routines
819 ******************************************************/
821 nsresult
ContentSubtreeIterator::Init(nsINode
* aRoot
) {
822 return NS_ERROR_NOT_IMPLEMENTED
;
825 nsresult
ContentSubtreeIterator::Init(AbstractRange
* aRange
) {
828 if (NS_WARN_IF(!aRange
->IsPositioned())) {
829 return NS_ERROR_INVALID_ARG
;
834 return InitWithRange();
837 nsresult
ContentSubtreeIterator::Init(nsINode
* aStartContainer
,
838 uint32_t aStartOffset
,
839 nsINode
* aEndContainer
,
840 uint32_t aEndOffset
) {
841 return Init(RawRangeBoundary(aStartContainer
, aStartOffset
),
842 RawRangeBoundary(aEndContainer
, aEndOffset
));
845 nsresult
ContentSubtreeIterator::Init(const RawRangeBoundary
& aStartBoundary
,
846 const RawRangeBoundary
& aEndBoundary
) {
847 RefPtr
<nsRange
> range
=
848 nsRange::Create(aStartBoundary
, aEndBoundary
, IgnoreErrors());
849 if (NS_WARN_IF(!range
) || NS_WARN_IF(!range
->IsPositioned())) {
850 return NS_ERROR_INVALID_ARG
;
853 if (NS_WARN_IF(range
->StartRef() != aStartBoundary
) ||
854 NS_WARN_IF(range
->EndRef() != aEndBoundary
)) {
855 return NS_ERROR_UNEXPECTED
;
858 mRange
= std::move(range
);
860 return InitWithRange();
863 void ContentSubtreeIterator::CacheInclusiveAncestorsOfEndContainer() {
864 mInclusiveAncestorsOfEndContainer
.Clear();
865 nsINode
* const endContainer
= mRange
->GetEndContainer();
866 nsIContent
* endNode
=
867 endContainer
->IsContent() ? endContainer
->AsContent() : nullptr;
869 mInclusiveAncestorsOfEndContainer
.AppendElement(endNode
);
870 endNode
= endNode
->GetParent();
874 nsIContent
* ContentSubtreeIterator::DetermineCandidateForFirstContent() const {
875 nsINode
* startContainer
= mRange
->GetStartContainer();
876 nsIContent
* firstCandidate
= nullptr;
877 // find first node in range
878 nsINode
* node
= nullptr;
879 if (!startContainer
->GetChildCount()) {
880 // no children, start at the node itself
881 node
= startContainer
;
883 nsIContent
* child
= mRange
->GetChildAtStartOffset();
885 startContainer
->GetChildAt_Deprecated(mRange
->StartOffset()));
887 // offset after last child
888 node
= startContainer
;
890 firstCandidate
= child
;
894 if (!firstCandidate
) {
895 // then firstCandidate is next node after node
896 firstCandidate
= ContentIteratorBase::GetNextSibling(node
);
899 if (firstCandidate
) {
900 firstCandidate
= ContentIteratorBase::GetDeepFirstChild(firstCandidate
);
903 return firstCandidate
;
906 nsIContent
* ContentSubtreeIterator::DetermineFirstContent() const {
907 nsIContent
* firstCandidate
= DetermineCandidateForFirstContent();
908 if (!firstCandidate
) {
912 // confirm that this first possible contained node is indeed contained. Else
913 // we have a range that does not fully contain any node.
914 const Maybe
<bool> isNodeContainedInRange
=
915 RangeUtils::IsNodeContainedInRange(*firstCandidate
, mRange
);
916 MOZ_ALWAYS_TRUE(isNodeContainedInRange
);
917 if (!isNodeContainedInRange
.value()) {
921 // cool, we have the first node in the range. Now we walk up its ancestors
922 // to find the most senior that is still in the range. That's the real first
924 return GetTopAncestorInRange(firstCandidate
);
927 nsIContent
* ContentSubtreeIterator::DetermineCandidateForLastContent() const {
928 nsIContent
* lastCandidate
{nullptr};
929 nsINode
* endContainer
= mRange
->GetEndContainer();
930 // now to find the last node
931 int32_t offset
= mRange
->EndOffset();
932 int32_t numChildren
= endContainer
->GetChildCount();
934 nsINode
* node
= nullptr;
935 if (offset
> numChildren
) {
936 // Can happen for text nodes
937 offset
= numChildren
;
939 if (!offset
|| !numChildren
) {
942 lastCandidate
= mRange
->EndRef().Ref();
943 MOZ_ASSERT(lastCandidate
== endContainer
->GetChildAt_Deprecated(--offset
));
944 NS_ASSERTION(lastCandidate
,
945 "tree traversal trouble in ContentSubtreeIterator::Init");
948 if (!lastCandidate
) {
949 // then lastCandidate is prev node before node
950 lastCandidate
= ContentIteratorBase::GetPrevSibling(node
);
954 lastCandidate
= ContentIteratorBase::GetDeepLastChild(lastCandidate
);
957 return lastCandidate
;
960 nsresult
ContentSubtreeIterator::InitWithRange() {
962 MOZ_ASSERT(mRange
->IsPositioned());
964 // get the start node and offset, convert to nsINode
965 mClosestCommonInclusiveAncestor
= mRange
->GetClosestCommonInclusiveAncestor();
966 nsINode
* startContainer
= mRange
->GetStartContainer();
967 const int32_t startOffset
= mRange
->StartOffset();
968 nsINode
* endContainer
= mRange
->GetEndContainer();
969 const int32_t endOffset
= mRange
->EndOffset();
970 MOZ_ASSERT(mClosestCommonInclusiveAncestor
&& startContainer
&& endContainer
);
972 MOZ_ASSERT(uint32_t(startOffset
) <= startContainer
->Length() &&
973 uint32_t(endOffset
) <= endContainer
->Length());
975 // short circuit when start node == end node
976 if (startContainer
== endContainer
) {
977 nsINode
* child
= startContainer
->GetFirstChild();
979 if (!child
|| startOffset
== endOffset
) {
980 // Text node, empty container, or collapsed
986 CacheInclusiveAncestorsOfEndContainer();
988 mFirst
= DetermineFirstContent();
994 mLast
= DetermineLastContent();
1005 nsIContent
* ContentSubtreeIterator::DetermineLastContent() const {
1006 nsIContent
* lastCandidate
= DetermineCandidateForLastContent();
1007 if (!lastCandidate
) {
1011 // confirm that this last possible contained node is indeed contained. Else
1012 // we have a range that does not fully contain any node.
1014 const Maybe
<bool> isNodeContainedInRange
=
1015 RangeUtils::IsNodeContainedInRange(*lastCandidate
, mRange
);
1016 MOZ_ALWAYS_TRUE(isNodeContainedInRange
);
1017 if (!isNodeContainedInRange
.value()) {
1021 // cool, we have the last node in the range. Now we walk up its ancestors to
1022 // find the most senior that is still in the range. That's the real first
1024 return GetTopAncestorInRange(lastCandidate
);
1027 /****************************************************************
1028 * ContentSubtreeIterator overrides of ContentIterator routines
1029 ****************************************************************/
1031 // we can't call PositionAt in a subtree iterator...
1032 void ContentSubtreeIterator::First() { mCurNode
= mFirst
; }
1034 // we can't call PositionAt in a subtree iterator...
1035 void ContentSubtreeIterator::Last() { mCurNode
= mLast
; }
1037 void ContentSubtreeIterator::Next() {
1042 if (mCurNode
== mLast
) {
1047 nsINode
* nextNode
= ContentIteratorBase::GetNextSibling(mCurNode
);
1048 NS_ASSERTION(nextNode
, "No next sibling!?! This could mean deadlock!");
1050 int32_t i
= mInclusiveAncestorsOfEndContainer
.IndexOf(nextNode
);
1052 // as long as we are finding ancestors of the endpoint of the range,
1053 // dive down into their children
1054 nextNode
= nextNode
->GetFirstChild();
1055 NS_ASSERTION(nextNode
, "Iterator error, expected a child node!");
1057 // should be impossible to get a null pointer. If we went all the way
1058 // down the child chain to the bottom without finding an interior node,
1059 // then the previous node should have been the last, which was
1060 // was tested at top of routine.
1061 i
= mInclusiveAncestorsOfEndContainer
.IndexOf(nextNode
);
1064 mCurNode
= nextNode
;
1067 void ContentSubtreeIterator::Prev() {
1068 // Prev should be optimized to use the mStartNodes, just as Next
1069 // uses mInclusiveAncestorsOfEndContainer.
1074 if (mCurNode
== mFirst
) {
1079 // If any of these function calls return null, so will all succeeding ones,
1080 // so mCurNode will wind up set to null.
1081 nsINode
* prevNode
= ContentIteratorBase::GetDeepFirstChild(mCurNode
);
1083 prevNode
= PrevNode(prevNode
);
1085 prevNode
= ContentIteratorBase::GetDeepLastChild(prevNode
);
1087 mCurNode
= GetTopAncestorInRange(prevNode
);
1090 nsresult
ContentSubtreeIterator::PositionAt(nsINode
* aCurNode
) {
1091 NS_ERROR("Not implemented!");
1092 return NS_ERROR_NOT_IMPLEMENTED
;
1095 /****************************************************************
1096 * ContentSubtreeIterator helper routines
1097 ****************************************************************/
1099 nsIContent
* ContentSubtreeIterator::GetTopAncestorInRange(
1100 nsINode
* aNode
) const {
1101 if (!aNode
|| !aNode
->GetParentNode()) {
1105 // aNode has a parent, so it must be content.
1106 nsIContent
* content
= aNode
->AsContent();
1108 // sanity check: aNode is itself in the range
1109 Maybe
<bool> isNodeContainedInRange
=
1110 RangeUtils::IsNodeContainedInRange(*aNode
, mRange
);
1111 NS_ASSERTION(isNodeContainedInRange
&& isNodeContainedInRange
.value(),
1112 "aNode isn't in mRange, or something else weird happened");
1113 if (!isNodeContainedInRange
|| !isNodeContainedInRange
.value()) {
1118 nsIContent
* parent
= content
->GetParent();
1119 // content always has a parent. If its parent is the root, however --
1120 // i.e., either it's not content, or it is content but its own parent is
1121 // null -- then we're finished, since we don't go up to the root.
1123 // We have to special-case this because CompareNodeToRange treats the root
1124 // node differently -- see bug 765205.
1125 if (!parent
|| !parent
->GetParentNode()) {
1129 isNodeContainedInRange
=
1130 RangeUtils::IsNodeContainedInRange(*parent
, mRange
);
1131 MOZ_ALWAYS_TRUE(isNodeContainedInRange
);
1132 if (!isNodeContainedInRange
.value()) {
1139 MOZ_CRASH("This should only be possible if aNode was null");
1142 #undef NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD
1144 } // namespace mozilla