Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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"
14 namespace mozilla {
15 namespace dom {
17 class MatchedNodes {
18 public:
19 MatchedNodes(HTMLContentElement* aInsertionPoint)
20 : mIsContentElement(true), mContentElement(aInsertionPoint) {}
22 MatchedNodes(XBLChildrenElement* aInsertionPoint)
23 : mIsContentElement(false), mChildrenElement(aInsertionPoint) {}
25 uint32_t Length() const
26 {
27 return mIsContentElement ? mContentElement->MatchedNodes().Length()
28 : mChildrenElement->mInsertedChildren.Length();
29 }
31 nsIContent* operator[](int32_t aIndex) const
32 {
33 return mIsContentElement ? mContentElement->MatchedNodes()[aIndex]
34 : mChildrenElement->mInsertedChildren[aIndex];
35 }
37 bool IsEmpty() const
38 {
39 return mIsContentElement ? mContentElement->MatchedNodes().IsEmpty()
40 : mChildrenElement->mInsertedChildren.IsEmpty();
41 }
42 protected:
43 bool mIsContentElement;
44 union {
45 HTMLContentElement* mContentElement;
46 XBLChildrenElement* mChildrenElement;
47 };
48 };
50 static inline MatchedNodes
51 GetMatchedNodesForPoint(nsIContent* aContent)
52 {
53 if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
54 // XBL case
55 return MatchedNodes(static_cast<XBLChildrenElement*>(aContent));
56 }
58 // Web components case
59 MOZ_ASSERT(aContent->IsHTML(nsGkAtoms::content));
60 return MatchedNodes(static_cast<HTMLContentElement*>(aContent));
61 }
63 nsIContent*
64 ExplicitChildIterator::GetNextChild()
65 {
66 // If we're already in the inserted-children array, look there first
67 if (mIndexInInserted) {
68 MOZ_ASSERT(mChild);
69 MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
70 MOZ_ASSERT(!mDefaultChild);
72 MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
73 if (mIndexInInserted < assignedChildren.Length()) {
74 return assignedChildren[mIndexInInserted++];
75 }
76 mIndexInInserted = 0;
77 mChild = mChild->GetNextSibling();
78 } else if (mShadowIterator) {
79 // If we're inside of a <shadow> element, look through the
80 // explicit children of the projected ShadowRoot via
81 // the mShadowIterator.
82 nsIContent* nextChild = mShadowIterator->GetNextChild();
83 if (nextChild) {
84 return nextChild;
85 }
87 mShadowIterator = nullptr;
88 mChild = mChild->GetNextSibling();
89 } else if (mDefaultChild) {
90 // If we're already in default content, check if there are more nodes there
91 MOZ_ASSERT(mChild);
92 MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
94 mDefaultChild = mDefaultChild->GetNextSibling();
95 if (mDefaultChild) {
96 return mDefaultChild;
97 }
99 mChild = mChild->GetNextSibling();
100 } else if (mIsFirst) { // at the beginning of the child list
101 mChild = mParent->GetFirstChild();
102 mIsFirst = false;
103 } else if (mChild) { // in the middle of the child list
104 mChild = mChild->GetNextSibling();
105 }
107 // Iterate until we find a non-insertion point, or an insertion point with
108 // content.
109 while (mChild) {
110 // If the current child being iterated is a shadow insertion point then
111 // the iterator needs to go into the projected ShadowRoot.
112 if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
113 // Look for the next child in the projected ShadowRoot for the <shadow>
114 // element.
115 HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild);
116 ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
117 if (projectedShadow) {
118 mShadowIterator = new ExplicitChildIterator(projectedShadow);
119 nsIContent* nextChild = mShadowIterator->GetNextChild();
120 if (nextChild) {
121 return nextChild;
122 }
123 mShadowIterator = nullptr;
124 }
125 mChild = mChild->GetNextSibling();
126 } else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
127 // If the current child being iterated is a content insertion point
128 // then the iterator needs to return the nodes distributed into
129 // the content insertion point.
130 MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
131 if (!assignedChildren.IsEmpty()) {
132 // Iterate through elements projected on insertion point.
133 mIndexInInserted = 1;
134 return assignedChildren[0];
135 }
137 // Insertion points inside fallback/default content
138 // are considered inactive and do not get assigned nodes.
139 mDefaultChild = mChild->GetFirstChild();
140 if (mDefaultChild) {
141 return mDefaultChild;
142 }
144 // If we have an insertion point with no assigned nodes and
145 // no default content, move on to the next node.
146 mChild = mChild->GetNextSibling();
147 } else {
148 // mChild is not an insertion point, thus it is the next node to
149 // return from this iterator.
150 break;
151 }
152 }
154 return mChild;
155 }
157 FlattenedChildIterator::FlattenedChildIterator(nsIContent* aParent)
158 : ExplicitChildIterator(aParent), mXBLInvolved(false)
159 {
160 nsXBLBinding* binding =
161 aParent->OwnerDoc()->BindingManager()->GetBindingWithContent(aParent);
163 if (binding) {
164 nsIContent* anon = binding->GetAnonymousContent();
165 if (anon) {
166 mParent = anon;
167 mXBLInvolved = true;
168 }
169 }
171 // We set mXBLInvolved to true if either:
172 // - The node we're iterating has a binding with content attached to it.
173 // - The node is generated XBL content and has an <xbl:children> child.
174 if (!mXBLInvolved && aParent->GetBindingParent()) {
175 for (nsIContent* child = aParent->GetFirstChild();
176 child;
177 child = child->GetNextSibling()) {
178 if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
179 MOZ_ASSERT(child->GetBindingParent());
180 mXBLInvolved = true;
181 break;
182 }
183 }
184 }
185 }
187 nsIContent*
188 ExplicitChildIterator::Get()
189 {
190 MOZ_ASSERT(!mIsFirst);
192 if (mIndexInInserted) {
193 XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
194 return point->mInsertedChildren[mIndexInInserted - 1];
195 } else if (mShadowIterator) {
196 return mShadowIterator->Get();
197 }
198 return mDefaultChild ? mDefaultChild : mChild;
199 }
201 nsIContent*
202 ExplicitChildIterator::GetPreviousChild()
203 {
204 // If we're already in the inserted-children array, look there first
205 if (mIndexInInserted) {
206 // NB: mIndexInInserted points one past the last returned child so we need
207 // to look *two* indices back in order to return the previous child.
208 MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
209 if (--mIndexInInserted) {
210 return assignedChildren[mIndexInInserted - 1];
211 }
212 mChild = mChild->GetPreviousSibling();
213 } else if (mShadowIterator) {
214 nsIContent* previousChild = mShadowIterator->GetPreviousChild();
215 if (previousChild) {
216 return previousChild;
217 }
218 mShadowIterator = nullptr;
219 mChild = mChild->GetPreviousSibling();
220 } else if (mDefaultChild) {
221 // If we're already in default content, check if there are more nodes there
222 mDefaultChild = mDefaultChild->GetPreviousSibling();
223 if (mDefaultChild) {
224 return mDefaultChild;
225 }
227 mChild = mChild->GetPreviousSibling();
228 } else if (mIsFirst) { // at the beginning of the child list
229 return nullptr;
230 } else if (mChild) { // in the middle of the child list
231 mChild = mChild->GetPreviousSibling();
232 } else { // at the end of the child list
233 mChild = mParent->GetLastChild();
234 }
236 // Iterate until we find a non-insertion point, or an insertion point with
237 // content.
238 while (mChild) {
239 if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
240 // If the current child being iterated is a shadow insertion point then
241 // the iterator needs to go into the projected ShadowRoot.
242 HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild);
243 ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
244 if (projectedShadow) {
245 // Create a ExplicitChildIterator that begins iterating from the end.
246 mShadowIterator = new ExplicitChildIterator(projectedShadow, false);
247 nsIContent* previousChild = mShadowIterator->GetPreviousChild();
248 if (previousChild) {
249 return previousChild;
250 }
251 mShadowIterator = nullptr;
252 }
253 mChild = mChild->GetPreviousSibling();
254 } else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
255 // If the current child being iterated is a content insertion point
256 // then the iterator needs to return the nodes distributed into
257 // the content insertion point.
258 MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
259 if (!assignedChildren.IsEmpty()) {
260 mIndexInInserted = assignedChildren.Length();
261 return assignedChildren[mIndexInInserted - 1];
262 }
264 mDefaultChild = mChild->GetLastChild();
265 if (mDefaultChild) {
266 return mDefaultChild;
267 }
269 mChild = mChild->GetPreviousSibling();
270 } else {
271 // mChild is not an insertion point, thus it is the next node to
272 // return from this iterator.
273 break;
274 }
275 }
277 if (!mChild) {
278 mIsFirst = true;
279 }
281 return mChild;
282 }
284 } // namespace dom
285 } // namespace mozilla