/src/mozilla-central/dom/base/ChildIterator.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #ifndef ChildIterator_h |
8 | | #define ChildIterator_h |
9 | | |
10 | | #include "nsIContent.h" |
11 | | #include "nsIContentInlines.h" |
12 | | #include <stdint.h> |
13 | | |
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. |
18 | | * |
19 | | * The FlattenedChildIterator expands any anonymous content bound from an XBL |
20 | | * binding's <xbl:content> element. |
21 | | */ |
22 | | |
23 | | |
24 | | class nsIContent; |
25 | | |
26 | | namespace mozilla { |
27 | | namespace dom { |
28 | | |
29 | | // This class iterates normal DOM child nodes of a given DOM node with |
30 | | // <xbl:children> nodes replaced by the elements that have been filtered into that |
31 | | // insertion point. Any bindings on the given element are ignored for purposes |
32 | | // of determining which insertion point children are filtered into. The iterator |
33 | | // can be initialized to start at the end by providing false for aStartAtBeginning |
34 | | // in order to start iterating in reverse from the last child. |
35 | | class ExplicitChildIterator |
36 | | { |
37 | | public: |
38 | | explicit ExplicitChildIterator(const nsIContent* aParent, |
39 | | bool aStartAtBeginning = true); |
40 | | |
41 | | ExplicitChildIterator(const ExplicitChildIterator& aOther) |
42 | | : mParent(aOther.mParent), |
43 | | mParentAsSlot(aOther.mParentAsSlot), |
44 | | mChild(aOther.mChild), |
45 | | mDefaultChild(aOther.mDefaultChild), |
46 | | mIsFirst(aOther.mIsFirst), |
47 | 0 | mIndexInInserted(aOther.mIndexInInserted) {} |
48 | | |
49 | | ExplicitChildIterator(ExplicitChildIterator&& aOther) |
50 | | : mParent(aOther.mParent), |
51 | | mParentAsSlot(aOther.mParentAsSlot), |
52 | | mChild(aOther.mChild), |
53 | | mDefaultChild(aOther.mDefaultChild), |
54 | | mIsFirst(aOther.mIsFirst), |
55 | | mIndexInInserted(aOther.mIndexInInserted) {} |
56 | | |
57 | | nsIContent* GetNextChild(); |
58 | | |
59 | | // Looks for aChildToFind respecting insertion points until aChildToFind is |
60 | | // found. This version can take shortcuts that the two-argument version |
61 | | // can't, so can be faster (and in fact can be O(1) instead of O(N) in many |
62 | | // cases). |
63 | | bool Seek(const nsIContent* aChildToFind); |
64 | | |
65 | | // Looks for aChildToFind respecting insertion points until aChildToFind is found. |
66 | | // or aBound is found. If aBound is nullptr then the seek is unbounded. Returns |
67 | | // whether aChildToFind was found as an explicit child prior to encountering |
68 | | // aBound. |
69 | | bool Seek(const nsIContent* aChildToFind, nsIContent* aBound) |
70 | 0 | { |
71 | 0 | // It would be nice to assert that we find aChildToFind, but bz thinks that |
72 | 0 | // we might not find aChildToFind when called from ContentInserted |
73 | 0 | // if first-letter frames are about. |
74 | 0 |
|
75 | 0 | // We can't easily take shortcuts here because we'd have to have a way to |
76 | 0 | // compare aChildToFind to aBound. |
77 | 0 | nsIContent* child; |
78 | 0 | do { |
79 | 0 | child = GetNextChild(); |
80 | 0 | } while (child && child != aChildToFind && child != aBound); |
81 | 0 |
|
82 | 0 | return child == aChildToFind; |
83 | 0 | } |
84 | | |
85 | | // Returns the current target of this iterator (which might be an explicit |
86 | | // child of the node, fallback content of an insertion point or |
87 | | // a node distributed to an insertion point. |
88 | | nsIContent* Get() const; |
89 | | |
90 | | // The inverse of GetNextChild. Properly steps in and out of insertion |
91 | | // points. |
92 | | nsIContent* GetPreviousChild(); |
93 | | |
94 | | protected: |
95 | | // The parent of the children being iterated. For the FlattenedChildIterator, |
96 | | // if there is a binding attached to the original parent, mParent points to |
97 | | // the <xbl:content> element for the binding. |
98 | | const nsIContent* mParent; |
99 | | |
100 | | // If parent is a slot element, this points to the parent as HTMLSlotElement, |
101 | | // otherwise, it's null. |
102 | | const HTMLSlotElement* mParentAsSlot; |
103 | | |
104 | | // The current child. When we encounter an insertion point, |
105 | | // mChild remains as the insertion point whose content we're iterating (and |
106 | | // our state is controled by mDefaultChild or mIndexInInserted depending on |
107 | | // whether the insertion point expands to its default content or not). |
108 | | nsIContent* mChild; |
109 | | |
110 | | // If non-null, this points to the current default content for the current |
111 | | // insertion point that we're iterating (i.e. mChild, which must be an |
112 | | // nsXBLChildrenElement or HTMLContentElement). Once this transitions back |
113 | | // to null, we continue iterating at mChild's next sibling. |
114 | | nsIContent* mDefaultChild; |
115 | | |
116 | | // A flag to let us know that we haven't started iterating yet. |
117 | | bool mIsFirst; |
118 | | |
119 | | // If not zero, we're iterating inserted children for an insertion point. This |
120 | | // is an index into mChild's inserted children array (mChild must be an |
121 | | // nsXBLChildrenElement). The index is one past the "current" child (as |
122 | | // opposed to mChild which represents the "current" child). |
123 | | uint32_t mIndexInInserted; |
124 | | }; |
125 | | |
126 | | // Iterates over the flattened children of a node, which accounts for anonymous |
127 | | // children and nodes moved by insertion points. If a node has anonymous |
128 | | // children, those are iterated over. The iterator can be initialized to start |
129 | | // at the end by providing false for aStartAtBeginning in order to start |
130 | | // iterating in reverse from the last child. |
131 | | class FlattenedChildIterator : public ExplicitChildIterator |
132 | | { |
133 | | public: |
134 | | explicit FlattenedChildIterator(const nsIContent* aParent, |
135 | | bool aStartAtBeginning = true) |
136 | | : ExplicitChildIterator(aParent, aStartAtBeginning) |
137 | | , mOriginalContent(aParent) |
138 | 0 | { |
139 | 0 | Init(false); |
140 | 0 | } |
141 | | |
142 | | FlattenedChildIterator(FlattenedChildIterator&& aOther) |
143 | | : ExplicitChildIterator(std::move(aOther)) |
144 | | , mOriginalContent(aOther.mOriginalContent) |
145 | | , mXBLInvolved(aOther.mXBLInvolved) |
146 | | {} |
147 | | |
148 | | FlattenedChildIterator(const FlattenedChildIterator& aOther) |
149 | | : ExplicitChildIterator(aOther) |
150 | | , mOriginalContent(aOther.mOriginalContent) |
151 | | , mXBLInvolved(aOther.mXBLInvolved) |
152 | 0 | {} |
153 | | |
154 | 0 | bool XBLInvolved() { |
155 | 0 | if (mXBLInvolved.isNothing()) { |
156 | 0 | mXBLInvolved = Some(ComputeWhetherXBLIsInvolved()); |
157 | 0 | } |
158 | 0 | return *mXBLInvolved; |
159 | 0 | } |
160 | | |
161 | 0 | const nsIContent* Parent() const { return mOriginalContent; } |
162 | | |
163 | | private: |
164 | | bool ComputeWhetherXBLIsInvolved() const; |
165 | | |
166 | | void Init(bool aIgnoreXBL); |
167 | | |
168 | | protected: |
169 | | /** |
170 | | * This constructor is a hack to help AllChildrenIterator which sometimes |
171 | | * doesn't want to consider XBL. |
172 | | */ |
173 | | FlattenedChildIterator(const nsIContent* aParent, uint32_t aFlags, |
174 | | bool aStartAtBeginning = true) |
175 | | : ExplicitChildIterator(aParent, aStartAtBeginning) |
176 | | , mOriginalContent(aParent) |
177 | 0 | { |
178 | 0 | bool ignoreXBL = aFlags & nsIContent::eAllButXBL; |
179 | 0 | Init(ignoreXBL); |
180 | 0 | } |
181 | | |
182 | | const nsIContent* mOriginalContent; |
183 | | |
184 | | private: |
185 | | // For certain optimizations, nsCSSFrameConstructor needs to know if the child |
186 | | // list of the element that we're iterating matches its .childNodes. |
187 | | // |
188 | | // This is lazily computed when asked for it. |
189 | | Maybe<bool> mXBLInvolved; |
190 | | }; |
191 | | |
192 | | /** |
193 | | * AllChildrenIterator traverses the children of an element including before / |
194 | | * after content and optionally XBL children. The iterator can be initialized |
195 | | * to start at the end by providing false for aStartAtBeginning in order to |
196 | | * start iterating in reverse from the last child. |
197 | | * |
198 | | * Note: it assumes that no mutation of the DOM or frame tree takes place during |
199 | | * iteration, and will break horribly if that is not true. |
200 | | */ |
201 | | class AllChildrenIterator : private FlattenedChildIterator |
202 | | { |
203 | | public: |
204 | | AllChildrenIterator(const nsIContent* aNode, |
205 | | uint32_t aFlags, |
206 | | bool aStartAtBeginning = true) |
207 | | : FlattenedChildIterator(aNode, aFlags, aStartAtBeginning) |
208 | | , mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0) |
209 | | , mFlags(aFlags) |
210 | | , mPhase(aStartAtBeginning ? eAtBegin : eAtEnd) |
211 | 0 | { |
212 | 0 | } |
213 | | |
214 | | AllChildrenIterator(AllChildrenIterator&& aOther) |
215 | | : FlattenedChildIterator(std::move(aOther)) |
216 | | , mAnonKids(std::move(aOther.mAnonKids)) |
217 | | , mAnonKidsIdx(aOther.mAnonKidsIdx) |
218 | | , mFlags(aOther.mFlags) |
219 | | , mPhase(aOther.mPhase) |
220 | | #ifdef DEBUG |
221 | | , mMutationGuard(aOther.mMutationGuard) |
222 | | #endif |
223 | | { |
224 | | } |
225 | | |
226 | | AllChildrenIterator& operator=(AllChildrenIterator&& aOther) |
227 | | { |
228 | | this->~AllChildrenIterator(); |
229 | | new (this) AllChildrenIterator(std::move(aOther)); |
230 | | return *this; |
231 | | } |
232 | | |
233 | | #ifdef DEBUG |
234 | | ~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); } |
235 | | #endif |
236 | | |
237 | | // Returns the current target the iterator is at, or null if the iterator |
238 | | // doesn't point to any child node (either eAtBegin or eAtEnd phase). |
239 | | nsIContent* Get() const; |
240 | | |
241 | | // Seeks the given node in children of a parent element, starting from |
242 | | // the current iterator's position, and sets the iterator at the given child |
243 | | // node if it was found. |
244 | | bool Seek(const nsIContent* aChildToFind); |
245 | | |
246 | | nsIContent* GetNextChild(); |
247 | | nsIContent* GetPreviousChild(); |
248 | | |
249 | | enum IteratorPhase |
250 | | { |
251 | | eAtBegin, |
252 | | eAtBeforeKid, |
253 | | eAtExplicitKids, |
254 | | eAtAnonKids, |
255 | | eAtAfterKid, |
256 | | eAtEnd |
257 | | }; |
258 | 0 | IteratorPhase Phase() const { return mPhase; } |
259 | | |
260 | | private: |
261 | | // Helpers. |
262 | | void AppendNativeAnonymousChildren(); |
263 | | |
264 | | // mAnonKids is an array of native anonymous children, mAnonKidsIdx is index |
265 | | // in the array. If mAnonKidsIdx < mAnonKids.Length() and mPhase is |
266 | | // eAtAnonKids then the iterator points at a child at mAnonKidsIdx index. If |
267 | | // mAnonKidsIdx == mAnonKids.Length() then the iterator is somewhere after |
268 | | // the last native anon child. If mAnonKidsIdx == UINT32_MAX then the iterator |
269 | | // is somewhere before the first native anon child. |
270 | | nsTArray<nsIContent*> mAnonKids; |
271 | | uint32_t mAnonKidsIdx; |
272 | | |
273 | | uint32_t mFlags; |
274 | | IteratorPhase mPhase; |
275 | | #ifdef DEBUG |
276 | | // XXX we should really assert there are no frame tree changes as well, but |
277 | | // there's no easy way to do that. |
278 | | nsMutationGuard mMutationGuard; |
279 | | #endif |
280 | | }; |
281 | | |
282 | | /** |
283 | | * StyleChildrenIterator traverses the children of the element from the |
284 | | * perspective of the style system, particularly the children we need to |
285 | | * traverse during restyle. |
286 | | * |
287 | | * At present, this is identical to AllChildrenIterator with |
288 | | * (eAllChildren | eSkipDocumentLevelNativeAnonymousContent). We used to have |
289 | | * detect and skip any native anonymous children that are used to implement some |
290 | | * special magic in here that went away, but we keep the separate class so |
291 | | * we can reintroduce special magic back if needed. |
292 | | * |
293 | | * Note: it assumes that no mutation of the DOM or frame tree takes place during |
294 | | * iteration, and will break horribly if that is not true. |
295 | | * |
296 | | * We require this to be memmovable since Rust code can create and move |
297 | | * StyleChildrenIterators. |
298 | | */ |
299 | | class MOZ_NEEDS_MEMMOVABLE_MEMBERS StyleChildrenIterator : private AllChildrenIterator |
300 | | { |
301 | | public: |
302 | | static nsIContent* GetParent(const nsIContent& aContent) |
303 | | { |
304 | | nsINode* node = aContent.GetFlattenedTreeParentNodeForStyle(); |
305 | | return node && node->IsContent() ? node->AsContent() : nullptr; |
306 | | } |
307 | | |
308 | | explicit StyleChildrenIterator(const nsIContent* aContent, bool aStartAtBeginning = true) |
309 | | : AllChildrenIterator(aContent, |
310 | | nsIContent::eAllChildren | |
311 | | nsIContent::eSkipDocumentLevelNativeAnonymousContent, |
312 | | aStartAtBeginning) |
313 | | { |
314 | | MOZ_COUNT_CTOR(StyleChildrenIterator); |
315 | | } |
316 | | |
317 | | StyleChildrenIterator(StyleChildrenIterator&& aOther) |
318 | | : AllChildrenIterator(std::move(aOther)) |
319 | | { |
320 | | MOZ_COUNT_CTOR(StyleChildrenIterator); |
321 | | } |
322 | | |
323 | | StyleChildrenIterator& operator=(StyleChildrenIterator&& aOther) |
324 | | { |
325 | | AllChildrenIterator::operator=(std::move(aOther)); |
326 | | return *this; |
327 | | } |
328 | | |
329 | | |
330 | | ~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator); } |
331 | | |
332 | | using AllChildrenIterator::GetNextChild; |
333 | | using AllChildrenIterator::GetPreviousChild; |
334 | | using AllChildrenIterator::Seek; |
335 | | }; |
336 | | |
337 | | } // namespace dom |
338 | | } // namespace mozilla |
339 | | |
340 | | #endif |