Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / base / ContentIterator.cpp
blob6819353520318ce2297882d4333e6d50443cb2de
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"
18 #include "nsRange.h"
20 namespace mozilla {
22 using namespace dom;
24 #define NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(aResultType, aMethodName, ...) \
25 template aResultType ContentIteratorBase<RefPtr<nsINode>>::aMethodName( \
26 __VA_ARGS__); \
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();
32 if (!parent) {
33 return false;
36 // aNode should always be content, as we have a parent, but let's just be
37 // extra careful and check.
38 nsIContent* content =
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();
63 if (!parent) {
64 return false;
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()) ||
95 NS_WARN_IF(!aNode)) {
96 return false;
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()) {
106 MOZ_ASSERT(
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");
113 return true;
117 if (aIsPreMode) {
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)
167 : mOrder(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 /******************************************************
179 * Init routines
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) {
191 mFirst = aRoot;
192 mLast = ContentIteratorBase::GetDeepLastChild(aRoot);
193 NS_WARNING_ASSERTION(mLast, "GetDeepLastChild returned null");
194 } else {
195 mFirst = ContentIteratorBase::GetDeepFirstChild(aRoot);
196 NS_WARNING_ASSERTION(mFirst, "GetDeepFirstChild returned null");
197 mLast = aRoot;
200 mClosestCommonInclusiveAncestor = aRoot;
201 mCurNode = mFirst;
202 return NS_OK;
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,
221 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 {
252 public:
253 Initializer(ContentIteratorBase<NodeType>& aIterator,
254 const RawRangeBoundary& aStart, const RawRangeBoundary& aEnd)
255 : mIterator{aIterator},
256 mStart{aStart},
257 mEnd{aEnd},
258 mStartIsCharacterData{mStart.Container()->IsCharacterData()} {
259 MOZ_ASSERT(mStart.IsSetAndValid());
260 MOZ_ASSERT(mEnd.IsSetAndValid());
263 nsresult Run();
265 private:
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;
285 template <>
286 nsresult ContentIteratorBase<RefPtr<nsINode>>::InitInternal(
287 const RawRangeBoundary& aStart, const RawRangeBoundary& aEnd) {
288 Initializer initializer{*this, aStart, aEnd};
289 return initializer.Run();
292 template <>
293 nsresult ContentIteratorBase<nsINode*>::InitInternal(
294 const RawRangeBoundary& aStart, const RawRangeBoundary& aEnd) {
295 Initializer initializer{*this, aStart, aEnd};
296 nsresult rv = initializer.Run();
297 if (NS_FAILED(rv)) {
298 return rv;
300 mMutationGuard.emplace();
301 mAssertNoGC.emplace();
302 return NS_OK;
305 template <typename NodeType>
306 bool ContentIteratorBase<NodeType>::Initializer::IsCollapsedNonCharacterRange()
307 const {
308 return !mStartIsCharacterData && mStart == mEnd;
311 template <typename NodeType>
312 bool ContentIteratorBase<NodeType>::Initializer::IsSingleNodeCharacterRange()
313 const {
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(),
322 mEnd.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
328 // iterate over.
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
332 // of a range.
334 if (IsCollapsedNonCharacterRange()) {
335 mIterator.SetEmpty();
336 return NS_OK;
339 if (IsSingleNodeCharacterRange()) {
340 mIterator.mFirst = mStart.Container()->AsContent();
341 mIterator.mLast = mIterator.mFirst;
342 mIterator.mCurNode = mIterator.mFirst;
344 return NS_OK;
347 mIterator.mFirst = DetermineFirstNode();
349 if (Result<nsINode*, nsresult> lastNode = DetermineLastNode();
350 NS_WARN_IF(lastNode.isErr())) {
351 return lastNode.unwrapErr();
352 } else {
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;
363 return NS_OK;
366 template <typename NodeType>
367 nsINode* ContentIteratorBase<NodeType>::Initializer::DetermineFirstNode()
368 const {
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();
377 if (!cChild) {
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
383 // the next sibling?
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();
392 startIsContainer =
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.
403 if (result &&
404 NS_WARN_IF(!NodeIsInTraversalRange(
405 result, mIterator.mOrder == Order::Pre, mStart, mEnd))) {
406 return nullptr;
409 return result;
411 return mStart.Container()->AsContent();
414 // post-order
415 if (NS_WARN_IF(!mStart.Container()->IsContent())) {
416 // What else can we do?
417 return nullptr;
419 return mStart.Container()->AsContent();
422 if (mIterator.mOrder == Order::Pre) {
423 return cChild;
426 // post-order
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,
433 mStart, mEnd)) {
434 return nullptr;
437 return result;
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...
450 return nullptr;
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();
459 endIsContainer =
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))) {
469 return nullptr;
472 return result;
475 return mEnd.Container()->AsContent();
478 // post-order
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,
489 mStart, mEnd)) {
490 return nullptr;
492 return result;
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))) {
511 return nullptr;
514 return result;
517 // post-order
518 return cChild;
521 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, SetEmpty);
523 template <typename NodeType>
524 void ContentIteratorBase<NodeType>::SetEmpty() {
525 mCurNode = nullptr;
526 mFirst = nullptr;
527 mLast = nullptr;
528 mClosestCommonInclusiveAncestor = nullptr;
531 // static
532 template <typename NodeType>
533 nsINode* ContentIteratorBase<NodeType>::GetDeepFirstChild(nsINode* aRoot) {
534 if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
535 return aRoot;
538 return ContentIteratorBase::GetDeepFirstChild(aRoot->GetFirstChild());
541 // static
542 template <typename NodeType>
543 nsIContent* ContentIteratorBase<NodeType>::GetDeepFirstChild(
544 nsIContent* aRoot) {
545 if (NS_WARN_IF(!aRoot)) {
546 return nullptr;
549 nsIContent* node = aRoot;
550 nsIContent* child = node->GetFirstChild();
552 while (child) {
553 node = child;
554 child = node->GetFirstChild();
557 return node;
560 // static
561 template <typename NodeType>
562 nsINode* ContentIteratorBase<NodeType>::GetDeepLastChild(nsINode* aRoot) {
563 if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
564 return aRoot;
567 return ContentIteratorBase::GetDeepLastChild(aRoot->GetLastChild());
570 // static
571 template <typename NodeType>
572 nsIContent* ContentIteratorBase<NodeType>::GetDeepLastChild(nsIContent* aRoot) {
573 if (NS_WARN_IF(!aRoot)) {
574 return nullptr;
577 nsIContent* node = aRoot;
578 while (node->HasChildren()) {
579 nsIContent* child = node->GetLastChild();
580 node = child;
582 return node;
585 // Get the next sibling, or parent's next sibling, or grandpa's next sibling...
586 // static
587 template <typename NodeType>
588 nsIContent* ContentIteratorBase<NodeType>::GetNextSibling(nsINode* aNode) {
589 if (NS_WARN_IF(!aNode)) {
590 return nullptr;
593 if (nsIContent* next = aNode->GetNextSibling()) {
594 return next;
597 nsINode* parent = aNode->GetParentNode();
598 if (NS_WARN_IF(!parent)) {
599 return nullptr;
602 return ContentIteratorBase::GetNextSibling(parent);
605 // Get the prev sibling, or parent's prev sibling, or grandpa's prev sibling...
606 // static
607 template <typename NodeType>
608 nsIContent* ContentIteratorBase<NodeType>::GetPrevSibling(nsINode* aNode) {
609 if (NS_WARN_IF(!aNode)) {
610 return nullptr;
613 if (nsIContent* prev = aNode->GetPreviousSibling()) {
614 return prev;
617 nsINode* parent = aNode->GetParentNode();
618 if (NS_WARN_IF(!parent)) {
619 return nullptr;
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);
636 return firstChild;
639 // else next sibling is next
640 return ContentIteratorBase::GetNextSibling(node);
643 // post-order
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");
647 mCurNode = nullptr;
648 return node;
651 if (nsIContent* sibling = node->GetNextSibling()) {
652 // next node is sibling's "deep left" child
653 return ContentIteratorBase::GetDeepFirstChild(sibling);
656 return parent;
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");
668 mCurNode = nullptr;
669 return aNode;
672 nsIContent* sibling = node->GetPreviousSibling();
673 if (sibling) {
674 return ContentIteratorBase::GetDeepLastChild(sibling);
677 return parent;
680 // post-order
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() {
697 if (!mFirst) {
698 MOZ_ASSERT(IsDone());
699 mCurNode = nullptr;
700 return;
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.
713 if (!mLast) {
714 MOZ_ASSERT(IsDone());
715 mCurNode = nullptr;
716 return;
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() {
727 if (IsDone()) {
728 return;
731 if (mCurNode == mLast) {
732 mCurNode = nullptr;
733 return;
736 mCurNode = NextNode(mCurNode);
739 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, Prev);
741 template <typename NodeType>
742 void ContentIteratorBase<NodeType>::Prev() {
743 if (IsDone()) {
744 return;
747 if (mCurNode == mFirst) {
748 mCurNode = nullptr;
749 return;
752 mCurNode = PrevNode(mCurNode);
755 // Keeping arrays of indexes for the stack of nodes makes PositionAt
756 // interesting...
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) {
767 return NS_OK;
769 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()};
786 } else {
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()};
791 } else {
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,
809 last)))) {
810 mCurNode = nullptr;
811 return NS_ERROR_FAILURE;
814 return NS_OK;
817 /******************************************************
818 * ContentSubtreeIterator init routines
819 ******************************************************/
821 nsresult ContentSubtreeIterator::Init(nsINode* aRoot) {
822 return NS_ERROR_NOT_IMPLEMENTED;
825 nsresult ContentSubtreeIterator::Init(AbstractRange* aRange) {
826 MOZ_ASSERT(aRange);
828 if (NS_WARN_IF(!aRange->IsPositioned())) {
829 return NS_ERROR_INVALID_ARG;
832 mRange = aRange;
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;
868 while (endNode) {
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;
882 } else {
883 nsIContent* child = mRange->GetChildAtStartOffset();
884 MOZ_ASSERT(child ==
885 startContainer->GetChildAt_Deprecated(mRange->StartOffset()));
886 if (!child) {
887 // offset after last child
888 node = startContainer;
889 } else {
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) {
909 return nullptr;
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()) {
918 return nullptr;
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
923 // node.
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) {
940 node = endContainer;
941 } else {
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);
953 if (lastCandidate) {
954 lastCandidate = ContentIteratorBase::GetDeepLastChild(lastCandidate);
957 return lastCandidate;
960 nsresult ContentSubtreeIterator::InitWithRange() {
961 MOZ_ASSERT(mRange);
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);
971 // Bug 767169
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
981 SetEmpty();
982 return NS_OK;
986 CacheInclusiveAncestorsOfEndContainer();
988 mFirst = DetermineFirstContent();
989 if (!mFirst) {
990 SetEmpty();
991 return NS_OK;
994 mLast = DetermineLastContent();
995 if (!mLast) {
996 SetEmpty();
997 return NS_OK;
1000 mCurNode = mFirst;
1002 return NS_OK;
1005 nsIContent* ContentSubtreeIterator::DetermineLastContent() const {
1006 nsIContent* lastCandidate = DetermineCandidateForLastContent();
1007 if (!lastCandidate) {
1008 return nullptr;
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()) {
1018 return nullptr;
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
1023 // node.
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() {
1038 if (IsDone()) {
1039 return;
1042 if (mCurNode == mLast) {
1043 mCurNode = nullptr;
1044 return;
1047 nsINode* nextNode = ContentIteratorBase::GetNextSibling(mCurNode);
1048 NS_ASSERTION(nextNode, "No next sibling!?! This could mean deadlock!");
1050 int32_t i = mInclusiveAncestorsOfEndContainer.IndexOf(nextNode);
1051 while (i != -1) {
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.
1070 if (IsDone()) {
1071 return;
1074 if (mCurNode == mFirst) {
1075 mCurNode = nullptr;
1076 return;
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()) {
1102 return nullptr;
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()) {
1114 return nullptr;
1117 while (content) {
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()) {
1126 return content;
1129 isNodeContainedInRange =
1130 RangeUtils::IsNodeContainedInRange(*parent, mRange);
1131 MOZ_ALWAYS_TRUE(isNodeContainedInRange);
1132 if (!isNodeContainedInRange.value()) {
1133 return content;
1136 content = parent;
1139 MOZ_CRASH("This should only be possible if aNode was null");
1142 #undef NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD
1144 } // namespace mozilla