Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 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
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/ShadowRoot.h"
8 :
9 : #include "ChildIterator.h"
10 : #include "nsContentUtils.h"
11 : #include "nsDocument.h"
12 : #include "mozilla/dom/HTMLShadowElement.h"
13 : #include "mozilla/dom/HTMLUnknownElement.h"
14 : #include "mozilla/dom/HTMLShadowElementBinding.h"
15 :
16 : // Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Shadow) to add check for web components
17 : // being enabled.
18 : nsGenericHTMLElement*
19 0 : NS_NewHTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
20 : mozilla::dom::FromParser aFromParser)
21 : {
22 : // When this check is removed, remove the nsDocument.h and
23 : // HTMLUnknownElement.h includes. Also remove nsINode::IsHTMLShadowElement.
24 : //
25 : // We have to jump through some hoops to be able to produce both NodeInfo* and
26 : // already_AddRefed<NodeInfo>& for our callees.
27 0 : RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
28 0 : if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) {
29 0 : already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
30 0 : return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
31 : }
32 :
33 0 : already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
34 0 : return new mozilla::dom::HTMLShadowElement(nodeInfoArg);
35 : }
36 :
37 : using namespace mozilla::dom;
38 :
39 0 : HTMLShadowElement::HTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
40 0 : : nsGenericHTMLElement(aNodeInfo), mIsInsertionPoint(false)
41 : {
42 0 : }
43 :
44 0 : HTMLShadowElement::~HTMLShadowElement()
45 : {
46 0 : if (mProjectedShadow) {
47 0 : mProjectedShadow->RemoveMutationObserver(this);
48 : }
49 0 : }
50 :
51 : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLShadowElement)
52 :
53 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLShadowElement,
54 : nsGenericHTMLElement)
55 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProjectedShadow)
56 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
57 :
58 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLShadowElement,
59 : nsGenericHTMLElement)
60 0 : if (tmp->mProjectedShadow) {
61 0 : tmp->mProjectedShadow->RemoveMutationObserver(tmp);
62 0 : tmp->mProjectedShadow = nullptr;
63 : }
64 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
65 :
66 0 : NS_IMPL_ADDREF_INHERITED(HTMLShadowElement, Element)
67 0 : NS_IMPL_RELEASE_INHERITED(HTMLShadowElement, Element)
68 :
69 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLShadowElement)
70 0 : NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
71 :
72 0 : NS_IMPL_ELEMENT_CLONE(HTMLShadowElement)
73 :
74 : JSObject*
75 0 : HTMLShadowElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
76 : {
77 0 : return HTMLShadowElementBinding::Wrap(aCx, this, aGivenProto);
78 : }
79 :
80 : void
81 0 : HTMLShadowElement::SetProjectedShadow(ShadowRoot* aProjectedShadow)
82 : {
83 0 : if (mProjectedShadow) {
84 0 : mProjectedShadow->RemoveMutationObserver(this);
85 :
86 : // The currently projected ShadowRoot is going away,
87 : // thus the destination insertion points need to be updated.
88 0 : ExplicitChildIterator childIterator(mProjectedShadow);
89 0 : for (nsIContent* content = childIterator.GetNextChild();
90 0 : content;
91 : content = childIterator.GetNextChild()) {
92 0 : ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
93 : }
94 : }
95 :
96 0 : mProjectedShadow = aProjectedShadow;
97 0 : if (mProjectedShadow) {
98 : // A new ShadowRoot is being projected, thus its explcit
99 : // children will be distributed to this shadow insertion point.
100 0 : ExplicitChildIterator childIterator(mProjectedShadow);
101 0 : for (nsIContent* content = childIterator.GetNextChild();
102 0 : content;
103 : content = childIterator.GetNextChild()) {
104 0 : content->DestInsertionPoints().AppendElement(this);
105 : }
106 :
107 : // Watch for mutations on the projected shadow because
108 : // it affects the nodes that are distributed to this shadow
109 : // insertion point.
110 0 : mProjectedShadow->AddMutationObserver(this);
111 : }
112 0 : }
113 :
114 : static bool
115 0 : IsInFallbackContent(nsIContent* aContent)
116 : {
117 0 : nsINode* parentNode = aContent->GetParentNode();
118 0 : while (parentNode) {
119 0 : if (parentNode->IsHTMLElement(nsGkAtoms::content)) {
120 0 : return true;
121 : }
122 0 : parentNode = parentNode->GetParentNode();
123 : }
124 :
125 0 : return false;
126 : }
127 :
128 : nsresult
129 0 : HTMLShadowElement::BindToTree(nsIDocument* aDocument,
130 : nsIContent* aParent,
131 : nsIContent* aBindingParent,
132 : bool aCompileEventHandlers)
133 : {
134 0 : RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
135 :
136 0 : nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
137 : aBindingParent,
138 0 : aCompileEventHandlers);
139 0 : NS_ENSURE_SUCCESS(rv, rv);
140 :
141 0 : ShadowRoot* containingShadow = GetContainingShadow();
142 0 : if (containingShadow && !oldContainingShadow) {
143 : // Keep track of all descendant <shadow> elements in tree order so
144 : // that when the current shadow insertion point is removed, the next
145 : // one can be found quickly.
146 : TreeOrderComparator comparator;
147 0 : containingShadow->ShadowDescendants().InsertElementSorted(this, comparator);
148 :
149 0 : if (containingShadow->ShadowDescendants()[0] != this) {
150 : // Only the first <shadow> (in tree order) of a ShadowRoot can be an insertion point.
151 0 : return NS_OK;
152 : }
153 :
154 0 : if (IsInFallbackContent(this)) {
155 : // If the first shadow element in tree order is invalid (in fallback content),
156 : // the containing ShadowRoot will not have a shadow insertion point.
157 0 : containingShadow->SetShadowElement(nullptr);
158 : } else {
159 0 : mIsInsertionPoint = true;
160 0 : containingShadow->SetShadowElement(this);
161 : }
162 :
163 0 : containingShadow->SetInsertionPointChanged();
164 : }
165 :
166 0 : if (mIsInsertionPoint && containingShadow) {
167 : // Propagate BindToTree calls to projected shadow root children.
168 0 : ShadowRoot* projectedShadow = containingShadow->GetOlderShadowRoot();
169 0 : if (projectedShadow) {
170 0 : projectedShadow->SetIsComposedDocParticipant(IsInComposedDoc());
171 :
172 0 : for (nsIContent* child = projectedShadow->GetFirstChild(); child;
173 0 : child = child->GetNextSibling()) {
174 0 : rv = child->BindToTree(nullptr, projectedShadow,
175 0 : projectedShadow->GetBindingParent(),
176 0 : aCompileEventHandlers);
177 0 : NS_ENSURE_SUCCESS(rv, rv);
178 : }
179 : }
180 : }
181 :
182 0 : return NS_OK;
183 : }
184 :
185 : void
186 0 : HTMLShadowElement::UnbindFromTree(bool aDeep, bool aNullParent)
187 : {
188 0 : RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
189 :
190 0 : if (mIsInsertionPoint && oldContainingShadow) {
191 : // Propagate UnbindFromTree call to previous projected shadow
192 : // root children.
193 0 : ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadowRoot();
194 0 : if (projectedShadow) {
195 0 : for (nsIContent* child = projectedShadow->GetFirstChild(); child;
196 0 : child = child->GetNextSibling()) {
197 0 : child->UnbindFromTree(true, false);
198 : }
199 :
200 0 : projectedShadow->SetIsComposedDocParticipant(false);
201 : }
202 : }
203 :
204 0 : nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
205 :
206 0 : if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) {
207 : nsTArray<HTMLShadowElement*>& shadowDescendants =
208 0 : oldContainingShadow->ShadowDescendants();
209 0 : shadowDescendants.RemoveElement(this);
210 0 : oldContainingShadow->SetShadowElement(nullptr);
211 :
212 : // Find the next shadow insertion point.
213 0 : if (shadowDescendants.Length() > 0 &&
214 0 : !IsInFallbackContent(shadowDescendants[0])) {
215 0 : oldContainingShadow->SetShadowElement(shadowDescendants[0]);
216 : }
217 :
218 0 : oldContainingShadow->SetInsertionPointChanged();
219 :
220 0 : mIsInsertionPoint = false;
221 : }
222 0 : }
223 :
224 : void
225 0 : HTMLShadowElement::DistributeSingleNode(nsIContent* aContent)
226 : {
227 0 : if (aContent->DestInsertionPoints().Contains(this)) {
228 : // Node has already been distrbuted this this node,
229 : // we are done.
230 0 : return;
231 : }
232 :
233 0 : aContent->DestInsertionPoints().AppendElement(this);
234 :
235 : // Handle the case where the shadow element is a child of
236 : // a node with a ShadowRoot. The nodes that have been distributed to
237 : // this shadow insertion point will need to be reprojected into the
238 : // insertion points of the parent's ShadowRoot.
239 0 : ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
240 0 : if (parentShadowRoot) {
241 0 : parentShadowRoot->DistributeSingleNode(aContent);
242 0 : return;
243 : }
244 :
245 : // Handle the case where the parent of this shadow element is a ShadowRoot
246 : // that is projected into a shadow insertion point in the younger ShadowRoot.
247 0 : ShadowRoot* containingShadow = GetContainingShadow();
248 0 : ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
249 0 : if (youngerShadow && GetParent() == containingShadow) {
250 0 : HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
251 0 : if (youngerShadowElement) {
252 0 : youngerShadowElement->DistributeSingleNode(aContent);
253 : }
254 : }
255 : }
256 :
257 : void
258 0 : HTMLShadowElement::RemoveDistributedNode(nsIContent* aContent)
259 : {
260 0 : ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints());
261 :
262 : // Handle the case where the shadow element is a child of
263 : // a node with a ShadowRoot. The nodes that have been distributed to
264 : // this shadow insertion point will need to be removed from the
265 : // insertion points of the parent's ShadowRoot.
266 0 : ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
267 0 : if (parentShadowRoot) {
268 0 : parentShadowRoot->RemoveDistributedNode(aContent);
269 0 : return;
270 : }
271 :
272 : // Handle the case where the parent of this shadow element is a ShadowRoot
273 : // that is projected into a shadow insertion point in the younger ShadowRoot.
274 0 : ShadowRoot* containingShadow = GetContainingShadow();
275 0 : ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
276 0 : if (youngerShadow && GetParent() == containingShadow) {
277 0 : HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
278 0 : if (youngerShadowElement) {
279 0 : youngerShadowElement->RemoveDistributedNode(aContent);
280 : }
281 : }
282 : }
283 :
284 : void
285 0 : HTMLShadowElement::DistributeAllNodes()
286 : {
287 : // All the explicit children of the projected ShadowRoot are distributed
288 : // into this shadow insertion point so update the destination insertion
289 : // points.
290 0 : ShadowRoot* containingShadow = GetContainingShadow();
291 0 : ShadowRoot* olderShadow = containingShadow->GetOlderShadowRoot();
292 0 : if (olderShadow) {
293 0 : ExplicitChildIterator childIterator(olderShadow);
294 0 : for (nsIContent* content = childIterator.GetNextChild();
295 0 : content;
296 : content = childIterator.GetNextChild()) {
297 0 : ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
298 0 : content->DestInsertionPoints().AppendElement(this);
299 : }
300 : }
301 :
302 : // Handle the case where the shadow element is a child of
303 : // a node with a ShadowRoot. The nodes that have been distributed to
304 : // this shadow insertion point will need to be reprojected into the
305 : // insertion points of the parent's ShadowRoot.
306 0 : ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
307 0 : if (parentShadowRoot) {
308 0 : parentShadowRoot->DistributeAllNodes();
309 0 : return;
310 : }
311 :
312 : // Handle the case where the parent of this shadow element is a ShadowRoot
313 : // that is projected into a shadow insertion point in the younger ShadowRoot.
314 0 : ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
315 0 : if (youngerShadow && GetParent() == containingShadow) {
316 0 : HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
317 0 : if (youngerShadowElement) {
318 0 : youngerShadowElement->DistributeAllNodes();
319 : }
320 : }
321 : }
322 :
323 : void
324 0 : HTMLShadowElement::ContentAppended(nsIDocument* aDocument,
325 : nsIContent* aContainer,
326 : nsIContent* aFirstNewContent,
327 : int32_t aNewIndexInContainer)
328 : {
329 : // Watch for content appended to the projected shadow (the ShadowRoot that
330 : // will be rendered in place of this shadow insertion point) because the
331 : // nodes may need to be distributed into other insertion points.
332 0 : nsIContent* currentChild = aFirstNewContent;
333 0 : while (currentChild) {
334 0 : if (ShadowRoot::IsPooledNode(currentChild, aContainer, mProjectedShadow)) {
335 0 : DistributeSingleNode(currentChild);
336 : }
337 0 : currentChild = currentChild->GetNextSibling();
338 : }
339 0 : }
340 :
341 : void
342 0 : HTMLShadowElement::ContentInserted(nsIDocument* aDocument,
343 : nsIContent* aContainer,
344 : nsIContent* aChild,
345 : int32_t aIndexInContainer)
346 : {
347 : // Watch for content appended to the projected shadow (the ShadowRoot that
348 : // will be rendered in place of this shadow insertion point) because the
349 : // nodes may need to be distributed into other insertion points.
350 0 : if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
351 0 : return;
352 : }
353 :
354 0 : DistributeSingleNode(aChild);
355 : }
356 :
357 : void
358 0 : HTMLShadowElement::ContentRemoved(nsIDocument* aDocument,
359 : nsIContent* aContainer,
360 : nsIContent* aChild,
361 : int32_t aIndexInContainer,
362 : nsIContent* aPreviousSibling)
363 : {
364 : // Watch for content removed from the projected shadow (the ShadowRoot that
365 : // will be rendered in place of this shadow insertion point) because the
366 : // nodes may need to be removed from other insertion points.
367 0 : if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
368 0 : return;
369 : }
370 :
371 0 : RemoveDistributedNode(aChild);
372 : }
373 :
|