Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / dom / base / ChildIterator.cpp
blob36d8434e8279bc30d605d7f60f4bb96387a93184
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 #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 #include "nsLayoutUtils.h"
17 namespace mozilla::dom {
19 FlattenedChildIterator::FlattenedChildIterator(const nsIContent* aParent,
20 bool aStartAtBeginning)
21 : mParent(aParent), mOriginalParent(aParent), mIsFirst(aStartAtBeginning) {
22 if (!mParent->IsElement()) {
23 // TODO(emilio): I think it probably makes sense to only allow constructing
24 // FlattenedChildIterators with Element.
25 return;
28 if (ShadowRoot* shadow = mParent->AsElement()->GetShadowRoot()) {
29 mParent = shadow;
30 mShadowDOMInvolved = true;
31 return;
34 if (const auto* slot = HTMLSlotElement::FromNode(mParent)) {
35 if (!slot->AssignedNodes().IsEmpty()) {
36 mParentAsSlot = slot;
37 if (!aStartAtBeginning) {
38 mIndexInInserted = slot->AssignedNodes().Length();
40 mShadowDOMInvolved = true;
45 nsIContent* FlattenedChildIterator::GetNextChild() {
46 // If we're already in the inserted-children array, look there first
47 if (mParentAsSlot) {
48 const nsTArray<RefPtr<nsINode>>& assignedNodes =
49 mParentAsSlot->AssignedNodes();
50 if (mIsFirst) {
51 mIsFirst = false;
52 MOZ_ASSERT(mIndexInInserted == 0);
53 mChild = assignedNodes[0]->AsContent();
54 return mChild;
56 MOZ_ASSERT(mIndexInInserted <= assignedNodes.Length());
57 if (mIndexInInserted + 1 >= assignedNodes.Length()) {
58 mIndexInInserted = assignedNodes.Length();
59 return nullptr;
61 mChild = assignedNodes[++mIndexInInserted]->AsContent();
62 return mChild;
65 if (mIsFirst) { // at the beginning of the child list
66 mChild = mParent->GetFirstChild();
67 mIsFirst = false;
68 } else if (mChild) { // in the middle of the child list
69 mChild = mChild->GetNextSibling();
72 return mChild;
75 bool FlattenedChildIterator::Seek(const nsIContent* aChildToFind) {
76 if (!mParentAsSlot && aChildToFind->GetParent() == mParent &&
77 !aChildToFind->IsRootOfNativeAnonymousSubtree()) {
78 // Fast path: just point ourselves to aChildToFind, which is a
79 // normal DOM child of ours.
80 mChild = const_cast<nsIContent*>(aChildToFind);
81 mIndexInInserted = 0;
82 mIsFirst = false;
83 return true;
86 // Can we add more fast paths here based on whether the parent of aChildToFind
87 // is a This version can take shortcuts that the two-argument version
88 // can't, so can be faster (and in fact cshadow insertion point or content
89 // insertion point?
91 // It would be nice to assert that we find aChildToFind, but bz thinks that
92 // we might not find aChildToFind when called from ContentInserted
93 // if first-letter frames are about.
94 while (nsIContent* child = GetNextChild()) {
95 if (child == aChildToFind) {
96 return true;
100 return false;
103 nsIContent* FlattenedChildIterator::GetPreviousChild() {
104 if (mIsFirst) { // at the beginning of the child list
105 return nullptr;
107 if (mParentAsSlot) {
108 const nsTArray<RefPtr<nsINode>>& assignedNodes =
109 mParentAsSlot->AssignedNodes();
110 MOZ_ASSERT(mIndexInInserted <= assignedNodes.Length());
111 if (mIndexInInserted == 0) {
112 mIsFirst = true;
113 return nullptr;
115 mChild = assignedNodes[--mIndexInInserted]->AsContent();
116 return mChild;
118 if (mChild) { // in the middle of the child list
119 mChild = mChild->GetPreviousSibling();
120 } else { // at the end of the child list
121 mChild = mParent->GetLastChild();
123 if (!mChild) {
124 mIsFirst = true;
127 return mChild;
130 nsIContent* AllChildrenIterator::Get() const {
131 switch (mPhase) {
132 case eAtMarkerKid: {
133 Element* marker = nsLayoutUtils::GetMarkerPseudo(Parent());
134 MOZ_ASSERT(marker, "No content marker frame at eAtMarkerKid phase");
135 return marker;
138 case eAtBeforeKid: {
139 Element* before = nsLayoutUtils::GetBeforePseudo(Parent());
140 MOZ_ASSERT(before, "No content before frame at eAtBeforeKid phase");
141 return before;
144 case eAtFlatTreeKids:
145 return FlattenedChildIterator::Get();
147 case eAtAnonKids:
148 return mAnonKids[mAnonKidsIdx];
150 case eAtAfterKid: {
151 Element* after = nsLayoutUtils::GetAfterPseudo(Parent());
152 MOZ_ASSERT(after, "No content after frame at eAtAfterKid phase");
153 return after;
156 default:
157 return nullptr;
161 bool AllChildrenIterator::Seek(const nsIContent* aChildToFind) {
162 if (mPhase == eAtBegin || mPhase == eAtMarkerKid) {
163 Element* markerPseudo = nsLayoutUtils::GetMarkerPseudo(Parent());
164 if (markerPseudo && markerPseudo == aChildToFind) {
165 mPhase = eAtMarkerKid;
166 return true;
168 mPhase = eAtBeforeKid;
170 if (mPhase == eAtBeforeKid) {
171 Element* beforePseudo = nsLayoutUtils::GetBeforePseudo(Parent());
172 if (beforePseudo && beforePseudo == aChildToFind) {
173 return true;
175 mPhase = eAtFlatTreeKids;
178 if (mPhase == eAtFlatTreeKids) {
179 if (FlattenedChildIterator::Seek(aChildToFind)) {
180 return true;
182 mPhase = eAtAnonKids;
185 nsIContent* child = nullptr;
186 do {
187 child = GetNextChild();
188 } while (child && child != aChildToFind);
190 return child == aChildToFind;
193 void AllChildrenIterator::AppendNativeAnonymousChildren() {
194 nsContentUtils::AppendNativeAnonymousChildren(Parent(), mAnonKids, mFlags);
197 nsIContent* AllChildrenIterator::GetNextChild() {
198 if (mPhase == eAtBegin) {
199 mPhase = eAtMarkerKid;
200 if (Element* markerContent = nsLayoutUtils::GetMarkerPseudo(Parent())) {
201 return markerContent;
205 if (mPhase == eAtMarkerKid) {
206 mPhase = eAtBeforeKid;
207 if (Element* beforeContent = nsLayoutUtils::GetBeforePseudo(Parent())) {
208 return beforeContent;
212 if (mPhase == eAtBeforeKid) {
213 // Advance into our explicit kids.
214 mPhase = eAtFlatTreeKids;
217 if (mPhase == eAtFlatTreeKids) {
218 if (nsIContent* kid = FlattenedChildIterator::GetNextChild()) {
219 return kid;
221 mPhase = eAtAnonKids;
224 if (mPhase == eAtAnonKids) {
225 if (mAnonKids.IsEmpty()) {
226 MOZ_ASSERT(mAnonKidsIdx == UINT32_MAX);
227 AppendNativeAnonymousChildren();
228 mAnonKidsIdx = 0;
229 } else {
230 if (mAnonKidsIdx == UINT32_MAX) {
231 mAnonKidsIdx = 0;
232 } else {
233 mAnonKidsIdx++;
237 if (mAnonKidsIdx < mAnonKids.Length()) {
238 return mAnonKids[mAnonKidsIdx];
241 mPhase = eAtAfterKid;
242 if (Element* afterContent = nsLayoutUtils::GetAfterPseudo(Parent())) {
243 return afterContent;
247 mPhase = eAtEnd;
248 return nullptr;
251 nsIContent* AllChildrenIterator::GetPreviousChild() {
252 if (mPhase == eAtEnd) {
253 MOZ_ASSERT(mAnonKidsIdx == mAnonKids.Length());
254 mPhase = eAtAnonKids;
255 Element* afterContent = nsLayoutUtils::GetAfterPseudo(Parent());
256 if (afterContent) {
257 mPhase = eAtAfterKid;
258 return afterContent;
262 if (mPhase == eAtAfterKid) {
263 mPhase = eAtAnonKids;
266 if (mPhase == eAtAnonKids) {
267 if (mAnonKids.IsEmpty()) {
268 AppendNativeAnonymousChildren();
269 mAnonKidsIdx = mAnonKids.Length();
272 // If 0 then it turns into UINT32_MAX, which indicates the iterator is
273 // before the anonymous children.
274 --mAnonKidsIdx;
275 if (mAnonKidsIdx < mAnonKids.Length()) {
276 return mAnonKids[mAnonKidsIdx];
278 mPhase = eAtFlatTreeKids;
281 if (mPhase == eAtFlatTreeKids) {
282 if (nsIContent* kid = FlattenedChildIterator::GetPreviousChild()) {
283 return kid;
286 Element* beforeContent = nsLayoutUtils::GetBeforePseudo(Parent());
287 if (beforeContent) {
288 mPhase = eAtBeforeKid;
289 return beforeContent;
293 if (mPhase == eAtFlatTreeKids || mPhase == eAtBeforeKid) {
294 Element* markerContent = nsLayoutUtils::GetMarkerPseudo(Parent());
295 if (markerContent) {
296 mPhase = eAtMarkerKid;
297 return markerContent;
301 mPhase = eAtBegin;
302 return nullptr;
305 } // namespace mozilla::dom