Bug 1850713: remove duplicated setting of early hint preloader id in `ScriptLoader...
[gecko.git] / dom / base / ShadowRoot.cpp
blob30c12a14b32fdcc4e120b9d89fffd0e6a6eecde4
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 "nsINode.h"
14 #include "nsWindowSizes.h"
15 #include "mozilla/dom/DirectionalityUtils.h"
16 #include "mozilla/dom/Element.h"
17 #include "mozilla/dom/HTMLDetailsElement.h"
18 #include "mozilla/dom/HTMLSlotElement.h"
19 #include "mozilla/dom/HTMLSummaryElement.h"
20 #include "mozilla/dom/Text.h"
21 #include "mozilla/dom/TreeOrderedArrayInlines.h"
22 #include "mozilla/EventDispatcher.h"
23 #include "mozilla/IdentifierMapEntry.h"
24 #include "mozilla/PresShell.h"
25 #include "mozilla/PresShellInlines.h"
26 #include "mozilla/ScopeExit.h"
27 #include "mozilla/ServoStyleRuleMap.h"
28 #include "mozilla/StyleSheet.h"
29 #include "mozilla/StyleSheetInlines.h"
30 #include "mozilla/dom/StyleSheetList.h"
32 using namespace mozilla;
33 using namespace mozilla::dom;
35 NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment)
38 DocumentOrShadowRoot::Traverse(tmp, cb);
39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
41 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
42 DocumentOrShadowRoot::Unlink(tmp);
43 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
45 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
46 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
47 NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer)
48 NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
50 NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
51 NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
53 ShadowRoot::ShadowRoot(Element* aElement, ShadowRootMode aMode,
54 Element::DelegatesFocus aDelegatesFocus,
55 SlotAssignmentMode aSlotAssignment,
56 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
57 : DocumentFragment(std::move(aNodeInfo)),
58 DocumentOrShadowRoot(this),
59 mMode(aMode),
60 mDelegatesFocus(aDelegatesFocus),
61 mSlotAssignment(aSlotAssignment),
62 mIsDetailsShadowTree(aElement->IsHTMLElement(nsGkAtoms::details)),
63 mIsAvailableToElementInternals(false) {
64 // nsINode.h relies on this.
65 MOZ_ASSERT(static_cast<nsINode*>(this) == reinterpret_cast<nsINode*>(this));
66 MOZ_ASSERT(static_cast<nsIContent*>(this) ==
67 reinterpret_cast<nsIContent*>(this));
69 SetHost(aElement);
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 ClearSubtreeRootPointer();
76 SetFlags(NODE_IS_IN_SHADOW_TREE);
77 if (Host()->IsInNativeAnonymousSubtree()) {
78 // NOTE(emilio): We could consider just propagating the
79 // IN_NATIVE_ANONYMOUS_SUBTREE flag (not making this an anonymous root), but
80 // that breaks the invariant that if two nodes have the same
81 // NativeAnonymousSubtreeRoot() they are in the same DOM tree, which we rely
82 // on a couple places and would need extra fixes.
84 // We don't hit this case for now anyways, bug 1824886 would start hitting
85 // it.
86 SetIsNativeAnonymousRoot();
88 Bind();
90 ExtendedDOMSlots()->mContainingShadow = this;
93 ShadowRoot::~ShadowRoot() {
94 if (IsInComposedDoc()) {
95 OwnerDoc()->RemoveComposedDocShadowRoot(*this);
98 MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->IsComposedDocShadowRoot(*this));
100 UnsetFlags(NODE_IS_IN_SHADOW_TREE);
102 // nsINode destructor expects mSubtreeRoot == this.
103 SetSubtreeRootPointer(this);
106 MOZ_DEFINE_MALLOC_SIZE_OF(ShadowRootAuthorStylesMallocSizeOf)
107 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ShadowRootAuthorStylesMallocEnclosingSizeOf)
109 void ShadowRoot::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
110 size_t* aNodeSize) const {
111 DocumentFragment::AddSizeOfExcludingThis(aSizes, aNodeSize);
112 DocumentOrShadowRoot::AddSizeOfExcludingThis(aSizes);
113 aSizes.mLayoutShadowDomAuthorStyles += Servo_AuthorStyles_SizeOfIncludingThis(
114 ShadowRootAuthorStylesMallocSizeOf,
115 ShadowRootAuthorStylesMallocEnclosingSizeOf, mServoStyles.get());
118 JSObject* ShadowRoot::WrapNode(JSContext* aCx,
119 JS::Handle<JSObject*> aGivenProto) {
120 return mozilla::dom::ShadowRoot_Binding::Wrap(aCx, this, aGivenProto);
123 void ShadowRoot::NodeInfoChanged(Document* aOldDoc) {
124 DocumentFragment::NodeInfoChanged(aOldDoc);
125 Document* newDoc = OwnerDoc();
126 const bool fromOrToTemplate =
127 aOldDoc->GetTemplateContentsOwnerIfExists() == newDoc ||
128 newDoc->GetTemplateContentsOwnerIfExists() == aOldDoc;
129 if (!fromOrToTemplate) {
130 ClearAdoptedStyleSheets();
134 void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
135 if (aOther->IsRootOfNativeAnonymousSubtree()) {
136 SetIsNativeAnonymousRoot();
139 if (aOther->IsUAWidget()) {
140 SetIsUAWidget();
143 size_t sheetCount = aOther->SheetCount();
144 for (size_t i = 0; i < sheetCount; ++i) {
145 StyleSheet* sheet = aOther->SheetAt(i);
146 if (sheet->IsApplicable()) {
147 RefPtr<StyleSheet> clonedSheet = sheet->Clone(nullptr, this);
148 if (clonedSheet) {
149 AppendStyleSheet(*clonedSheet.get());
153 CloneAdoptedSheetsFrom(*aOther);
156 nsresult ShadowRoot::Bind() {
157 MOZ_ASSERT(!IsInComposedDoc(), "Forgot to unbind?");
158 if (Host()->IsInComposedDoc()) {
159 SetIsConnected(true);
160 Document* doc = OwnerDoc();
161 doc->AddComposedDocShadowRoot(*this);
162 // If our stylesheets somehow mutated when we were disconnected, we need to
163 // ensure that our style data gets flushed as appropriate.
164 if (mServoStyles && Servo_AuthorStyles_IsDirty(mServoStyles.get())) {
165 doc->RecordShadowStyleChange(*this);
169 BindContext context(*this);
170 for (nsIContent* child = GetFirstChild(); child;
171 child = child->GetNextSibling()) {
172 nsresult rv = child->BindToTree(context, *this);
173 NS_ENSURE_SUCCESS(rv, rv);
176 return NS_OK;
179 void ShadowRoot::Unbind() {
180 if (IsInComposedDoc()) {
181 SetIsConnected(false);
182 OwnerDoc()->RemoveComposedDocShadowRoot(*this);
185 for (nsIContent* child = GetFirstChild(); child;
186 child = child->GetNextSibling()) {
187 child->UnbindFromTree(false);
191 void ShadowRoot::Unattach() {
192 MOZ_ASSERT(!HasSlots(), "Won't work!");
193 if (!GetHost()) {
194 // It is possible that we've been unlinked already. In such case host
195 // should have called Unbind and ShadowRoot's own unlink.
196 return;
199 Unbind();
200 SetHost(nullptr);
203 void ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element* aElement) {
204 MOZ_ASSERT(aElement);
205 Document* doc = GetComposedDoc();
206 if (!doc) {
207 return;
210 PresShell* presShell = doc->GetPresShell();
211 if (!presShell) {
212 return;
215 presShell->DestroyFramesForAndRestyle(aElement);
218 void ShadowRoot::PartAdded(const Element& aPart) {
219 MOZ_ASSERT(aPart.HasPartAttribute());
220 MOZ_ASSERT(!mParts.Contains(&aPart));
221 mParts.AppendElement(&aPart);
224 void ShadowRoot::PartRemoved(const Element& aPart) {
225 MOZ_ASSERT(mParts.Contains(&aPart));
226 mParts.RemoveElement(&aPart);
227 MOZ_ASSERT(!mParts.Contains(&aPart));
230 void ShadowRoot::AddSlot(HTMLSlotElement* aSlot) {
231 MOZ_ASSERT(aSlot);
233 // Note that if name attribute missing, the slot is a default slot.
234 nsAutoString name;
235 aSlot->GetName(name);
237 SlotArray& currentSlots = *mSlotMap.GetOrInsertNew(name);
239 size_t index = currentSlots.Insert(*aSlot);
241 // For Named slots, slottables are inserted into the other slot
242 // which has the same name already, however it's not the case
243 // for manual slots
244 if (index != 0 && SlotAssignment() == SlotAssignmentMode::Named) {
245 return;
248 HTMLSlotElement* oldSlot = currentSlots->SafeElementAt(1);
249 if (SlotAssignment() == SlotAssignmentMode::Named) {
250 if (oldSlot) {
251 MOZ_DIAGNOSTIC_ASSERT(oldSlot != aSlot);
253 // Move assigned nodes from old slot to new slot.
254 InvalidateStyleAndLayoutOnSubtree(oldSlot);
255 const nsTArray<RefPtr<nsINode>>& assignedNodes = oldSlot->AssignedNodes();
256 bool doEnqueueSlotChange = false;
257 while (assignedNodes.Length() > 0) {
258 nsINode* assignedNode = assignedNodes[0];
260 oldSlot->RemoveAssignedNode(*assignedNode->AsContent());
261 aSlot->AppendAssignedNode(*assignedNode->AsContent());
262 doEnqueueSlotChange = true;
265 if (doEnqueueSlotChange) {
266 oldSlot->EnqueueSlotChangeEvent();
267 aSlot->EnqueueSlotChangeEvent();
268 SlotStateChanged(oldSlot);
269 SlotStateChanged(aSlot);
271 } else {
272 bool doEnqueueSlotChange = false;
273 // Otherwise add appropriate nodes to this slot from the host.
274 for (nsIContent* child = GetHost()->GetFirstChild(); child;
275 child = child->GetNextSibling()) {
276 nsAutoString slotName;
277 GetSlotNameFor(*child, slotName);
278 if (!child->IsSlotable() || !slotName.Equals(name)) {
279 continue;
281 doEnqueueSlotChange = true;
282 aSlot->AppendAssignedNode(*child);
285 if (doEnqueueSlotChange) {
286 aSlot->EnqueueSlotChangeEvent();
287 SlotStateChanged(aSlot);
290 } else {
291 bool doEnqueueSlotChange = false;
292 for (const auto& node : aSlot->ManuallyAssignedNodes()) {
293 if (GetHost() != node->GetParent()) {
294 continue;
297 MOZ_ASSERT(node->IsContent(),
298 "Manually assigned nodes should be an element or a text");
299 nsIContent* content = node->AsContent();
301 aSlot->AppendAssignedNode(*content);
302 doEnqueueSlotChange = true;
304 if (doEnqueueSlotChange) {
305 aSlot->EnqueueSlotChangeEvent();
306 SlotStateChanged(aSlot);
311 void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
312 MOZ_ASSERT(aSlot);
314 nsAutoString name;
315 aSlot->GetName(name);
317 MOZ_ASSERT(mSlotMap.Get(name));
319 SlotArray& currentSlots = *mSlotMap.Get(name);
320 MOZ_DIAGNOSTIC_ASSERT(currentSlots->Contains(aSlot),
321 "Slot to de-register wasn't found?");
322 if (currentSlots->Length() == 1) {
323 MOZ_ASSERT_IF(SlotAssignment() == SlotAssignmentMode::Named,
324 currentSlots->ElementAt(0) == aSlot);
326 InvalidateStyleAndLayoutOnSubtree(aSlot);
328 mSlotMap.Remove(name);
329 if (!aSlot->AssignedNodes().IsEmpty()) {
330 aSlot->ClearAssignedNodes();
331 aSlot->EnqueueSlotChangeEvent();
334 return;
336 if (SlotAssignment() == SlotAssignmentMode::Manual) {
337 InvalidateStyleAndLayoutOnSubtree(aSlot);
338 if (!aSlot->AssignedNodes().IsEmpty()) {
339 aSlot->ClearAssignedNodes();
340 aSlot->EnqueueSlotChangeEvent();
344 const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot;
345 currentSlots.RemoveElement(*aSlot);
346 if (!wasFirstSlot || SlotAssignment() == SlotAssignmentMode::Manual) {
347 return;
350 // Move assigned nodes from removed slot to the next slot in
351 // tree order with the same name.
352 InvalidateStyleAndLayoutOnSubtree(aSlot);
353 HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
354 const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
355 if (assignedNodes.IsEmpty()) {
356 return;
359 InvalidateStyleAndLayoutOnSubtree(replacementSlot);
360 while (!assignedNodes.IsEmpty()) {
361 nsINode* assignedNode = assignedNodes[0];
363 aSlot->RemoveAssignedNode(*assignedNode->AsContent());
364 replacementSlot->AppendAssignedNode(*assignedNode->AsContent());
367 aSlot->EnqueueSlotChangeEvent();
368 replacementSlot->EnqueueSlotChangeEvent();
371 // FIXME(emilio): There's a bit of code duplication between this and the
372 // equivalent ServoStyleSet methods, it'd be nice to not duplicate it...
373 void ShadowRoot::RuleAdded(StyleSheet& aSheet, css::Rule& aRule) {
374 if (!aSheet.IsApplicable()) {
375 return;
378 MOZ_ASSERT(mServoStyles);
379 if (mStyleRuleMap) {
380 mStyleRuleMap->RuleAdded(aSheet, aRule);
383 if (aRule.IsIncompleteImportRule()) {
384 return;
387 Servo_AuthorStyles_ForceDirty(mServoStyles.get());
388 ApplicableRulesChanged();
391 void ShadowRoot::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule) {
392 if (!aSheet.IsApplicable()) {
393 return;
396 MOZ_ASSERT(mServoStyles);
397 if (mStyleRuleMap) {
398 mStyleRuleMap->RuleRemoved(aSheet, aRule);
400 Servo_AuthorStyles_ForceDirty(mServoStyles.get());
401 ApplicableRulesChanged();
404 void ShadowRoot::RuleChanged(StyleSheet& aSheet, css::Rule*,
405 StyleRuleChangeKind) {
406 if (!aSheet.IsApplicable()) {
407 return;
410 MOZ_ASSERT(mServoStyles);
411 Servo_AuthorStyles_ForceDirty(mServoStyles.get());
412 ApplicableRulesChanged();
415 void ShadowRoot::ImportRuleLoaded(CSSImportRule&, StyleSheet& aSheet) {
416 if (mStyleRuleMap) {
417 mStyleRuleMap->SheetAdded(aSheet);
420 if (!aSheet.IsApplicable()) {
421 return;
424 // TODO(emilio): Could handle it like a regular sheet insertion, I guess, to
425 // avoid throwing away the whole style data.
426 Servo_AuthorStyles_ForceDirty(mServoStyles.get());
427 ApplicableRulesChanged();
430 // We don't need to do anything else than forwarding to the document if
431 // necessary.
432 void ShadowRoot::SheetCloned(StyleSheet& aSheet) {
433 if (Document* doc = GetComposedDoc()) {
434 if (PresShell* shell = doc->GetPresShell()) {
435 shell->StyleSet()->SheetCloned(aSheet);
440 void ShadowRoot::ApplicableRulesChanged() {
441 if (Document* doc = GetComposedDoc()) {
442 doc->RecordShadowStyleChange(*this);
446 void ShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
447 DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet);
448 if (aSheet.IsApplicable()) {
449 InsertSheetIntoAuthorData(aIndex, aSheet, mStyleSheets);
453 StyleSheet* FirstApplicableAdoptedStyleSheet(
454 const nsTArray<RefPtr<StyleSheet>>& aList) {
455 size_t i = 0;
456 for (StyleSheet* sheet : aList) {
457 // Deal with duplicate sheets by only considering the last one.
458 if (sheet->IsApplicable() && MOZ_LIKELY(aList.LastIndexOf(sheet) == i)) {
459 return sheet;
461 i++;
463 return nullptr;
466 void ShadowRoot::InsertSheetIntoAuthorData(
467 size_t aIndex, StyleSheet& aSheet,
468 const nsTArray<RefPtr<StyleSheet>>& aList) {
469 MOZ_ASSERT(aSheet.IsApplicable());
470 MOZ_ASSERT(aList[aIndex] == &aSheet);
471 MOZ_ASSERT(aList.LastIndexOf(&aSheet) == aIndex);
472 MOZ_ASSERT(&aList == &mAdoptedStyleSheets || &aList == &mStyleSheets);
474 if (!mServoStyles) {
475 mServoStyles.reset(Servo_AuthorStyles_Create());
478 if (mStyleRuleMap) {
479 mStyleRuleMap->SheetAdded(aSheet);
482 auto changedOnExit =
483 mozilla::MakeScopeExit([&] { ApplicableRulesChanged(); });
485 for (size_t i = aIndex + 1; i < aList.Length(); ++i) {
486 StyleSheet* beforeSheet = aList.ElementAt(i);
487 if (!beforeSheet->IsApplicable()) {
488 continue;
491 // If this is a duplicate adopted stylesheet that is not in the right
492 // position (the last one) then we skip over it. Otherwise we're done.
493 if (&aList == &mAdoptedStyleSheets &&
494 MOZ_UNLIKELY(aList.LastIndexOf(beforeSheet) != i)) {
495 continue;
498 Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles.get(), &aSheet,
499 beforeSheet);
500 return;
503 if (mAdoptedStyleSheets.IsEmpty() || &aList == &mAdoptedStyleSheets) {
504 Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
505 return;
508 if (auto* before = FirstApplicableAdoptedStyleSheet(mAdoptedStyleSheets)) {
509 Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles.get(), &aSheet,
510 before);
511 } else {
512 Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
516 // FIXME(emilio): This needs to notify document observers and such,
517 // presumably.
518 void ShadowRoot::StyleSheetApplicableStateChanged(StyleSheet& aSheet) {
519 auto& sheetList = aSheet.IsConstructed() ? mAdoptedStyleSheets : mStyleSheets;
520 int32_t index = sheetList.LastIndexOf(&aSheet);
521 if (index < 0) {
522 // NOTE(emilio): @import sheets are handled in the relevant RuleAdded
523 // notification, which only notifies after the sheet is loaded.
525 // This setup causes weirdness in other places, we may want to fix this in
526 // bug 1465031.
527 MOZ_DIAGNOSTIC_ASSERT(aSheet.GetParentSheet(),
528 "It'd better be an @import sheet");
529 return;
531 if (aSheet.IsApplicable()) {
532 InsertSheetIntoAuthorData(size_t(index), aSheet, sheetList);
533 } else {
534 MOZ_ASSERT(mServoStyles);
535 if (mStyleRuleMap) {
536 mStyleRuleMap->SheetRemoved(aSheet);
538 Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
539 ApplicableRulesChanged();
543 void ShadowRoot::RemoveSheetFromStyles(StyleSheet& aSheet) {
544 MOZ_ASSERT(aSheet.IsApplicable());
545 MOZ_ASSERT(mServoStyles);
546 if (mStyleRuleMap) {
547 mStyleRuleMap->SheetRemoved(aSheet);
549 Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
550 ApplicableRulesChanged();
553 void ShadowRoot::AddToIdTable(Element* aElement, nsAtom* aId) {
554 IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
555 if (entry) {
556 entry->AddIdElement(aElement);
560 void ShadowRoot::RemoveFromIdTable(Element* aElement, nsAtom* aId) {
561 IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
562 if (entry) {
563 entry->RemoveIdElement(aElement);
564 if (entry->IsEmpty()) {
565 mIdentifierMap.RemoveEntry(entry);
570 void ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
571 aVisitor.mCanHandle = true;
572 aVisitor.mRootOfClosedTree = IsClosed();
573 // Inform that we're about to exit the current scope.
574 aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
576 // https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
577 if (!aVisitor.mEvent->mFlags.mComposed) {
578 nsCOMPtr<nsIContent> originalTarget =
579 nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
580 if (originalTarget && originalTarget->GetContainingShadow() == this) {
581 // If we do stop propagation, we still want to propagate
582 // the event to chrome (nsPIDOMWindow::GetParentTarget()).
583 // The load event is special in that we don't ever propagate it
584 // to chrome.
585 nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
586 EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
587 ? win->GetParentTarget()
588 : nullptr;
590 aVisitor.SetParentTarget(parentTarget, true);
591 return;
595 nsIContent* shadowHost = GetHost();
596 aVisitor.SetParentTarget(shadowHost, false);
598 nsCOMPtr<nsIContent> content(
599 nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mTarget));
600 if (content && content->GetContainingShadow() == this) {
601 aVisitor.mEventTargetAtParent = shadowHost;
605 void ShadowRoot::GetSlotNameFor(const nsIContent& aContent,
606 nsAString& aName) const {
607 if (mIsDetailsShadowTree) {
608 const auto* summary = HTMLSummaryElement::FromNode(aContent);
609 if (summary && summary->IsMainSummary()) {
610 aName.AssignLiteral("internal-main-summary");
612 // Otherwise use the default slot.
613 return;
616 // Note that if slot attribute is missing, assign it to the first default
617 // slot, if exists.
618 if (const Element* element = Element::FromNode(aContent)) {
619 element->GetAttr(nsGkAtoms::slot, aName);
623 ShadowRoot::SlotInsertionPoint ShadowRoot::SlotInsertionPointFor(
624 nsIContent& aContent) {
625 HTMLSlotElement* slot = nullptr;
627 if (SlotAssignment() == SlotAssignmentMode::Manual) {
628 slot = aContent.GetManualSlotAssignment();
629 if (!slot || slot->GetContainingShadow() != this) {
630 return {};
632 } else {
633 nsAutoString slotName;
634 GetSlotNameFor(aContent, slotName);
636 SlotArray* slots = mSlotMap.Get(slotName);
637 if (!slots) {
638 return {};
640 slot = (*slots)->ElementAt(0);
643 MOZ_ASSERT(slot);
645 if (SlotAssignment() == SlotAssignmentMode::Named) {
646 if (!aContent.GetNextSibling()) {
647 // aContent is the last child, no need to loop through the assigned nodes,
648 // we're necessarily the last one.
650 // This prevents multiple appends into the host from getting quadratic.
651 return {slot, Nothing()};
653 } else {
654 // For manual slots, if aContent is the last element, we return Nothing
655 // because we just need to append the element to the assigned nodes. No need
656 // to return an index.
657 if (slot->ManuallyAssignedNodes().SafeLastElement(nullptr) == &aContent) {
658 return {slot, Nothing()};
662 // Find the appropriate position in the assigned node list for the newly
663 // assigned content.
664 if (SlotAssignment() == SlotAssignmentMode::Manual) {
665 const nsTArray<nsINode*>& manuallyAssignedNodes =
666 slot->ManuallyAssignedNodes();
667 auto index = manuallyAssignedNodes.IndexOf(&aContent);
668 if (index != manuallyAssignedNodes.NoIndex) {
669 return {slot, Some(index)};
671 } else {
672 const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
673 nsIContent* currentContent = GetHost()->GetFirstChild();
674 for (uint32_t i = 0; i < assignedNodes.Length(); i++) {
675 // Seek through the host's explicit children until the
676 // assigned content is found.
677 while (currentContent && currentContent != assignedNodes[i]) {
678 if (currentContent == &aContent) {
679 return {slot, Some(i)};
681 currentContent = currentContent->GetNextSibling();
686 return {slot, Nothing()};
689 void ShadowRoot::MaybeReassignContent(nsIContent& aElementOrText) {
690 MOZ_ASSERT(aElementOrText.GetParent() == GetHost());
691 MOZ_ASSERT(aElementOrText.IsElement() || aElementOrText.IsText());
692 HTMLSlotElement* oldSlot = aElementOrText.GetAssignedSlot();
694 SlotInsertionPoint assignment = SlotInsertionPointFor(aElementOrText);
696 if (assignment.mSlot == oldSlot) {
697 // Nothing to do here.
698 return;
701 // The layout invalidation piece for Manual slots is handled in
702 // HTMLSlotElement::Assign
703 if (aElementOrText.IsElement() &&
704 SlotAssignment() == SlotAssignmentMode::Named) {
705 if (Document* doc = GetComposedDoc()) {
706 if (RefPtr<PresShell> presShell = doc->GetPresShell()) {
707 presShell->SlotAssignmentWillChange(*aElementOrText.AsElement(),
708 oldSlot, assignment.mSlot);
713 if (oldSlot) {
714 if (SlotAssignment() == SlotAssignmentMode::Named) {
715 oldSlot->RemoveAssignedNode(aElementOrText);
716 // Don't need to EnqueueSlotChangeEvent for Manual slots because it
717 // needs to be done in tree order, so
718 // HTMLSlotElement::Assign will handle it explicitly.
719 oldSlot->EnqueueSlotChangeEvent();
720 } else {
721 oldSlot->RemoveManuallyAssignedNode(aElementOrText);
725 if (assignment.mSlot) {
726 if (assignment.mIndex) {
727 assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aElementOrText);
728 } else {
729 assignment.mSlot->AppendAssignedNode(aElementOrText);
731 // Similar as above, HTMLSlotElement::Assign handles enqueuing
732 // slotchange event.
733 if (SlotAssignment() == SlotAssignmentMode::Named) {
734 assignment.mSlot->EnqueueSlotChangeEvent();
739 void ShadowRoot::MaybeReassignMainSummary(SummaryChangeReason aReason) {
740 MOZ_ASSERT(mIsDetailsShadowTree);
741 if (aReason == SummaryChangeReason::Insertion) {
742 // We've inserted a summary element, may need to remove the existing one.
743 SlotArray* array = mSlotMap.Get(u"internal-main-summary"_ns);
744 MOZ_RELEASE_ASSERT(array && (*array)->Length() == 1);
745 HTMLSlotElement* slot = (*array)->ElementAt(0);
746 auto* summary = HTMLSummaryElement::FromNodeOrNull(
747 slot->AssignedNodes().SafeElementAt(0));
748 if (summary) {
749 MaybeReassignContent(*summary);
751 } else if (MOZ_LIKELY(GetHost())) {
752 // We need to null-check GetHost() in case we're unlinking already.
753 auto* details = HTMLDetailsElement::FromNode(Host());
754 MOZ_DIAGNOSTIC_ASSERT(details);
755 // We've removed a summary element, we may need to assign the new one.
756 if (HTMLSummaryElement* newMainSummary = details->GetFirstSummary()) {
757 MaybeReassignContent(*newMainSummary);
762 Element* ShadowRoot::GetActiveElement() {
763 return GetRetargetedFocusedElement();
766 nsINode* ShadowRoot::ImportNodeAndAppendChildAt(nsINode& aParentNode,
767 nsINode& aNode, bool aDeep,
768 mozilla::ErrorResult& rv) {
769 MOZ_ASSERT(IsUAWidget());
771 if (aParentNode.SubtreeRoot() != this) {
772 rv.Throw(NS_ERROR_INVALID_ARG);
773 return nullptr;
776 RefPtr<nsINode> node = OwnerDoc()->ImportNode(aNode, aDeep, rv);
777 if (rv.Failed()) {
778 return nullptr;
781 return aParentNode.AppendChild(*node, rv);
784 nsINode* ShadowRoot::CreateElementAndAppendChildAt(nsINode& aParentNode,
785 const nsAString& aTagName,
786 mozilla::ErrorResult& rv) {
787 MOZ_ASSERT(IsUAWidget());
789 if (aParentNode.SubtreeRoot() != this) {
790 rv.Throw(NS_ERROR_INVALID_ARG);
791 return nullptr;
794 // This option is not exposed to UA Widgets
795 ElementCreationOptionsOrString options;
797 RefPtr<nsINode> node = OwnerDoc()->CreateElement(aTagName, options, rv);
798 if (rv.Failed()) {
799 return nullptr;
802 return aParentNode.AppendChild(*node, rv);
805 void ShadowRoot::MaybeUnslotHostChild(nsIContent& aChild) {
806 // Need to null-check the host because we may be unlinked already.
807 MOZ_ASSERT(!GetHost() || aChild.GetParent() == GetHost());
809 HTMLSlotElement* slot = aChild.GetAssignedSlot();
810 if (!slot) {
811 return;
814 MOZ_DIAGNOSTIC_ASSERT(!aChild.IsRootOfNativeAnonymousSubtree(),
815 "How did aChild end up assigned to a slot?");
816 // If the slot is going to start showing fallback content, we need to tell
817 // layout about it.
818 if (slot->AssignedNodes().Length() == 1 && slot->HasChildren()) {
819 InvalidateStyleAndLayoutOnSubtree(slot);
822 slot->RemoveAssignedNode(aChild);
823 slot->EnqueueSlotChangeEvent();
825 if (mIsDetailsShadowTree && aChild.IsHTMLElement(nsGkAtoms::summary)) {
826 MaybeReassignMainSummary(SummaryChangeReason::Deletion);
830 void ShadowRoot::MaybeSlotHostChild(nsIContent& aChild) {
831 MOZ_ASSERT(aChild.GetParent() == GetHost());
832 // Check to ensure that the child not an anonymous subtree root because even
833 // though its parent could be the host it may not be in the host's child
834 // list.
835 if (aChild.IsRootOfNativeAnonymousSubtree()) {
836 return;
839 if (!aChild.IsSlotable()) {
840 return;
843 if (mIsDetailsShadowTree && aChild.IsHTMLElement(nsGkAtoms::summary)) {
844 MaybeReassignMainSummary(SummaryChangeReason::Insertion);
847 SlotInsertionPoint assignment = SlotInsertionPointFor(aChild);
848 if (!assignment.mSlot) {
849 return;
852 // Fallback content will go away, let layout know.
853 if (assignment.mSlot->AssignedNodes().IsEmpty() &&
854 assignment.mSlot->HasChildren()) {
855 InvalidateStyleAndLayoutOnSubtree(assignment.mSlot);
858 if (assignment.mIndex) {
859 assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aChild);
860 } else {
861 assignment.mSlot->AppendAssignedNode(aChild);
863 assignment.mSlot->EnqueueSlotChangeEvent();
866 ServoStyleRuleMap& ShadowRoot::ServoStyleRuleMap() {
867 if (!mStyleRuleMap) {
868 mStyleRuleMap = MakeUnique<mozilla::ServoStyleRuleMap>();
870 mStyleRuleMap->EnsureTable(*this);
871 return *mStyleRuleMap;
874 nsresult ShadowRoot::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
875 *aResult = nullptr;
876 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;