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/. */
7 #include "mozilla/Preferences.h"
8 #include "mozilla/dom/BindContext.h"
9 #include "mozilla/dom/ShadowRoot.h"
10 #include "mozilla/dom/DocumentFragment.h"
11 #include "ChildIterator.h"
12 #include "nsContentUtils.h"
13 #include "nsWindowSizes.h"
14 #include "mozilla/dom/DirectionalityUtils.h"
15 #include "mozilla/dom/Element.h"
16 #include "mozilla/dom/HTMLSlotElement.h"
17 #include "mozilla/dom/TreeOrderedArrayInlines.h"
18 #include "mozilla/EventDispatcher.h"
19 #include "mozilla/IdentifierMapEntry.h"
20 #include "mozilla/PresShell.h"
21 #include "mozilla/PresShellInlines.h"
22 #include "mozilla/ScopeExit.h"
23 #include "mozilla/ServoStyleRuleMap.h"
24 #include "mozilla/StyleSheet.h"
25 #include "mozilla/StyleSheetInlines.h"
26 #include "mozilla/dom/StyleSheetList.h"
28 using namespace mozilla
;
29 using namespace mozilla::dom
;
31 NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot
)
33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot
, DocumentFragment
)
34 DocumentOrShadowRoot::Traverse(tmp
, cb
);
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
37 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot
)
38 DocumentOrShadowRoot::Unlink(tmp
);
39 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment
)
41 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot
)
42 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIContent
)
43 NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer
)
44 NS_INTERFACE_MAP_END_INHERITING(DocumentFragment
)
46 NS_IMPL_ADDREF_INHERITED(ShadowRoot
, DocumentFragment
)
47 NS_IMPL_RELEASE_INHERITED(ShadowRoot
, DocumentFragment
)
49 ShadowRoot::ShadowRoot(Element
* aElement
, ShadowRootMode aMode
,
50 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
51 : DocumentFragment(std::move(aNodeInfo
)),
52 DocumentOrShadowRoot(this),
57 // Nodes in a shadow tree should never store a value
58 // in the subtree root pointer, nodes in the shadow tree
59 // track the subtree root using GetContainingShadow().
60 ClearSubtreeRootPointer();
62 SetFlags(NODE_IS_IN_SHADOW_TREE
);
65 ExtendedDOMSlots()->mContainingShadow
= this;
68 ShadowRoot::~ShadowRoot() {
69 if (IsInComposedDoc()) {
70 OwnerDoc()->RemoveComposedDocShadowRoot(*this);
73 MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->IsComposedDocShadowRoot(*this));
75 UnsetFlags(NODE_IS_IN_SHADOW_TREE
);
77 // nsINode destructor expects mSubtreeRoot == this.
78 SetSubtreeRootPointer(this);
81 MOZ_DEFINE_MALLOC_SIZE_OF(ShadowRootAuthorStylesMallocSizeOf
)
82 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ShadowRootAuthorStylesMallocEnclosingSizeOf
)
84 void ShadowRoot::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
85 size_t* aNodeSize
) const {
86 DocumentFragment::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
87 DocumentOrShadowRoot::AddSizeOfExcludingThis(aSizes
);
88 aSizes
.mLayoutShadowDomAuthorStyles
+= Servo_AuthorStyles_SizeOfIncludingThis(
89 ShadowRootAuthorStylesMallocSizeOf
,
90 ShadowRootAuthorStylesMallocEnclosingSizeOf
, mServoStyles
.get());
93 JSObject
* ShadowRoot::WrapNode(JSContext
* aCx
,
94 JS::Handle
<JSObject
*> aGivenProto
) {
95 return mozilla::dom::ShadowRoot_Binding::Wrap(aCx
, this, aGivenProto
);
98 void ShadowRoot::CloneInternalDataFrom(ShadowRoot
* aOther
) {
99 if (aOther
->IsUAWidget()) {
103 size_t sheetCount
= aOther
->SheetCount();
104 for (size_t i
= 0; i
< sheetCount
; ++i
) {
105 StyleSheet
* sheet
= aOther
->SheetAt(i
);
106 if (sheet
->IsApplicable()) {
107 RefPtr
<StyleSheet
> clonedSheet
=
108 sheet
->Clone(nullptr, nullptr, this, nullptr);
110 AppendStyleSheet(*clonedSheet
.get());
114 CloneAdoptedSheetsFrom(*aOther
);
117 nsresult
ShadowRoot::Bind() {
118 MOZ_ASSERT(!IsInComposedDoc(), "Forgot to unbind?");
119 if (Host()->IsInComposedDoc()) {
120 SetIsConnected(true);
121 Document
* doc
= OwnerDoc();
122 doc
->AddComposedDocShadowRoot(*this);
123 // If our stylesheets somehow mutated when we were disconnected, we need to
124 // ensure that our style data gets flushed as appropriate.
125 if (mServoStyles
&& Servo_AuthorStyles_IsDirty(mServoStyles
.get())) {
126 doc
->RecordShadowStyleChange(*this);
130 BindContext
context(*this);
131 for (nsIContent
* child
= GetFirstChild(); child
;
132 child
= child
->GetNextSibling()) {
133 nsresult rv
= child
->BindToTree(context
, *this);
134 NS_ENSURE_SUCCESS(rv
, rv
);
140 void ShadowRoot::Unbind() {
141 if (IsInComposedDoc()) {
142 SetIsConnected(false);
143 OwnerDoc()->RemoveComposedDocShadowRoot(*this);
146 for (nsIContent
* child
= GetFirstChild(); child
;
147 child
= child
->GetNextSibling()) {
148 child
->UnbindFromTree(false);
152 void ShadowRoot::Unattach() {
153 MOZ_ASSERT(!HasSlots(), "Won't work!");
155 // It is possible that we've been unlinked already. In such case host
156 // should have called Unbind and ShadowRoot's own unlink.
164 void ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element
* aElement
) {
165 MOZ_ASSERT(aElement
);
166 Document
* doc
= GetComposedDoc();
171 PresShell
* presShell
= doc
->GetPresShell();
176 presShell
->DestroyFramesForAndRestyle(aElement
);
179 void ShadowRoot::PartAdded(const Element
& aPart
) {
180 MOZ_ASSERT(aPart
.HasPartAttribute());
181 MOZ_ASSERT(!mParts
.Contains(&aPart
));
182 mParts
.AppendElement(&aPart
);
185 void ShadowRoot::PartRemoved(const Element
& aPart
) {
186 MOZ_ASSERT(mParts
.Contains(&aPart
));
187 mParts
.RemoveElement(&aPart
);
188 MOZ_ASSERT(!mParts
.Contains(&aPart
));
191 void ShadowRoot::AddSlot(HTMLSlotElement
* aSlot
) {
194 // Note that if name attribute missing, the slot is a default slot.
196 aSlot
->GetName(name
);
198 SlotArray
& currentSlots
= *mSlotMap
.GetOrInsertNew(name
);
200 size_t index
= currentSlots
.Insert(*aSlot
);
205 HTMLSlotElement
* oldSlot
= currentSlots
->SafeElementAt(1);
207 MOZ_DIAGNOSTIC_ASSERT(oldSlot
!= aSlot
);
209 // Move assigned nodes from old slot to new slot.
210 InvalidateStyleAndLayoutOnSubtree(oldSlot
);
211 const nsTArray
<RefPtr
<nsINode
>>& assignedNodes
= oldSlot
->AssignedNodes();
212 bool doEnqueueSlotChange
= false;
213 while (assignedNodes
.Length() > 0) {
214 nsINode
* assignedNode
= assignedNodes
[0];
216 oldSlot
->RemoveAssignedNode(*assignedNode
->AsContent());
217 aSlot
->AppendAssignedNode(*assignedNode
->AsContent());
218 doEnqueueSlotChange
= true;
221 if (doEnqueueSlotChange
) {
222 oldSlot
->EnqueueSlotChangeEvent();
223 aSlot
->EnqueueSlotChangeEvent();
224 SlotStateChanged(oldSlot
);
225 SlotStateChanged(aSlot
);
228 bool doEnqueueSlotChange
= false;
229 // Otherwise add appropriate nodes to this slot from the host.
230 for (nsIContent
* child
= GetHost()->GetFirstChild(); child
;
231 child
= child
->GetNextSibling()) {
232 nsAutoString slotName
;
233 if (auto* element
= Element::FromNode(*child
)) {
234 element
->GetAttr(nsGkAtoms::slot
, slotName
);
236 if (!child
->IsSlotable() || !slotName
.Equals(name
)) {
239 doEnqueueSlotChange
= true;
240 aSlot
->AppendAssignedNode(*child
);
243 if (doEnqueueSlotChange
) {
244 aSlot
->EnqueueSlotChangeEvent();
245 SlotStateChanged(aSlot
);
250 void ShadowRoot::RemoveSlot(HTMLSlotElement
* aSlot
) {
254 aSlot
->GetName(name
);
256 MOZ_ASSERT(mSlotMap
.Get(name
));
258 SlotArray
& currentSlots
= *mSlotMap
.Get(name
);
259 MOZ_DIAGNOSTIC_ASSERT(currentSlots
->Contains(aSlot
),
260 "Slot to de-register wasn't found?");
261 if (currentSlots
->Length() == 1) {
262 MOZ_ASSERT(currentSlots
->ElementAt(0) == aSlot
);
264 InvalidateStyleAndLayoutOnSubtree(aSlot
);
266 mSlotMap
.Remove(name
);
267 if (!aSlot
->AssignedNodes().IsEmpty()) {
268 aSlot
->ClearAssignedNodes();
269 aSlot
->EnqueueSlotChangeEvent();
275 const bool wasFirstSlot
= currentSlots
->ElementAt(0) == aSlot
;
276 currentSlots
.RemoveElement(*aSlot
);
281 // Move assigned nodes from removed slot to the next slot in
282 // tree order with the same name.
283 InvalidateStyleAndLayoutOnSubtree(aSlot
);
284 HTMLSlotElement
* replacementSlot
= currentSlots
->ElementAt(0);
285 const nsTArray
<RefPtr
<nsINode
>>& assignedNodes
= aSlot
->AssignedNodes();
286 if (assignedNodes
.IsEmpty()) {
290 InvalidateStyleAndLayoutOnSubtree(replacementSlot
);
291 while (!assignedNodes
.IsEmpty()) {
292 nsINode
* assignedNode
= assignedNodes
[0];
294 aSlot
->RemoveAssignedNode(*assignedNode
->AsContent());
295 replacementSlot
->AppendAssignedNode(*assignedNode
->AsContent());
298 aSlot
->EnqueueSlotChangeEvent();
299 replacementSlot
->EnqueueSlotChangeEvent();
302 // FIXME(emilio): There's a bit of code duplication between this and the
303 // equivalent ServoStyleSet methods, it'd be nice to not duplicate it...
304 void ShadowRoot::RuleAdded(StyleSheet
& aSheet
, css::Rule
& aRule
) {
305 if (!aSheet
.IsApplicable()) {
309 MOZ_ASSERT(mServoStyles
);
311 mStyleRuleMap
->RuleAdded(aSheet
, aRule
);
314 if (aRule
.IsIncompleteImportRule()) {
318 Servo_AuthorStyles_ForceDirty(mServoStyles
.get());
319 ApplicableRulesChanged();
322 void ShadowRoot::RuleRemoved(StyleSheet
& aSheet
, css::Rule
& aRule
) {
323 if (!aSheet
.IsApplicable()) {
327 MOZ_ASSERT(mServoStyles
);
329 mStyleRuleMap
->RuleRemoved(aSheet
, aRule
);
331 Servo_AuthorStyles_ForceDirty(mServoStyles
.get());
332 ApplicableRulesChanged();
335 void ShadowRoot::RuleChanged(StyleSheet
& aSheet
, css::Rule
*,
336 StyleRuleChangeKind
) {
337 if (!aSheet
.IsApplicable()) {
341 MOZ_ASSERT(mServoStyles
);
342 Servo_AuthorStyles_ForceDirty(mServoStyles
.get());
343 ApplicableRulesChanged();
346 void ShadowRoot::ImportRuleLoaded(CSSImportRule
&, StyleSheet
& aSheet
) {
348 mStyleRuleMap
->SheetAdded(aSheet
);
351 if (!aSheet
.IsApplicable()) {
355 // TODO(emilio): Could handle it like a regular sheet insertion, I guess, to
356 // avoid throwing away the whole style data.
357 Servo_AuthorStyles_ForceDirty(mServoStyles
.get());
358 ApplicableRulesChanged();
361 // We don't need to do anything else than forwarding to the document if
363 void ShadowRoot::SheetCloned(StyleSheet
& aSheet
) {
364 if (Document
* doc
= GetComposedDoc()) {
365 if (PresShell
* shell
= doc
->GetPresShell()) {
366 shell
->StyleSet()->SheetCloned(aSheet
);
371 void ShadowRoot::ApplicableRulesChanged() {
372 if (Document
* doc
= GetComposedDoc()) {
373 doc
->RecordShadowStyleChange(*this);
377 void ShadowRoot::InsertSheetAt(size_t aIndex
, StyleSheet
& aSheet
) {
378 DocumentOrShadowRoot::InsertSheetAt(aIndex
, aSheet
);
379 if (aSheet
.IsApplicable()) {
380 InsertSheetIntoAuthorData(aIndex
, aSheet
, mStyleSheets
);
384 StyleSheet
* FirstApplicableAdoptedStyleSheet(
385 const nsTArray
<RefPtr
<StyleSheet
>>& aList
) {
387 for (StyleSheet
* sheet
: aList
) {
388 // Deal with duplicate sheets by only considering the last one.
389 if (sheet
->IsApplicable() && MOZ_LIKELY(aList
.LastIndexOf(sheet
) == i
)) {
397 void ShadowRoot::InsertSheetIntoAuthorData(
398 size_t aIndex
, StyleSheet
& aSheet
,
399 const nsTArray
<RefPtr
<StyleSheet
>>& aList
) {
400 MOZ_ASSERT(aSheet
.IsApplicable());
401 MOZ_ASSERT(aList
[aIndex
] == &aSheet
);
402 MOZ_ASSERT(aList
.LastIndexOf(&aSheet
) == aIndex
);
403 MOZ_ASSERT(&aList
== &mAdoptedStyleSheets
|| &aList
== &mStyleSheets
);
406 mServoStyles
= Servo_AuthorStyles_Create().Consume();
410 mStyleRuleMap
->SheetAdded(aSheet
);
414 mozilla::MakeScopeExit([&] { ApplicableRulesChanged(); });
416 for (size_t i
= aIndex
+ 1; i
< aList
.Length(); ++i
) {
417 StyleSheet
* beforeSheet
= aList
.ElementAt(i
);
418 if (!beforeSheet
->IsApplicable()) {
422 // If this is a duplicate adopted stylesheet that is not in the right
423 // position (the last one) then we skip over it. Otherwise we're done.
424 if (&aList
== &mAdoptedStyleSheets
&&
425 MOZ_UNLIKELY(aList
.LastIndexOf(beforeSheet
) != i
)) {
429 Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles
.get(), &aSheet
,
434 if (mAdoptedStyleSheets
.IsEmpty() || &aList
== &mAdoptedStyleSheets
) {
435 Servo_AuthorStyles_AppendStyleSheet(mServoStyles
.get(), &aSheet
);
439 if (auto* before
= FirstApplicableAdoptedStyleSheet(mAdoptedStyleSheets
)) {
440 Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles
.get(), &aSheet
,
443 Servo_AuthorStyles_AppendStyleSheet(mServoStyles
.get(), &aSheet
);
447 // FIXME(emilio): This needs to notify document observers and such,
449 void ShadowRoot::StyleSheetApplicableStateChanged(StyleSheet
& aSheet
) {
450 auto& sheetList
= aSheet
.IsConstructed() ? mAdoptedStyleSheets
: mStyleSheets
;
451 int32_t index
= sheetList
.LastIndexOf(&aSheet
);
453 // NOTE(emilio): @import sheets are handled in the relevant RuleAdded
454 // notification, which only notifies after the sheet is loaded.
456 // This setup causes weirdness in other places, we may want to fix this in
458 MOZ_DIAGNOSTIC_ASSERT(aSheet
.GetParentSheet(),
459 "It'd better be an @import sheet");
462 if (aSheet
.IsApplicable()) {
463 InsertSheetIntoAuthorData(size_t(index
), aSheet
, sheetList
);
465 MOZ_ASSERT(mServoStyles
);
467 mStyleRuleMap
->SheetRemoved(aSheet
);
469 Servo_AuthorStyles_RemoveStyleSheet(mServoStyles
.get(), &aSheet
);
470 ApplicableRulesChanged();
474 void ShadowRoot::RemoveSheetFromStyles(StyleSheet
& aSheet
) {
475 MOZ_ASSERT(aSheet
.IsApplicable());
476 MOZ_ASSERT(mServoStyles
);
478 mStyleRuleMap
->SheetRemoved(aSheet
);
480 Servo_AuthorStyles_RemoveStyleSheet(mServoStyles
.get(), &aSheet
);
481 ApplicableRulesChanged();
484 void ShadowRoot::AddToIdTable(Element
* aElement
, nsAtom
* aId
) {
485 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aId
);
487 entry
->AddIdElement(aElement
);
491 void ShadowRoot::RemoveFromIdTable(Element
* aElement
, nsAtom
* aId
) {
492 IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aId
);
494 entry
->RemoveIdElement(aElement
);
495 if (entry
->IsEmpty()) {
496 mIdentifierMap
.RemoveEntry(entry
);
501 void ShadowRoot::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
502 aVisitor
.mCanHandle
= true;
503 aVisitor
.mRootOfClosedTree
= IsClosed();
504 // Inform that we're about to exit the current scope.
505 aVisitor
.mRelatedTargetRetargetedInCurrentScope
= false;
507 // https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
508 if (!aVisitor
.mEvent
->mFlags
.mComposed
) {
509 nsCOMPtr
<nsIContent
> originalTarget
=
510 do_QueryInterface(aVisitor
.mEvent
->mOriginalTarget
);
511 if (originalTarget
&& originalTarget
->GetContainingShadow() == this) {
512 // If we do stop propagation, we still want to propagate
513 // the event to chrome (nsPIDOMWindow::GetParentTarget()).
514 // The load event is special in that we don't ever propagate it
516 nsCOMPtr
<nsPIDOMWindowOuter
> win
= OwnerDoc()->GetWindow();
517 EventTarget
* parentTarget
= win
&& aVisitor
.mEvent
->mMessage
!= eLoad
518 ? win
->GetParentTarget()
521 aVisitor
.SetParentTarget(parentTarget
, true);
526 nsIContent
* shadowHost
= GetHost();
527 aVisitor
.SetParentTarget(shadowHost
, false);
529 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aVisitor
.mEvent
->mTarget
));
530 if (content
&& content
->GetContainingShadow() == this) {
531 aVisitor
.mEventTargetAtParent
= shadowHost
;
535 ShadowRoot::SlotAssignment
ShadowRoot::SlotAssignmentFor(nsIContent
& aContent
) {
536 nsAutoString slotName
;
537 // Note that if slot attribute is missing, assign it to the first default
539 if (Element
* element
= Element::FromNode(aContent
)) {
540 element
->GetAttr(nsGkAtoms::slot
, slotName
);
543 SlotArray
* slots
= mSlotMap
.Get(slotName
);
548 HTMLSlotElement
* slot
= (*slots
)->ElementAt(0);
551 if (!aContent
.GetNextSibling()) {
552 // aContent is the last child, no need to loop through the assigned nodes,
553 // we're necessarily the last one.
555 // This prevents multiple appends into the host from getting quadratic.
556 return {slot
, Nothing()};
559 // Find the appropriate position in the assigned node list for the newly
561 const nsTArray
<RefPtr
<nsINode
>>& assignedNodes
= slot
->AssignedNodes();
562 nsIContent
* currentContent
= GetHost()->GetFirstChild();
563 for (uint32_t i
= 0; i
< assignedNodes
.Length(); i
++) {
564 // Seek through the host's explicit children until the
565 // assigned content is found.
566 while (currentContent
&& currentContent
!= assignedNodes
[i
]) {
567 if (currentContent
== &aContent
) {
568 return {slot
, Some(i
)};
570 currentContent
= currentContent
->GetNextSibling();
574 return {slot
, Nothing()};
577 void ShadowRoot::MaybeReassignElement(Element
& aElement
) {
578 MOZ_ASSERT(aElement
.GetParent() == GetHost());
579 HTMLSlotElement
* oldSlot
= aElement
.GetAssignedSlot();
580 SlotAssignment assignment
= SlotAssignmentFor(aElement
);
582 if (assignment
.mSlot
== oldSlot
) {
583 // Nothing to do here.
587 if (Document
* doc
= GetComposedDoc()) {
588 if (RefPtr
<PresShell
> presShell
= doc
->GetPresShell()) {
589 presShell
->SlotAssignmentWillChange(aElement
, oldSlot
, assignment
.mSlot
);
594 oldSlot
->RemoveAssignedNode(aElement
);
595 oldSlot
->EnqueueSlotChangeEvent();
598 if (assignment
.mSlot
) {
599 if (assignment
.mIndex
) {
600 assignment
.mSlot
->InsertAssignedNode(*assignment
.mIndex
, aElement
);
602 assignment
.mSlot
->AppendAssignedNode(aElement
);
604 assignment
.mSlot
->EnqueueSlotChangeEvent();
607 SlotAssignedNodeChanged(oldSlot
, aElement
);
608 SlotAssignedNodeChanged(assignment
.mSlot
, aElement
);
611 Element
* ShadowRoot::GetActiveElement() {
612 return GetRetargetedFocusedElement();
615 nsINode
* ShadowRoot::ImportNodeAndAppendChildAt(nsINode
& aParentNode
,
616 nsINode
& aNode
, bool aDeep
,
617 mozilla::ErrorResult
& rv
) {
618 MOZ_ASSERT(mIsUAWidget
);
620 if (!aParentNode
.IsInUAWidget()) {
621 rv
.Throw(NS_ERROR_INVALID_ARG
);
625 RefPtr
<nsINode
> node
= OwnerDoc()->ImportNode(aNode
, aDeep
, rv
);
630 return aParentNode
.AppendChild(*node
, rv
);
633 nsINode
* ShadowRoot::CreateElementAndAppendChildAt(nsINode
& aParentNode
,
634 const nsAString
& aTagName
,
635 mozilla::ErrorResult
& rv
) {
636 MOZ_ASSERT(mIsUAWidget
);
638 if (!aParentNode
.IsInUAWidget()) {
639 rv
.Throw(NS_ERROR_INVALID_ARG
);
643 // This option is not exposed to UA Widgets
644 ElementCreationOptionsOrString options
;
646 RefPtr
<nsINode
> node
= OwnerDoc()->CreateElement(aTagName
, options
, rv
);
651 return aParentNode
.AppendChild(*node
, rv
);
654 void ShadowRoot::MaybeUnslotHostChild(nsIContent
& aChild
) {
655 // Need to null-check the host because we may be unlinked already.
656 MOZ_ASSERT(!GetHost() || aChild
.GetParent() == GetHost());
658 HTMLSlotElement
* slot
= aChild
.GetAssignedSlot();
663 MOZ_DIAGNOSTIC_ASSERT(!aChild
.IsRootOfNativeAnonymousSubtree(),
664 "How did aChild end up assigned to a slot?");
665 // If the slot is going to start showing fallback content, we need to tell
667 if (slot
->AssignedNodes().Length() == 1 && slot
->HasChildren()) {
668 InvalidateStyleAndLayoutOnSubtree(slot
);
671 slot
->RemoveAssignedNode(aChild
);
672 slot
->EnqueueSlotChangeEvent();
675 void ShadowRoot::MaybeSlotHostChild(nsIContent
& aChild
) {
676 MOZ_ASSERT(aChild
.GetParent() == GetHost());
677 // Check to ensure that the child not an anonymous subtree root because even
678 // though its parent could be the host it may not be in the host's child list.
679 if (aChild
.IsRootOfNativeAnonymousSubtree()) {
683 if (!aChild
.IsSlotable()) {
687 SlotAssignment assignment
= SlotAssignmentFor(aChild
);
688 if (!assignment
.mSlot
) {
692 // Fallback content will go away, let layout know.
693 if (assignment
.mSlot
->AssignedNodes().IsEmpty() &&
694 assignment
.mSlot
->HasChildren()) {
695 InvalidateStyleAndLayoutOnSubtree(assignment
.mSlot
);
698 if (assignment
.mIndex
) {
699 assignment
.mSlot
->InsertAssignedNode(*assignment
.mIndex
, aChild
);
701 assignment
.mSlot
->AppendAssignedNode(aChild
);
703 assignment
.mSlot
->EnqueueSlotChangeEvent();
704 SlotAssignedNodeChanged(assignment
.mSlot
, aChild
);
707 ServoStyleRuleMap
& ShadowRoot::ServoStyleRuleMap() {
708 if (!mStyleRuleMap
) {
709 mStyleRuleMap
= MakeUnique
<mozilla::ServoStyleRuleMap
>();
711 mStyleRuleMap
->EnsureTable(*this);
712 return *mStyleRuleMap
;
715 nsresult
ShadowRoot::Clone(dom::NodeInfo
* aNodeInfo
, nsINode
** aResult
) const {
717 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;