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 #ifndef ChildIterator_h
8 #define ChildIterator_h
10 #include "nsIContent.h"
11 #include "nsIContentInlines.h"
15 * Iterates over the children on a node. If a child is an insertion point,
16 * iterates over the children inserted there instead, or the default content
17 * if no children are inserted there.
19 * The FlattenedChildIterator expands any anonymous content bound from an XBL
20 * binding's <xbl:content> element.
28 // This class iterates normal DOM child nodes of a given DOM node with
29 // <xbl:children> nodes replaced by the elements that have been filtered into
30 // that insertion point. Any bindings on the given element are ignored for
31 // purposes of determining which insertion point children are filtered into. The
32 // iterator can be initialized to start at the end by providing false for
33 // aStartAtBeginning in order to start iterating in reverse from the last child.
34 class ExplicitChildIterator
{
36 explicit ExplicitChildIterator(const nsIContent
* aParent
,
37 bool aStartAtBeginning
= true);
39 ExplicitChildIterator(const ExplicitChildIterator
& aOther
)
40 : mParent(aOther
.mParent
),
41 mParentAsSlot(aOther
.mParentAsSlot
),
42 mChild(aOther
.mChild
),
43 mDefaultChild(aOther
.mDefaultChild
),
44 mIsFirst(aOther
.mIsFirst
),
45 mIndexInInserted(aOther
.mIndexInInserted
) {}
47 ExplicitChildIterator(ExplicitChildIterator
&& aOther
)
48 : mParent(aOther
.mParent
),
49 mParentAsSlot(aOther
.mParentAsSlot
),
50 mChild(aOther
.mChild
),
51 mDefaultChild(aOther
.mDefaultChild
),
52 mIsFirst(aOther
.mIsFirst
),
53 mIndexInInserted(aOther
.mIndexInInserted
) {}
55 nsIContent
* GetNextChild();
57 // Looks for aChildToFind respecting insertion points until aChildToFind is
58 // found. This version can take shortcuts that the two-argument version
59 // can't, so can be faster (and in fact can be O(1) instead of O(N) in many
61 bool Seek(const nsIContent
* aChildToFind
);
63 // Looks for aChildToFind respecting insertion points until aChildToFind is
64 // found. or aBound is found. If aBound is nullptr then the seek is unbounded.
65 // Returns whether aChildToFind was found as an explicit child prior to
66 // encountering aBound.
67 bool Seek(const nsIContent
* aChildToFind
, nsIContent
* aBound
) {
68 // It would be nice to assert that we find aChildToFind, but bz thinks that
69 // we might not find aChildToFind when called from ContentInserted
70 // if first-letter frames are about.
72 // We can't easily take shortcuts here because we'd have to have a way to
73 // compare aChildToFind to aBound.
76 child
= GetNextChild();
77 } while (child
&& child
!= aChildToFind
&& child
!= aBound
);
79 return child
== aChildToFind
;
82 // Returns the current target of this iterator (which might be an explicit
83 // child of the node, fallback content of an insertion point or
84 // a node distributed to an insertion point.
85 nsIContent
* Get() const;
87 // The inverse of GetNextChild. Properly steps in and out of insertion
89 nsIContent
* GetPreviousChild();
92 // The parent of the children being iterated. For the FlattenedChildIterator,
93 // if there is a binding attached to the original parent, mParent points to
94 // the <xbl:content> element for the binding.
95 const nsIContent
* mParent
;
97 // If parent is a slot element, this points to the parent as HTMLSlotElement,
98 // otherwise, it's null.
99 const HTMLSlotElement
* mParentAsSlot
;
101 // The current child. When we encounter an insertion point,
102 // mChild remains as the insertion point whose content we're iterating (and
103 // our state is controled by mDefaultChild or mIndexInInserted depending on
104 // whether the insertion point expands to its default content or not).
107 // If non-null, this points to the current default content for the current
108 // insertion point that we're iterating (i.e. mChild, which must be an
109 // nsXBLChildrenElement or HTMLContentElement). Once this transitions back
110 // to null, we continue iterating at mChild's next sibling.
111 nsIContent
* mDefaultChild
;
113 // A flag to let us know that we haven't started iterating yet.
116 // If not zero, we're iterating inserted children for an insertion point. This
117 // is an index into mChild's inserted children array (mChild must be an
118 // nsXBLChildrenElement). The index is one past the "current" child (as
119 // opposed to mChild which represents the "current" child).
120 uint32_t mIndexInInserted
;
123 // Iterates over the flattened children of a node, which accounts for anonymous
124 // children and nodes moved by insertion points. If a node has anonymous
125 // children, those are iterated over. The iterator can be initialized to start
126 // at the end by providing false for aStartAtBeginning in order to start
127 // iterating in reverse from the last child.
128 class FlattenedChildIterator
: public ExplicitChildIterator
{
130 explicit FlattenedChildIterator(const nsIContent
* aParent
,
131 bool aStartAtBeginning
= true)
132 : ExplicitChildIterator(aParent
, aStartAtBeginning
),
133 mOriginalContent(aParent
) {
137 FlattenedChildIterator(FlattenedChildIterator
&& aOther
)
138 : ExplicitChildIterator(std::move(aOther
)),
139 mOriginalContent(aOther
.mOriginalContent
),
140 mXBLInvolved(aOther
.mXBLInvolved
) {}
142 FlattenedChildIterator(const FlattenedChildIterator
& aOther
)
143 : ExplicitChildIterator(aOther
),
144 mOriginalContent(aOther
.mOriginalContent
),
145 mXBLInvolved(aOther
.mXBLInvolved
) {}
148 if (mXBLInvolved
.isNothing()) {
149 mXBLInvolved
= Some(ComputeWhetherXBLIsInvolved());
151 return *mXBLInvolved
;
154 const nsIContent
* Parent() const { return mOriginalContent
; }
157 bool ComputeWhetherXBLIsInvolved() const;
159 void Init(bool aIgnoreXBL
);
163 * This constructor is a hack to help AllChildrenIterator which sometimes
164 * doesn't want to consider XBL.
166 FlattenedChildIterator(const nsIContent
* aParent
, uint32_t aFlags
,
167 bool aStartAtBeginning
= true)
168 : ExplicitChildIterator(aParent
, aStartAtBeginning
),
169 mOriginalContent(aParent
) {
170 bool ignoreXBL
= aFlags
& nsIContent::eAllButXBL
;
174 const nsIContent
* mOriginalContent
;
177 // For certain optimizations, nsCSSFrameConstructor needs to know if the child
178 // list of the element that we're iterating matches its .childNodes.
180 // This is lazily computed when asked for it.
181 Maybe
<bool> mXBLInvolved
;
185 * AllChildrenIterator traverses the children of an element including before /
186 * after content and optionally XBL children. The iterator can be initialized
187 * to start at the end by providing false for aStartAtBeginning in order to
188 * start iterating in reverse from the last child.
190 * Note: it assumes that no mutation of the DOM or frame tree takes place during
191 * iteration, and will break horribly if that is not true.
193 class AllChildrenIterator
: private FlattenedChildIterator
{
195 AllChildrenIterator(const nsIContent
* aNode
, uint32_t aFlags
,
196 bool aStartAtBeginning
= true)
197 : FlattenedChildIterator(aNode
, aFlags
, aStartAtBeginning
),
198 mAnonKidsIdx(aStartAtBeginning
? UINT32_MAX
: 0),
200 mPhase(aStartAtBeginning
? eAtBegin
: eAtEnd
) {}
202 AllChildrenIterator(AllChildrenIterator
&& aOther
)
203 : FlattenedChildIterator(std::move(aOther
)),
204 mAnonKids(std::move(aOther
.mAnonKids
)),
205 mAnonKidsIdx(aOther
.mAnonKidsIdx
),
206 mFlags(aOther
.mFlags
),
207 mPhase(aOther
.mPhase
)
210 mMutationGuard(aOther
.mMutationGuard
)
215 AllChildrenIterator
& operator=(AllChildrenIterator
&& aOther
) {
216 this->~AllChildrenIterator();
217 new (this) AllChildrenIterator(std::move(aOther
));
222 ~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard
.Mutated(0)); }
225 // Returns the current target the iterator is at, or null if the iterator
226 // doesn't point to any child node (either eAtBegin or eAtEnd phase).
227 nsIContent
* Get() const;
229 // Seeks the given node in children of a parent element, starting from
230 // the current iterator's position, and sets the iterator at the given child
231 // node if it was found.
232 bool Seek(const nsIContent
* aChildToFind
);
234 nsIContent
* GetNextChild();
235 nsIContent
* GetPreviousChild();
246 IteratorPhase
Phase() const { return mPhase
; }
250 void AppendNativeAnonymousChildren();
252 // mAnonKids is an array of native anonymous children, mAnonKidsIdx is index
253 // in the array. If mAnonKidsIdx < mAnonKids.Length() and mPhase is
254 // eAtAnonKids then the iterator points at a child at mAnonKidsIdx index. If
255 // mAnonKidsIdx == mAnonKids.Length() then the iterator is somewhere after
256 // the last native anon child. If mAnonKidsIdx == UINT32_MAX then the iterator
257 // is somewhere before the first native anon child.
258 nsTArray
<nsIContent
*> mAnonKids
;
259 uint32_t mAnonKidsIdx
;
262 IteratorPhase mPhase
;
264 // XXX we should really assert there are no frame tree changes as well, but
265 // there's no easy way to do that.
266 nsMutationGuard mMutationGuard
;
271 * StyleChildrenIterator traverses the children of the element from the
272 * perspective of the style system, particularly the children we need to
273 * traverse during restyle.
275 * At present, this is identical to AllChildrenIterator with
276 * (eAllChildren | eSkipDocumentLevelNativeAnonymousContent). We used to have
277 * detect and skip any native anonymous children that are used to implement some
278 * special magic in here that went away, but we keep the separate class so
279 * we can reintroduce special magic back if needed.
281 * Note: it assumes that no mutation of the DOM or frame tree takes place during
282 * iteration, and will break horribly if that is not true.
284 * We require this to be memmovable since Rust code can create and move
285 * StyleChildrenIterators.
287 class MOZ_NEEDS_MEMMOVABLE_MEMBERS StyleChildrenIterator
288 : private AllChildrenIterator
{
290 static nsIContent
* GetParent(const nsIContent
& aContent
) {
291 nsINode
* node
= aContent
.GetFlattenedTreeParentNodeForStyle();
292 return node
&& node
->IsContent() ? node
->AsContent() : nullptr;
295 explicit StyleChildrenIterator(const nsIContent
* aContent
,
296 bool aStartAtBeginning
= true)
297 : AllChildrenIterator(
299 nsIContent::eAllChildren
|
300 nsIContent::eSkipDocumentLevelNativeAnonymousContent
,
302 MOZ_COUNT_CTOR(StyleChildrenIterator
);
305 StyleChildrenIterator(StyleChildrenIterator
&& aOther
)
306 : AllChildrenIterator(std::move(aOther
)) {
307 MOZ_COUNT_CTOR(StyleChildrenIterator
);
310 StyleChildrenIterator
& operator=(StyleChildrenIterator
&& aOther
) {
311 AllChildrenIterator::operator=(std::move(aOther
));
315 ~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator
); }
317 using AllChildrenIterator::GetNextChild
;
318 using AllChildrenIterator::GetPreviousChild
;
319 using AllChildrenIterator::Seek
;
323 } // namespace mozilla