Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / html / HTMLShadowElement.cpp
blob9084c86eb0721554393cf13f8c5720e8e3c6b303
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/ShadowRoot.h"
8 #include "ChildIterator.h"
9 #include "nsContentUtils.h"
10 #include "mozilla/dom/HTMLShadowElement.h"
11 #include "mozilla/dom/HTMLShadowElementBinding.h"
13 NS_IMPL_NS_NEW_HTML_ELEMENT(Shadow)
15 using namespace mozilla::dom;
17 HTMLShadowElement::HTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
18 : nsGenericHTMLElement(aNodeInfo), mIsInsertionPoint(false)
22 HTMLShadowElement::~HTMLShadowElement()
24 if (mProjectedShadow) {
25 mProjectedShadow->RemoveMutationObserver(this);
29 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLShadowElement)
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLShadowElement,
32 nsGenericHTMLElement)
33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProjectedShadow)
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLShadowElement,
37 nsGenericHTMLElement)
38 if (tmp->mProjectedShadow) {
39 tmp->mProjectedShadow->RemoveMutationObserver(tmp);
40 tmp->mProjectedShadow = nullptr;
42 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
44 NS_IMPL_ADDREF_INHERITED(HTMLShadowElement, Element)
45 NS_IMPL_RELEASE_INHERITED(HTMLShadowElement, Element)
47 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLShadowElement)
48 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
50 NS_IMPL_ELEMENT_CLONE(HTMLShadowElement)
52 JSObject*
53 HTMLShadowElement::WrapNode(JSContext *aCx)
55 return HTMLShadowElementBinding::Wrap(aCx, this);
58 void
59 HTMLShadowElement::SetProjectedShadow(ShadowRoot* aProjectedShadow)
61 if (mProjectedShadow) {
62 mProjectedShadow->RemoveMutationObserver(this);
64 // The currently projected ShadowRoot is going away,
65 // thus the destination insertion points need to be updated.
66 ExplicitChildIterator childIterator(mProjectedShadow);
67 for (nsIContent* content = childIterator.GetNextChild();
68 content;
69 content = childIterator.GetNextChild()) {
70 ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
74 mProjectedShadow = aProjectedShadow;
75 if (mProjectedShadow) {
76 // A new ShadowRoot is being projected, thus its explcit
77 // children will be distributed to this shadow insertion point.
78 ExplicitChildIterator childIterator(mProjectedShadow);
79 for (nsIContent* content = childIterator.GetNextChild();
80 content;
81 content = childIterator.GetNextChild()) {
82 content->DestInsertionPoints().AppendElement(this);
85 // Watch for mutations on the projected shadow because
86 // it affects the nodes that are distributed to this shadow
87 // insertion point.
88 mProjectedShadow->AddMutationObserver(this);
92 static bool
93 IsInFallbackContent(nsIContent* aContent)
95 nsINode* parentNode = aContent->GetParentNode();
96 while (parentNode) {
97 if (parentNode->IsElement() &&
98 parentNode->AsElement()->IsHTML(nsGkAtoms::content)) {
99 return true;
101 parentNode = parentNode->GetParentNode();
104 return false;
107 nsresult
108 HTMLShadowElement::BindToTree(nsIDocument* aDocument,
109 nsIContent* aParent,
110 nsIContent* aBindingParent,
111 bool aCompileEventHandlers)
113 nsRefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
115 nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
116 aBindingParent,
117 aCompileEventHandlers);
118 NS_ENSURE_SUCCESS(rv, rv);
120 ShadowRoot* containingShadow = GetContainingShadow();
121 if (containingShadow && !oldContainingShadow) {
122 // Keep track of all descendant <shadow> elements in tree order so
123 // that when the current shadow insertion point is removed, the next
124 // one can be found quickly.
125 TreeOrderComparator comparator;
126 containingShadow->ShadowDescendants().InsertElementSorted(this, comparator);
128 if (containingShadow->ShadowDescendants()[0] != this) {
129 // Only the first <shadow> (in tree order) of a ShadowRoot can be an insertion point.
130 return NS_OK;
133 if (IsInFallbackContent(this)) {
134 // If the first shadow element in tree order is invalid (in fallback content),
135 // the containing ShadowRoot will not have a shadow insertion point.
136 containingShadow->SetShadowElement(nullptr);
137 } else {
138 mIsInsertionPoint = true;
139 containingShadow->SetShadowElement(this);
142 containingShadow->SetInsertionPointChanged();
145 if (mIsInsertionPoint && containingShadow) {
146 // Propagate BindToTree calls to projected shadow root children.
147 ShadowRoot* projectedShadow = containingShadow->GetOlderShadowRoot();
148 if (projectedShadow) {
149 for (nsIContent* child = projectedShadow->GetFirstChild(); child;
150 child = child->GetNextSibling()) {
151 rv = child->BindToTree(nullptr, projectedShadow,
152 projectedShadow->GetBindingParent(),
153 aCompileEventHandlers);
154 NS_ENSURE_SUCCESS(rv, rv);
159 return NS_OK;
162 void
163 HTMLShadowElement::UnbindFromTree(bool aDeep, bool aNullParent)
165 nsRefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
167 if (mIsInsertionPoint && oldContainingShadow) {
168 // Propagate UnbindFromTree call to previous projected shadow
169 // root children.
170 ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadowRoot();
171 if (projectedShadow) {
172 for (nsIContent* child = projectedShadow->GetFirstChild(); child;
173 child = child->GetNextSibling()) {
174 child->UnbindFromTree(true, false);
179 nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
181 if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) {
182 nsTArray<HTMLShadowElement*>& shadowDescendants =
183 oldContainingShadow->ShadowDescendants();
184 shadowDescendants.RemoveElement(this);
185 oldContainingShadow->SetShadowElement(nullptr);
187 // Find the next shadow insertion point.
188 if (shadowDescendants.Length() > 0 &&
189 !IsInFallbackContent(shadowDescendants[0])) {
190 oldContainingShadow->SetShadowElement(shadowDescendants[0]);
193 oldContainingShadow->SetInsertionPointChanged();
195 mIsInsertionPoint = false;
199 void
200 HTMLShadowElement::DistributeSingleNode(nsIContent* aContent)
202 if (aContent->DestInsertionPoints().Contains(this)) {
203 // Node has already been distrbuted this this node,
204 // we are done.
205 return;
208 aContent->DestInsertionPoints().AppendElement(this);
210 // Handle the case where the shadow element is a child of
211 // a node with a ShadowRoot. The nodes that have been distributed to
212 // this shadow insertion point will need to be reprojected into the
213 // insertion points of the parent's ShadowRoot.
214 ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
215 if (parentShadowRoot) {
216 parentShadowRoot->DistributeSingleNode(aContent);
217 return;
220 // Handle the case where the parent of this shadow element is a ShadowRoot
221 // that is projected into a shadow insertion point in the younger ShadowRoot.
222 ShadowRoot* containingShadow = GetContainingShadow();
223 ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
224 if (youngerShadow && GetParent() == containingShadow) {
225 HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
226 if (youngerShadowElement) {
227 youngerShadowElement->DistributeSingleNode(aContent);
232 void
233 HTMLShadowElement::RemoveDistributedNode(nsIContent* aContent)
235 ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints());
237 // Handle the case where the shadow element is a child of
238 // a node with a ShadowRoot. The nodes that have been distributed to
239 // this shadow insertion point will need to be removed from the
240 // insertion points of the parent's ShadowRoot.
241 ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
242 if (parentShadowRoot) {
243 parentShadowRoot->RemoveDistributedNode(aContent);
244 return;
247 // Handle the case where the parent of this shadow element is a ShadowRoot
248 // that is projected into a shadow insertion point in the younger ShadowRoot.
249 ShadowRoot* containingShadow = GetContainingShadow();
250 ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
251 if (youngerShadow && GetParent() == containingShadow) {
252 HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
253 if (youngerShadowElement) {
254 youngerShadowElement->RemoveDistributedNode(aContent);
259 void
260 HTMLShadowElement::DistributeAllNodes()
262 // All the explicit children of the projected ShadowRoot are distributed
263 // into this shadow insertion point so update the destination insertion
264 // points.
265 ShadowRoot* containingShadow = GetContainingShadow();
266 ShadowRoot* olderShadow = containingShadow->GetOlderShadowRoot();
267 if (olderShadow) {
268 ExplicitChildIterator childIterator(olderShadow);
269 for (nsIContent* content = childIterator.GetNextChild();
270 content;
271 content = childIterator.GetNextChild()) {
272 ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
273 content->DestInsertionPoints().AppendElement(this);
277 // Handle the case where the shadow element is a child of
278 // a node with a ShadowRoot. The nodes that have been distributed to
279 // this shadow insertion point will need to be reprojected into the
280 // insertion points of the parent's ShadowRoot.
281 ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
282 if (parentShadowRoot) {
283 parentShadowRoot->DistributeAllNodes();
284 return;
287 // Handle the case where the parent of this shadow element is a ShadowRoot
288 // that is projected into a shadow insertion point in the younger ShadowRoot.
289 ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
290 if (youngerShadow && GetParent() == containingShadow) {
291 HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
292 if (youngerShadowElement) {
293 youngerShadowElement->DistributeAllNodes();
298 void
299 HTMLShadowElement::ContentAppended(nsIDocument* aDocument,
300 nsIContent* aContainer,
301 nsIContent* aFirstNewContent,
302 int32_t aNewIndexInContainer)
304 // Watch for content appended to the projected shadow (the ShadowRoot that
305 // will be rendered in place of this shadow insertion point) because the
306 // nodes may need to be distributed into other insertion points.
307 nsIContent* currentChild = aFirstNewContent;
308 while (currentChild) {
309 if (ShadowRoot::IsPooledNode(currentChild, aContainer, mProjectedShadow)) {
310 DistributeSingleNode(currentChild);
312 currentChild = currentChild->GetNextSibling();
316 void
317 HTMLShadowElement::ContentInserted(nsIDocument* aDocument,
318 nsIContent* aContainer,
319 nsIContent* aChild,
320 int32_t aIndexInContainer)
322 // Watch for content appended to the projected shadow (the ShadowRoot that
323 // will be rendered in place of this shadow insertion point) because the
324 // nodes may need to be distributed into other insertion points.
325 if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
326 return;
329 DistributeSingleNode(aChild);
332 void
333 HTMLShadowElement::ContentRemoved(nsIDocument* aDocument,
334 nsIContent* aContainer,
335 nsIContent* aChild,
336 int32_t aIndexInContainer,
337 nsIContent* aPreviousSibling)
339 // Watch for content removed from the projected shadow (the ShadowRoot that
340 // will be rendered in place of this shadow insertion point) because the
341 // nodes may need to be removed from other insertion points.
342 if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
343 return;
346 RemoveDistributedNode(aChild);