Coverage Report

Created: 2018-09-25 14:53

/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