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/Preferences.h"
8 : #include "mozilla/dom/ShadowRoot.h"
9 : #include "mozilla/dom/ShadowRootBinding.h"
10 : #include "mozilla/dom/DocumentFragment.h"
11 : #include "ChildIterator.h"
12 : #include "nsContentUtils.h"
13 : #include "nsDOMClassInfoID.h"
14 : #include "nsIDOMHTMLElement.h"
15 : #include "nsIStyleSheetLinkingElement.h"
16 : #include "mozilla/dom/Element.h"
17 : #include "mozilla/dom/HTMLContentElement.h"
18 : #include "mozilla/dom/HTMLShadowElement.h"
19 : #include "nsXBLPrototypeBinding.h"
20 : #include "mozilla/StyleSheet.h"
21 : #include "mozilla/StyleSheetInlines.h"
22 :
23 : using namespace mozilla;
24 : using namespace mozilla::dom;
25 :
26 : NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
27 :
28 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot,
29 : DocumentFragment)
30 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPoolHost)
31 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetList)
32 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOlderShadow)
33 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mYoungerShadow)
34 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAssociatedBinding)
35 0 : for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done();
36 0 : iter.Next()) {
37 0 : iter.Get()->Traverse(&cb);
38 : }
39 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
40 :
41 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ShadowRoot,
42 : DocumentFragment)
43 0 : if (tmp->mPoolHost) {
44 0 : tmp->mPoolHost->RemoveMutationObserver(tmp);
45 : }
46 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPoolHost)
47 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheetList)
48 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mOlderShadow)
49 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mYoungerShadow)
50 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mAssociatedBinding)
51 0 : tmp->mIdentifierMap.Clear();
52 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
53 :
54 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRoot)
55 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
56 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
57 0 : NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
58 :
59 0 : NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
60 0 : NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
61 :
62 0 : ShadowRoot::ShadowRoot(nsIContent* aContent,
63 : already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
64 0 : nsXBLPrototypeBinding* aProtoBinding)
65 : : DocumentFragment(aNodeInfo), mPoolHost(aContent),
66 : mProtoBinding(aProtoBinding), mShadowElement(nullptr),
67 0 : mInsertionPointChanged(false), mIsComposedDocParticipant(false)
68 : {
69 0 : SetHost(aContent);
70 :
71 : // Nodes in a shadow tree should never store a value
72 : // in the subtree root pointer, nodes in the shadow tree
73 : // track the subtree root using GetContainingShadow().
74 0 : ClearSubtreeRootPointer();
75 :
76 0 : SetFlags(NODE_IS_IN_SHADOW_TREE);
77 :
78 0 : DOMSlots()->mBindingParent = aContent;
79 0 : DOMSlots()->mContainingShadow = this;
80 :
81 : // Add the ShadowRoot as a mutation observer on the host to watch
82 : // for mutations because the insertion points in this ShadowRoot
83 : // may need to be updated when the host children are modified.
84 0 : mPoolHost->AddMutationObserver(this);
85 0 : }
86 :
87 0 : ShadowRoot::~ShadowRoot()
88 : {
89 0 : if (mPoolHost) {
90 : // mPoolHost may have been unlinked or a new ShadowRoot may have been
91 : // creating, making this one obsolete.
92 0 : mPoolHost->RemoveMutationObserver(this);
93 : }
94 :
95 0 : UnsetFlags(NODE_IS_IN_SHADOW_TREE);
96 :
97 : // nsINode destructor expects mSubtreeRoot == this.
98 0 : SetSubtreeRootPointer(this);
99 :
100 0 : SetHost(nullptr);
101 0 : }
102 :
103 : JSObject*
104 0 : ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
105 : {
106 0 : return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto);
107 : }
108 :
109 : ShadowRoot*
110 156964 : ShadowRoot::FromNode(nsINode* aNode)
111 : {
112 156964 : if (aNode->IsInShadowTree() && !aNode->GetParentNode()) {
113 0 : MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE,
114 : "ShadowRoot is a document fragment.");
115 0 : return static_cast<ShadowRoot*>(aNode);
116 : }
117 :
118 156964 : return nullptr;
119 : }
120 :
121 : void
122 0 : ShadowRoot::StyleSheetChanged()
123 : {
124 0 : mProtoBinding->FlushSkinSheets();
125 :
126 0 : nsIPresShell* shell = OwnerDoc()->GetShell();
127 0 : if (shell) {
128 0 : OwnerDoc()->BeginUpdate(UPDATE_STYLE);
129 0 : shell->RecordShadowStyleChange(this);
130 0 : OwnerDoc()->EndUpdate(UPDATE_STYLE);
131 : }
132 0 : }
133 :
134 : void
135 0 : ShadowRoot::InsertSheet(StyleSheet* aSheet,
136 : nsIContent* aLinkingContent)
137 : {
138 : nsCOMPtr<nsIStyleSheetLinkingElement>
139 0 : linkingElement = do_QueryInterface(aLinkingContent);
140 0 : MOZ_ASSERT(linkingElement, "The only styles in a ShadowRoot should come "
141 : "from <style>.");
142 :
143 0 : linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet
144 :
145 : // Find the correct position to insert into the style sheet list (must
146 : // be in tree order).
147 0 : for (size_t i = 0; i <= mProtoBinding->SheetCount(); i++) {
148 0 : if (i == mProtoBinding->SheetCount()) {
149 0 : mProtoBinding->AppendStyleSheet(aSheet);
150 0 : break;
151 : }
152 :
153 0 : nsINode* sheetOwningNode = mProtoBinding->StyleSheetAt(i)->GetOwnerNode();
154 0 : if (nsContentUtils::PositionIsBefore(aLinkingContent, sheetOwningNode)) {
155 0 : mProtoBinding->InsertStyleSheetAt(i, aSheet);
156 0 : break;
157 : }
158 : }
159 :
160 0 : if (aSheet->IsApplicable()) {
161 0 : StyleSheetChanged();
162 : }
163 0 : }
164 :
165 : void
166 0 : ShadowRoot::RemoveSheet(StyleSheet* aSheet)
167 : {
168 0 : mProtoBinding->RemoveStyleSheet(aSheet);
169 :
170 0 : if (aSheet->IsApplicable()) {
171 0 : StyleSheetChanged();
172 : }
173 0 : }
174 :
175 : Element*
176 0 : ShadowRoot::GetElementById(const nsAString& aElementId)
177 : {
178 0 : nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
179 0 : return entry ? entry->GetIdElement() : nullptr;
180 : }
181 :
182 : already_AddRefed<nsContentList>
183 0 : ShadowRoot::GetElementsByTagName(const nsAString& aTagName)
184 : {
185 0 : return NS_GetContentList(this, kNameSpaceID_Unknown, aTagName);
186 : }
187 :
188 : already_AddRefed<nsContentList>
189 0 : ShadowRoot::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
190 : const nsAString& aLocalName)
191 : {
192 0 : int32_t nameSpaceId = kNameSpaceID_Wildcard;
193 :
194 0 : if (!aNamespaceURI.EqualsLiteral("*")) {
195 : nsresult rv =
196 0 : nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
197 0 : nameSpaceId);
198 0 : NS_ENSURE_SUCCESS(rv, nullptr);
199 : }
200 :
201 0 : NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
202 :
203 0 : return NS_GetContentList(this, nameSpaceId, aLocalName);
204 : }
205 :
206 : void
207 0 : ShadowRoot::AddToIdTable(Element* aElement, nsIAtom* aId)
208 : {
209 0 : nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
210 0 : if (entry) {
211 0 : entry->AddIdElement(aElement);
212 : }
213 0 : }
214 :
215 : void
216 0 : ShadowRoot::RemoveFromIdTable(Element* aElement, nsIAtom* aId)
217 : {
218 0 : nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
219 0 : if (entry) {
220 0 : entry->RemoveIdElement(aElement);
221 0 : if (entry->IsEmpty()) {
222 0 : mIdentifierMap.RemoveEntry(entry);
223 : }
224 : }
225 0 : }
226 :
227 : already_AddRefed<nsContentList>
228 0 : ShadowRoot::GetElementsByClassName(const nsAString& aClasses)
229 : {
230 0 : return nsContentUtils::GetElementsByClassName(this, aClasses);
231 : }
232 :
233 : void
234 0 : ShadowRoot::AddInsertionPoint(HTMLContentElement* aInsertionPoint)
235 : {
236 : TreeOrderComparator comparator;
237 0 : mInsertionPoints.InsertElementSorted(aInsertionPoint, comparator);
238 0 : }
239 :
240 : void
241 0 : ShadowRoot::RemoveInsertionPoint(HTMLContentElement* aInsertionPoint)
242 : {
243 0 : mInsertionPoints.RemoveElement(aInsertionPoint);
244 0 : }
245 :
246 : void
247 0 : ShadowRoot::SetYoungerShadow(ShadowRoot* aYoungerShadow)
248 : {
249 0 : mYoungerShadow = aYoungerShadow;
250 0 : mYoungerShadow->mOlderShadow = this;
251 :
252 0 : ChangePoolHost(mYoungerShadow->GetShadowElement());
253 0 : }
254 :
255 : void
256 0 : ShadowRoot::RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
257 : nsTArray<nsIContent*>& aDestInsertionPoints)
258 : {
259 : // Remove the insertion point from the destination insertion points.
260 : // Also remove all succeeding insertion points because it is no longer
261 : // possible for the content to be distributed into deeper node trees.
262 0 : int32_t index = aDestInsertionPoints.IndexOf(aInsertionPoint);
263 :
264 : // It's possible that we already removed the insertion point while processing
265 : // other insertion point removals.
266 0 : if (index >= 0) {
267 0 : aDestInsertionPoints.SetLength(index);
268 : }
269 0 : }
270 :
271 : void
272 0 : ShadowRoot::DistributeSingleNode(nsIContent* aContent)
273 : {
274 : // Find the insertion point to which the content belongs.
275 0 : HTMLContentElement* insertionPoint = nullptr;
276 0 : for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
277 0 : if (mInsertionPoints[i]->Match(aContent)) {
278 0 : if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) {
279 : // Node is already matched into the insertion point. We are done.
280 0 : return;
281 : }
282 :
283 : // Matching may cause the insertion point to drop fallback content.
284 0 : if (mInsertionPoints[i]->MatchedNodes().IsEmpty() &&
285 0 : static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
286 : // This match will cause the insertion point to drop all fallback
287 : // content and used matched nodes instead. Give up on the optimization
288 : // and just distribute all nodes.
289 0 : DistributeAllNodes();
290 0 : return;
291 : }
292 0 : insertionPoint = mInsertionPoints[i];
293 0 : break;
294 : }
295 : }
296 :
297 : // Find the index into the insertion point.
298 0 : if (insertionPoint) {
299 0 : nsCOMArray<nsIContent>& matchedNodes = insertionPoint->MatchedNodes();
300 : // Find the appropriate position in the matched node list for the
301 : // newly distributed content.
302 0 : bool isIndexFound = false;
303 0 : MOZ_ASSERT(mPoolHost, "Where did the content come from if there is no pool host?");
304 0 : ExplicitChildIterator childIterator(mPoolHost);
305 0 : for (uint32_t i = 0; i < matchedNodes.Length(); i++) {
306 : // Seek through the host's explicit children until the inserted content
307 : // is found or when the current matched node is reached.
308 0 : if (childIterator.Seek(aContent, matchedNodes[i])) {
309 : // aContent was found before the current matched node.
310 0 : insertionPoint->InsertMatchedNode(i, aContent);
311 0 : isIndexFound = true;
312 0 : break;
313 : }
314 : }
315 :
316 0 : if (!isIndexFound) {
317 : // We have still not found an index in the insertion point,
318 : // thus it must be at the end.
319 0 : MOZ_ASSERT(childIterator.Seek(aContent, nullptr),
320 : "Trying to match a node that is not a candidate to be matched");
321 0 : insertionPoint->AppendMatchedNode(aContent);
322 : }
323 :
324 : // Handle the case where the parent of the insertion point is a ShadowRoot
325 : // that is projected into the younger ShadowRoot's shadow insertion point.
326 : // The node distributed into the insertion point must be reprojected
327 : // to the shadow insertion point.
328 0 : if (insertionPoint->GetParent() == this &&
329 0 : mYoungerShadow && mYoungerShadow->GetShadowElement()) {
330 0 : mYoungerShadow->GetShadowElement()->DistributeSingleNode(aContent);
331 : }
332 :
333 : // Handle the case where the parent of the insertion point has a ShadowRoot.
334 : // The node distributed into the insertion point must be reprojected to the
335 : // insertion points of the parent's ShadowRoot.
336 0 : ShadowRoot* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
337 0 : if (parentShadow) {
338 0 : parentShadow->DistributeSingleNode(aContent);
339 : }
340 :
341 : // Handle the case where the parent of the insertion point is the <shadow>
342 : // element. The node distributed into the insertion point must be reprojected
343 : // into the older ShadowRoot's insertion points.
344 0 : if (mShadowElement && mShadowElement == insertionPoint->GetParent()) {
345 0 : ShadowRoot* olderShadow = mShadowElement->GetOlderShadowRoot();
346 0 : if (olderShadow) {
347 0 : olderShadow->DistributeSingleNode(aContent);
348 : }
349 : }
350 : }
351 : }
352 :
353 : void
354 0 : ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
355 : {
356 : // Find insertion point containing the content and remove the node.
357 0 : for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
358 0 : if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) {
359 : // Removing the matched node may cause the insertion point to use
360 : // fallback content.
361 0 : if (mInsertionPoints[i]->MatchedNodes().Length() == 1 &&
362 0 : static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
363 : // Removing the matched node will cause fallback content to be
364 : // used instead. Give up optimization and distribute all nodes.
365 0 : DistributeAllNodes();
366 0 : return;
367 : }
368 :
369 0 : mInsertionPoints[i]->RemoveMatchedNode(aContent);
370 :
371 : // Handle the case where the parent of the insertion point is a ShadowRoot
372 : // that is projected into the younger ShadowRoot's shadow insertion point.
373 : // The removed node needs to be removed from the shadow insertion point.
374 0 : if (mInsertionPoints[i]->GetParent() == this) {
375 0 : if (mYoungerShadow && mYoungerShadow->GetShadowElement()) {
376 0 : mYoungerShadow->GetShadowElement()->RemoveDistributedNode(aContent);
377 : }
378 : }
379 :
380 : // Handle the case where the parent of the insertion point has a ShadowRoot.
381 : // The removed node needs to be removed from the insertion points of the
382 : // parent's ShadowRoot.
383 0 : ShadowRoot* parentShadow = mInsertionPoints[i]->GetParent()->GetShadowRoot();
384 0 : if (parentShadow) {
385 0 : parentShadow->RemoveDistributedNode(aContent);
386 : }
387 :
388 : // Handle the case where the parent of the insertion point is the <shadow>
389 : // element. The removed node must be removed from the older ShadowRoot's
390 : // insertion points.
391 0 : if (mShadowElement && mShadowElement == mInsertionPoints[i]->GetParent()) {
392 0 : ShadowRoot* olderShadow = mShadowElement->GetOlderShadowRoot();
393 0 : if (olderShadow) {
394 0 : olderShadow->RemoveDistributedNode(aContent);
395 : }
396 : }
397 :
398 0 : break;
399 : }
400 : }
401 : }
402 :
403 : void
404 0 : ShadowRoot::DistributeAllNodes()
405 : {
406 : // Create node pool.
407 0 : nsTArray<nsIContent*> nodePool;
408 :
409 : // Make sure there is a pool host, an older shadow may not have
410 : // one if the younger shadow does not have a <shadow> element.
411 0 : if (mPoolHost) {
412 0 : ExplicitChildIterator childIterator(mPoolHost);
413 0 : for (nsIContent* content = childIterator.GetNextChild();
414 0 : content;
415 0 : content = childIterator.GetNextChild()) {
416 0 : nodePool.AppendElement(content);
417 : }
418 : }
419 :
420 0 : nsTArray<ShadowRoot*> shadowsToUpdate;
421 :
422 0 : for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
423 0 : mInsertionPoints[i]->ClearMatchedNodes();
424 : // Assign matching nodes from node pool.
425 0 : for (uint32_t j = 0; j < nodePool.Length(); j++) {
426 0 : if (mInsertionPoints[i]->Match(nodePool[j])) {
427 0 : mInsertionPoints[i]->AppendMatchedNode(nodePool[j]);
428 0 : nodePool.RemoveElementAt(j--);
429 : }
430 : }
431 :
432 : // Keep track of instances where the content insertion point is distributed
433 : // (parent of insertion point has a ShadowRoot).
434 0 : nsIContent* insertionParent = mInsertionPoints[i]->GetParent();
435 0 : MOZ_ASSERT(insertionParent, "The only way for an insertion point to be in the"
436 : "mInsertionPoints array is to be a descendant of a"
437 : "ShadowRoot, in which case, it should have a parent");
438 :
439 : // If the parent of the insertion point has a ShadowRoot, the nodes distributed
440 : // to the insertion point must be reprojected to the insertion points of the
441 : // parent's ShadowRoot.
442 0 : ShadowRoot* parentShadow = insertionParent->GetShadowRoot();
443 0 : if (parentShadow && !shadowsToUpdate.Contains(parentShadow)) {
444 0 : shadowsToUpdate.AppendElement(parentShadow);
445 : }
446 : }
447 :
448 : // If there is a shadow insertion point in this ShadowRoot, the children
449 : // of the shadow insertion point needs to be distributed into the insertion
450 : // points of the older ShadowRoot.
451 0 : if (mShadowElement && mOlderShadow) {
452 0 : mOlderShadow->DistributeAllNodes();
453 : }
454 :
455 : // If there is a younger ShadowRoot with a shadow insertion point,
456 : // then the children of this ShadowRoot needs to be distributed to
457 : // the younger ShadowRoot's shadow insertion point.
458 0 : if (mYoungerShadow && mYoungerShadow->GetShadowElement()) {
459 0 : mYoungerShadow->GetShadowElement()->DistributeAllNodes();
460 : }
461 :
462 0 : for (uint32_t i = 0; i < shadowsToUpdate.Length(); i++) {
463 0 : shadowsToUpdate[i]->DistributeAllNodes();
464 : }
465 0 : }
466 :
467 : void
468 0 : ShadowRoot::GetInnerHTML(nsAString& aInnerHTML)
469 : {
470 0 : GetMarkup(false, aInnerHTML);
471 0 : }
472 :
473 : void
474 0 : ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
475 : {
476 0 : SetInnerHTMLInternal(aInnerHTML, aError);
477 0 : }
478 :
479 : Element*
480 0 : ShadowRoot::Host()
481 : {
482 0 : nsIContent* host = GetHost();
483 0 : MOZ_ASSERT(host && host->IsElement(),
484 : "ShadowRoot host should always be an element, "
485 : "how else did we create this ShadowRoot?");
486 0 : return host->AsElement();
487 : }
488 :
489 : bool
490 0 : ShadowRoot::ApplyAuthorStyles()
491 : {
492 0 : return mProtoBinding->InheritsStyle();
493 : }
494 :
495 : void
496 0 : ShadowRoot::SetApplyAuthorStyles(bool aApplyAuthorStyles)
497 : {
498 0 : mProtoBinding->SetInheritsStyle(aApplyAuthorStyles);
499 :
500 0 : nsIPresShell* shell = OwnerDoc()->GetShell();
501 0 : if (shell) {
502 0 : OwnerDoc()->BeginUpdate(UPDATE_STYLE);
503 0 : shell->RecordShadowStyleChange(this);
504 0 : OwnerDoc()->EndUpdate(UPDATE_STYLE);
505 : }
506 0 : }
507 :
508 : StyleSheetList*
509 0 : ShadowRoot::StyleSheets()
510 : {
511 0 : if (!mStyleSheetList) {
512 0 : mStyleSheetList = new ShadowRootStyleSheetList(this);
513 : }
514 :
515 0 : return mStyleSheetList;
516 : }
517 :
518 : void
519 0 : ShadowRoot::SetShadowElement(HTMLShadowElement* aShadowElement)
520 : {
521 : // If there is already a shadow element point, remove
522 : // the projected shadow because it is no longer an insertion
523 : // point.
524 0 : if (mShadowElement) {
525 0 : mShadowElement->SetProjectedShadow(nullptr);
526 : }
527 :
528 0 : if (mOlderShadow) {
529 : // Nodes for distribution will come from the new shadow element.
530 0 : mOlderShadow->ChangePoolHost(aShadowElement);
531 : }
532 :
533 : // Set the new shadow element to project the older ShadowRoot because
534 : // it is the current shadow insertion point.
535 0 : mShadowElement = aShadowElement;
536 0 : if (mShadowElement) {
537 0 : mShadowElement->SetProjectedShadow(mOlderShadow);
538 : }
539 0 : }
540 :
541 : void
542 0 : ShadowRoot::ChangePoolHost(nsIContent* aNewHost)
543 : {
544 0 : if (mPoolHost) {
545 0 : mPoolHost->RemoveMutationObserver(this);
546 : }
547 :
548 : // Clear the nodes matched to content insertion points
549 : // because it is no longer relevant.
550 0 : for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
551 0 : mInsertionPoints[i]->ClearMatchedNodes();
552 : }
553 :
554 0 : mPoolHost = aNewHost;
555 0 : if (mPoolHost) {
556 0 : mPoolHost->AddMutationObserver(this);
557 : }
558 0 : }
559 :
560 : bool
561 1638 : ShadowRoot::IsShadowInsertionPoint(nsIContent* aContent)
562 : {
563 1638 : if (!aContent) {
564 0 : return false;
565 : }
566 :
567 1638 : HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(aContent);
568 1638 : return shadowElem && shadowElem->IsInsertionPoint();
569 : }
570 :
571 : /**
572 : * Returns whether the web components pool population algorithm
573 : * on the host would contain |aContent|. This function ignores
574 : * insertion points in the pool, thus should only be used to
575 : * test nodes that have not yet been distributed.
576 : */
577 : bool
578 0 : ShadowRoot::IsPooledNode(nsIContent* aContent, nsIContent* aContainer,
579 : nsIContent* aHost)
580 : {
581 0 : if (nsContentUtils::IsContentInsertionPoint(aContent) ||
582 0 : IsShadowInsertionPoint(aContent)) {
583 : // Insertion points never end up in the pool.
584 0 : return false;
585 : }
586 :
587 0 : if (aContainer == aHost &&
588 0 : nsContentUtils::IsInSameAnonymousTree(aContainer, aContent)) {
589 : // Children of the host will end up in the pool. We check to ensure
590 : // that the content is in the same anonymous tree as the container
591 : // because anonymous content may report its container as the host
592 : // but it may not be in the host's child list.
593 0 : return true;
594 : }
595 :
596 0 : if (aContainer) {
597 : // Fallback content will end up in pool if its parent is a child of the host.
598 0 : HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
599 0 : return content && content->IsInsertionPoint() &&
600 0 : content->MatchedNodes().IsEmpty() &&
601 0 : aContainer->GetParentNode() == aHost;
602 : }
603 :
604 0 : return false;
605 : }
606 :
607 : void
608 0 : ShadowRoot::AttributeChanged(nsIDocument* aDocument,
609 : Element* aElement,
610 : int32_t aNameSpaceID,
611 : nsIAtom* aAttribute,
612 : int32_t aModType,
613 : const nsAttrValue* aOldValue)
614 : {
615 0 : if (!IsPooledNode(aElement, aElement->GetParent(), mPoolHost)) {
616 0 : return;
617 : }
618 :
619 : // Attributes may change insertion point matching, find its new distribution.
620 0 : RemoveDistributedNode(aElement);
621 0 : DistributeSingleNode(aElement);
622 : }
623 :
624 : void
625 0 : ShadowRoot::ContentAppended(nsIDocument* aDocument,
626 : nsIContent* aContainer,
627 : nsIContent* aFirstNewContent,
628 : int32_t aNewIndexInContainer)
629 : {
630 0 : if (mInsertionPointChanged) {
631 0 : DistributeAllNodes();
632 0 : mInsertionPointChanged = false;
633 0 : return;
634 : }
635 :
636 : // Watch for new nodes added to the pool because the node
637 : // may need to be added to an insertion point.
638 0 : nsIContent* currentChild = aFirstNewContent;
639 0 : while (currentChild) {
640 : // Add insertion point to destination insertion points of fallback content.
641 0 : if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
642 0 : HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
643 0 : if (content->MatchedNodes().IsEmpty()) {
644 0 : currentChild->DestInsertionPoints().AppendElement(aContainer);
645 : }
646 : }
647 :
648 0 : if (IsPooledNode(currentChild, aContainer, mPoolHost)) {
649 0 : DistributeSingleNode(currentChild);
650 : }
651 :
652 0 : currentChild = currentChild->GetNextSibling();
653 : }
654 : }
655 :
656 : void
657 0 : ShadowRoot::ContentInserted(nsIDocument* aDocument,
658 : nsIContent* aContainer,
659 : nsIContent* aChild,
660 : int32_t aIndexInContainer)
661 : {
662 0 : if (mInsertionPointChanged) {
663 0 : DistributeAllNodes();
664 0 : mInsertionPointChanged = false;
665 0 : return;
666 : }
667 :
668 : // Watch for new nodes added to the pool because the node
669 : // may need to be added to an insertion point.
670 0 : if (IsPooledNode(aChild, aContainer, mPoolHost)) {
671 : // Add insertion point to destination insertion points of fallback content.
672 0 : if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
673 0 : HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
674 0 : if (content->MatchedNodes().IsEmpty()) {
675 0 : aChild->DestInsertionPoints().AppendElement(aContainer);
676 : }
677 : }
678 :
679 0 : DistributeSingleNode(aChild);
680 : }
681 : }
682 :
683 : void
684 0 : ShadowRoot::ContentRemoved(nsIDocument* aDocument,
685 : nsIContent* aContainer,
686 : nsIContent* aChild,
687 : int32_t aIndexInContainer,
688 : nsIContent* aPreviousSibling)
689 : {
690 0 : if (mInsertionPointChanged) {
691 0 : DistributeAllNodes();
692 0 : mInsertionPointChanged = false;
693 0 : return;
694 : }
695 :
696 : // Clear destination insertion points for removed
697 : // fallback content.
698 0 : if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
699 0 : HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
700 0 : if (content->MatchedNodes().IsEmpty()) {
701 0 : aChild->DestInsertionPoints().Clear();
702 : }
703 : }
704 :
705 : // Watch for node that is removed from the pool because
706 : // it may need to be removed from an insertion point.
707 0 : if (IsPooledNode(aChild, aContainer, mPoolHost)) {
708 0 : RemoveDistributedNode(aChild);
709 : }
710 : }
711 :
712 : nsresult
713 0 : ShadowRoot::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
714 : bool aPreallocateChildren) const
715 : {
716 0 : *aResult = nullptr;
717 0 : return NS_ERROR_DOM_DATA_CLONE_ERR;
718 : }
719 :
720 : void
721 0 : ShadowRoot::DestroyContent()
722 : {
723 0 : if (mOlderShadow) {
724 0 : mOlderShadow->DestroyContent();
725 : }
726 0 : DocumentFragment::DestroyContent();
727 0 : }
728 :
729 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList, StyleSheetList,
730 : mShadowRoot)
731 :
732 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList)
733 0 : NS_INTERFACE_MAP_END_INHERITING(StyleSheetList)
734 :
735 0 : NS_IMPL_ADDREF_INHERITED(ShadowRootStyleSheetList, StyleSheetList)
736 0 : NS_IMPL_RELEASE_INHERITED(ShadowRootStyleSheetList, StyleSheetList)
737 :
738 0 : ShadowRootStyleSheetList::ShadowRootStyleSheetList(ShadowRoot* aShadowRoot)
739 0 : : mShadowRoot(aShadowRoot)
740 : {
741 0 : }
742 :
743 0 : ShadowRootStyleSheetList::~ShadowRootStyleSheetList()
744 : {
745 0 : }
746 :
747 : StyleSheet*
748 0 : ShadowRootStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound)
749 : {
750 0 : aFound = aIndex < mShadowRoot->mProtoBinding->SheetCount();
751 0 : if (!aFound) {
752 0 : return nullptr;
753 : }
754 0 : return mShadowRoot->mProtoBinding->StyleSheetAt(aIndex);
755 : }
756 :
757 : uint32_t
758 0 : ShadowRootStyleSheetList::Length()
759 : {
760 0 : return mShadowRoot->mProtoBinding->SheetCount();
761 : }
762 :
|