Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / ChildIterator.cpp
blob032aeb90a1405eff24561c37f35920d73e89a2c0
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et 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/XBLChildrenElement.h"
10 #include "mozilla/dom/HTMLContentElement.h"
11 #include "mozilla/dom/HTMLShadowElement.h"
12 #include "mozilla/dom/ShadowRoot.h"
13 #include "nsIAnonymousContentCreator.h"
14 #include "nsIFrame.h"
16 namespace mozilla {
17 namespace dom {
19 class MatchedNodes {
20 public:
21 explicit MatchedNodes(HTMLContentElement* aInsertionPoint)
22 : mIsContentElement(true), mContentElement(aInsertionPoint) {}
24 explicit MatchedNodes(XBLChildrenElement* aInsertionPoint)
25 : mIsContentElement(false), mChildrenElement(aInsertionPoint) {}
27 uint32_t Length() const
29 return mIsContentElement ? mContentElement->MatchedNodes().Length()
30 : mChildrenElement->InsertedChildrenLength();
33 nsIContent* operator[](int32_t aIndex) const
35 return mIsContentElement ? mContentElement->MatchedNodes()[aIndex]
36 : mChildrenElement->InsertedChild(aIndex);
39 bool IsEmpty() const
41 return mIsContentElement ? mContentElement->MatchedNodes().IsEmpty()
42 : !mChildrenElement->HasInsertedChildren();
44 protected:
45 bool mIsContentElement;
46 union {
47 HTMLContentElement* mContentElement;
48 XBLChildrenElement* mChildrenElement;
52 static inline MatchedNodes
53 GetMatchedNodesForPoint(nsIContent* aContent)
55 if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
56 // XBL case
57 return MatchedNodes(static_cast<XBLChildrenElement*>(aContent));
60 // Web components case
61 MOZ_ASSERT(aContent->IsHTML(nsGkAtoms::content));
62 return MatchedNodes(static_cast<HTMLContentElement*>(aContent));
65 nsIContent*
66 ExplicitChildIterator::GetNextChild()
68 // If we're already in the inserted-children array, look there first
69 if (mIndexInInserted) {
70 MOZ_ASSERT(mChild);
71 MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
72 MOZ_ASSERT(!mDefaultChild);
74 MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
75 if (mIndexInInserted < assignedChildren.Length()) {
76 return assignedChildren[mIndexInInserted++];
78 mIndexInInserted = 0;
79 mChild = mChild->GetNextSibling();
80 } else if (mShadowIterator) {
81 // If we're inside of a <shadow> element, look through the
82 // explicit children of the projected ShadowRoot via
83 // the mShadowIterator.
84 nsIContent* nextChild = mShadowIterator->GetNextChild();
85 if (nextChild) {
86 return nextChild;
89 mShadowIterator = nullptr;
90 mChild = mChild->GetNextSibling();
91 } else if (mDefaultChild) {
92 // If we're already in default content, check if there are more nodes there
93 MOZ_ASSERT(mChild);
94 MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
96 mDefaultChild = mDefaultChild->GetNextSibling();
97 if (mDefaultChild) {
98 return mDefaultChild;
101 mChild = mChild->GetNextSibling();
102 } else if (mIsFirst) { // at the beginning of the child list
103 mChild = mParent->GetFirstChild();
104 mIsFirst = false;
105 } else if (mChild) { // in the middle of the child list
106 mChild = mChild->GetNextSibling();
109 // Iterate until we find a non-insertion point, or an insertion point with
110 // content.
111 while (mChild) {
112 // If the current child being iterated is a shadow insertion point then
113 // the iterator needs to go into the projected ShadowRoot.
114 if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
115 // Look for the next child in the projected ShadowRoot for the <shadow>
116 // element.
117 HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild);
118 ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
119 if (projectedShadow) {
120 mShadowIterator = new ExplicitChildIterator(projectedShadow);
121 nsIContent* nextChild = mShadowIterator->GetNextChild();
122 if (nextChild) {
123 return nextChild;
125 mShadowIterator = nullptr;
127 mChild = mChild->GetNextSibling();
128 } else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
129 // If the current child being iterated is a content insertion point
130 // then the iterator needs to return the nodes distributed into
131 // the content insertion point.
132 MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
133 if (!assignedChildren.IsEmpty()) {
134 // Iterate through elements projected on insertion point.
135 mIndexInInserted = 1;
136 return assignedChildren[0];
139 // Insertion points inside fallback/default content
140 // are considered inactive and do not get assigned nodes.
141 mDefaultChild = mChild->GetFirstChild();
142 if (mDefaultChild) {
143 return mDefaultChild;
146 // If we have an insertion point with no assigned nodes and
147 // no default content, move on to the next node.
148 mChild = mChild->GetNextSibling();
149 } else {
150 // mChild is not an insertion point, thus it is the next node to
151 // return from this iterator.
152 break;
156 return mChild;
159 void
160 FlattenedChildIterator::Init(bool aIgnoreXBL)
162 if (aIgnoreXBL) {
163 return;
166 nsXBLBinding* binding =
167 mParent->OwnerDoc()->BindingManager()->GetBindingWithContent(mParent);
169 if (binding) {
170 nsIContent* anon = binding->GetAnonymousContent();
171 if (anon) {
172 mParent = anon;
173 mXBLInvolved = true;
177 // We set mXBLInvolved to true if either:
178 // - The node we're iterating has a binding with content attached to it.
179 // - The node is generated XBL content and has an <xbl:children> child.
180 if (!mXBLInvolved && mParent->GetBindingParent()) {
181 for (nsIContent* child = mParent->GetFirstChild();
182 child;
183 child = child->GetNextSibling()) {
184 if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
185 MOZ_ASSERT(child->GetBindingParent());
186 mXBLInvolved = true;
187 break;
193 nsIContent*
194 ExplicitChildIterator::Get()
196 MOZ_ASSERT(!mIsFirst);
198 if (mIndexInInserted) {
199 MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
200 return assignedChildren[mIndexInInserted - 1];
201 } else if (mShadowIterator) {
202 return mShadowIterator->Get();
204 return mDefaultChild ? mDefaultChild : mChild;
207 nsIContent*
208 ExplicitChildIterator::GetPreviousChild()
210 // If we're already in the inserted-children array, look there first
211 if (mIndexInInserted) {
212 // NB: mIndexInInserted points one past the last returned child so we need
213 // to look *two* indices back in order to return the previous child.
214 MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
215 if (--mIndexInInserted) {
216 return assignedChildren[mIndexInInserted - 1];
218 mChild = mChild->GetPreviousSibling();
219 } else if (mShadowIterator) {
220 nsIContent* previousChild = mShadowIterator->GetPreviousChild();
221 if (previousChild) {
222 return previousChild;
224 mShadowIterator = nullptr;
225 mChild = mChild->GetPreviousSibling();
226 } else if (mDefaultChild) {
227 // If we're already in default content, check if there are more nodes there
228 mDefaultChild = mDefaultChild->GetPreviousSibling();
229 if (mDefaultChild) {
230 return mDefaultChild;
233 mChild = mChild->GetPreviousSibling();
234 } else if (mIsFirst) { // at the beginning of the child list
235 return nullptr;
236 } else if (mChild) { // in the middle of the child list
237 mChild = mChild->GetPreviousSibling();
238 } else { // at the end of the child list
239 mChild = mParent->GetLastChild();
242 // Iterate until we find a non-insertion point, or an insertion point with
243 // content.
244 while (mChild) {
245 if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
246 // If the current child being iterated is a shadow insertion point then
247 // the iterator needs to go into the projected ShadowRoot.
248 HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild);
249 ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
250 if (projectedShadow) {
251 // Create a ExplicitChildIterator that begins iterating from the end.
252 mShadowIterator = new ExplicitChildIterator(projectedShadow, false);
253 nsIContent* previousChild = mShadowIterator->GetPreviousChild();
254 if (previousChild) {
255 return previousChild;
257 mShadowIterator = nullptr;
259 mChild = mChild->GetPreviousSibling();
260 } else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
261 // If the current child being iterated is a content insertion point
262 // then the iterator needs to return the nodes distributed into
263 // the content insertion point.
264 MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
265 if (!assignedChildren.IsEmpty()) {
266 mIndexInInserted = assignedChildren.Length();
267 return assignedChildren[mIndexInInserted - 1];
270 mDefaultChild = mChild->GetLastChild();
271 if (mDefaultChild) {
272 return mDefaultChild;
275 mChild = mChild->GetPreviousSibling();
276 } else {
277 // mChild is not an insertion point, thus it is the next node to
278 // return from this iterator.
279 break;
283 if (!mChild) {
284 mIsFirst = true;
287 return mChild;
290 nsIContent*
291 AllChildrenIterator::GetNextChild()
293 if (mPhase == eNeedBeforeKid) {
294 mPhase = eNeedExplicitKids;
295 nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
296 if (frame) {
297 nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
298 if (beforeFrame) {
299 return beforeFrame->GetContent();
304 if (mPhase == eNeedExplicitKids) {
305 nsIContent* kid = ExplicitChildIterator::GetNextChild();
306 if (kid) {
307 return kid;
310 mPhase = eNeedAnonKids;
313 if (mPhase == eNeedAnonKids) {
314 if (mAnonKids.IsEmpty()) {
315 nsIAnonymousContentCreator* ac =
316 do_QueryFrame(mOriginalContent->GetPrimaryFrame());
317 if (ac) {
318 ac->AppendAnonymousContentTo(mAnonKids, mFlags);
322 if (!mAnonKids.IsEmpty()) {
323 nsIContent* nextKid = mAnonKids[0];
324 mAnonKids.RemoveElementAt(0);
325 if (mAnonKids.IsEmpty()) {
326 mPhase = eNeedAfterKid;
329 return nextKid;
332 mPhase = eNeedAfterKid;
335 if (mPhase == eNeedAfterKid) {
336 mPhase = eDone;
337 nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
338 if (frame) {
339 nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(frame);
340 if (afterFrame) {
341 return afterFrame->GetContent();
346 return nullptr;
348 } // namespace dom
349 } // namespace mozilla