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 nsIContent
* GetNextChild();
41 // Looks for aChildToFind respecting insertion points until aChildToFind is
42 // found. This version can take shortcuts that the two-argument version
43 // can't, so can be faster (and in fact can be O(1) instead of O(N) in many
45 bool Seek(const nsIContent
* aChildToFind
);
47 // Looks for aChildToFind respecting insertion points until aChildToFind is
48 // found. or aBound is found. If aBound is nullptr then the seek is unbounded.
49 // Returns whether aChildToFind was found as an explicit child prior to
50 // encountering aBound.
51 bool Seek(const nsIContent
* aChildToFind
, nsIContent
* aBound
) {
52 // It would be nice to assert that we find aChildToFind, but bz thinks that
53 // we might not find aChildToFind when called from ContentInserted
54 // if first-letter frames are about.
56 // We can't easily take shortcuts here because we'd have to have a way to
57 // compare aChildToFind to aBound.
60 child
= GetNextChild();
61 } while (child
&& child
!= aChildToFind
&& child
!= aBound
);
63 return child
== aChildToFind
;
66 // Returns the current target of this iterator (which might be an explicit
67 // child of the node, fallback content of an insertion point or
68 // a node distributed to an insertion point.
69 nsIContent
* Get() const;
71 // The inverse of GetNextChild. Properly steps in and out of insertion
73 nsIContent
* GetPreviousChild();
76 // The parent of the children being iterated. For the FlattenedChildIterator,
77 // if there is a binding attached to the original parent, mParent points to
78 // the <xbl:content> element for the binding.
79 const nsIContent
* mParent
;
81 // If parent is a slot element, this points to the parent as HTMLSlotElement,
82 // otherwise, it's null.
83 const HTMLSlotElement
* mParentAsSlot
;
85 // The current child. When we encounter an insertion point,
86 // mChild remains as the insertion point whose content we're iterating (and
87 // our state is controled by mDefaultChild or mIndexInInserted depending on
88 // whether the insertion point expands to its default content or not).
91 // If non-null, this points to the current default content for the current
92 // insertion point that we're iterating (i.e. mChild, which must be an
93 // nsXBLChildrenElement or HTMLContentElement). Once this transitions back
94 // to null, we continue iterating at mChild's next sibling.
95 nsIContent
* mDefaultChild
;
97 // A flag to let us know that we haven't started iterating yet.
100 // If not zero, we're iterating inserted children for an insertion point. This
101 // is an index into mChild's inserted children array (mChild must be an
102 // nsXBLChildrenElement). The index is one past the "current" child (as
103 // opposed to mChild which represents the "current" child).
104 uint32_t mIndexInInserted
;
107 // Iterates over the flattened children of a node, which accounts for anonymous
108 // children and nodes moved by insertion points. If a node has anonymous
109 // children, those are iterated over. The iterator can be initialized to start
110 // at the end by providing false for aStartAtBeginning in order to start
111 // iterating in reverse from the last child.
112 class FlattenedChildIterator
: public ExplicitChildIterator
{
114 explicit FlattenedChildIterator(const nsIContent
* aParent
,
115 bool aStartAtBeginning
= true)
116 : ExplicitChildIterator(aParent
, aStartAtBeginning
),
117 mOriginalContent(aParent
) {
121 bool ShadowDOMInvolved() { return mShadowDOMInvolved
; }
123 const nsIContent
* Parent() const { return mOriginalContent
; }
126 void Init(bool aIgnoreXBL
);
130 * This constructor is a hack to help AllChildrenIterator which sometimes
131 * doesn't want to consider XBL.
133 FlattenedChildIterator(const nsIContent
* aParent
, uint32_t aFlags
,
134 bool aStartAtBeginning
= true)
135 : ExplicitChildIterator(aParent
, aStartAtBeginning
),
136 mOriginalContent(aParent
) {
137 bool ignoreXBL
= aFlags
& nsIContent::eAllButXBL
;
141 const nsIContent
* mOriginalContent
;
144 // For certain optimizations, nsCSSFrameConstructor needs to know if the child
145 // list of the element that we're iterating matches its .childNodes.
146 bool mShadowDOMInvolved
= false;
150 * AllChildrenIterator traverses the children of an element including before /
151 * after content and optionally XBL children. The iterator can be initialized
152 * to start at the end by providing false for aStartAtBeginning in order to
153 * start iterating in reverse from the last child.
155 * Note: it assumes that no mutation of the DOM or frame tree takes place during
156 * iteration, and will break horribly if that is not true.
158 class AllChildrenIterator
: private FlattenedChildIterator
{
160 AllChildrenIterator(const nsIContent
* aNode
, uint32_t aFlags
,
161 bool aStartAtBeginning
= true)
162 : FlattenedChildIterator(aNode
, aFlags
, aStartAtBeginning
),
163 mAnonKidsIdx(aStartAtBeginning
? UINT32_MAX
: 0),
165 mPhase(aStartAtBeginning
? eAtBegin
: eAtEnd
) {}
168 AllChildrenIterator(AllChildrenIterator
&&) = default;
170 AllChildrenIterator
& operator=(AllChildrenIterator
&& aOther
) {
171 // Explicitly call the destructor to ensure the assertion in the destructor
173 this->~AllChildrenIterator();
174 new (this) AllChildrenIterator(std::move(aOther
));
178 ~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard
.Mutated(0)); }
181 // Returns the current target the iterator is at, or null if the iterator
182 // doesn't point to any child node (either eAtBegin or eAtEnd phase).
183 nsIContent
* Get() const;
185 // Seeks the given node in children of a parent element, starting from
186 // the current iterator's position, and sets the iterator at the given child
187 // node if it was found.
188 bool Seek(const nsIContent
* aChildToFind
);
190 nsIContent
* GetNextChild();
191 nsIContent
* GetPreviousChild();
202 IteratorPhase
Phase() const { return mPhase
; }
206 void AppendNativeAnonymousChildren();
208 // mAnonKids is an array of native anonymous children, mAnonKidsIdx is index
209 // in the array. If mAnonKidsIdx < mAnonKids.Length() and mPhase is
210 // eAtAnonKids then the iterator points at a child at mAnonKidsIdx index. If
211 // mAnonKidsIdx == mAnonKids.Length() then the iterator is somewhere after
212 // the last native anon child. If mAnonKidsIdx == UINT32_MAX then the iterator
213 // is somewhere before the first native anon child.
214 nsTArray
<nsIContent
*> mAnonKids
;
215 uint32_t mAnonKidsIdx
;
218 IteratorPhase mPhase
;
220 // XXX we should really assert there are no frame tree changes as well, but
221 // there's no easy way to do that.
222 nsMutationGuard mMutationGuard
;
227 * StyleChildrenIterator traverses the children of the element from the
228 * perspective of the style system, particularly the children we need to
229 * traverse during restyle.
231 * At present, this is identical to AllChildrenIterator with
232 * (eAllChildren | eSkipDocumentLevelNativeAnonymousContent). We used to have
233 * detect and skip any native anonymous children that are used to implement some
234 * special magic in here that went away, but we keep the separate class so
235 * we can reintroduce special magic back if needed.
237 * Note: it assumes that no mutation of the DOM or frame tree takes place during
238 * iteration, and will break horribly if that is not true.
240 * We require this to be memmovable since Rust code can create and move
241 * StyleChildrenIterators.
243 class MOZ_NEEDS_MEMMOVABLE_MEMBERS StyleChildrenIterator
244 : private AllChildrenIterator
{
246 static nsIContent
* GetParent(const nsIContent
& aContent
) {
247 nsINode
* node
= aContent
.GetFlattenedTreeParentNodeForStyle();
248 return node
&& node
->IsContent() ? node
->AsContent() : nullptr;
251 explicit StyleChildrenIterator(const nsIContent
* aContent
,
252 bool aStartAtBeginning
= true)
253 : AllChildrenIterator(
255 nsIContent::eAllChildren
|
256 nsIContent::eSkipDocumentLevelNativeAnonymousContent
,
258 MOZ_COUNT_CTOR(StyleChildrenIterator
);
261 StyleChildrenIterator(StyleChildrenIterator
&& aOther
)
262 : AllChildrenIterator(std::move(aOther
)) {
263 MOZ_COUNT_CTOR(StyleChildrenIterator
);
266 StyleChildrenIterator
& operator=(StyleChildrenIterator
&& aOther
) = default;
268 MOZ_COUNTED_DTOR(StyleChildrenIterator
)
270 using AllChildrenIterator::GetNextChild
;
271 using AllChildrenIterator::GetPreviousChild
;
272 using AllChildrenIterator::Seek
;
276 } // namespace mozilla