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/dom/ShadowRoot.h"
11 #include "mozilla/DebugOnly.h"
12 #include "mozilla/RangeBoundary.h"
13 #include "mozilla/RangeUtils.h"
14 #include "mozilla/Result.h"
16 #include "nsContentUtils.h"
17 #include "nsElementTable.h"
18 #include "nsIContent.h"
25 #define NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(aResultType, aMethodName, ...) \
26 template aResultType ContentIteratorBase<RefPtr<nsINode>>::aMethodName( \
28 template aResultType ContentIteratorBase<nsINode*>::aMethodName(__VA_ARGS__)
31 * IteratorHelpers contains the static methods to help extra values
32 * based on whether or not the iterator allows to iterate nodes cross the shadow
35 struct IteratorHelpers
{
36 IteratorHelpers() = delete;
38 static nsINode
* GetStartContainer(AbstractRange
* aRange
,
39 bool aAllowCrossShadowBoundary
) {
41 return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
42 aAllowCrossShadowBoundary
)
43 ? aRange
->GetMayCrossShadowBoundaryStartContainer()
44 : aRange
->GetStartContainer();
47 static int32_t StartOffset(AbstractRange
* aRange
,
48 bool aAllowCrossShadowBoundary
) {
50 return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
51 aAllowCrossShadowBoundary
)
52 ? aRange
->MayCrossShadowBoundaryStartOffset()
53 : aRange
->StartOffset();
56 static nsINode
* GetEndContainer(AbstractRange
* aRange
,
57 bool aAllowCrossShadowBoundary
) {
59 return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
60 aAllowCrossShadowBoundary
)
61 ? aRange
->GetMayCrossShadowBoundaryEndContainer()
62 : aRange
->GetEndContainer();
65 static int32_t EndOffset(AbstractRange
* aRange
,
66 bool aAllowCrossShadowBoundary
) {
68 return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
69 aAllowCrossShadowBoundary
)
70 ? aRange
->MayCrossShadowBoundaryEndOffset()
71 : aRange
->EndOffset();
74 // FIXME(sefeng): This doesn't work with slots / flattened tree.
75 static nsINode
* GetParentNode(nsINode
& aNode
,
76 bool aAllowCrossShadowBoundary
) {
77 return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
78 aAllowCrossShadowBoundary
)
79 ? aNode
.GetParentOrShadowHostNode()
80 : aNode
.GetParentNode();
83 static ShadowRoot
* GetShadowRoot(const nsINode
* aNode
,
84 bool aAllowCrossShadowBoundary
) {
86 return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
87 aAllowCrossShadowBoundary
)
88 ? aNode
->GetShadowRootForSelection()
93 static bool ComparePostMode(const RawRangeBoundary
& aStart
,
94 const RawRangeBoundary
& aEnd
, nsINode
& aNode
) {
95 nsINode
* parent
= aNode
.GetParentNode();
100 // aNode should always be content, as we have a parent, but let's just be
101 // extra careful and check.
102 nsIContent
* content
=
103 NS_WARN_IF(!aNode
.IsContent()) ? nullptr : aNode
.AsContent();
105 // Post mode: start < node <= end.
106 RawRangeBoundary
afterNode(parent
, content
);
107 const auto isStartLessThanAfterNode
= [&]() {
108 const Maybe
<int32_t> startComparedToAfterNode
=
109 nsContentUtils::ComparePoints(aStart
, afterNode
);
110 return !NS_WARN_IF(!startComparedToAfterNode
) &&
111 (*startComparedToAfterNode
< 0);
114 const auto isAfterNodeLessOrEqualToEnd
= [&]() {
115 const Maybe
<int32_t> afterNodeComparedToEnd
=
116 nsContentUtils::ComparePoints(afterNode
, aEnd
);
117 return !NS_WARN_IF(!afterNodeComparedToEnd
) &&
118 (*afterNodeComparedToEnd
<= 0);
121 return isStartLessThanAfterNode() && isAfterNodeLessOrEqualToEnd();
124 static bool ComparePreMode(const RawRangeBoundary
& aStart
,
125 const RawRangeBoundary
& aEnd
, nsINode
& aNode
) {
126 nsINode
* parent
= aNode
.GetParentNode();
131 // Pre mode: start <= node < end.
132 RawRangeBoundary
beforeNode(parent
, aNode
.GetPreviousSibling());
134 const auto isStartLessOrEqualToBeforeNode
= [&]() {
135 const Maybe
<int32_t> startComparedToBeforeNode
=
136 nsContentUtils::ComparePoints(aStart
, beforeNode
);
137 return !NS_WARN_IF(!startComparedToBeforeNode
) &&
138 (*startComparedToBeforeNode
<= 0);
141 const auto isBeforeNodeLessThanEndNode
= [&]() {
142 const Maybe
<int32_t> beforeNodeComparedToEnd
=
143 nsContentUtils::ComparePoints(beforeNode
, aEnd
);
144 return !NS_WARN_IF(!beforeNodeComparedToEnd
) &&
145 (*beforeNodeComparedToEnd
< 0);
148 return isStartLessOrEqualToBeforeNode() && isBeforeNodeLessThanEndNode();
151 ///////////////////////////////////////////////////////////////////////////
152 // NodeIsInTraversalRange: returns true if content is visited during
153 // the traversal of the range in the specified mode.
155 static bool NodeIsInTraversalRange(nsINode
* aNode
, bool aIsPreMode
,
156 const RawRangeBoundary
& aStart
,
157 const RawRangeBoundary
& aEnd
) {
158 if (NS_WARN_IF(!aStart
.IsSet()) || NS_WARN_IF(!aEnd
.IsSet()) ||
159 NS_WARN_IF(!aNode
)) {
163 // If a leaf node contains an end point of the traversal range, it is
164 // always in the traversal range.
165 if (aNode
== aStart
.Container() || aNode
== aEnd
.Container()) {
166 if (aNode
->IsCharacterData()) {
167 return true; // text node or something
169 if (!aNode
->HasChildren()) {
171 aNode
!= aStart
.Container() || aStart
.IsStartOfContainer(),
172 "aStart.Container() doesn't have children and not a data node, "
173 "aStart should be at the beginning of its container");
174 MOZ_ASSERT(aNode
!= aEnd
.Container() || aEnd
.IsStartOfContainer(),
175 "aEnd.Container() doesn't have children and not a data node, "
176 "aEnd should be at the beginning of its container");
182 return ComparePreMode(aStart
, aEnd
, *aNode
);
185 return ComparePostMode(aStart
, aEnd
, *aNode
);
188 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
189 PostContentIterator
& aField
, const char* aName
,
190 uint32_t aFlags
= 0) {
191 ImplCycleCollectionTraverse(
192 aCallback
, static_cast<SafeContentIteratorBase
&>(aField
), aName
, aFlags
);
195 void ImplCycleCollectionUnlink(PostContentIterator
& aField
) {
196 ImplCycleCollectionUnlink(static_cast<SafeContentIteratorBase
&>(aField
));
199 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
200 PreContentIterator
& aField
, const char* aName
,
201 uint32_t aFlags
= 0) {
202 ImplCycleCollectionTraverse(
203 aCallback
, static_cast<SafeContentIteratorBase
&>(aField
), aName
, aFlags
);
206 void ImplCycleCollectionUnlink(PreContentIterator
& aField
) {
207 ImplCycleCollectionUnlink(static_cast<SafeContentIteratorBase
&>(aField
));
210 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
211 ContentSubtreeIterator
& aField
,
212 const char* aName
, uint32_t aFlags
= 0) {
213 ImplCycleCollectionTraverse(aCallback
, aField
.mRange
, aName
, aFlags
);
214 ImplCycleCollectionTraverse(
215 aCallback
, static_cast<SafeContentIteratorBase
&>(aField
), aName
, aFlags
);
218 void ImplCycleCollectionUnlink(ContentSubtreeIterator
& aField
) {
219 ImplCycleCollectionUnlink(aField
.mRange
);
220 ImplCycleCollectionUnlink(static_cast<SafeContentIteratorBase
&>(aField
));
223 /******************************************************
224 * ContentIteratorBase
225 ******************************************************/
227 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(, ContentIteratorBase
, Order
);
229 template <typename NodeType
>
230 ContentIteratorBase
<NodeType
>::ContentIteratorBase(Order aOrder
)
233 template ContentIteratorBase
<RefPtr
<nsINode
>>::~ContentIteratorBase();
234 template ContentIteratorBase
<nsINode
*>::~ContentIteratorBase();
236 template <typename NodeType
>
237 ContentIteratorBase
<NodeType
>::~ContentIteratorBase() {
238 MOZ_DIAGNOSTIC_ASSERT_IF(mMutationGuard
.isSome(),
239 !mMutationGuard
->Mutated(0));
242 /******************************************************
244 ******************************************************/
246 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, Init
, nsINode
*);
248 template <typename NodeType
>
249 nsresult ContentIteratorBase
<NodeType
>::Init(nsINode
* aRoot
) {
250 if (NS_WARN_IF(!aRoot
)) {
251 return NS_ERROR_NULL_POINTER
;
254 if (mOrder
== Order::Pre
) {
256 mLast
= ContentIteratorBase::GetDeepLastChild(aRoot
);
257 NS_WARNING_ASSERTION(mLast
, "GetDeepLastChild returned null");
259 mFirst
= ContentIteratorBase::GetDeepFirstChild(aRoot
);
260 NS_WARNING_ASSERTION(mFirst
, "GetDeepFirstChild returned null");
264 mClosestCommonInclusiveAncestor
= aRoot
;
269 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, Init
, AbstractRange
*);
271 template <typename NodeType
>
272 nsresult ContentIteratorBase
<NodeType
>::Init(AbstractRange
* aRange
) {
273 if (NS_WARN_IF(!aRange
)) {
274 return NS_ERROR_INVALID_ARG
;
277 if (NS_WARN_IF(!aRange
->IsPositioned())) {
278 return NS_ERROR_INVALID_ARG
;
281 return InitInternal(aRange
->StartRef().AsRaw(), aRange
->EndRef().AsRaw());
284 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, Init
, nsINode
*, uint32_t,
287 template <typename NodeType
>
288 nsresult ContentIteratorBase
<NodeType
>::Init(nsINode
* aStartContainer
,
289 uint32_t aStartOffset
,
290 nsINode
* aEndContainer
,
291 uint32_t aEndOffset
) {
292 if (NS_WARN_IF(!RangeUtils::IsValidPoints(aStartContainer
, aStartOffset
,
293 aEndContainer
, aEndOffset
))) {
294 return NS_ERROR_INVALID_ARG
;
297 return InitInternal(RawRangeBoundary(aStartContainer
, aStartOffset
),
298 RawRangeBoundary(aEndContainer
, aEndOffset
));
301 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, Init
, const RawRangeBoundary
&,
302 const RawRangeBoundary
&);
304 template <typename NodeType
>
305 nsresult ContentIteratorBase
<NodeType
>::Init(const RawRangeBoundary
& aStart
,
306 const RawRangeBoundary
& aEnd
) {
307 if (NS_WARN_IF(!RangeUtils::IsValidPoints(aStart
, aEnd
))) {
308 return NS_ERROR_INVALID_ARG
;
311 return InitInternal(aStart
, aEnd
);
314 template <typename NodeType
>
315 class MOZ_STACK_CLASS ContentIteratorBase
<NodeType
>::Initializer final
{
317 Initializer(ContentIteratorBase
<NodeType
>& aIterator
,
318 const RawRangeBoundary
& aStart
, const RawRangeBoundary
& aEnd
)
319 : mIterator
{aIterator
},
322 mStartIsCharacterData
{mStart
.Container()->IsCharacterData()} {
323 MOZ_ASSERT(mStart
.IsSetAndValid());
324 MOZ_ASSERT(mEnd
.IsSetAndValid());
331 * @return may be nullptr.
333 nsINode
* DetermineFirstNode() const;
336 * @return may be nullptr.
338 [[nodiscard
]] Result
<nsINode
*, nsresult
> DetermineLastNode() const;
340 bool IsCollapsedNonCharacterRange() const;
341 bool IsSingleNodeCharacterRange() const;
343 ContentIteratorBase
& mIterator
;
344 const RawRangeBoundary
& mStart
;
345 const RawRangeBoundary
& mEnd
;
346 const bool mStartIsCharacterData
;
350 nsresult ContentIteratorBase
<RefPtr
<nsINode
>>::InitInternal(
351 const RawRangeBoundary
& aStart
, const RawRangeBoundary
& aEnd
) {
352 Initializer initializer
{*this, aStart
, aEnd
};
353 return initializer
.Run();
357 nsresult ContentIteratorBase
<nsINode
*>::InitInternal(
358 const RawRangeBoundary
& aStart
, const RawRangeBoundary
& aEnd
) {
359 Initializer initializer
{*this, aStart
, aEnd
};
360 nsresult rv
= initializer
.Run();
364 mMutationGuard
.emplace();
365 mAssertNoGC
.emplace();
369 template <typename NodeType
>
370 bool ContentIteratorBase
<NodeType
>::Initializer::IsCollapsedNonCharacterRange()
372 return !mStartIsCharacterData
&& mStart
== mEnd
;
375 template <typename NodeType
>
376 bool ContentIteratorBase
<NodeType
>::Initializer::IsSingleNodeCharacterRange()
378 return mStartIsCharacterData
&& mStart
.Container() == mEnd
.Container();
381 template <typename NodeType
>
382 nsresult ContentIteratorBase
<NodeType
>::Initializer::Run() {
383 // get common content parent
384 mIterator
.mClosestCommonInclusiveAncestor
=
385 nsContentUtils::GetClosestCommonInclusiveAncestor(mStart
.Container(),
387 if (NS_WARN_IF(!mIterator
.mClosestCommonInclusiveAncestor
)) {
388 return NS_ERROR_FAILURE
;
391 // Check to see if we have a collapsed range, if so, there is nothing to
394 // XXX: CharacterDataNodes (text nodes) are currently an exception, since
395 // we always want to be able to iterate text nodes at the end points
398 if (IsCollapsedNonCharacterRange()) {
399 mIterator
.SetEmpty();
403 if (IsSingleNodeCharacterRange()) {
404 mIterator
.mFirst
= mStart
.Container()->AsContent();
405 mIterator
.mLast
= mIterator
.mFirst
;
406 mIterator
.mCurNode
= mIterator
.mFirst
;
411 mIterator
.mFirst
= DetermineFirstNode();
413 if (Result
<nsINode
*, nsresult
> lastNode
= DetermineLastNode();
414 NS_WARN_IF(lastNode
.isErr())) {
415 return lastNode
.unwrapErr();
417 mIterator
.mLast
= lastNode
.unwrap();
420 // If either first or last is null, they both have to be null!
421 if (!mIterator
.mFirst
|| !mIterator
.mLast
) {
422 mIterator
.SetEmpty();
425 mIterator
.mCurNode
= mIterator
.mFirst
;
430 template <typename NodeType
>
431 nsINode
* ContentIteratorBase
<NodeType
>::Initializer::DetermineFirstNode()
433 nsIContent
* cChild
= nullptr;
435 // Try to get the child at our starting point. This might return null if
436 // mStart is immediately after the last node in mStart.Container().
437 if (!mStartIsCharacterData
) {
438 cChild
= mStart
.GetChildAtOffset();
442 // No children (possibly a <br> or text node), or index is after last child.
444 if (mIterator
.mOrder
== Order::Pre
) {
445 // XXX: In the future, if start offset is after the last
446 // character in the cdata node, should we set mFirst to
449 // Normally we would skip the start node because the start node is outside
450 // of the range in pre mode. However, if aStartOffset == 0, and the node
451 // is a non-container node (e.g. <br>), we don't skip the node in this
452 // case in order to address bug 1215798.
453 bool startIsContainer
= true;
454 if (mStart
.Container()->IsHTMLElement()) {
455 nsAtom
* name
= mStart
.Container()->NodeInfo()->NameAtom();
457 nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name
));
459 if (!mStartIsCharacterData
&&
460 (startIsContainer
|| !mStart
.IsStartOfContainer())) {
461 nsINode
* const result
=
462 ContentIteratorBase::GetNextSibling(mStart
.Container());
463 NS_WARNING_ASSERTION(result
, "GetNextSibling returned null");
465 // Does mFirst node really intersect the range? The range could be
466 // 'degenerate', i.e., not collapsed but still contain no content.
468 NS_WARN_IF(!NodeIsInTraversalRange(
469 result
, mIterator
.mOrder
== Order::Pre
, mStart
, mEnd
))) {
475 return mStart
.Container()->AsContent();
479 if (NS_WARN_IF(!mStart
.Container()->IsContent())) {
480 // What else can we do?
483 return mStart
.Container()->AsContent();
486 if (mIterator
.mOrder
== Order::Pre
) {
491 nsINode
* const result
= ContentIteratorBase::GetDeepFirstChild(cChild
);
492 NS_WARNING_ASSERTION(result
, "GetDeepFirstChild returned null");
494 // Does mFirst node really intersect the range? The range could be
495 // 'degenerate', i.e., not collapsed but still contain no content.
496 if (result
&& !NodeIsInTraversalRange(result
, mIterator
.mOrder
== Order::Pre
,
504 template <typename NodeType
>
505 Result
<nsINode
*, nsresult
>
506 ContentIteratorBase
<NodeType
>::Initializer::DetermineLastNode() const {
507 const bool endIsCharacterData
= mEnd
.Container()->IsCharacterData();
509 if (endIsCharacterData
|| !mEnd
.Container()->HasChildren() ||
510 mEnd
.IsStartOfContainer()) {
511 if (mIterator
.mOrder
== Order::Pre
) {
512 if (NS_WARN_IF(!mEnd
.Container()->IsContent())) {
513 // Not much else to do here...
517 // If the end node is a non-container element and the end offset is 0,
518 // the last element should be the previous node (i.e., shouldn't
519 // include the end node in the range).
520 bool endIsContainer
= true;
521 if (mEnd
.Container()->IsHTMLElement()) {
522 nsAtom
* name
= mEnd
.Container()->NodeInfo()->NameAtom();
524 nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name
));
526 if (!endIsCharacterData
&& !endIsContainer
&& mEnd
.IsStartOfContainer()) {
527 nsINode
* const result
= mIterator
.PrevNode(mEnd
.Container());
528 NS_WARNING_ASSERTION(result
, "PrevNode returned null");
529 if (result
&& result
!= mIterator
.mFirst
&&
530 NS_WARN_IF(!NodeIsInTraversalRange(
531 result
, mIterator
.mOrder
== Order::Pre
,
532 RawRangeBoundary(mIterator
.mFirst
, 0u), mEnd
))) {
539 return mEnd
.Container()->AsContent();
544 // XXX: In the future, if end offset is before the first character in the
545 // cdata node, should we set mLast to the prev sibling?
547 if (!endIsCharacterData
) {
548 nsINode
* const result
=
549 ContentIteratorBase::GetPrevSibling(mEnd
.Container());
550 NS_WARNING_ASSERTION(result
, "GetPrevSibling returned null");
552 if (!NodeIsInTraversalRange(result
, mIterator
.mOrder
== Order::Pre
,
558 return mEnd
.Container()->AsContent();
561 nsIContent
* cChild
= mEnd
.Ref();
563 if (NS_WARN_IF(!cChild
)) {
564 // No child at offset!
565 MOZ_ASSERT_UNREACHABLE("ContentIterator::ContentIterator");
566 return Err(NS_ERROR_FAILURE
);
569 if (mIterator
.mOrder
== Order::Pre
) {
570 nsINode
* const result
= ContentIteratorBase::GetDeepLastChild(cChild
);
571 NS_WARNING_ASSERTION(result
, "GetDeepLastChild returned null");
573 if (NS_WARN_IF(!NodeIsInTraversalRange(
574 result
, mIterator
.mOrder
== Order::Pre
, mStart
, mEnd
))) {
585 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, SetEmpty
);
587 template <typename NodeType
>
588 void ContentIteratorBase
<NodeType
>::SetEmpty() {
592 mClosestCommonInclusiveAncestor
= nullptr;
596 template <typename NodeType
>
597 nsINode
* ContentIteratorBase
<NodeType
>::GetDeepFirstChild(nsINode
* aRoot
) {
598 if (NS_WARN_IF(!aRoot
) || !aRoot
->HasChildren()) {
602 return ContentIteratorBase::GetDeepFirstChild(aRoot
->GetFirstChild());
606 template <typename NodeType
>
607 nsIContent
* ContentIteratorBase
<NodeType
>::GetDeepFirstChild(
608 nsIContent
* aRoot
, bool aAllowCrossShadowBoundary
) {
609 if (NS_WARN_IF(!aRoot
)) {
613 nsIContent
* node
= aRoot
;
614 nsIContent
* child
= node
->GetFirstChild();
617 if (ShadowRoot
* shadowRoot
=
618 IteratorHelpers::GetShadowRoot(node
, aAllowCrossShadowBoundary
)) {
619 // If this node doesn't have a child, but it's also a shadow host
620 // that can be selected, we go into this shadow tree.
621 child
= shadowRoot
->GetFirstChild();
625 // FIXME(sefeng): This is problematic for slotted contents
628 child
= node
->GetFirstChild();
630 if (ShadowRoot
* shadowRoot
=
631 IteratorHelpers::GetShadowRoot(node
, aAllowCrossShadowBoundary
)) {
632 child
= shadowRoot
->GetFirstChild();
641 template <typename NodeType
>
642 nsINode
* ContentIteratorBase
<NodeType
>::GetDeepLastChild(nsINode
* aRoot
) {
643 if (NS_WARN_IF(!aRoot
) || !aRoot
->HasChildren()) {
647 return ContentIteratorBase::GetDeepLastChild(aRoot
->GetLastChild());
651 template <typename NodeType
>
652 nsIContent
* ContentIteratorBase
<NodeType
>::GetDeepLastChild(
653 nsIContent
* aRoot
, bool aAllowCrossShadowBoundary
) {
654 if (NS_WARN_IF(!aRoot
)) {
658 nsIContent
* node
= aRoot
;
660 ShadowRoot
* shadowRoot
=
661 IteratorHelpers::GetShadowRoot(node
, aAllowCrossShadowBoundary
);
662 // FIXME(sefeng): This doesn't work with slots / flattened tree.
663 while (node
->HasChildren() || (shadowRoot
&& shadowRoot
->HasChildren())) {
664 if (node
->HasChildren()) {
665 node
= node
->GetLastChild();
667 MOZ_ASSERT(shadowRoot
);
668 // If this node doesn't have a child, but it's also a shadow host
669 // that can be selected, we go into this shadow tree.
670 node
= shadowRoot
->GetLastChild();
673 IteratorHelpers::GetShadowRoot(node
, aAllowCrossShadowBoundary
);
678 // Get the next sibling, or parent's next sibling, or shadow host's next
679 // sibling (when aAllowCrossShadowBoundary is true), or grandpa's next
684 template <typename NodeType
>
685 nsIContent
* ContentIteratorBase
<NodeType
>::GetNextSibling(
686 nsINode
* aNode
, bool aAllowCrossShadowBoundary
) {
687 if (NS_WARN_IF(!aNode
)) {
691 if (nsIContent
* next
= aNode
->GetNextSibling()) {
696 IteratorHelpers::GetParentNode(*aNode
, aAllowCrossShadowBoundary
);
697 if (NS_WARN_IF(!parent
)) {
701 if (aAllowCrossShadowBoundary
) {
702 // This is temporary solution.
703 // For shadow root, instead of getting to the sibling of the parent
704 // directly, we need to get into the light tree of the parent to handle
706 if (ShadowRoot
* shadowRoot
= ShadowRoot::FromNode(aNode
)) {
707 if (nsIContent
* child
= parent
->GetFirstChild()) {
713 return ContentIteratorBase::GetNextSibling(parent
, aAllowCrossShadowBoundary
);
716 // Get the prev sibling, or parent's prev sibling, or shadow host's prev sibling
717 // (when aAllowCrossShadowBoundary is true), or grandpa's prev sibling... static
718 template <typename NodeType
>
719 nsIContent
* ContentIteratorBase
<NodeType
>::GetPrevSibling(
720 nsINode
* aNode
, bool aAllowCrossShadowBoundary
) {
721 if (NS_WARN_IF(!aNode
)) {
725 if (nsIContent
* prev
= aNode
->GetPreviousSibling()) {
730 IteratorHelpers::GetParentNode(*aNode
, aAllowCrossShadowBoundary
);
731 if (NS_WARN_IF(!parent
)) {
735 return ContentIteratorBase::GetPrevSibling(parent
, aAllowCrossShadowBoundary
);
738 template <typename NodeType
>
739 nsINode
* ContentIteratorBase
<NodeType
>::NextNode(nsINode
* aNode
) {
740 nsINode
* node
= aNode
;
742 // if we are a Pre-order iterator, use pre-order
743 if (mOrder
== Order::Pre
) {
744 // if it has children then next node is first child
745 if (node
->HasChildren()) {
746 nsIContent
* firstChild
= node
->GetFirstChild();
747 MOZ_ASSERT(firstChild
);
752 // else next sibling is next
753 return ContentIteratorBase::GetNextSibling(node
);
757 nsINode
* parent
= node
->GetParentNode();
758 if (NS_WARN_IF(!parent
)) {
759 MOZ_ASSERT(parent
, "The node is the root node but not the last node");
764 if (nsIContent
* sibling
= node
->GetNextSibling()) {
765 // next node is sibling's "deep left" child
766 return ContentIteratorBase::GetDeepFirstChild(sibling
);
772 template <typename NodeType
>
773 nsINode
* ContentIteratorBase
<NodeType
>::PrevNode(nsINode
* aNode
) {
774 nsINode
* node
= aNode
;
776 // if we are a Pre-order iterator, use pre-order
777 if (mOrder
== Order::Pre
) {
778 nsINode
* parent
= node
->GetParentNode();
779 if (NS_WARN_IF(!parent
)) {
780 MOZ_ASSERT(parent
, "The node is the root node but not the first node");
785 nsIContent
* sibling
= node
->GetPreviousSibling();
787 return ContentIteratorBase::GetDeepLastChild(sibling
);
794 if (node
->HasChildren()) {
795 return node
->GetLastChild();
798 // else prev sibling is previous
799 return ContentIteratorBase::GetPrevSibling(node
);
802 /******************************************************
803 * ContentIteratorBase routines
804 ******************************************************/
806 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, First
);
808 template <typename NodeType
>
809 void ContentIteratorBase
<NodeType
>::First() {
811 MOZ_ASSERT(IsDone());
816 mozilla::DebugOnly
<nsresult
> rv
= PositionAt(mFirst
);
817 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to position iterator!");
820 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, Last
);
822 template <typename NodeType
>
823 void ContentIteratorBase
<NodeType
>::Last() {
824 // Note that mLast can be nullptr if SetEmpty() is called in Init()
825 // since at that time, Init() returns NS_OK.
827 MOZ_ASSERT(IsDone());
832 mozilla::DebugOnly
<nsresult
> rv
= PositionAt(mLast
);
833 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to position iterator!");
836 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, Next
);
838 template <typename NodeType
>
839 void ContentIteratorBase
<NodeType
>::Next() {
844 if (mCurNode
== mLast
) {
849 mCurNode
= NextNode(mCurNode
);
852 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(void, Prev
);
854 template <typename NodeType
>
855 void ContentIteratorBase
<NodeType
>::Prev() {
860 if (mCurNode
== mFirst
) {
865 mCurNode
= PrevNode(mCurNode
);
868 // Keeping arrays of indexes for the stack of nodes makes PositionAt
870 NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult
, PositionAt
, nsINode
*);
872 template <typename NodeType
>
873 nsresult ContentIteratorBase
<NodeType
>::PositionAt(nsINode
* aCurNode
) {
874 if (NS_WARN_IF(!aCurNode
)) {
875 return NS_ERROR_NULL_POINTER
;
878 // take an early out if this doesn't actually change the position
879 if (mCurNode
== aCurNode
) {
884 // Check to see if the node falls within the traversal range.
886 RawRangeBoundary
first(mFirst
, 0u);
887 RawRangeBoundary
last(mLast
, 0u);
889 if (mFirst
&& mLast
) {
890 if (mOrder
== Order::Pre
) {
891 // In pre we want to record the point immediately before mFirst, which is
892 // the point immediately after mFirst's previous sibling.
893 first
= {mFirst
->GetParentNode(), mFirst
->GetPreviousSibling()};
895 // If mLast has no children, then we want to make sure to include it.
896 if (!mLast
->HasChildren()) {
897 last
= {mLast
->GetParentNode(), mLast
->AsContent()};
900 // If the first node has any children, we want to be immediately after the
901 // last. Otherwise we want to be immediately before mFirst.
902 if (mFirst
->HasChildren()) {
903 first
= {mFirst
, mFirst
->GetLastChild()};
905 first
= {mFirst
->GetParentNode(), mFirst
->GetPreviousSibling()};
908 // Set the last point immediately after the final node.
909 last
= {mLast
->GetParentNode(), mLast
->AsContent()};
913 NS_WARNING_ASSERTION(first
.IsSetAndValid(), "first is not valid");
914 NS_WARNING_ASSERTION(last
.IsSetAndValid(), "last is not valid");
916 // The end positions are always in the range even if it has no parent. We
917 // need to allow that or 'iter->Init(root)' would assert in Last() or First()
918 // for example, bug 327694.
919 if (mFirst
!= mCurNode
&& mLast
!= mCurNode
&&
920 (NS_WARN_IF(!first
.IsSet()) || NS_WARN_IF(!last
.IsSet()) ||
921 NS_WARN_IF(!NodeIsInTraversalRange(mCurNode
, mOrder
== Order::Pre
, first
,
924 return NS_ERROR_FAILURE
;
930 /******************************************************
931 * ContentSubtreeIterator init routines
932 ******************************************************/
934 nsresult
ContentSubtreeIterator::Init(nsINode
* aRoot
) {
935 return NS_ERROR_NOT_IMPLEMENTED
;
938 nsresult
ContentSubtreeIterator::Init(AbstractRange
* aRange
) {
941 if (NS_WARN_IF(!aRange
->IsPositioned())) {
942 return NS_ERROR_INVALID_ARG
;
947 return InitWithRange();
950 nsresult
ContentSubtreeIterator::Init(nsINode
* aStartContainer
,
951 uint32_t aStartOffset
,
952 nsINode
* aEndContainer
,
953 uint32_t aEndOffset
) {
954 return Init(RawRangeBoundary(aStartContainer
, aStartOffset
),
955 RawRangeBoundary(aEndContainer
, aEndOffset
));
958 nsresult
ContentSubtreeIterator::Init(const RawRangeBoundary
& aStartBoundary
,
959 const RawRangeBoundary
& aEndBoundary
) {
960 RefPtr
<nsRange
> range
=
961 nsRange::Create(aStartBoundary
, aEndBoundary
, IgnoreErrors());
962 if (NS_WARN_IF(!range
) || NS_WARN_IF(!range
->IsPositioned())) {
963 return NS_ERROR_INVALID_ARG
;
966 if (NS_WARN_IF(range
->StartRef() != aStartBoundary
) ||
967 NS_WARN_IF(range
->EndRef() != aEndBoundary
)) {
968 return NS_ERROR_UNEXPECTED
;
971 mRange
= std::move(range
);
973 return InitWithRange();
976 nsresult
ContentSubtreeIterator::InitWithAllowCrossShadowBoundary(
977 AbstractRange
* aRange
) {
980 if (NS_WARN_IF(!aRange
->IsPositioned())) {
981 return NS_ERROR_INVALID_ARG
;
986 mAllowCrossShadowBoundary
= AllowRangeCrossShadowBoundary::Yes
;
987 return InitWithRange();
990 void ContentSubtreeIterator::CacheInclusiveAncestorsOfEndContainer() {
991 mInclusiveAncestorsOfEndContainer
.Clear();
992 nsINode
* const endContainer
=
993 IteratorHelpers::GetEndContainer(mRange
, IterAllowCrossShadowBoundary());
994 nsIContent
* endNode
=
995 endContainer
->IsContent() ? endContainer
->AsContent() : nullptr;
997 mInclusiveAncestorsOfEndContainer
.AppendElement(endNode
);
998 // Cross the boundary for contents in shadow tree.
999 nsINode
* parent
= IteratorHelpers::GetParentNode(
1000 *endNode
, IterAllowCrossShadowBoundary());
1001 if (!parent
|| !parent
->IsContent()) {
1004 endNode
= parent
->AsContent();
1008 nsIContent
* ContentSubtreeIterator::DetermineCandidateForFirstContent() const {
1009 nsINode
* startContainer
= IteratorHelpers::GetStartContainer(
1010 mRange
, IterAllowCrossShadowBoundary());
1011 nsIContent
* firstCandidate
= nullptr;
1012 // find first node in range
1013 nsINode
* node
= nullptr;
1014 if (!startContainer
->GetChildCount()) {
1015 // no children, start at the node itself
1016 node
= startContainer
;
1019 IterAllowCrossShadowBoundary()
1020 ? mRange
->GetMayCrossShadowBoundaryChildAtStartOffset()
1021 : mRange
->GetChildAtStartOffset();
1023 MOZ_ASSERT(child
== startContainer
->GetChildAt_Deprecated(
1024 IteratorHelpers::StartOffset(
1025 mRange
, IterAllowCrossShadowBoundary())));
1027 // offset after last child
1028 node
= startContainer
;
1030 firstCandidate
= child
;
1034 if (!firstCandidate
) {
1035 // then firstCandidate is next node after node
1036 firstCandidate
= ContentIteratorBase::GetNextSibling(
1037 node
, IterAllowCrossShadowBoundary());
1040 if (firstCandidate
) {
1041 firstCandidate
= ContentIteratorBase::GetDeepFirstChild(
1042 firstCandidate
, IterAllowCrossShadowBoundary());
1045 return firstCandidate
;
1048 nsIContent
* ContentSubtreeIterator::DetermineFirstContent() const {
1049 nsIContent
* firstCandidate
= DetermineCandidateForFirstContent();
1050 if (!firstCandidate
) {
1054 // confirm that this first possible contained node is indeed contained. Else
1055 // we have a range that does not fully contain any node.
1056 const Maybe
<bool> isNodeContainedInRange
=
1057 RangeUtils::IsNodeContainedInRange(*firstCandidate
, mRange
);
1058 MOZ_ALWAYS_TRUE(isNodeContainedInRange
);
1059 if (!isNodeContainedInRange
.value()) {
1063 // cool, we have the first node in the range. Now we walk up its ancestors
1064 // to find the most senior that is still in the range. That's the real first
1066 return GetTopAncestorInRange(firstCandidate
);
1069 nsIContent
* ContentSubtreeIterator::DetermineCandidateForLastContent() const {
1070 nsIContent
* lastCandidate
{nullptr};
1071 nsINode
* endContainer
=
1072 IteratorHelpers::GetEndContainer(mRange
, IterAllowCrossShadowBoundary());
1073 // now to find the last node
1075 IteratorHelpers::EndOffset(mRange
, IterAllowCrossShadowBoundary());
1077 int32_t numChildren
= endContainer
->GetChildCount();
1079 nsINode
* node
= nullptr;
1080 if (offset
> numChildren
) {
1081 // Can happen for text nodes
1082 offset
= numChildren
;
1084 if (!offset
|| !numChildren
) {
1085 node
= endContainer
;
1087 lastCandidate
= IterAllowCrossShadowBoundary()
1088 ? mRange
->MayCrossShadowBoundaryEndRef().Ref()
1089 : mRange
->EndRef().Ref();
1090 MOZ_ASSERT(lastCandidate
== endContainer
->GetChildAt_Deprecated(--offset
));
1091 NS_ASSERTION(lastCandidate
,
1092 "tree traversal trouble in ContentSubtreeIterator::Init");
1095 if (!lastCandidate
) {
1096 // then lastCandidate is prev node before node
1097 lastCandidate
= ContentIteratorBase::GetPrevSibling(
1098 node
, IterAllowCrossShadowBoundary());
1101 if (lastCandidate
) {
1102 lastCandidate
= ContentIteratorBase::GetDeepLastChild(
1103 lastCandidate
, IterAllowCrossShadowBoundary());
1106 return lastCandidate
;
1109 nsresult
ContentSubtreeIterator::InitWithRange() {
1111 MOZ_ASSERT(mRange
->IsPositioned());
1113 // get the start node and offset, convert to nsINode
1114 mClosestCommonInclusiveAncestor
=
1115 mRange
->GetClosestCommonInclusiveAncestor(mAllowCrossShadowBoundary
);
1117 nsINode
* startContainer
= IteratorHelpers::GetStartContainer(
1118 mRange
, IterAllowCrossShadowBoundary());
1119 const int32_t startOffset
=
1120 IteratorHelpers::StartOffset(mRange
, IterAllowCrossShadowBoundary());
1121 nsINode
* endContainer
=
1122 IteratorHelpers::GetEndContainer(mRange
, IterAllowCrossShadowBoundary());
1123 const int32_t endOffset
=
1124 IteratorHelpers::EndOffset(mRange
, IterAllowCrossShadowBoundary());
1125 MOZ_ASSERT(mClosestCommonInclusiveAncestor
&& startContainer
&& endContainer
);
1127 MOZ_ASSERT(uint32_t(startOffset
) <= startContainer
->Length() &&
1128 uint32_t(endOffset
) <= endContainer
->Length());
1130 // short circuit when start node == end node
1131 if (startContainer
== endContainer
) {
1132 nsINode
* child
= startContainer
->GetFirstChild();
1134 if (!child
|| startOffset
== endOffset
) {
1135 // Text node, empty container, or collapsed
1141 CacheInclusiveAncestorsOfEndContainer();
1143 mFirst
= DetermineFirstContent();
1149 mLast
= DetermineLastContent();
1160 nsIContent
* ContentSubtreeIterator::DetermineLastContent() const {
1161 nsIContent
* lastCandidate
= DetermineCandidateForLastContent();
1162 if (!lastCandidate
) {
1166 // confirm that this last possible contained node is indeed contained. Else
1167 // we have a range that does not fully contain any node.
1169 const Maybe
<bool> isNodeContainedInRange
=
1170 RangeUtils::IsNodeContainedInRange(*lastCandidate
, mRange
);
1171 MOZ_ALWAYS_TRUE(isNodeContainedInRange
);
1172 if (!isNodeContainedInRange
.value()) {
1176 // cool, we have the last node in the range. Now we walk up its ancestors to
1177 // find the most senior that is still in the range. That's the real first
1179 return GetTopAncestorInRange(lastCandidate
);
1182 /****************************************************************
1183 * ContentSubtreeIterator overrides of ContentIterator routines
1184 ****************************************************************/
1186 // we can't call PositionAt in a subtree iterator...
1187 void ContentSubtreeIterator::First() { mCurNode
= mFirst
; }
1189 // we can't call PositionAt in a subtree iterator...
1190 void ContentSubtreeIterator::Last() { mCurNode
= mLast
; }
1192 void ContentSubtreeIterator::Next() {
1197 if (mCurNode
== mLast
) {
1202 nsINode
* nextNode
= ContentIteratorBase::GetNextSibling(
1203 mCurNode
, IterAllowCrossShadowBoundary());
1205 MOZ_ASSERT(nextNode
, "No next sibling!?! This could mean deadlock!");
1207 int32_t i
= mInclusiveAncestorsOfEndContainer
.IndexOf(nextNode
);
1209 // as long as we are finding ancestors of the endpoint of the range,
1210 // dive down into their children
1211 ShadowRoot
* root
= IteratorHelpers::GetShadowRoot(
1212 Element::FromNode(nextNode
), IterAllowCrossShadowBoundary());
1214 nextNode
= nextNode
->GetFirstChild();
1216 nextNode
= mRange
->MayCrossShadowBoundary() ? root
->GetFirstChild()
1217 : nextNode
->GetFirstChild();
1219 MOZ_ASSERT(nextNode
, "Iterator error, expected a child node!");
1221 // should be impossible to get a null pointer. If we went all the way
1222 // down the child chain to the bottom without finding an interior node,
1223 // then the previous node should have been the last, which was
1224 // was tested at top of routine.
1225 i
= mInclusiveAncestorsOfEndContainer
.IndexOf(nextNode
);
1228 mCurNode
= nextNode
;
1231 void ContentSubtreeIterator::Prev() {
1232 // Prev should be optimized to use the mStartNodes, just as Next
1233 // uses mInclusiveAncestorsOfEndContainer.
1238 if (mCurNode
== mFirst
) {
1243 // If any of these function calls return null, so will all succeeding ones,
1244 // so mCurNode will wind up set to null.
1245 nsINode
* prevNode
= ContentIteratorBase::GetDeepFirstChild(mCurNode
);
1247 prevNode
= PrevNode(prevNode
);
1249 prevNode
= ContentIteratorBase::GetDeepLastChild(prevNode
);
1251 mCurNode
= GetTopAncestorInRange(prevNode
);
1254 nsresult
ContentSubtreeIterator::PositionAt(nsINode
* aCurNode
) {
1255 NS_ERROR("Not implemented!");
1256 return NS_ERROR_NOT_IMPLEMENTED
;
1259 /****************************************************************
1260 * ContentSubtreeIterator helper routines
1261 ****************************************************************/
1263 nsIContent
* ContentSubtreeIterator::GetTopAncestorInRange(
1264 nsINode
* aNode
) const {
1266 !IteratorHelpers::GetParentNode(*aNode
, IterAllowCrossShadowBoundary())) {
1270 // aNode has a parent, so it must be content.
1271 nsIContent
* content
= aNode
->AsContent();
1273 // sanity check: aNode is itself in the range
1274 Maybe
<bool> isNodeContainedInRange
=
1275 RangeUtils::IsNodeContainedInRange(*aNode
, mRange
);
1276 NS_ASSERTION(isNodeContainedInRange
&& isNodeContainedInRange
.value(),
1277 "aNode isn't in mRange, or something else weird happened");
1278 if (!isNodeContainedInRange
|| !isNodeContainedInRange
.value()) {
1282 nsIContent
* lastContentInShadowTree
= nullptr;
1284 nsINode
* parent
= IteratorHelpers::GetParentNode(
1285 *content
, IterAllowCrossShadowBoundary());
1287 // content always has a parent. If its parent is the root, however --
1288 // i.e., either it's not content, or it is content but its own parent is
1289 // null -- then we're finished, since we don't go up to the root.
1291 // Caveat: If iteration crossing shadow boundary is allowed
1292 // and the root is a shadow root, we keep going up to the
1293 // shadow host and continue.
1295 // We have to special-case this because CompareNodeToRange treats the root
1296 // node differently -- see bug 765205.
1297 if (!parent
|| !IteratorHelpers::GetParentNode(
1298 *parent
, IterAllowCrossShadowBoundary())) {
1302 isNodeContainedInRange
=
1303 RangeUtils::IsNodeContainedInRange(*parent
, mRange
);
1304 MOZ_ALWAYS_TRUE(isNodeContainedInRange
);
1305 if (!isNodeContainedInRange
.value()) {
1306 if (IterAllowCrossShadowBoundary() && content
->IsShadowRoot()) {
1307 MOZ_ASSERT(parent
->GetShadowRoot() == content
);
1308 // host element is not in range, the last content in tree
1309 // should be the ancestor.
1310 MOZ_ASSERT(lastContentInShadowTree
);
1311 return lastContentInShadowTree
;
1316 // When we cross the boundary, we keep a reference to the
1317 // last content that is in tree, because if we later
1318 // find the shadow host element is not in the range, that means
1319 // the last content in the tree should be top ancestor in range.
1321 // Using shadow root doesn't make sense here because it doesn't
1322 // represent a actual content.
1323 if (IterAllowCrossShadowBoundary() && parent
->IsShadowRoot()) {
1324 lastContentInShadowTree
= content
;
1327 content
= parent
->AsContent();
1330 MOZ_CRASH("This should only be possible if aNode was null");
1333 #undef NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD
1335 } // namespace mozilla