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