Bug 1728955: part 3) Add logging to `nsBaseClipboard`. r=masayuki
[gecko.git] / dom / base / ChildIterator.cpp
blob5292bd63fccd198bfe593d0c8cc3d31e7a10f9d3
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 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);
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);
35 if (mParentAsSlot) {
36 const nsTArray<RefPtr<nsINode>>& assignedNodes =
37 mParentAsSlot->AssignedNodes();
39 mChild = (mIndexInInserted < assignedNodes.Length())
40 ? assignedNodes[mIndexInInserted++]->AsContent()
41 : nullptr;
42 if (!mChild) {
43 mIndexInInserted = 0;
45 return mChild;
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);
53 mDefaultChild = mDefaultChild->GetNextSibling();
54 if (mDefaultChild) {
55 return mDefaultChild;
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;
73 mChild = mParent->GetFirstChild();
74 mIsFirst = false;
75 } else if (mChild) { // in the middle of the child list
76 mChild = mChild->GetNextSibling();
79 return mChild;
82 void FlattenedChildIterator::Init(bool aIgnoreXBL) {
83 if (aIgnoreXBL) {
84 return;
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 mShadowDOMInvolved = true;
93 return;
95 if (mParentAsSlot) {
96 mShadowDOMInvolved = true;
97 return;
102 bool ExplicitChildIterator::Seek(const nsIContent* aChildToFind) {
103 if (aChildToFind->GetParent() == mParent &&
104 !aChildToFind->IsRootOfNativeAnonymousSubtree()) {
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;
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?
117 // Slow path: just walk all our kids.
118 return Seek(aChildToFind, nullptr);
121 nsIContent* ExplicitChildIterator::Get() const {
122 MOZ_ASSERT(!mIsFirst);
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;
130 if (mIndexInInserted) {
131 MOZ_ASSERT_UNREACHABLE("This needs to be revisited");
134 return mDefaultChild ? mDefaultChild : mChild;
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();
144 mChild = (--mIndexInInserted)
145 ? assignedNodes[mIndexInInserted - 1]->AsContent()
146 : nullptr;
148 if (!mChild) {
149 mIsFirst = true;
151 return mChild;
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;
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;
180 mChild = mParent->GetLastChild();
183 if (!mChild) {
184 mIsFirst = true;
187 return mChild;
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;
198 case eAtBeforeKid: {
199 Element* before = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
200 MOZ_ASSERT(before, "No content before frame at eAtBeforeKid phase");
201 return before;
204 case eAtExplicitKids:
205 return ExplicitChildIterator::Get();
207 case eAtAnonKids:
208 return mAnonKids[mAnonKidsIdx];
210 case eAtAfterKid: {
211 Element* after = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
212 MOZ_ASSERT(after, "No content after frame at eAtAfterKid phase");
213 return after;
216 default:
217 return nullptr;
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;
230 if (mPhase == eAtBeforeKid) {
231 mPhase = eAtExplicitKids;
232 Element* beforePseudo = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
233 if (beforePseudo && beforePseudo == aChildToFind) {
234 mPhase = eAtBeforeKid;
235 return true;
239 if (mPhase == eAtExplicitKids) {
240 if (ExplicitChildIterator::Seek(aChildToFind)) {
241 return true;
243 mPhase = eAtAnonKids;
246 nsIContent* child = nullptr;
247 do {
248 child = GetNextChild();
249 } while (child && child != aChildToFind);
251 return child == aChildToFind;
254 void AllChildrenIterator::AppendNativeAnonymousChildren() {
255 nsContentUtils::AppendNativeAnonymousChildren(mOriginalContent, mAnonKids,
256 mFlags);
259 nsIContent* AllChildrenIterator::GetNextChild() {
260 if (mPhase == eAtBegin) {
261 Element* markerContent = nsLayoutUtils::GetMarkerPseudo(mOriginalContent);
262 if (markerContent) {
263 mPhase = eAtMarkerKid;
264 return markerContent;
268 if (mPhase == eAtBegin || mPhase == eAtMarkerKid) {
269 mPhase = eAtExplicitKids;
270 Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
271 if (beforeContent) {
272 mPhase = eAtBeforeKid;
273 return beforeContent;
277 if (mPhase == eAtBeforeKid) {
278 // Advance into our explicit kids.
279 mPhase = eAtExplicitKids;
282 if (mPhase == eAtExplicitKids) {
283 nsIContent* kid = ExplicitChildIterator::GetNextChild();
284 if (kid) {
285 return kid;
287 mPhase = eAtAnonKids;
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++;
303 if (mAnonKidsIdx < mAnonKids.Length()) {
304 return mAnonKids[mAnonKidsIdx];
307 Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
308 if (afterContent) {
309 mPhase = eAtAfterKid;
310 return afterContent;
314 mPhase = eAtEnd;
315 return nullptr;
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;
329 if (mPhase == eAtAfterKid) {
330 mPhase = eAtAnonKids;
333 if (mPhase == eAtAnonKids) {
334 if (mAnonKids.IsEmpty()) {
335 AppendNativeAnonymousChildren();
336 mAnonKidsIdx = mAnonKids.Length();
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];
345 mPhase = eAtExplicitKids;
348 if (mPhase == eAtExplicitKids) {
349 nsIContent* kid = ExplicitChildIterator::GetPreviousChild();
350 if (kid) {
351 return kid;
354 Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
355 if (beforeContent) {
356 mPhase = eAtBeforeKid;
357 return beforeContent;
361 if (mPhase == eAtExplicitKids || mPhase == eAtBeforeKid) {
362 Element* markerContent = nsLayoutUtils::GetMarkerPseudo(mOriginalContent);
363 if (markerContent) {
364 mPhase = eAtMarkerKid;
365 return markerContent;
369 mPhase = eAtBegin;
370 return nullptr;
373 } // namespace mozilla::dom