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"
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
);
41 return mIsContentElement
? mContentElement
->MatchedNodes().IsEmpty()
42 : !mChildrenElement
->HasInsertedChildren();
45 bool mIsContentElement
;
47 HTMLContentElement
* mContentElement
;
48 XBLChildrenElement
* mChildrenElement
;
52 static inline MatchedNodes
53 GetMatchedNodesForPoint(nsIContent
* aContent
)
55 if (aContent
->NodeInfo()->Equals(nsGkAtoms::children
, kNameSpaceID_XBL
)) {
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
));
66 ExplicitChildIterator::GetNextChild()
68 // If we're already in the inserted-children array, look there first
69 if (mIndexInInserted
) {
71 MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild
));
72 MOZ_ASSERT(!mDefaultChild
);
74 MatchedNodes assignedChildren
= GetMatchedNodesForPoint(mChild
);
75 if (mIndexInInserted
< assignedChildren
.Length()) {
76 return assignedChildren
[mIndexInInserted
++];
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();
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
94 MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild
));
96 mDefaultChild
= mDefaultChild
->GetNextSibling();
101 mChild
= mChild
->GetNextSibling();
102 } else if (mIsFirst
) { // at the beginning of the child list
103 mChild
= mParent
->GetFirstChild();
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
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>
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();
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();
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();
150 // mChild is not an insertion point, thus it is the next node to
151 // return from this iterator.
160 FlattenedChildIterator::Init(bool aIgnoreXBL
)
166 nsXBLBinding
* binding
=
167 mParent
->OwnerDoc()->BindingManager()->GetBindingWithContent(mParent
);
170 nsIContent
* anon
= binding
->GetAnonymousContent();
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();
183 child
= child
->GetNextSibling()) {
184 if (child
->NodeInfo()->Equals(nsGkAtoms::children
, kNameSpaceID_XBL
)) {
185 MOZ_ASSERT(child
->GetBindingParent());
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
;
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();
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();
230 return mDefaultChild
;
233 mChild
= mChild
->GetPreviousSibling();
234 } else if (mIsFirst
) { // at the beginning of the child list
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
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();
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();
272 return mDefaultChild
;
275 mChild
= mChild
->GetPreviousSibling();
277 // mChild is not an insertion point, thus it is the next node to
278 // return from this iterator.
291 AllChildrenIterator::GetNextChild()
293 if (mPhase
== eNeedBeforeKid
) {
294 mPhase
= eNeedExplicitKids
;
295 nsIFrame
* frame
= mOriginalContent
->GetPrimaryFrame();
297 nsIFrame
* beforeFrame
= nsLayoutUtils::GetBeforeFrame(frame
);
299 return beforeFrame
->GetContent();
304 if (mPhase
== eNeedExplicitKids
) {
305 nsIContent
* kid
= ExplicitChildIterator::GetNextChild();
310 mPhase
= eNeedAnonKids
;
313 if (mPhase
== eNeedAnonKids
) {
314 if (mAnonKids
.IsEmpty()) {
315 nsIAnonymousContentCreator
* ac
=
316 do_QueryFrame(mOriginalContent
->GetPrimaryFrame());
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
;
332 mPhase
= eNeedAfterKid
;
335 if (mPhase
== eNeedAfterKid
) {
337 nsIFrame
* frame
= mOriginalContent
->GetPrimaryFrame();
339 nsIFrame
* afterFrame
= nsLayoutUtils::GetAfterFrame(frame
);
341 return afterFrame
->GetContent();
349 } // namespace mozilla