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
,
33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProjectedShadow
)
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLShadowElement
,
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
)
53 HTMLShadowElement::WrapNode(JSContext
*aCx
)
55 return HTMLShadowElementBinding::Wrap(aCx
, this);
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();
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();
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
88 mProjectedShadow
->AddMutationObserver(this);
93 IsInFallbackContent(nsIContent
* aContent
)
95 nsINode
* parentNode
= aContent
->GetParentNode();
97 if (parentNode
->IsElement() &&
98 parentNode
->AsElement()->IsHTML(nsGkAtoms::content
)) {
101 parentNode
= parentNode
->GetParentNode();
108 HTMLShadowElement::BindToTree(nsIDocument
* aDocument
,
110 nsIContent
* aBindingParent
,
111 bool aCompileEventHandlers
)
113 nsRefPtr
<ShadowRoot
> oldContainingShadow
= GetContainingShadow();
115 nsresult rv
= nsGenericHTMLElement::BindToTree(aDocument
, aParent
,
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.
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);
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
);
163 HTMLShadowElement::UnbindFromTree(bool aDeep
, bool aNullParent
)
165 nsRefPtr
<ShadowRoot
> oldContainingShadow
= GetContainingShadow();
167 if (mIsInsertionPoint
&& oldContainingShadow
) {
168 // Propagate UnbindFromTree call to previous projected shadow
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;
200 HTMLShadowElement::DistributeSingleNode(nsIContent
* aContent
)
202 if (aContent
->DestInsertionPoints().Contains(this)) {
203 // Node has already been distrbuted this this node,
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
);
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
);
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
);
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
);
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
265 ShadowRoot
* containingShadow
= GetContainingShadow();
266 ShadowRoot
* olderShadow
= containingShadow
->GetOlderShadowRoot();
268 ExplicitChildIterator
childIterator(olderShadow
);
269 for (nsIContent
* content
= childIterator
.GetNextChild();
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();
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();
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();
317 HTMLShadowElement::ContentInserted(nsIDocument
* aDocument
,
318 nsIContent
* aContainer
,
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
)) {
329 DistributeSingleNode(aChild
);
333 HTMLShadowElement::ContentRemoved(nsIDocument
* aDocument
,
334 nsIContent
* aContainer
,
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
)) {
346 RemoveDistributedNode(aChild
);