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/DebugOnly.h"
10 #include "mozilla/RangeBoundary.h"
11 #include "mozilla/RangeUtils.h"
12 #include "mozilla/Result.h"
14 #include "nsContentUtils.h"
15 #include "nsElementTable.h"
16 #include "nsIContent.h"
21 static bool ComparePostMode(const RawRangeBoundary
& aStart
,
22 const RawRangeBoundary
& aEnd
, nsINode
& aNode
) {
23 nsINode
* parent
= aNode
.GetParentNode();
28 // aNode should always be content, as we have a parent, but let's just be
29 // extra careful and check.
31 NS_WARN_IF(!aNode
.IsContent()) ? nullptr : aNode
.AsContent();
33 // Post mode: start < node <= end.
34 RawRangeBoundary
afterNode(parent
, content
);
35 const auto isStartLessThanAfterNode
= [&]() {
36 const Maybe
<int32_t> startComparedToAfterNode
=
37 nsContentUtils::ComparePoints(aStart
, afterNode
);
38 return !NS_WARN_IF(!startComparedToAfterNode
) &&
39 (*startComparedToAfterNode
< 0);
42 const auto isAfterNodeLessOrEqualToEnd
= [&]() {
43 const Maybe
<int32_t> afterNodeComparedToEnd
=
44 nsContentUtils::ComparePoints(afterNode
, aEnd
);
45 return !NS_WARN_IF(!afterNodeComparedToEnd
) &&
46 (*afterNodeComparedToEnd
<= 0);
49 return isStartLessThanAfterNode() && isAfterNodeLessOrEqualToEnd();
52 static bool ComparePreMode(const RawRangeBoundary
& aStart
,
53 const RawRangeBoundary
& aEnd
, nsINode
& aNode
) {
54 nsINode
* parent
= aNode
.GetParentNode();
59 // Pre mode: start <= node < end.
60 RawRangeBoundary
beforeNode(parent
, aNode
.GetPreviousSibling());
62 const auto isStartLessOrEqualToBeforeNode
= [&]() {
63 const Maybe
<int32_t> startComparedToBeforeNode
=
64 nsContentUtils::ComparePoints(aStart
, beforeNode
);
65 return !NS_WARN_IF(!startComparedToBeforeNode
) &&
66 (*startComparedToBeforeNode
<= 0);
69 const auto isBeforeNodeLessThanEndNode
= [&]() {
70 const Maybe
<int32_t> beforeNodeComparedToEnd
=
71 nsContentUtils::ComparePoints(beforeNode
, aEnd
);
72 return !NS_WARN_IF(!beforeNodeComparedToEnd
) &&
73 (*beforeNodeComparedToEnd
< 0);
76 return isStartLessOrEqualToBeforeNode() && isBeforeNodeLessThanEndNode();
79 ///////////////////////////////////////////////////////////////////////////
80 // NodeIsInTraversalRange: returns true if content is visited during
81 // the traversal of the range in the specified mode.
83 static bool NodeIsInTraversalRange(nsINode
* aNode
, bool aIsPreMode
,
84 const RawRangeBoundary
& aStart
,
85 const RawRangeBoundary
& aEnd
) {
86 if (NS_WARN_IF(!aStart
.IsSet()) || NS_WARN_IF(!aEnd
.IsSet()) ||
91 // If a leaf node contains an end point of the traversal range, it is
92 // always in the traversal range.
93 if (aNode
== aStart
.Container() || aNode
== aEnd
.Container()) {
94 if (aNode
->IsCharacterData()) {
95 return true; // text node or something
97 if (!aNode
->HasChildren()) {
99 aNode
!= aStart
.Container() || aStart
.IsStartOfContainer(),
100 "aStart.Container() doesn't have children and not a data node, "
101 "aStart should be at the beginning of its container");
102 MOZ_ASSERT(aNode
!= aEnd
.Container() || aEnd
.IsStartOfContainer(),
103 "aEnd.Container() doesn't have children and not a data node, "
104 "aEnd should be at the beginning of its container");
110 return ComparePreMode(aStart
, aEnd
, *aNode
);
113 return ComparePostMode(aStart
, aEnd
, *aNode
);
116 // Each concreate class of ContentIteratorBase may be owned by another class
117 // which may be owned by JS. Therefore, all of them should be in the cycle
118 // collection. However, we cannot make non-refcountable classes only with the
119 // macros. So, we need to make them cycle collectable without the macros.
120 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
121 ContentIteratorBase
& aField
, const char* aName
,
122 uint32_t aFlags
= 0) {
123 ImplCycleCollectionTraverse(aCallback
, aField
.mCurNode
, aName
, aFlags
);
124 ImplCycleCollectionTraverse(aCallback
, aField
.mFirst
, aName
, aFlags
);
125 ImplCycleCollectionTraverse(aCallback
, aField
.mLast
, aName
, aFlags
);
126 ImplCycleCollectionTraverse(aCallback
, aField
.mClosestCommonInclusiveAncestor
,
130 void ImplCycleCollectionUnlink(ContentIteratorBase
& aField
) {
131 ImplCycleCollectionUnlink(aField
.mCurNode
);
132 ImplCycleCollectionUnlink(aField
.mFirst
);
133 ImplCycleCollectionUnlink(aField
.mLast
);
134 ImplCycleCollectionUnlink(aField
.mClosestCommonInclusiveAncestor
);
137 ContentIteratorBase::ContentIteratorBase(Order aOrder
) : mOrder(aOrder
) {}
139 ContentIteratorBase::~ContentIteratorBase() = default;
141 /******************************************************
143 ******************************************************/
145 nsresult
ContentIteratorBase::Init(nsINode
* aRoot
) {
146 if (NS_WARN_IF(!aRoot
)) {
147 return NS_ERROR_NULL_POINTER
;
150 if (mOrder
== Order::Pre
) {
152 mLast
= ContentIteratorBase::GetDeepLastChild(aRoot
);
153 NS_WARNING_ASSERTION(mLast
, "GetDeepLastChild returned null");
155 mFirst
= ContentIteratorBase::GetDeepFirstChild(aRoot
);
156 NS_WARNING_ASSERTION(mFirst
, "GetDeepFirstChild returned null");
160 mClosestCommonInclusiveAncestor
= aRoot
;
165 nsresult
ContentIteratorBase::Init(nsRange
* aRange
) {
166 if (NS_WARN_IF(!aRange
)) {
167 return NS_ERROR_INVALID_ARG
;
170 if (NS_WARN_IF(!aRange
->IsPositioned())) {
171 return NS_ERROR_INVALID_ARG
;
174 return InitInternal(aRange
->StartRef().AsRaw(), aRange
->EndRef().AsRaw());
177 nsresult
ContentIteratorBase::Init(nsINode
* aStartContainer
,
178 uint32_t aStartOffset
,
179 nsINode
* aEndContainer
,
180 uint32_t aEndOffset
) {
181 if (NS_WARN_IF(!RangeUtils::IsValidPoints(aStartContainer
, aStartOffset
,
182 aEndContainer
, aEndOffset
))) {
183 return NS_ERROR_INVALID_ARG
;
186 return InitInternal(RawRangeBoundary(aStartContainer
, aStartOffset
),
187 RawRangeBoundary(aEndContainer
, aEndOffset
));
190 nsresult
ContentIteratorBase::Init(const RawRangeBoundary
& aStart
,
191 const RawRangeBoundary
& aEnd
) {
192 if (NS_WARN_IF(!RangeUtils::IsValidPoints(aStart
, aEnd
))) {
193 return NS_ERROR_INVALID_ARG
;
196 return InitInternal(aStart
, aEnd
);
199 class MOZ_STACK_CLASS
ContentIteratorBase::Initializer final
{
201 Initializer(ContentIteratorBase
& aIterator
, const RawRangeBoundary
& aStart
,
202 const RawRangeBoundary
& aEnd
)
203 : mIterator
{aIterator
},
206 mStartIsCharacterData
{mStart
.Container()->IsCharacterData()} {
207 MOZ_ASSERT(mStart
.IsSetAndValid());
208 MOZ_ASSERT(mEnd
.IsSetAndValid());
215 * @return may be nullptr.
217 nsINode
* DetermineFirstNode() const;
220 * @return may be nullptr.
222 [[nodiscard
]] Result
<nsINode
*, nsresult
> DetermineLastNode() const;
224 bool IsCollapsedNonCharacterRange() const;
225 bool IsSingleNodeCharacterRange() const;
227 ContentIteratorBase
& mIterator
;
228 const RawRangeBoundary
& mStart
;
229 const RawRangeBoundary
& mEnd
;
230 const bool mStartIsCharacterData
;
233 nsresult
ContentIteratorBase::InitInternal(const RawRangeBoundary
& aStart
,
234 const RawRangeBoundary
& aEnd
) {
235 Initializer initializer
{*this, aStart
, aEnd
};
236 return initializer
.Run();
239 bool ContentIteratorBase::Initializer::IsCollapsedNonCharacterRange() const {
240 return !mStartIsCharacterData
&& mStart
== mEnd
;
243 bool ContentIteratorBase::Initializer::IsSingleNodeCharacterRange() const {
244 return mStartIsCharacterData
&& mStart
.Container() == mEnd
.Container();
247 nsresult
ContentIteratorBase::Initializer::Run() {
248 // get common content parent
249 mIterator
.mClosestCommonInclusiveAncestor
=
250 nsContentUtils::GetClosestCommonInclusiveAncestor(mStart
.Container(),
252 if (NS_WARN_IF(!mIterator
.mClosestCommonInclusiveAncestor
)) {
253 return NS_ERROR_FAILURE
;
256 // Check to see if we have a collapsed range, if so, there is nothing to
259 // XXX: CharacterDataNodes (text nodes) are currently an exception, since
260 // we always want to be able to iterate text nodes at the end points
263 if (IsCollapsedNonCharacterRange()) {
264 mIterator
.SetEmpty();
268 if (IsSingleNodeCharacterRange()) {
269 mIterator
.mFirst
= mStart
.Container()->AsContent();
270 mIterator
.mLast
= mIterator
.mFirst
;
271 mIterator
.mCurNode
= mIterator
.mFirst
;
276 mIterator
.mFirst
= DetermineFirstNode();
278 if (Result
<nsINode
*, nsresult
> lastNode
= DetermineLastNode();
279 NS_WARN_IF(lastNode
.isErr())) {
280 return lastNode
.unwrapErr();
282 mIterator
.mLast
= lastNode
.unwrap();
285 // If either first or last is null, they both have to be null!
286 if (!mIterator
.mFirst
|| !mIterator
.mLast
) {
287 mIterator
.SetEmpty();
290 mIterator
.mCurNode
= mIterator
.mFirst
;
295 nsINode
* ContentIteratorBase::Initializer::DetermineFirstNode() const {
296 nsIContent
* cChild
= nullptr;
298 // Try to get the child at our starting point. This might return null if
299 // mStart is immediately after the last node in mStart.Container().
300 if (!mStartIsCharacterData
) {
301 cChild
= mStart
.GetChildAtOffset();
305 // No children (possibly a <br> or text node), or index is after last child.
307 if (mIterator
.mOrder
== Order::Pre
) {
308 // XXX: In the future, if start offset is after the last
309 // character in the cdata node, should we set mFirst to
312 // Normally we would skip the start node because the start node is outside
313 // of the range in pre mode. However, if aStartOffset == 0, and the node
314 // is a non-container node (e.g. <br>), we don't skip the node in this
315 // case in order to address bug 1215798.
316 bool startIsContainer
= true;
317 if (mStart
.Container()->IsHTMLElement()) {
318 nsAtom
* name
= mStart
.Container()->NodeInfo()->NameAtom();
320 nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name
));
322 if (!mStartIsCharacterData
&&
323 (startIsContainer
|| !mStart
.IsStartOfContainer())) {
324 nsINode
* const result
=
325 ContentIteratorBase::GetNextSibling(mStart
.Container());
326 NS_WARNING_ASSERTION(result
, "GetNextSibling returned null");
328 // Does mFirst node really intersect the range? The range could be
329 // 'degenerate', i.e., not collapsed but still contain no content.
331 NS_WARN_IF(!NodeIsInTraversalRange(
332 result
, mIterator
.mOrder
== Order::Pre
, mStart
, mEnd
))) {
338 return mStart
.Container()->AsContent();
342 if (NS_WARN_IF(!mStart
.Container()->IsContent())) {
343 // What else can we do?
346 return mStart
.Container()->AsContent();
349 if (mIterator
.mOrder
== Order::Pre
) {
354 nsINode
* const result
= ContentIteratorBase::GetDeepFirstChild(cChild
);
355 NS_WARNING_ASSERTION(result
, "GetDeepFirstChild returned null");
357 // Does mFirst node really intersect the range? The range could be
358 // 'degenerate', i.e., not collapsed but still contain no content.
359 if (result
&& !NodeIsInTraversalRange(result
, mIterator
.mOrder
== Order::Pre
,
367 Result
<nsINode
*, nsresult
> ContentIteratorBase::Initializer::DetermineLastNode()
369 const bool endIsCharacterData
= mEnd
.Container()->IsCharacterData();
371 if (endIsCharacterData
|| !mEnd
.Container()->HasChildren() ||
372 mEnd
.IsStartOfContainer()) {
373 if (mIterator
.mOrder
== Order::Pre
) {
374 if (NS_WARN_IF(!mEnd
.Container()->IsContent())) {
375 // Not much else to do here...
379 // If the end node is a non-container element and the end offset is 0,
380 // the last element should be the previous node (i.e., shouldn't
381 // include the end node in the range).
382 bool endIsContainer
= true;
383 if (mEnd
.Container()->IsHTMLElement()) {
384 nsAtom
* name
= mEnd
.Container()->NodeInfo()->NameAtom();
386 nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name
));
388 if (!endIsCharacterData
&& !endIsContainer
&& mEnd
.IsStartOfContainer()) {
389 nsINode
* const result
= mIterator
.PrevNode(mEnd
.Container());
390 NS_WARNING_ASSERTION(result
, "PrevNode returned null");
391 if (result
&& result
!= mIterator
.mFirst
&&
392 NS_WARN_IF(!NodeIsInTraversalRange(
393 result
, mIterator
.mOrder
== Order::Pre
,
394 RawRangeBoundary(mIterator
.mFirst
, 0u), mEnd
))) {
401 return mEnd
.Container()->AsContent();
406 // XXX: In the future, if end offset is before the first character in the
407 // cdata node, should we set mLast to the prev sibling?
409 if (!endIsCharacterData
) {
410 nsINode
* const result
=
411 ContentIteratorBase::GetPrevSibling(mEnd
.Container());
412 NS_WARNING_ASSERTION(result
, "GetPrevSibling returned null");
414 if (!NodeIsInTraversalRange(result
, mIterator
.mOrder
== Order::Pre
,
420 return mEnd
.Container()->AsContent();
423 nsIContent
* cChild
= mEnd
.Ref();
425 if (NS_WARN_IF(!cChild
)) {
426 // No child at offset!
427 MOZ_ASSERT_UNREACHABLE("ContentIterator::ContentIterator");
428 return Err(NS_ERROR_FAILURE
);
431 if (mIterator
.mOrder
== Order::Pre
) {
432 nsINode
* const result
= ContentIteratorBase::GetDeepLastChild(cChild
);
433 NS_WARNING_ASSERTION(result
, "GetDeepLastChild returned null");
435 if (NS_WARN_IF(!NodeIsInTraversalRange(
436 result
, mIterator
.mOrder
== Order::Pre
, mStart
, mEnd
))) {
447 void ContentIteratorBase::SetEmpty() {
451 mClosestCommonInclusiveAncestor
= nullptr;
455 nsINode
* ContentIteratorBase::GetDeepFirstChild(nsINode
* aRoot
) {
456 if (NS_WARN_IF(!aRoot
) || !aRoot
->HasChildren()) {
460 return ContentIteratorBase::GetDeepFirstChild(aRoot
->GetFirstChild());
464 nsIContent
* ContentIteratorBase::GetDeepFirstChild(nsIContent
* aRoot
) {
465 if (NS_WARN_IF(!aRoot
)) {
469 nsIContent
* node
= aRoot
;
470 nsIContent
* child
= node
->GetFirstChild();
474 child
= node
->GetFirstChild();
481 nsINode
* ContentIteratorBase::GetDeepLastChild(nsINode
* aRoot
) {
482 if (NS_WARN_IF(!aRoot
) || !aRoot
->HasChildren()) {
486 return ContentIteratorBase::GetDeepLastChild(aRoot
->GetLastChild());
490 nsIContent
* ContentIteratorBase::GetDeepLastChild(nsIContent
* aRoot
) {
491 if (NS_WARN_IF(!aRoot
)) {
495 nsIContent
* node
= aRoot
;
496 while (node
->HasChildren()) {
497 nsIContent
* child
= node
->GetLastChild();
503 // Get the next sibling, or parent's next sibling, or grandpa's next sibling...
505 nsIContent
* ContentIteratorBase::GetNextSibling(nsINode
* aNode
) {
506 if (NS_WARN_IF(!aNode
)) {
510 if (aNode
->GetNextSibling()) {
511 return aNode
->GetNextSibling();
514 nsINode
* parent
= aNode
->GetParentNode();
515 if (NS_WARN_IF(!parent
)) {
519 // XXX This is a hack to preserve previous behaviour: This should be fixed
520 // in bug 1404916. If we were positioned on anonymous content, move to
521 // the first child of our parent.
522 if (parent
->GetLastChild() && parent
->GetLastChild() != aNode
) {
523 return parent
->GetFirstChild();
526 return ContentIteratorBase::GetNextSibling(parent
);
529 // Get the prev sibling, or parent's prev sibling, or grandpa's prev sibling...
531 nsIContent
* ContentIteratorBase::GetPrevSibling(nsINode
* aNode
) {
532 if (NS_WARN_IF(!aNode
)) {
536 if (aNode
->GetPreviousSibling()) {
537 return aNode
->GetPreviousSibling();
540 nsINode
* parent
= aNode
->GetParentNode();
541 if (NS_WARN_IF(!parent
)) {
545 // XXX This is a hack to preserve previous behaviour: This should be fixed
546 // in bug 1404916. If we were positioned on anonymous content, move to
547 // the last child of our parent.
548 if (parent
->GetFirstChild() && parent
->GetFirstChild() != aNode
) {
549 return parent
->GetLastChild();
552 return ContentIteratorBase::GetPrevSibling(parent
);
555 nsINode
* ContentIteratorBase::NextNode(nsINode
* aNode
) {
556 nsINode
* node
= aNode
;
558 // if we are a Pre-order iterator, use pre-order
559 if (mOrder
== Order::Pre
) {
560 // if it has children then next node is first child
561 if (node
->HasChildren()) {
562 nsIContent
* firstChild
= node
->GetFirstChild();
563 MOZ_ASSERT(firstChild
);
568 // else next sibling is next
569 return ContentIteratorBase::GetNextSibling(node
);
573 nsINode
* parent
= node
->GetParentNode();
574 if (NS_WARN_IF(!parent
)) {
575 MOZ_ASSERT(parent
, "The node is the root node but not the last node");
580 if (nsIContent
* sibling
= node
->GetNextSibling()) {
581 // next node is sibling's "deep left" child
582 return ContentIteratorBase::GetDeepFirstChild(sibling
);
588 nsINode
* ContentIteratorBase::PrevNode(nsINode
* aNode
) {
589 nsINode
* node
= aNode
;
591 // if we are a Pre-order iterator, use pre-order
592 if (mOrder
== Order::Pre
) {
593 nsINode
* parent
= node
->GetParentNode();
594 if (NS_WARN_IF(!parent
)) {
595 MOZ_ASSERT(parent
, "The node is the root node but not the first node");
600 nsIContent
* sibling
= node
->GetPreviousSibling();
602 return ContentIteratorBase::GetDeepLastChild(sibling
);
609 if (node
->HasChildren()) {
610 return node
->GetLastChild();
613 // else prev sibling is previous
614 return ContentIteratorBase::GetPrevSibling(node
);
617 /******************************************************
618 * ContentIteratorBase routines
619 ******************************************************/
621 void ContentIteratorBase::First() {
623 MOZ_ASSERT(IsDone());
628 mozilla::DebugOnly
<nsresult
> rv
= PositionAt(mFirst
);
629 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to position iterator!");
632 void ContentIteratorBase::Last() {
633 // Note that mLast can be nullptr if SetEmpty() is called in Init()
634 // since at that time, Init() returns NS_OK.
636 MOZ_ASSERT(IsDone());
641 mozilla::DebugOnly
<nsresult
> rv
= PositionAt(mLast
);
642 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to position iterator!");
645 void ContentIteratorBase::Next() {
650 if (mCurNode
== mLast
) {
655 mCurNode
= NextNode(mCurNode
);
658 void ContentIteratorBase::Prev() {
663 if (mCurNode
== mFirst
) {
668 mCurNode
= PrevNode(mCurNode
);
671 // Keeping arrays of indexes for the stack of nodes makes PositionAt
673 nsresult
ContentIteratorBase::PositionAt(nsINode
* aCurNode
) {
674 if (NS_WARN_IF(!aCurNode
)) {
675 return NS_ERROR_NULL_POINTER
;
678 // take an early out if this doesn't actually change the position
679 if (mCurNode
== aCurNode
) {
684 // Check to see if the node falls within the traversal range.
686 RawRangeBoundary
first(mFirst
, 0u);
687 RawRangeBoundary
last(mLast
, 0u);
689 if (mFirst
&& mLast
) {
690 if (mOrder
== Order::Pre
) {
691 // In pre we want to record the point immediately before mFirst, which is
692 // the point immediately after mFirst's previous sibling.
693 first
= {mFirst
->GetParentNode(), mFirst
->GetPreviousSibling()};
695 // If mLast has no children, then we want to make sure to include it.
696 if (!mLast
->HasChildren()) {
697 last
= {mLast
->GetParentNode(), mLast
->AsContent()};
700 // If the first node has any children, we want to be immediately after the
701 // last. Otherwise we want to be immediately before mFirst.
702 if (mFirst
->HasChildren()) {
703 first
= {mFirst
, mFirst
->GetLastChild()};
705 first
= {mFirst
->GetParentNode(), mFirst
->GetPreviousSibling()};
708 // Set the last point immediately after the final node.
709 last
= {mLast
->GetParentNode(), mLast
->AsContent()};
713 NS_WARNING_ASSERTION(first
.IsSetAndValid(), "first is not valid");
714 NS_WARNING_ASSERTION(last
.IsSetAndValid(), "last is not valid");
716 // The end positions are always in the range even if it has no parent. We
717 // need to allow that or 'iter->Init(root)' would assert in Last() or First()
718 // for example, bug 327694.
719 if (mFirst
!= mCurNode
&& mLast
!= mCurNode
&&
720 (NS_WARN_IF(!first
.IsSet()) || NS_WARN_IF(!last
.IsSet()) ||
721 NS_WARN_IF(!NodeIsInTraversalRange(mCurNode
, mOrder
== Order::Pre
, first
,
724 return NS_ERROR_FAILURE
;
730 /******************************************************
731 * ContentSubtreeIterator init routines
732 ******************************************************/
734 nsresult
ContentSubtreeIterator::Init(nsINode
* aRoot
) {
735 return NS_ERROR_NOT_IMPLEMENTED
;
738 nsresult
ContentSubtreeIterator::Init(nsRange
* aRange
) {
741 if (NS_WARN_IF(!aRange
->IsPositioned())) {
742 return NS_ERROR_INVALID_ARG
;
747 return InitWithRange();
750 nsresult
ContentSubtreeIterator::Init(nsINode
* aStartContainer
,
751 uint32_t aStartOffset
,
752 nsINode
* aEndContainer
,
753 uint32_t aEndOffset
) {
754 return Init(RawRangeBoundary(aStartContainer
, aStartOffset
),
755 RawRangeBoundary(aEndContainer
, aEndOffset
));
758 nsresult
ContentSubtreeIterator::Init(const RawRangeBoundary
& aStartBoundary
,
759 const RawRangeBoundary
& aEndBoundary
) {
760 RefPtr
<nsRange
> range
=
761 nsRange::Create(aStartBoundary
, aEndBoundary
, IgnoreErrors());
762 if (NS_WARN_IF(!range
) || NS_WARN_IF(!range
->IsPositioned())) {
763 return NS_ERROR_INVALID_ARG
;
766 if (NS_WARN_IF(range
->StartRef() != aStartBoundary
) ||
767 NS_WARN_IF(range
->EndRef() != aEndBoundary
)) {
768 return NS_ERROR_UNEXPECTED
;
771 mRange
= std::move(range
);
773 return InitWithRange();
776 void ContentSubtreeIterator::CacheInclusiveAncestorsOfEndContainer() {
777 mInclusiveAncestorsOfEndContainer
.Clear();
778 nsINode
* const endContainer
= mRange
->GetEndContainer();
779 nsIContent
* endNode
=
780 endContainer
->IsContent() ? endContainer
->AsContent() : nullptr;
782 mInclusiveAncestorsOfEndContainer
.AppendElement(endNode
);
783 endNode
= endNode
->GetParent();
787 nsIContent
* ContentSubtreeIterator::DetermineCandidateForFirstContent() const {
788 nsINode
* startContainer
= mRange
->GetStartContainer();
789 nsIContent
* firstCandidate
= nullptr;
790 // find first node in range
791 nsINode
* node
= nullptr;
792 if (!startContainer
->GetChildCount()) {
793 // no children, start at the node itself
794 node
= startContainer
;
796 nsIContent
* child
= mRange
->GetChildAtStartOffset();
798 startContainer
->GetChildAt_Deprecated(mRange
->StartOffset()));
800 // offset after last child
801 node
= startContainer
;
803 firstCandidate
= child
;
807 if (!firstCandidate
) {
808 // then firstCandidate is next node after node
809 firstCandidate
= ContentIteratorBase::GetNextSibling(node
);
812 if (firstCandidate
) {
813 firstCandidate
= ContentIteratorBase::GetDeepFirstChild(firstCandidate
);
816 return firstCandidate
;
819 nsIContent
* ContentSubtreeIterator::DetermineFirstContent() const {
820 nsIContent
* firstCandidate
= DetermineCandidateForFirstContent();
821 if (!firstCandidate
) {
825 // confirm that this first possible contained node is indeed contained. Else
826 // we have a range that does not fully contain any node.
827 const Maybe
<bool> isNodeContainedInRange
=
828 RangeUtils::IsNodeContainedInRange(*firstCandidate
, mRange
);
829 MOZ_ALWAYS_TRUE(isNodeContainedInRange
);
830 if (!isNodeContainedInRange
.value()) {
834 // cool, we have the first node in the range. Now we walk up its ancestors
835 // to find the most senior that is still in the range. That's the real first
837 return GetTopAncestorInRange(firstCandidate
);
840 nsIContent
* ContentSubtreeIterator::DetermineCandidateForLastContent() const {
841 nsIContent
* lastCandidate
{nullptr};
842 nsINode
* endContainer
= mRange
->GetEndContainer();
843 // now to find the last node
844 int32_t offset
= mRange
->EndOffset();
845 int32_t numChildren
= endContainer
->GetChildCount();
847 nsINode
* node
= nullptr;
848 if (offset
> numChildren
) {
849 // Can happen for text nodes
850 offset
= numChildren
;
852 if (!offset
|| !numChildren
) {
855 lastCandidate
= mRange
->EndRef().Ref();
856 MOZ_ASSERT(lastCandidate
== endContainer
->GetChildAt_Deprecated(--offset
));
857 NS_ASSERTION(lastCandidate
,
858 "tree traversal trouble in ContentSubtreeIterator::Init");
861 if (!lastCandidate
) {
862 // then lastCandidate is prev node before node
863 lastCandidate
= ContentIteratorBase::GetPrevSibling(node
);
867 lastCandidate
= ContentIteratorBase::GetDeepLastChild(lastCandidate
);
870 return lastCandidate
;
873 nsresult
ContentSubtreeIterator::InitWithRange() {
875 MOZ_ASSERT(mRange
->IsPositioned());
877 // get the start node and offset, convert to nsINode
878 mClosestCommonInclusiveAncestor
= mRange
->GetClosestCommonInclusiveAncestor();
879 nsINode
* startContainer
= mRange
->GetStartContainer();
880 const int32_t startOffset
= mRange
->StartOffset();
881 nsINode
* endContainer
= mRange
->GetEndContainer();
882 const int32_t endOffset
= mRange
->EndOffset();
883 MOZ_ASSERT(mClosestCommonInclusiveAncestor
&& startContainer
&& endContainer
);
885 MOZ_ASSERT(uint32_t(startOffset
) <= startContainer
->Length() &&
886 uint32_t(endOffset
) <= endContainer
->Length());
888 // short circuit when start node == end node
889 if (startContainer
== endContainer
) {
890 nsINode
* child
= startContainer
->GetFirstChild();
892 if (!child
|| startOffset
== endOffset
) {
893 // Text node, empty container, or collapsed
899 CacheInclusiveAncestorsOfEndContainer();
901 mFirst
= DetermineFirstContent();
907 mLast
= DetermineLastContent();
918 nsIContent
* ContentSubtreeIterator::DetermineLastContent() const {
919 nsIContent
* lastCandidate
= DetermineCandidateForLastContent();
920 if (!lastCandidate
) {
924 // confirm that this last possible contained node is indeed contained. Else
925 // we have a range that does not fully contain any node.
927 const Maybe
<bool> isNodeContainedInRange
=
928 RangeUtils::IsNodeContainedInRange(*lastCandidate
, mRange
);
929 MOZ_ALWAYS_TRUE(isNodeContainedInRange
);
930 if (!isNodeContainedInRange
.value()) {
934 // cool, we have the last node in the range. Now we walk up its ancestors to
935 // find the most senior that is still in the range. That's the real first
937 return GetTopAncestorInRange(lastCandidate
);
940 /****************************************************************
941 * ContentSubtreeIterator overrides of ContentIterator routines
942 ****************************************************************/
944 // we can't call PositionAt in a subtree iterator...
945 void ContentSubtreeIterator::First() { mCurNode
= mFirst
; }
947 // we can't call PositionAt in a subtree iterator...
948 void ContentSubtreeIterator::Last() { mCurNode
= mLast
; }
950 void ContentSubtreeIterator::Next() {
955 if (mCurNode
== mLast
) {
960 nsINode
* nextNode
= ContentIteratorBase::GetNextSibling(mCurNode
);
961 NS_ASSERTION(nextNode
, "No next sibling!?! This could mean deadlock!");
963 int32_t i
= mInclusiveAncestorsOfEndContainer
.IndexOf(nextNode
);
965 // as long as we are finding ancestors of the endpoint of the range,
966 // dive down into their children
967 nextNode
= nextNode
->GetFirstChild();
968 NS_ASSERTION(nextNode
, "Iterator error, expected a child node!");
970 // should be impossible to get a null pointer. If we went all the way
971 // down the child chain to the bottom without finding an interior node,
972 // then the previous node should have been the last, which was
973 // was tested at top of routine.
974 i
= mInclusiveAncestorsOfEndContainer
.IndexOf(nextNode
);
980 void ContentSubtreeIterator::Prev() {
981 // Prev should be optimized to use the mStartNodes, just as Next
982 // uses mInclusiveAncestorsOfEndContainer.
987 if (mCurNode
== mFirst
) {
992 // If any of these function calls return null, so will all succeeding ones,
993 // so mCurNode will wind up set to null.
994 nsINode
* prevNode
= ContentIteratorBase::GetDeepFirstChild(mCurNode
);
996 prevNode
= PrevNode(prevNode
);
998 prevNode
= ContentIteratorBase::GetDeepLastChild(prevNode
);
1000 mCurNode
= GetTopAncestorInRange(prevNode
);
1003 nsresult
ContentSubtreeIterator::PositionAt(nsINode
* aCurNode
) {
1004 NS_ERROR("Not implemented!");
1005 return NS_ERROR_NOT_IMPLEMENTED
;
1008 /****************************************************************
1009 * ContentSubtreeIterator helper routines
1010 ****************************************************************/
1012 nsIContent
* ContentSubtreeIterator::GetTopAncestorInRange(
1013 nsINode
* aNode
) const {
1014 if (!aNode
|| !aNode
->GetParentNode()) {
1018 // aNode has a parent, so it must be content.
1019 nsIContent
* content
= aNode
->AsContent();
1021 // sanity check: aNode is itself in the range
1022 Maybe
<bool> isNodeContainedInRange
=
1023 RangeUtils::IsNodeContainedInRange(*aNode
, mRange
);
1024 NS_ASSERTION(isNodeContainedInRange
&& isNodeContainedInRange
.value(),
1025 "aNode isn't in mRange, or something else weird happened");
1026 if (!isNodeContainedInRange
|| !isNodeContainedInRange
.value()) {
1031 nsIContent
* parent
= content
->GetParent();
1032 // content always has a parent. If its parent is the root, however --
1033 // i.e., either it's not content, or it is content but its own parent is
1034 // null -- then we're finished, since we don't go up to the root.
1036 // We have to special-case this because CompareNodeToRange treats the root
1037 // node differently -- see bug 765205.
1038 if (!parent
|| !parent
->GetParentNode()) {
1042 isNodeContainedInRange
=
1043 RangeUtils::IsNodeContainedInRange(*parent
, mRange
);
1044 MOZ_ALWAYS_TRUE(isNodeContainedInRange
);
1045 if (!isNodeContainedInRange
.value()) {
1052 MOZ_CRASH("This should only be possible if aNode was null");
1055 } // namespace mozilla