Bug 1550519 - Show a translucent parent highlight when a subgrid is highlighted....
[gecko.git] / dom / base / ChildIterator.h
blob0c192628debae6a6065be8fe5e6f46fe8bef0e22
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"
12 #include <stdint.h>
14 /**
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.
23 class nsIContent;
25 namespace mozilla {
26 namespace dom {
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 {
35 public:
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
60 // cases).
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.
74 nsIContent* child;
75 do {
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
88 // points.
89 nsIContent* GetPreviousChild();
91 protected:
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).
105 nsIContent* mChild;
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.
114 bool mIsFirst;
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 {
129 public:
130 explicit FlattenedChildIterator(const nsIContent* aParent,
131 bool aStartAtBeginning = true)
132 : ExplicitChildIterator(aParent, aStartAtBeginning),
133 mOriginalContent(aParent) {
134 Init(false);
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) {}
147 bool XBLInvolved() {
148 if (mXBLInvolved.isNothing()) {
149 mXBLInvolved = Some(ComputeWhetherXBLIsInvolved());
151 return *mXBLInvolved;
154 const nsIContent* Parent() const { return mOriginalContent; }
156 private:
157 bool ComputeWhetherXBLIsInvolved() const;
159 void Init(bool aIgnoreXBL);
161 protected:
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;
171 Init(ignoreXBL);
174 const nsIContent* mOriginalContent;
176 private:
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 {
194 public:
195 AllChildrenIterator(const nsIContent* aNode, uint32_t aFlags,
196 bool aStartAtBeginning = true)
197 : FlattenedChildIterator(aNode, aFlags, aStartAtBeginning),
198 mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0),
199 mFlags(aFlags),
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)
208 #ifdef DEBUG
210 mMutationGuard(aOther.mMutationGuard)
211 #endif
215 AllChildrenIterator& operator=(AllChildrenIterator&& aOther) {
216 this->~AllChildrenIterator();
217 new (this) AllChildrenIterator(std::move(aOther));
218 return *this;
221 #ifdef DEBUG
222 ~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); }
223 #endif
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();
237 enum IteratorPhase {
238 eAtBegin,
239 eAtMarkerKid,
240 eAtBeforeKid,
241 eAtExplicitKids,
242 eAtAnonKids,
243 eAtAfterKid,
244 eAtEnd
246 IteratorPhase Phase() const { return mPhase; }
248 private:
249 // Helpers.
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;
261 uint32_t mFlags;
262 IteratorPhase mPhase;
263 #ifdef DEBUG
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;
267 #endif
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 {
289 public:
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(
298 aContent,
299 nsIContent::eAllChildren |
300 nsIContent::eSkipDocumentLevelNativeAnonymousContent,
301 aStartAtBeginning) {
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));
312 return *this;
315 ~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator); }
317 using AllChildrenIterator::GetNextChild;
318 using AllChildrenIterator::GetPreviousChild;
319 using AllChildrenIterator::Seek;
322 } // namespace dom
323 } // namespace mozilla
325 #endif