Source code

Revision control

Other Tools

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
#include "ChildIterator.h"
8
#include "nsContentUtils.h"
9
#include "mozilla/dom/Document.h"
10
#include "mozilla/dom/HTMLSlotElement.h"
11
#include "mozilla/dom/ShadowRoot.h"
12
#include "nsIAnonymousContentCreator.h"
13
#include "nsIFrame.h"
14
#include "nsCSSAnonBoxes.h"
15
16
namespace mozilla {
17
namespace dom {
18
19
ExplicitChildIterator::ExplicitChildIterator(const nsIContent* aParent,
20
bool aStartAtBeginning)
21
: mParent(aParent),
22
mChild(nullptr),
23
mDefaultChild(nullptr),
24
mIsFirst(aStartAtBeginning),
25
mIndexInInserted(0) {
26
mParentAsSlot = HTMLSlotElement::FromNode(mParent);
27
}
28
29
nsIContent* ExplicitChildIterator::GetNextChild() {
30
// If we're already in the inserted-children array, look there first
31
if (mIndexInInserted) {
32
MOZ_ASSERT(mChild);
33
MOZ_ASSERT(!mDefaultChild);
34
35
if (mParentAsSlot) {
36
const nsTArray<RefPtr<nsINode>>& assignedNodes =
37
mParentAsSlot->AssignedNodes();
38
39
mChild = (mIndexInInserted < assignedNodes.Length())
40
? assignedNodes[mIndexInInserted++]->AsContent()
41
: nullptr;
42
if (!mChild) {
43
mIndexInInserted = 0;
44
}
45
return mChild;
46
}
47
48
MOZ_ASSERT_UNREACHABLE("This needs to be revisited");
49
} else if (mDefaultChild) {
50
// If we're already in default content, check if there are more nodes there
51
MOZ_ASSERT(mChild);
52
53
mDefaultChild = mDefaultChild->GetNextSibling();
54
if (mDefaultChild) {
55
return mDefaultChild;
56
}
57
58
mChild = mChild->GetNextSibling();
59
} else if (mIsFirst) { // at the beginning of the child list
60
// For slot parent, iterate over assigned nodes if not empty, otherwise
61
// fall through and iterate over direct children (fallback content).
62
if (mParentAsSlot) {
63
const nsTArray<RefPtr<nsINode>>& assignedNodes =
64
mParentAsSlot->AssignedNodes();
65
if (!assignedNodes.IsEmpty()) {
66
mIndexInInserted = 1;
67
mChild = assignedNodes[0]->AsContent();
68
mIsFirst = false;
69
return mChild;
70
}
71
}
72
73
mChild = mParent->GetFirstChild();
74
mIsFirst = false;
75
} else if (mChild) { // in the middle of the child list
76
mChild = mChild->GetNextSibling();
77
}
78
79
return mChild;
80
}
81
82
void FlattenedChildIterator::Init(bool aIgnoreXBL) {
83
if (aIgnoreXBL) {
84
return;
85
}
86
87
// TODO(emilio): I think it probably makes sense to only allow constructing
88
// FlattenedChildIterators with Element.
89
if (mParent->IsElement()) {
90
if (ShadowRoot* shadow = mParent->AsElement()->GetShadowRoot()) {
91
mParent = shadow;
92
mXBLInvolved = true;
93
return;
94
}
95
if (mParentAsSlot) {
96
mXBLInvolved = true;
97
return;
98
}
99
}
100
}
101
102
bool ExplicitChildIterator::Seek(const nsIContent* aChildToFind) {
103
if (aChildToFind->GetParent() == mParent &&
104
!aChildToFind->IsRootOfAnonymousSubtree()) {
105
// Fast path: just point ourselves to aChildToFind, which is a
106
// normal DOM child of ours.
107
mChild = const_cast<nsIContent*>(aChildToFind);
108
mIndexInInserted = 0;
109
mDefaultChild = nullptr;
110
mIsFirst = false;
111
return true;
112
}
113
114
// Can we add more fast paths here based on whether the parent of aChildToFind
115
// is a shadow insertion point or content insertion point?
116
117
// Slow path: just walk all our kids.
118
return Seek(aChildToFind, nullptr);
119
}
120
121
nsIContent* ExplicitChildIterator::Get() const {
122
MOZ_ASSERT(!mIsFirst);
123
124
// When mParentAsSlot is set, mChild is always set to the current child. It
125
// does not matter whether mChild is an assigned node or a fallback content.
126
if (mParentAsSlot) {
127
return mChild;
128
}
129
130
if (mIndexInInserted) {
131
MOZ_ASSERT_UNREACHABLE("This needs to be revisited");
132
}
133
134
return mDefaultChild ? mDefaultChild : mChild;
135
}
136
137
nsIContent* ExplicitChildIterator::GetPreviousChild() {
138
// If we're already in the inserted-children array, look there first
139
if (mIndexInInserted) {
140
if (mParentAsSlot) {
141
const nsTArray<RefPtr<nsINode>>& assignedNodes =
142
mParentAsSlot->AssignedNodes();
143
144
mChild = (--mIndexInInserted)
145
? assignedNodes[mIndexInInserted - 1]->AsContent()
146
: nullptr;
147
148
if (!mChild) {
149
mIsFirst = true;
150
}
151
return mChild;
152
}
153
154
MOZ_ASSERT_UNREACHABLE("This needs to be revisited");
155
} else if (mDefaultChild) {
156
// If we're already in default content, check if there are more nodes there
157
mDefaultChild = mDefaultChild->GetPreviousSibling();
158
if (mDefaultChild) {
159
return mDefaultChild;
160
}
161
162
mChild = mChild->GetPreviousSibling();
163
} else if (mIsFirst) { // at the beginning of the child list
164
return nullptr;
165
} else if (mChild) { // in the middle of the child list
166
mChild = mChild->GetPreviousSibling();
167
} else { // at the end of the child list
168
// For slot parent, iterate over assigned nodes if not empty, otherwise
169
// fall through and iterate over direct children (fallback content).
170
if (mParentAsSlot) {
171
const nsTArray<RefPtr<nsINode>>& assignedNodes =
172
mParentAsSlot->AssignedNodes();
173
if (!assignedNodes.IsEmpty()) {
174
mIndexInInserted = assignedNodes.Length();
175
mChild = assignedNodes[mIndexInInserted - 1]->AsContent();
176
return mChild;
177
}
178
}
179
180
mChild = mParent->GetLastChild();
181
}
182
183
if (!mChild) {
184
mIsFirst = true;
185
}
186
187
return mChild;
188
}
189
190
nsIContent* AllChildrenIterator::Get() const {
191
switch (mPhase) {
192
case eAtMarkerKid: {
193
Element* marker = nsLayoutUtils::GetMarkerPseudo(mOriginalContent);
194
MOZ_ASSERT(marker, "No content marker frame at eAtMarkerKid phase");
195
return marker;
196
}
197
198
case eAtBeforeKid: {
199
Element* before = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
200
MOZ_ASSERT(before, "No content before frame at eAtBeforeKid phase");
201
return before;
202
}
203
204
case eAtExplicitKids:
205
return ExplicitChildIterator::Get();
206
207
case eAtAnonKids:
208
return mAnonKids[mAnonKidsIdx];
209
210
case eAtAfterKid: {
211
Element* after = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
212
MOZ_ASSERT(after, "No content after frame at eAtAfterKid phase");
213
return after;
214
}
215
216
default:
217
return nullptr;
218
}
219
}
220
221
bool AllChildrenIterator::Seek(const nsIContent* aChildToFind) {
222
if (mPhase == eAtBegin || mPhase == eAtMarkerKid) {
223
mPhase = eAtBeforeKid;
224
Element* markerPseudo = nsLayoutUtils::GetMarkerPseudo(mOriginalContent);
225
if (markerPseudo && markerPseudo == aChildToFind) {
226
mPhase = eAtMarkerKid;
227
return true;
228
}
229
}
230
if (mPhase == eAtBeforeKid) {
231
mPhase = eAtExplicitKids;
232
Element* beforePseudo = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
233
if (beforePseudo && beforePseudo == aChildToFind) {
234
mPhase = eAtBeforeKid;
235
return true;
236
}
237
}
238
239
if (mPhase == eAtExplicitKids) {
240
if (ExplicitChildIterator::Seek(aChildToFind)) {
241
return true;
242
}
243
mPhase = eAtAnonKids;
244
}
245
246
nsIContent* child = nullptr;
247
do {
248
child = GetNextChild();
249
} while (child && child != aChildToFind);
250
251
return child == aChildToFind;
252
}
253
254
void AllChildrenIterator::AppendNativeAnonymousChildren() {
255
nsContentUtils::AppendNativeAnonymousChildren(mOriginalContent, mAnonKids,
256
mFlags);
257
}
258
259
nsIContent* AllChildrenIterator::GetNextChild() {
260
if (mPhase == eAtBegin) {
261
Element* markerContent = nsLayoutUtils::GetMarkerPseudo(mOriginalContent);
262
if (markerContent) {
263
mPhase = eAtMarkerKid;
264
return markerContent;
265
}
266
}
267
268
if (mPhase == eAtBegin || mPhase == eAtMarkerKid) {
269
mPhase = eAtExplicitKids;
270
Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
271
if (beforeContent) {
272
mPhase = eAtBeforeKid;
273
return beforeContent;
274
}
275
}
276
277
if (mPhase == eAtBeforeKid) {
278
// Advance into our explicit kids.
279
mPhase = eAtExplicitKids;
280
}
281
282
if (mPhase == eAtExplicitKids) {
283
nsIContent* kid = ExplicitChildIterator::GetNextChild();
284
if (kid) {
285
return kid;
286
}
287
mPhase = eAtAnonKids;
288
}
289
290
if (mPhase == eAtAnonKids) {
291
if (mAnonKids.IsEmpty()) {
292
MOZ_ASSERT(mAnonKidsIdx == UINT32_MAX);
293
AppendNativeAnonymousChildren();
294
mAnonKidsIdx = 0;
295
} else {
296
if (mAnonKidsIdx == UINT32_MAX) {
297
mAnonKidsIdx = 0;
298
} else {
299
mAnonKidsIdx++;
300
}
301
}
302
303
if (mAnonKidsIdx < mAnonKids.Length()) {
304
return mAnonKids[mAnonKidsIdx];
305
}
306
307
Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
308
if (afterContent) {
309
mPhase = eAtAfterKid;
310
return afterContent;
311
}
312
}
313
314
mPhase = eAtEnd;
315
return nullptr;
316
}
317
318
nsIContent* AllChildrenIterator::GetPreviousChild() {
319
if (mPhase == eAtEnd) {
320
MOZ_ASSERT(mAnonKidsIdx == mAnonKids.Length());
321
mPhase = eAtAnonKids;
322
Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
323
if (afterContent) {
324
mPhase = eAtAfterKid;
325
return afterContent;
326
}
327
}
328
329
if (mPhase == eAtAfterKid) {
330
mPhase = eAtAnonKids;
331
}
332
333
if (mPhase == eAtAnonKids) {
334
if (mAnonKids.IsEmpty()) {
335
AppendNativeAnonymousChildren();
336
mAnonKidsIdx = mAnonKids.Length();
337
}
338
339
// If 0 then it turns into UINT32_MAX, which indicates the iterator is
340
// before the anonymous children.
341
--mAnonKidsIdx;
342
if (mAnonKidsIdx < mAnonKids.Length()) {
343
return mAnonKids[mAnonKidsIdx];
344
}
345
mPhase = eAtExplicitKids;
346
}
347
348
if (mPhase == eAtExplicitKids) {
349
nsIContent* kid = ExplicitChildIterator::GetPreviousChild();
350
if (kid) {
351
return kid;
352
}
353
354
Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
355
if (beforeContent) {
356
mPhase = eAtBeforeKid;
357
return beforeContent;
358
}
359
}
360
361
if (mPhase == eAtExplicitKids || mPhase == eAtBeforeKid) {
362
Element* markerContent = nsLayoutUtils::GetMarkerPseudo(mOriginalContent);
363
if (markerContent) {
364
mPhase = eAtMarkerKid;
365
return markerContent;
366
}
367
}
368
369
mPhase = eAtBegin;
370
return nullptr;
371
}
372
373
} // namespace dom
374
} // namespace mozilla