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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ChildIterator.h"
8 #include "nsContentUtils.h"
9 #include "mozilla/dom/Document.h"
10 #include "mozilla/dom/HTMLSlotElement.h"
11 #include "mozilla/dom/ShadowRoot.h"
12 #include "nsIAnonymousContentCreator.h"
14 #include "nsCSSAnonBoxes.h"
15 #include "nsLayoutUtils.h"
17 namespace mozilla::dom
{
19 FlattenedChildIterator::FlattenedChildIterator(const nsIContent
* aParent
,
20 bool aStartAtBeginning
)
21 : mParent(aParent
), mOriginalParent(aParent
), mIsFirst(aStartAtBeginning
) {
22 if (!mParent
->IsElement()) {
23 // TODO(emilio): I think it probably makes sense to only allow constructing
24 // FlattenedChildIterators with Element.
28 if (ShadowRoot
* shadow
= mParent
->AsElement()->GetShadowRoot()) {
30 mShadowDOMInvolved
= true;
34 if (const auto* slot
= HTMLSlotElement::FromNode(mParent
)) {
35 if (!slot
->AssignedNodes().IsEmpty()) {
37 if (!aStartAtBeginning
) {
38 mIndexInInserted
= slot
->AssignedNodes().Length();
40 mShadowDOMInvolved
= true;
45 nsIContent
* FlattenedChildIterator::GetNextChild() {
46 // If we're already in the inserted-children array, look there first
48 const nsTArray
<RefPtr
<nsINode
>>& assignedNodes
=
49 mParentAsSlot
->AssignedNodes();
52 MOZ_ASSERT(mIndexInInserted
== 0);
53 mChild
= assignedNodes
[0]->AsContent();
56 MOZ_ASSERT(mIndexInInserted
<= assignedNodes
.Length());
57 if (mIndexInInserted
+ 1 >= assignedNodes
.Length()) {
58 mIndexInInserted
= assignedNodes
.Length();
61 mChild
= assignedNodes
[++mIndexInInserted
]->AsContent();
65 if (mIsFirst
) { // at the beginning of the child list
66 mChild
= mParent
->GetFirstChild();
68 } else if (mChild
) { // in the middle of the child list
69 mChild
= mChild
->GetNextSibling();
75 bool FlattenedChildIterator::Seek(const nsIContent
* aChildToFind
) {
76 if (!mParentAsSlot
&& aChildToFind
->GetParent() == mParent
&&
77 !aChildToFind
->IsRootOfNativeAnonymousSubtree()) {
78 // Fast path: just point ourselves to aChildToFind, which is a
79 // normal DOM child of ours.
80 mChild
= const_cast<nsIContent
*>(aChildToFind
);
86 // Can we add more fast paths here based on whether the parent of aChildToFind
87 // is a This version can take shortcuts that the two-argument version
88 // can't, so can be faster (and in fact cshadow insertion point or content
91 // It would be nice to assert that we find aChildToFind, but bz thinks that
92 // we might not find aChildToFind when called from ContentInserted
93 // if first-letter frames are about.
94 while (nsIContent
* child
= GetNextChild()) {
95 if (child
== aChildToFind
) {
103 nsIContent
* FlattenedChildIterator::GetPreviousChild() {
104 if (mIsFirst
) { // at the beginning of the child list
108 const nsTArray
<RefPtr
<nsINode
>>& assignedNodes
=
109 mParentAsSlot
->AssignedNodes();
110 MOZ_ASSERT(mIndexInInserted
<= assignedNodes
.Length());
111 if (mIndexInInserted
== 0) {
115 mChild
= assignedNodes
[--mIndexInInserted
]->AsContent();
118 if (mChild
) { // in the middle of the child list
119 mChild
= mChild
->GetPreviousSibling();
120 } else { // at the end of the child list
121 mChild
= mParent
->GetLastChild();
130 nsIContent
* AllChildrenIterator::Get() const {
133 Element
* marker
= nsLayoutUtils::GetMarkerPseudo(Parent());
134 MOZ_ASSERT(marker
, "No content marker frame at eAtMarkerKid phase");
139 Element
* before
= nsLayoutUtils::GetBeforePseudo(Parent());
140 MOZ_ASSERT(before
, "No content before frame at eAtBeforeKid phase");
144 case eAtFlatTreeKids
:
145 return FlattenedChildIterator::Get();
148 return mAnonKids
[mAnonKidsIdx
];
151 Element
* after
= nsLayoutUtils::GetAfterPseudo(Parent());
152 MOZ_ASSERT(after
, "No content after frame at eAtAfterKid phase");
161 bool AllChildrenIterator::Seek(const nsIContent
* aChildToFind
) {
162 if (mPhase
== eAtBegin
|| mPhase
== eAtMarkerKid
) {
163 Element
* markerPseudo
= nsLayoutUtils::GetMarkerPseudo(Parent());
164 if (markerPseudo
&& markerPseudo
== aChildToFind
) {
165 mPhase
= eAtMarkerKid
;
168 mPhase
= eAtBeforeKid
;
170 if (mPhase
== eAtBeforeKid
) {
171 Element
* beforePseudo
= nsLayoutUtils::GetBeforePseudo(Parent());
172 if (beforePseudo
&& beforePseudo
== aChildToFind
) {
175 mPhase
= eAtFlatTreeKids
;
178 if (mPhase
== eAtFlatTreeKids
) {
179 if (FlattenedChildIterator::Seek(aChildToFind
)) {
182 mPhase
= eAtAnonKids
;
185 nsIContent
* child
= nullptr;
187 child
= GetNextChild();
188 } while (child
&& child
!= aChildToFind
);
190 return child
== aChildToFind
;
193 void AllChildrenIterator::AppendNativeAnonymousChildren() {
194 nsContentUtils::AppendNativeAnonymousChildren(Parent(), mAnonKids
, mFlags
);
197 nsIContent
* AllChildrenIterator::GetNextChild() {
198 if (mPhase
== eAtBegin
) {
199 mPhase
= eAtMarkerKid
;
200 if (Element
* markerContent
= nsLayoutUtils::GetMarkerPseudo(Parent())) {
201 return markerContent
;
205 if (mPhase
== eAtMarkerKid
) {
206 mPhase
= eAtBeforeKid
;
207 if (Element
* beforeContent
= nsLayoutUtils::GetBeforePseudo(Parent())) {
208 return beforeContent
;
212 if (mPhase
== eAtBeforeKid
) {
213 // Advance into our explicit kids.
214 mPhase
= eAtFlatTreeKids
;
217 if (mPhase
== eAtFlatTreeKids
) {
218 if (nsIContent
* kid
= FlattenedChildIterator::GetNextChild()) {
221 mPhase
= eAtAnonKids
;
224 if (mPhase
== eAtAnonKids
) {
225 if (mAnonKids
.IsEmpty()) {
226 MOZ_ASSERT(mAnonKidsIdx
== UINT32_MAX
);
227 AppendNativeAnonymousChildren();
230 if (mAnonKidsIdx
== UINT32_MAX
) {
237 if (mAnonKidsIdx
< mAnonKids
.Length()) {
238 return mAnonKids
[mAnonKidsIdx
];
241 mPhase
= eAtAfterKid
;
242 if (Element
* afterContent
= nsLayoutUtils::GetAfterPseudo(Parent())) {
251 nsIContent
* AllChildrenIterator::GetPreviousChild() {
252 if (mPhase
== eAtEnd
) {
253 MOZ_ASSERT(mAnonKidsIdx
== mAnonKids
.Length());
254 mPhase
= eAtAnonKids
;
255 Element
* afterContent
= nsLayoutUtils::GetAfterPseudo(Parent());
257 mPhase
= eAtAfterKid
;
262 if (mPhase
== eAtAfterKid
) {
263 mPhase
= eAtAnonKids
;
266 if (mPhase
== eAtAnonKids
) {
267 if (mAnonKids
.IsEmpty()) {
268 AppendNativeAnonymousChildren();
269 mAnonKidsIdx
= mAnonKids
.Length();
272 // If 0 then it turns into UINT32_MAX, which indicates the iterator is
273 // before the anonymous children.
275 if (mAnonKidsIdx
< mAnonKids
.Length()) {
276 return mAnonKids
[mAnonKidsIdx
];
278 mPhase
= eAtFlatTreeKids
;
281 if (mPhase
== eAtFlatTreeKids
) {
282 if (nsIContent
* kid
= FlattenedChildIterator::GetPreviousChild()) {
286 Element
* beforeContent
= nsLayoutUtils::GetBeforePseudo(Parent());
288 mPhase
= eAtBeforeKid
;
289 return beforeContent
;
293 if (mPhase
== eAtFlatTreeKids
|| mPhase
== eAtBeforeKid
) {
294 Element
* markerContent
= nsLayoutUtils::GetMarkerPseudo(Parent());
296 mPhase
= eAtMarkerKid
;
297 return markerContent
;
305 } // namespace mozilla::dom