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/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"
27 
28 using namespace mozilla;
29 using namespace mozilla::dom;
30 
31 NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
32 
33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment)
34   DocumentOrShadowRoot::Traverse(tmp, cb);
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
36 
37 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
38   DocumentOrShadowRoot::Unlink(tmp);
39 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
40 
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)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)
45 
46 NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
47 NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
48 
49 ShadowRoot::ShadowRoot(Element* aElement, ShadowRootMode aMode,
50                        already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
51     : DocumentFragment(std::move(aNodeInfo)),
52       DocumentOrShadowRoot(this),
53       mMode(aMode),
54       mIsUAWidget(false) {
55   SetHost(aElement);
56 
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();
61 
62   SetFlags(NODE_IS_IN_SHADOW_TREE);
63   Bind();
64 
65   ExtendedDOMSlots()->mContainingShadow = this;
66 }
67 
~ShadowRoot()68 ShadowRoot::~ShadowRoot() {
69   if (IsInComposedDoc()) {
70     OwnerDoc()->RemoveComposedDocShadowRoot(*this);
71   }
72 
73   MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->IsComposedDocShadowRoot(*this));
74 
75   UnsetFlags(NODE_IS_IN_SHADOW_TREE);
76 
77   // nsINode destructor expects mSubtreeRoot == this.
78   SetSubtreeRootPointer(this);
79 }
80 
81 MOZ_DEFINE_MALLOC_SIZE_OF(ShadowRootAuthorStylesMallocSizeOf)
MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ShadowRootAuthorStylesMallocEnclosingSizeOf)82 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ShadowRootAuthorStylesMallocEnclosingSizeOf)
83 
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());
91 }
92 
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)93 JSObject* ShadowRoot::WrapNode(JSContext* aCx,
94                                JS::Handle<JSObject*> aGivenProto) {
95   return mozilla::dom::ShadowRoot_Binding::Wrap(aCx, this, aGivenProto);
96 }
97 
CloneInternalDataFrom(ShadowRoot * aOther)98 void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
99   if (aOther->IsUAWidget()) {
100     SetIsUAWidget();
101   }
102 
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);
109       if (clonedSheet) {
110         AppendStyleSheet(*clonedSheet.get());
111       }
112     }
113   }
114   CloneAdoptedSheetsFrom(*aOther);
115 }
116 
Bind()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);
127     }
128   }
129 
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);
135   }
136 
137   return NS_OK;
138 }
139 
Unbind()140 void ShadowRoot::Unbind() {
141   if (IsInComposedDoc()) {
142     SetIsConnected(false);
143     OwnerDoc()->RemoveComposedDocShadowRoot(*this);
144   }
145 
146   for (nsIContent* child = GetFirstChild(); child;
147        child = child->GetNextSibling()) {
148     child->UnbindFromTree(false);
149   }
150 }
151 
Unattach()152 void ShadowRoot::Unattach() {
153   MOZ_ASSERT(!HasSlots(), "Won't work!");
154   if (!GetHost()) {
155     // It is possible that we've been unlinked already. In such case host
156     // should have called Unbind and ShadowRoot's own unlink.
157     return;
158   }
159 
160   Unbind();
161   SetHost(nullptr);
162 }
163 
InvalidateStyleAndLayoutOnSubtree(Element * aElement)164 void ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element* aElement) {
165   MOZ_ASSERT(aElement);
166   Document* doc = GetComposedDoc();
167   if (!doc) {
168     return;
169   }
170 
171   PresShell* presShell = doc->GetPresShell();
172   if (!presShell) {
173     return;
174   }
175 
176   presShell->DestroyFramesForAndRestyle(aElement);
177 }
178 
PartAdded(const Element & aPart)179 void ShadowRoot::PartAdded(const Element& aPart) {
180   MOZ_ASSERT(aPart.HasPartAttribute());
181   MOZ_ASSERT(!mParts.Contains(&aPart));
182   mParts.AppendElement(&aPart);
183 }
184 
PartRemoved(const Element & aPart)185 void ShadowRoot::PartRemoved(const Element& aPart) {
186   MOZ_ASSERT(mParts.Contains(&aPart));
187   mParts.RemoveElement(&aPart);
188   MOZ_ASSERT(!mParts.Contains(&aPart));
189 }
190 
AddSlot(HTMLSlotElement * aSlot)191 void ShadowRoot::AddSlot(HTMLSlotElement* aSlot) {
192   MOZ_ASSERT(aSlot);
193 
194   // Note that if name attribute missing, the slot is a default slot.
195   nsAutoString name;
196   aSlot->GetName(name);
197 
198   SlotArray& currentSlots = *mSlotMap.GetOrInsertNew(name);
199 
200   size_t index = currentSlots.Insert(*aSlot);
201   if (index != 0) {
202     return;
203   }
204 
205   HTMLSlotElement* oldSlot = currentSlots->SafeElementAt(1);
206   if (oldSlot) {
207     MOZ_DIAGNOSTIC_ASSERT(oldSlot != aSlot);
208 
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];
215 
216       oldSlot->RemoveAssignedNode(*assignedNode->AsContent());
217       aSlot->AppendAssignedNode(*assignedNode->AsContent());
218       doEnqueueSlotChange = true;
219     }
220 
221     if (doEnqueueSlotChange) {
222       oldSlot->EnqueueSlotChangeEvent();
223       aSlot->EnqueueSlotChangeEvent();
224       SlotStateChanged(oldSlot);
225       SlotStateChanged(aSlot);
226     }
227   } else {
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);
235       }
236       if (!child->IsSlotable() || !slotName.Equals(name)) {
237         continue;
238       }
239       doEnqueueSlotChange = true;
240       aSlot->AppendAssignedNode(*child);
241     }
242 
243     if (doEnqueueSlotChange) {
244       aSlot->EnqueueSlotChangeEvent();
245       SlotStateChanged(aSlot);
246     }
247   }
248 }
249 
RemoveSlot(HTMLSlotElement * aSlot)250 void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
251   MOZ_ASSERT(aSlot);
252 
253   nsAutoString name;
254   aSlot->GetName(name);
255 
256   MOZ_ASSERT(mSlotMap.Get(name));
257 
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);
263 
264     InvalidateStyleAndLayoutOnSubtree(aSlot);
265 
266     mSlotMap.Remove(name);
267     if (!aSlot->AssignedNodes().IsEmpty()) {
268       aSlot->ClearAssignedNodes();
269       aSlot->EnqueueSlotChangeEvent();
270     }
271 
272     return;
273   }
274 
275   const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot;
276   currentSlots.RemoveElement(*aSlot);
277   if (!wasFirstSlot) {
278     return;
279   }
280 
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()) {
287     return;
288   }
289 
290   InvalidateStyleAndLayoutOnSubtree(replacementSlot);
291   while (!assignedNodes.IsEmpty()) {
292     nsINode* assignedNode = assignedNodes[0];
293 
294     aSlot->RemoveAssignedNode(*assignedNode->AsContent());
295     replacementSlot->AppendAssignedNode(*assignedNode->AsContent());
296   }
297 
298   aSlot->EnqueueSlotChangeEvent();
299   replacementSlot->EnqueueSlotChangeEvent();
300 }
301 
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...
RuleAdded(StyleSheet & aSheet,css::Rule & aRule)304 void ShadowRoot::RuleAdded(StyleSheet& aSheet, css::Rule& aRule) {
305   if (!aSheet.IsApplicable()) {
306     return;
307   }
308 
309   MOZ_ASSERT(mServoStyles);
310   if (mStyleRuleMap) {
311     mStyleRuleMap->RuleAdded(aSheet, aRule);
312   }
313 
314   if (aRule.IsIncompleteImportRule()) {
315     return;
316   }
317 
318   Servo_AuthorStyles_ForceDirty(mServoStyles.get());
319   ApplicableRulesChanged();
320 }
321 
RuleRemoved(StyleSheet & aSheet,css::Rule & aRule)322 void ShadowRoot::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule) {
323   if (!aSheet.IsApplicable()) {
324     return;
325   }
326 
327   MOZ_ASSERT(mServoStyles);
328   if (mStyleRuleMap) {
329     mStyleRuleMap->RuleRemoved(aSheet, aRule);
330   }
331   Servo_AuthorStyles_ForceDirty(mServoStyles.get());
332   ApplicableRulesChanged();
333 }
334 
RuleChanged(StyleSheet & aSheet,css::Rule *,StyleRuleChangeKind)335 void ShadowRoot::RuleChanged(StyleSheet& aSheet, css::Rule*,
336                              StyleRuleChangeKind) {
337   if (!aSheet.IsApplicable()) {
338     return;
339   }
340 
341   MOZ_ASSERT(mServoStyles);
342   Servo_AuthorStyles_ForceDirty(mServoStyles.get());
343   ApplicableRulesChanged();
344 }
345 
ImportRuleLoaded(CSSImportRule &,StyleSheet & aSheet)346 void ShadowRoot::ImportRuleLoaded(CSSImportRule&, StyleSheet& aSheet) {
347   if (mStyleRuleMap) {
348     mStyleRuleMap->SheetAdded(aSheet);
349   }
350 
351   if (!aSheet.IsApplicable()) {
352     return;
353   }
354 
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();
359 }
360 
361 // We don't need to do anything else than forwarding to the document if
362 // necessary.
SheetCloned(StyleSheet & aSheet)363 void ShadowRoot::SheetCloned(StyleSheet& aSheet) {
364   if (Document* doc = GetComposedDoc()) {
365     if (PresShell* shell = doc->GetPresShell()) {
366       shell->StyleSet()->SheetCloned(aSheet);
367     }
368   }
369 }
370 
ApplicableRulesChanged()371 void ShadowRoot::ApplicableRulesChanged() {
372   if (Document* doc = GetComposedDoc()) {
373     doc->RecordShadowStyleChange(*this);
374   }
375 }
376 
InsertSheetAt(size_t aIndex,StyleSheet & aSheet)377 void ShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
378   DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet);
379   if (aSheet.IsApplicable()) {
380     InsertSheetIntoAuthorData(aIndex, aSheet, mStyleSheets);
381   }
382 }
383 
FirstApplicableAdoptedStyleSheet(const nsTArray<RefPtr<StyleSheet>> & aList)384 StyleSheet* FirstApplicableAdoptedStyleSheet(
385     const nsTArray<RefPtr<StyleSheet>>& aList) {
386   size_t i = 0;
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)) {
390       return sheet;
391     }
392     i++;
393   }
394   return nullptr;
395 }
396 
InsertSheetIntoAuthorData(size_t aIndex,StyleSheet & aSheet,const nsTArray<RefPtr<StyleSheet>> & aList)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);
404 
405   if (!mServoStyles) {
406     mServoStyles = Servo_AuthorStyles_Create().Consume();
407   }
408 
409   if (mStyleRuleMap) {
410     mStyleRuleMap->SheetAdded(aSheet);
411   }
412 
413   auto changedOnExit =
414       mozilla::MakeScopeExit([&] { ApplicableRulesChanged(); });
415 
416   for (size_t i = aIndex + 1; i < aList.Length(); ++i) {
417     StyleSheet* beforeSheet = aList.ElementAt(i);
418     if (!beforeSheet->IsApplicable()) {
419       continue;
420     }
421 
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)) {
426       continue;
427     }
428 
429     Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles.get(), &aSheet,
430                                               beforeSheet);
431     return;
432   }
433 
434   if (mAdoptedStyleSheets.IsEmpty() || &aList == &mAdoptedStyleSheets) {
435     Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
436     return;
437   }
438 
439   if (auto* before = FirstApplicableAdoptedStyleSheet(mAdoptedStyleSheets)) {
440     Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles.get(), &aSheet,
441                                               before);
442   } else {
443     Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
444   }
445 }
446 
447 // FIXME(emilio): This needs to notify document observers and such,
448 // presumably.
StyleSheetApplicableStateChanged(StyleSheet & aSheet)449 void ShadowRoot::StyleSheetApplicableStateChanged(StyleSheet& aSheet) {
450   auto& sheetList = aSheet.IsConstructed() ? mAdoptedStyleSheets : mStyleSheets;
451   int32_t index = sheetList.LastIndexOf(&aSheet);
452   if (index < 0) {
453     // NOTE(emilio): @import sheets are handled in the relevant RuleAdded
454     // notification, which only notifies after the sheet is loaded.
455     //
456     // This setup causes weirdness in other places, we may want to fix this in
457     // bug 1465031.
458     MOZ_DIAGNOSTIC_ASSERT(aSheet.GetParentSheet(),
459                           "It'd better be an @import sheet");
460     return;
461   }
462   if (aSheet.IsApplicable()) {
463     InsertSheetIntoAuthorData(size_t(index), aSheet, sheetList);
464   } else {
465     MOZ_ASSERT(mServoStyles);
466     if (mStyleRuleMap) {
467       mStyleRuleMap->SheetRemoved(aSheet);
468     }
469     Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
470     ApplicableRulesChanged();
471   }
472 }
473 
RemoveSheetFromStyles(StyleSheet & aSheet)474 void ShadowRoot::RemoveSheetFromStyles(StyleSheet& aSheet) {
475   MOZ_ASSERT(aSheet.IsApplicable());
476   MOZ_ASSERT(mServoStyles);
477   if (mStyleRuleMap) {
478     mStyleRuleMap->SheetRemoved(aSheet);
479   }
480   Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
481   ApplicableRulesChanged();
482 }
483 
AddToIdTable(Element * aElement,nsAtom * aId)484 void ShadowRoot::AddToIdTable(Element* aElement, nsAtom* aId) {
485   IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
486   if (entry) {
487     entry->AddIdElement(aElement);
488   }
489 }
490 
RemoveFromIdTable(Element * aElement,nsAtom * aId)491 void ShadowRoot::RemoveFromIdTable(Element* aElement, nsAtom* aId) {
492   IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
493   if (entry) {
494     entry->RemoveIdElement(aElement);
495     if (entry->IsEmpty()) {
496       mIdentifierMap.RemoveEntry(entry);
497     }
498   }
499 }
500 
GetEventTargetParent(EventChainPreVisitor & aVisitor)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;
506 
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
515       // to chrome.
516       nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
517       EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
518                                       ? win->GetParentTarget()
519                                       : nullptr;
520 
521       aVisitor.SetParentTarget(parentTarget, true);
522       return;
523     }
524   }
525 
526   nsIContent* shadowHost = GetHost();
527   aVisitor.SetParentTarget(shadowHost, false);
528 
529   nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
530   if (content && content->GetContainingShadow() == this) {
531     aVisitor.mEventTargetAtParent = shadowHost;
532   }
533 }
534 
SlotAssignmentFor(nsIContent & aContent)535 ShadowRoot::SlotAssignment ShadowRoot::SlotAssignmentFor(nsIContent& aContent) {
536   nsAutoString slotName;
537   // Note that if slot attribute is missing, assign it to the first default
538   // slot, if exists.
539   if (Element* element = Element::FromNode(aContent)) {
540     element->GetAttr(nsGkAtoms::slot, slotName);
541   }
542 
543   SlotArray* slots = mSlotMap.Get(slotName);
544   if (!slots) {
545     return {};
546   }
547 
548   HTMLSlotElement* slot = (*slots)->ElementAt(0);
549   MOZ_ASSERT(slot);
550 
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.
554     //
555     // This prevents multiple appends into the host from getting quadratic.
556     return {slot, Nothing()};
557   }
558 
559   // Find the appropriate position in the assigned node list for the newly
560   // assigned content.
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)};
569       }
570       currentContent = currentContent->GetNextSibling();
571     }
572   }
573 
574   return {slot, Nothing()};
575 }
576 
MaybeReassignElement(Element & aElement)577 void ShadowRoot::MaybeReassignElement(Element& aElement) {
578   MOZ_ASSERT(aElement.GetParent() == GetHost());
579   HTMLSlotElement* oldSlot = aElement.GetAssignedSlot();
580   SlotAssignment assignment = SlotAssignmentFor(aElement);
581 
582   if (assignment.mSlot == oldSlot) {
583     // Nothing to do here.
584     return;
585   }
586 
587   if (Document* doc = GetComposedDoc()) {
588     if (RefPtr<PresShell> presShell = doc->GetPresShell()) {
589       presShell->SlotAssignmentWillChange(aElement, oldSlot, assignment.mSlot);
590     }
591   }
592 
593   if (oldSlot) {
594     oldSlot->RemoveAssignedNode(aElement);
595     oldSlot->EnqueueSlotChangeEvent();
596   }
597 
598   if (assignment.mSlot) {
599     if (assignment.mIndex) {
600       assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aElement);
601     } else {
602       assignment.mSlot->AppendAssignedNode(aElement);
603     }
604     assignment.mSlot->EnqueueSlotChangeEvent();
605   }
606 
607   SlotAssignedNodeChanged(oldSlot, aElement);
608   SlotAssignedNodeChanged(assignment.mSlot, aElement);
609 }
610 
GetActiveElement()611 Element* ShadowRoot::GetActiveElement() {
612   return GetRetargetedFocusedElement();
613 }
614 
ImportNodeAndAppendChildAt(nsINode & aParentNode,nsINode & aNode,bool aDeep,mozilla::ErrorResult & rv)615 nsINode* ShadowRoot::ImportNodeAndAppendChildAt(nsINode& aParentNode,
616                                                 nsINode& aNode, bool aDeep,
617                                                 mozilla::ErrorResult& rv) {
618   MOZ_ASSERT(mIsUAWidget);
619 
620   if (!aParentNode.IsInUAWidget()) {
621     rv.Throw(NS_ERROR_INVALID_ARG);
622     return nullptr;
623   }
624 
625   RefPtr<nsINode> node = OwnerDoc()->ImportNode(aNode, aDeep, rv);
626   if (rv.Failed()) {
627     return nullptr;
628   }
629 
630   return aParentNode.AppendChild(*node, rv);
631 }
632 
CreateElementAndAppendChildAt(nsINode & aParentNode,const nsAString & aTagName,mozilla::ErrorResult & rv)633 nsINode* ShadowRoot::CreateElementAndAppendChildAt(nsINode& aParentNode,
634                                                    const nsAString& aTagName,
635                                                    mozilla::ErrorResult& rv) {
636   MOZ_ASSERT(mIsUAWidget);
637 
638   if (!aParentNode.IsInUAWidget()) {
639     rv.Throw(NS_ERROR_INVALID_ARG);
640     return nullptr;
641   }
642 
643   // This option is not exposed to UA Widgets
644   ElementCreationOptionsOrString options;
645 
646   RefPtr<nsINode> node = OwnerDoc()->CreateElement(aTagName, options, rv);
647   if (rv.Failed()) {
648     return nullptr;
649   }
650 
651   return aParentNode.AppendChild(*node, rv);
652 }
653 
MaybeUnslotHostChild(nsIContent & aChild)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());
657 
658   HTMLSlotElement* slot = aChild.GetAssignedSlot();
659   if (!slot) {
660     return;
661   }
662 
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
666   // layout about it.
667   if (slot->AssignedNodes().Length() == 1 && slot->HasChildren()) {
668     InvalidateStyleAndLayoutOnSubtree(slot);
669   }
670 
671   slot->RemoveAssignedNode(aChild);
672   slot->EnqueueSlotChangeEvent();
673 }
674 
MaybeSlotHostChild(nsIContent & aChild)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()) {
680     return;
681   }
682 
683   if (!aChild.IsSlotable()) {
684     return;
685   }
686 
687   SlotAssignment assignment = SlotAssignmentFor(aChild);
688   if (!assignment.mSlot) {
689     return;
690   }
691 
692   // Fallback content will go away, let layout know.
693   if (assignment.mSlot->AssignedNodes().IsEmpty() &&
694       assignment.mSlot->HasChildren()) {
695     InvalidateStyleAndLayoutOnSubtree(assignment.mSlot);
696   }
697 
698   if (assignment.mIndex) {
699     assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aChild);
700   } else {
701     assignment.mSlot->AppendAssignedNode(aChild);
702   }
703   assignment.mSlot->EnqueueSlotChangeEvent();
704   SlotAssignedNodeChanged(assignment.mSlot, aChild);
705 }
706 
ServoStyleRuleMap()707 ServoStyleRuleMap& ShadowRoot::ServoStyleRuleMap() {
708   if (!mStyleRuleMap) {
709     mStyleRuleMap = MakeUnique<mozilla::ServoStyleRuleMap>();
710   }
711   mStyleRuleMap->EnsureTable(*this);
712   return *mStyleRuleMap;
713 }
714 
Clone(dom::NodeInfo * aNodeInfo,nsINode ** aResult) const715 nsresult ShadowRoot::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
716   *aResult = nullptr;
717   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
718 }
719