tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ShadowRoot.cpp (30852B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/ShadowRoot.h"
      8 
      9 #include "ChildIterator.h"
     10 #include "mozilla/DeclarationBlock.h"
     11 #include "mozilla/EventDispatcher.h"
     12 #include "mozilla/GlobalStyleSheetCache.h"
     13 #include "mozilla/IdentifierMapEntry.h"
     14 #include "mozilla/PresShell.h"
     15 #include "mozilla/PresShellInlines.h"
     16 #include "mozilla/ScopeExit.h"
     17 #include "mozilla/ServoBindings.h"
     18 #include "mozilla/ServoStyleRuleMap.h"
     19 #include "mozilla/StyleSheet.h"
     20 #include "mozilla/dom/BindContext.h"
     21 #include "mozilla/dom/DirectionalityUtils.h"
     22 #include "mozilla/dom/DocumentFragment.h"
     23 #include "mozilla/dom/Element.h"
     24 #include "mozilla/dom/ElementBinding.h"
     25 #include "mozilla/dom/HTMLDetailsElement.h"
     26 #include "mozilla/dom/HTMLSlotElement.h"
     27 #include "mozilla/dom/HTMLSummaryElement.h"
     28 #include "mozilla/dom/MutationObservers.h"
     29 #include "mozilla/dom/StyleSheetList.h"
     30 #include "mozilla/dom/Text.h"
     31 #include "mozilla/dom/TreeOrderedArrayInlines.h"
     32 #include "mozilla/dom/TrustedTypeUtils.h"
     33 #include "mozilla/dom/TrustedTypesConstants.h"
     34 #include "mozilla/dom/UnbindContext.h"
     35 #include "nsContentUtils.h"
     36 #include "nsINode.h"
     37 #include "nsWindowSizes.h"
     38 
     39 using namespace mozilla;
     40 using namespace mozilla::dom;
     41 
     42 NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
     43 
     44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment)
     45  DocumentOrShadowRoot::Traverse(tmp, cb);
     46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     47 
     48 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
     49  DocumentOrShadowRoot::Unlink(tmp);
     50 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
     51 
     52 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
     53  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
     54 NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
     55 
     56 NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
     57 NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
     58 
     59 ShadowRoot::ShadowRoot(Element* aElement, ShadowRootMode aMode,
     60                       Element::DelegatesFocus aDelegatesFocus,
     61                       SlotAssignmentMode aSlotAssignment,
     62                       IsClonable aIsClonable, IsSerializable aIsSerializable,
     63                       Declarative aDeclarative,
     64                       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     65    : DocumentFragment(std::move(aNodeInfo)),
     66      DocumentOrShadowRoot(this),
     67      mMode(aMode),
     68      mDelegatesFocus(aDelegatesFocus),
     69      mSlotAssignment(aSlotAssignment),
     70      mIsDetailsShadowTree(aElement->IsHTMLElement(nsGkAtoms::details)),
     71      mIsAvailableToElementInternals(false),
     72      mIsDeclarative(aDeclarative),
     73      mIsClonable(aIsClonable),
     74      mIsSerializable(aIsSerializable),
     75      mReferenceTarget(nsGkAtoms::_empty) {
     76  // nsINode.h relies on this.
     77  MOZ_ASSERT(static_cast<nsINode*>(this) == reinterpret_cast<nsINode*>(this));
     78  MOZ_ASSERT(static_cast<nsIContent*>(this) ==
     79             reinterpret_cast<nsIContent*>(this));
     80 
     81  SetHost(aElement);
     82 
     83  // Nodes in a shadow tree should never store a value
     84  // in the subtree root pointer, nodes in the shadow tree
     85  // track the subtree root using GetContainingShadow().
     86  ClearSubtreeRootPointer();
     87 
     88  SetFlags(NODE_IS_IN_SHADOW_TREE);
     89  if (Host()->IsInNativeAnonymousSubtree()) {
     90    // NOTE(emilio): We could consider just propagating the
     91    // IN_NATIVE_ANONYMOUS_SUBTREE flag (not making this an anonymous root), but
     92    // that breaks the invariant that if two nodes have the same
     93    // NativeAnonymousSubtreeRoot() they are in the same DOM tree, which we rely
     94    // on a couple places and would need extra fixes.
     95    //
     96    // We don't hit this case for now anyways, bug 1824886 would start hitting
     97    // it.
     98    SetIsNativeAnonymousRoot();
     99  }
    100  Bind();
    101 
    102  ExtendedDOMSlots()->mContainingShadow = this;
    103 }
    104 
    105 ShadowRoot::~ShadowRoot() {
    106  if (IsInComposedDoc()) {
    107    OwnerDoc()->RemoveComposedDocShadowRoot(*this);
    108  }
    109 
    110  MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->IsComposedDocShadowRoot(*this));
    111 
    112  UnsetFlags(NODE_IS_IN_SHADOW_TREE);
    113 
    114  // nsINode destructor expects mSubtreeRoot == this.
    115  SetSubtreeRootPointer(this);
    116 }
    117 
    118 MOZ_DEFINE_MALLOC_SIZE_OF(ShadowRootAuthorStylesMallocSizeOf)
    119 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ShadowRootAuthorStylesMallocEnclosingSizeOf)
    120 
    121 void ShadowRoot::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
    122                                        size_t* aNodeSize) const {
    123  DocumentFragment::AddSizeOfExcludingThis(aSizes, aNodeSize);
    124  DocumentOrShadowRoot::AddSizeOfExcludingThis(aSizes);
    125  aSizes.mLayoutShadowDomAuthorStyles += Servo_AuthorStyles_SizeOfIncludingThis(
    126      ShadowRootAuthorStylesMallocSizeOf,
    127      ShadowRootAuthorStylesMallocEnclosingSizeOf, mServoStyles.get());
    128 }
    129 
    130 JSObject* ShadowRoot::WrapNode(JSContext* aCx,
    131                               JS::Handle<JSObject*> aGivenProto) {
    132  return mozilla::dom::ShadowRoot_Binding::Wrap(aCx, this, aGivenProto);
    133 }
    134 
    135 void ShadowRoot::NodeInfoChanged(Document* aOldDoc) {
    136  DocumentFragment::NodeInfoChanged(aOldDoc);
    137  Document* newDoc = OwnerDoc();
    138  const bool fromOrToTemplate =
    139      aOldDoc->GetTemplateContentsOwnerIfExists() == newDoc ||
    140      newDoc->GetTemplateContentsOwnerIfExists() == aOldDoc;
    141  if (!fromOrToTemplate) {
    142    ClearAdoptedStyleSheets();
    143  }
    144 }
    145 
    146 void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
    147  if (aOther->IsRootOfNativeAnonymousSubtree()) {
    148    SetIsNativeAnonymousRoot();
    149  }
    150 
    151  if (aOther->IsUAWidget()) {
    152    SetIsUAWidget();
    153  }
    154 
    155  CloneAdoptedSheetsFrom(*aOther);
    156 }
    157 
    158 nsresult ShadowRoot::Bind() {
    159  MOZ_ASSERT(!IsInComposedDoc(), "Forgot to unbind?");
    160  if (Host()->IsInComposedDoc()) {
    161    SetIsConnected(true);
    162    Document* doc = OwnerDoc();
    163    doc->AddComposedDocShadowRoot(*this);
    164    // If our stylesheets somehow mutated when we were disconnected, we need to
    165    // ensure that our style data gets flushed as appropriate.
    166    if (mServoStyles && Servo_AuthorStyles_IsDirty(mServoStyles.get())) {
    167      doc->RecordShadowStyleChange(*this);
    168    }
    169  }
    170 
    171  BindContext context(*this);
    172  for (nsIContent* child = GetFirstChild(); child;
    173       child = child->GetNextSibling()) {
    174    nsresult rv = child->BindToTree(context, *this);
    175    NS_ENSURE_SUCCESS(rv, rv);
    176  }
    177 
    178  return NS_OK;
    179 }
    180 
    181 void ShadowRoot::Unbind() {
    182  if (IsInComposedDoc()) {
    183    SetIsConnected(false);
    184    OwnerDoc()->RemoveComposedDocShadowRoot(*this);
    185  }
    186 
    187  UnbindContext context(*this, /* aBatchState = */ nullptr);
    188  for (nsIContent* child = GetFirstChild(); child;
    189       child = child->GetNextSibling()) {
    190    child->UnbindFromTree(context);
    191  }
    192 
    193  MutationObservers::NotifyParentChainChanged(this);
    194 }
    195 
    196 void ShadowRoot::Unattach() {
    197  MOZ_ASSERT(!HasSlots(), "Won't work!");
    198  if (!GetHost()) {
    199    // It is possible that we've been unlinked already. In such case host
    200    // should have called Unbind and ShadowRoot's own unlink.
    201    return;
    202  }
    203 
    204  Unbind();
    205  SetHost(nullptr);
    206 }
    207 
    208 void ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element* aElement) {
    209  MOZ_ASSERT(aElement);
    210  Document* doc = GetComposedDoc();
    211  if (!doc) {
    212    return;
    213  }
    214 
    215  if (!aElement->IsInComposedDoc()) {
    216    // If RemoveSlot is called from UnbindFromTree while we're moving
    217    // (moveBefore) the slot elsewhere, invalidating styles and layout tree
    218    // is done explicitly elsewhere.
    219    return;
    220  }
    221 
    222  PresShell* presShell = doc->GetPresShell();
    223  if (!presShell) {
    224    return;
    225  }
    226 
    227  presShell->DestroyFramesForAndRestyle(aElement);
    228 }
    229 
    230 void ShadowRoot::PartAdded(const Element& aPart) {
    231  MOZ_ASSERT(aPart.HasPartAttribute());
    232  MOZ_ASSERT(!mParts.Contains(&aPart));
    233  mParts.AppendElement(&aPart);
    234 }
    235 
    236 void ShadowRoot::PartRemoved(const Element& aPart) {
    237  MOZ_ASSERT(mParts.Contains(&aPart));
    238  mParts.RemoveElement(&aPart);
    239  MOZ_ASSERT(!mParts.Contains(&aPart));
    240 }
    241 
    242 void ShadowRoot::AddSlot(HTMLSlotElement* aSlot) {
    243  MOZ_ASSERT(aSlot);
    244 
    245  // Note that if name attribute missing, the slot is a default slot.
    246  nsAutoString name;
    247  aSlot->GetName(name);
    248 
    249  SlotArray& currentSlots = *mSlotMap.GetOrInsertNew(name);
    250 
    251  size_t index = currentSlots.Insert(*aSlot);
    252 
    253  // For Named slots, slottables are inserted into the other slot
    254  // which has the same name already, however it's not the case
    255  // for manual slots
    256  if (index != 0 && SlotAssignment() == SlotAssignmentMode::Named) {
    257    return;
    258  }
    259 
    260  InvalidateStyleAndLayoutOnSubtree(aSlot);
    261 
    262  HTMLSlotElement* oldSlot = currentSlots.SafeElementAt(1, nullptr);
    263  if (SlotAssignment() == SlotAssignmentMode::Named) {
    264    if (oldSlot) {
    265      MOZ_DIAGNOSTIC_ASSERT(oldSlot != aSlot);
    266 
    267      // Move assigned nodes from old slot to new slot.
    268      InvalidateStyleAndLayoutOnSubtree(oldSlot);
    269      bool doEnqueueSlotChange = false;
    270      auto assignedNodes =
    271          ToTArray<AutoTArray<nsINode*, 8>>(oldSlot->AssignedNodes());
    272      for (nsINode* assignedNode : assignedNodes) {
    273        oldSlot->RemoveAssignedNode(*assignedNode->AsContent());
    274        aSlot->AppendAssignedNode(*assignedNode->AsContent());
    275        doEnqueueSlotChange = true;
    276      }
    277 
    278      if (doEnqueueSlotChange) {
    279        oldSlot->EnqueueSlotChangeEvent();
    280        aSlot->EnqueueSlotChangeEvent();
    281      }
    282    } else {
    283      bool doEnqueueSlotChange = false;
    284      // Otherwise add appropriate nodes to this slot from the host.
    285      for (nsIContent* child = GetHost()->GetFirstChild(); child;
    286           child = child->GetNextSibling()) {
    287        nsAutoString slotName;
    288        GetSlotNameFor(*child, slotName);
    289        if (!child->IsSlotable() || !slotName.Equals(name)) {
    290          continue;
    291        }
    292        doEnqueueSlotChange = true;
    293        aSlot->AppendAssignedNode(*child);
    294      }
    295 
    296      if (doEnqueueSlotChange) {
    297        aSlot->EnqueueSlotChangeEvent();
    298      }
    299    }
    300  } else {
    301    bool doEnqueueSlotChange = false;
    302    for (const auto& node : aSlot->ManuallyAssignedNodes()) {
    303      if (GetHost() != node->GetParent()) {
    304        continue;
    305      }
    306 
    307      MOZ_ASSERT(node->IsContent(),
    308                 "Manually assigned nodes should be an element or a text");
    309      nsIContent* content = node->AsContent();
    310 
    311      aSlot->AppendAssignedNode(*content);
    312      doEnqueueSlotChange = true;
    313    }
    314    if (doEnqueueSlotChange) {
    315      aSlot->EnqueueSlotChangeEvent();
    316    }
    317  }
    318 }
    319 
    320 void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
    321  MOZ_ASSERT(aSlot);
    322 
    323  nsAutoString name;
    324  aSlot->GetName(name);
    325 
    326  MOZ_ASSERT(mSlotMap.Get(name));
    327 
    328  SlotArray& currentSlots = *mSlotMap.Get(name);
    329  MOZ_DIAGNOSTIC_ASSERT(currentSlots.Contains(aSlot),
    330                        "Slot to de-register wasn't found?");
    331  if (currentSlots.Length() == 1) {
    332    MOZ_ASSERT_IF(SlotAssignment() == SlotAssignmentMode::Named,
    333                  currentSlots.ElementAt(0) == aSlot);
    334 
    335    InvalidateStyleAndLayoutOnSubtree(aSlot);
    336 
    337    mSlotMap.Remove(name);
    338    if (!aSlot->AssignedNodes().IsEmpty()) {
    339      aSlot->ClearAssignedNodes();
    340      aSlot->EnqueueSlotChangeEvent();
    341    }
    342 
    343    return;
    344  }
    345  if (SlotAssignment() == SlotAssignmentMode::Manual) {
    346    InvalidateStyleAndLayoutOnSubtree(aSlot);
    347    if (!aSlot->AssignedNodes().IsEmpty()) {
    348      aSlot->ClearAssignedNodes();
    349      aSlot->EnqueueSlotChangeEvent();
    350    }
    351  }
    352 
    353  const bool wasFirstSlot = currentSlots.ElementAt(0) == aSlot;
    354  currentSlots.RemoveElement(*aSlot);
    355  if (!wasFirstSlot || SlotAssignment() == SlotAssignmentMode::Manual) {
    356    return;
    357  }
    358 
    359  // Move assigned nodes from removed slot to the next slot in
    360  // tree order with the same name.
    361  InvalidateStyleAndLayoutOnSubtree(aSlot);
    362  HTMLSlotElement* replacementSlot = currentSlots.ElementAt(0);
    363  auto assignedNodes =
    364      ToTArray<AutoTArray<nsINode*, 8>>(aSlot->AssignedNodes());
    365  if (assignedNodes.IsEmpty()) {
    366    return;
    367  }
    368 
    369  InvalidateStyleAndLayoutOnSubtree(replacementSlot);
    370  for (auto* assignedNode : assignedNodes) {
    371    aSlot->RemoveAssignedNode(*assignedNode->AsContent());
    372    replacementSlot->AppendAssignedNode(*assignedNode->AsContent());
    373  }
    374 
    375  aSlot->EnqueueSlotChangeEvent();
    376  replacementSlot->EnqueueSlotChangeEvent();
    377 }
    378 
    379 // FIXME(emilio): There's a bit of code duplication between this and the
    380 // equivalent ServoStyleSet methods, it'd be nice to not duplicate it...
    381 void ShadowRoot::RuleAdded(StyleSheet& aSheet, css::Rule& aRule) {
    382  if (!aSheet.IsApplicable()) {
    383    return;
    384  }
    385 
    386  MOZ_ASSERT(mServoStyles);
    387  if (mStyleRuleMap) {
    388    mStyleRuleMap->RuleAdded(aSheet, aRule);
    389  }
    390 
    391  if (aRule.IsIncompleteImportRule()) {
    392    return;
    393  }
    394 
    395  Servo_AuthorStyles_ForceDirty(mServoStyles.get());
    396  ApplicableRulesChanged();
    397 }
    398 
    399 void ShadowRoot::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule) {
    400  if (!aSheet.IsApplicable()) {
    401    return;
    402  }
    403 
    404  MOZ_ASSERT(mServoStyles);
    405  if (mStyleRuleMap) {
    406    mStyleRuleMap->RuleRemoved(aSheet, aRule);
    407  }
    408  Servo_AuthorStyles_ForceDirty(mServoStyles.get());
    409  ApplicableRulesChanged();
    410 }
    411 
    412 void ShadowRoot::RuleChanged(StyleSheet& aSheet, css::Rule* aRule,
    413                             const StyleRuleChange& aChange) {
    414  if (!aSheet.IsApplicable()) {
    415    return;
    416  }
    417  if (mStyleRuleMap && aChange.mOldBlock != aChange.mNewBlock) {
    418    mStyleRuleMap->RuleDeclarationsChanged(*aRule, aChange.mOldBlock->Raw(),
    419                                           aChange.mNewBlock->Raw());
    420  }
    421  MOZ_ASSERT(mServoStyles);
    422  Servo_AuthorStyles_ForceDirty(mServoStyles.get());
    423  ApplicableRulesChanged();
    424 }
    425 
    426 void ShadowRoot::ImportRuleLoaded(StyleSheet& aSheet) {
    427  if (mStyleRuleMap) {
    428    mStyleRuleMap->SheetAdded(aSheet);
    429  }
    430 
    431  if (!aSheet.IsApplicable()) {
    432    return;
    433  }
    434 
    435  // TODO(emilio): Could handle it like a regular sheet insertion, I guess, to
    436  // avoid throwing away the whole style data.
    437  Servo_AuthorStyles_ForceDirty(mServoStyles.get());
    438  ApplicableRulesChanged();
    439 }
    440 
    441 // We don't need to do anything else than forwarding to the document if
    442 // necessary.
    443 void ShadowRoot::SheetCloned(StyleSheet& aSheet) {
    444  if (Document* doc = GetComposedDoc()) {
    445    if (PresShell* shell = doc->GetPresShell()) {
    446      shell->StyleSet()->SheetCloned(aSheet);
    447    }
    448  }
    449 }
    450 
    451 void ShadowRoot::ApplicableRulesChanged() {
    452  if (Document* doc = GetComposedDoc()) {
    453    doc->RecordShadowStyleChange(*this);
    454  }
    455 }
    456 
    457 void ShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
    458  DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet);
    459  if (aSheet.IsApplicable()) {
    460    InsertSheetIntoAuthorData(aIndex, aSheet, mStyleSheets);
    461  }
    462 }
    463 
    464 StyleSheet* FirstApplicableAdoptedStyleSheet(
    465    const nsTArray<RefPtr<StyleSheet>>& aList) {
    466  size_t i = 0;
    467  for (StyleSheet* sheet : aList) {
    468    // Deal with duplicate sheets by only considering the last one.
    469    if (sheet->IsApplicable() && MOZ_LIKELY(aList.LastIndexOf(sheet) == i)) {
    470      return sheet;
    471    }
    472    i++;
    473  }
    474  return nullptr;
    475 }
    476 
    477 void ShadowRoot::InsertSheetIntoAuthorData(
    478    size_t aIndex, StyleSheet& aSheet,
    479    const nsTArray<RefPtr<StyleSheet>>& aList) {
    480  MOZ_ASSERT(aSheet.IsApplicable());
    481  MOZ_ASSERT(aList[aIndex] == &aSheet);
    482  MOZ_ASSERT(aList.LastIndexOf(&aSheet) == aIndex);
    483  MOZ_ASSERT(&aList == &mAdoptedStyleSheets || &aList == &mStyleSheets);
    484 
    485  if (!mServoStyles) {
    486    mServoStyles.reset(Servo_AuthorStyles_Create());
    487  }
    488 
    489  if (mStyleRuleMap) {
    490    mStyleRuleMap->SheetAdded(aSheet);
    491  }
    492 
    493  auto changedOnExit =
    494      mozilla::MakeScopeExit([&] { ApplicableRulesChanged(); });
    495 
    496  for (size_t i = aIndex + 1; i < aList.Length(); ++i) {
    497    StyleSheet* beforeSheet = aList.ElementAt(i);
    498    if (!beforeSheet->IsApplicable()) {
    499      continue;
    500    }
    501 
    502    // If this is a duplicate adopted stylesheet that is not in the right
    503    // position (the last one) then we skip over it. Otherwise we're done.
    504    if (&aList == &mAdoptedStyleSheets &&
    505        MOZ_UNLIKELY(aList.LastIndexOf(beforeSheet) != i)) {
    506      continue;
    507    }
    508 
    509    Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles.get(), &aSheet,
    510                                              beforeSheet);
    511    return;
    512  }
    513 
    514  if (mAdoptedStyleSheets.IsEmpty() || &aList == &mAdoptedStyleSheets) {
    515    Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
    516    return;
    517  }
    518 
    519  if (auto* before = FirstApplicableAdoptedStyleSheet(mAdoptedStyleSheets)) {
    520    Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles.get(), &aSheet,
    521                                              before);
    522  } else {
    523    Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
    524  }
    525 }
    526 
    527 // FIXME(emilio): This needs to notify document observers and such,
    528 // presumably.
    529 void ShadowRoot::StyleSheetApplicableStateChanged(StyleSheet& aSheet) {
    530  auto& sheetList = aSheet.IsConstructed() ? mAdoptedStyleSheets : mStyleSheets;
    531  size_t index = sheetList.LastIndexOf(&aSheet);
    532  if (index == sheetList.NoIndex) {
    533    // NOTE(emilio): @import sheets are handled in the relevant RuleAdded
    534    // notification, which only notifies after the sheet is loaded.
    535    //
    536    // This setup causes weirdness in other places, we may want to fix this in
    537    // bug 1465031.
    538    MOZ_DIAGNOSTIC_ASSERT(aSheet.GetParentSheet(),
    539                          "It'd better be an @import sheet");
    540    return;
    541  }
    542  if (aSheet.IsApplicable()) {
    543    InsertSheetIntoAuthorData(index, aSheet, sheetList);
    544  } else {
    545    MOZ_ASSERT(mServoStyles);
    546    if (mStyleRuleMap) {
    547      mStyleRuleMap->SheetRemoved(aSheet);
    548    }
    549    Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
    550    ApplicableRulesChanged();
    551  }
    552 }
    553 
    554 void ShadowRoot::AppendBuiltInStyleSheet(BuiltInStyleSheet aSheet) {
    555  auto* cache = GlobalStyleSheetCache::Singleton();
    556  // NOTE(emilio): It's important to Clone() the stylesheet to avoid leaking,
    557  // since the built-in sheet is kept alive forever, and AppendStyleSheet will
    558  // set the associated global of the stylesheet.
    559  RefPtr sheet = cache->BuiltInSheet(aSheet)->Clone(nullptr, nullptr);
    560  AppendStyleSheet(*sheet);
    561 }
    562 
    563 void ShadowRoot::RemoveSheetFromStyles(StyleSheet& aSheet) {
    564  MOZ_ASSERT(aSheet.IsApplicable());
    565  MOZ_ASSERT(mServoStyles);
    566  if (mStyleRuleMap) {
    567    mStyleRuleMap->SheetRemoved(aSheet);
    568  }
    569  Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
    570  ApplicableRulesChanged();
    571 }
    572 
    573 void ShadowRoot::AddToIdTable(Element* aElement, nsAtom* aId) {
    574  IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
    575  if (entry) {
    576    entry->AddIdElement(aElement);
    577  }
    578 }
    579 
    580 void ShadowRoot::RemoveFromIdTable(Element* aElement, nsAtom* aId) {
    581  IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
    582  if (entry) {
    583    entry->RemoveIdElement(aElement);
    584    if (entry->IsEmpty()) {
    585      mIdentifierMap.RemoveEntry(entry);
    586    }
    587  }
    588 }
    589 
    590 void ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
    591  aVisitor.mCanHandle = true;
    592  aVisitor.mRootOfClosedTree = IsClosed();
    593  // Inform that we're about to exit the current scope.
    594  aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
    595 
    596  // https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
    597  if (!aVisitor.mEvent->mFlags.mComposed) {
    598    nsIContent* originalTarget =
    599        nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
    600    if (originalTarget && originalTarget->GetContainingShadow() == this) {
    601      // If we do stop propagation, we still want to propagate
    602      // the event to chrome (nsPIDOMWindow::GetParentTarget()).
    603      // The load event is special in that we don't ever propagate it
    604      // to chrome.
    605      nsPIDOMWindowOuter* win = OwnerDoc()->GetWindow();
    606      EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
    607                                      ? win->GetParentTarget()
    608                                      : nullptr;
    609 
    610      aVisitor.SetParentTarget(parentTarget, true);
    611      return;
    612    }
    613  }
    614 
    615  nsIContent* shadowHost = GetHost();
    616  aVisitor.SetParentTarget(shadowHost, false);
    617 
    618  nsIContent* content =
    619      nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mTarget);
    620  if (content && content->GetContainingShadow() == this) {
    621    aVisitor.mEventTargetAtParent = shadowHost;
    622  }
    623 }
    624 
    625 void ShadowRoot::GetSlotNameFor(const nsIContent& aContent,
    626                                nsAString& aName) const {
    627  if (mIsDetailsShadowTree) {
    628    const auto* summary = HTMLSummaryElement::FromNode(aContent);
    629    if (summary && summary->IsMainSummary()) {
    630      aName.AssignLiteral("internal-main-summary");
    631    }
    632    // Otherwise use the default slot.
    633    return;
    634  }
    635 
    636  // Note that if slot attribute is missing, assign it to the first default
    637  // slot, if exists.
    638  if (const Element* element = Element::FromNode(aContent)) {
    639    element->GetAttr(nsGkAtoms::slot, aName);
    640  }
    641 }
    642 
    643 ShadowRoot::SlotInsertionPoint ShadowRoot::SlotInsertionPointFor(
    644    nsIContent& aContent) {
    645  HTMLSlotElement* slot = nullptr;
    646 
    647  if (SlotAssignment() == SlotAssignmentMode::Manual) {
    648    slot = aContent.GetManualSlotAssignment();
    649    if (!slot || slot->GetContainingShadow() != this) {
    650      return {};
    651    }
    652  } else {
    653    nsAutoString slotName;
    654    GetSlotNameFor(aContent, slotName);
    655 
    656    SlotArray* slots = mSlotMap.Get(slotName);
    657    if (!slots) {
    658      return {};
    659    }
    660    slot = (*slots).ElementAt(0);
    661  }
    662 
    663  MOZ_ASSERT(slot);
    664 
    665  if (SlotAssignment() == SlotAssignmentMode::Named) {
    666    if (!aContent.GetNextSibling()) {
    667      // aContent is the last child, no need to loop through the assigned nodes,
    668      // we're necessarily the last one.
    669      //
    670      // This prevents multiple appends into the host from getting quadratic.
    671      return {slot, Nothing()};
    672    }
    673  } else {
    674    // For manual slots, if aContent is the last element, we return Nothing
    675    // because we just need to append the element to the assigned nodes. No need
    676    // to return an index.
    677    if (slot->ManuallyAssignedNodes().SafeLastElement(nullptr) == &aContent) {
    678      return {slot, Nothing()};
    679    }
    680  }
    681 
    682  // Find the appropriate position in the assigned node list for the newly
    683  // assigned content.
    684  if (SlotAssignment() == SlotAssignmentMode::Manual) {
    685    const nsTArray<nsINode*>& manuallyAssignedNodes =
    686        slot->ManuallyAssignedNodes();
    687    auto index = manuallyAssignedNodes.IndexOf(&aContent);
    688    if (index != manuallyAssignedNodes.NoIndex) {
    689      return {slot, Some(index)};
    690    }
    691  } else {
    692    const Span assignedNodes = slot->AssignedNodes();
    693    nsIContent* currentContent = GetHost()->GetFirstChild();
    694    for (uint32_t i = 0; i < assignedNodes.Length(); i++) {
    695      // Seek through the host's explicit children until the
    696      // assigned content is found.
    697      while (currentContent && currentContent != assignedNodes[i]) {
    698        if (currentContent == &aContent) {
    699          return {slot, Some(i)};
    700        }
    701        currentContent = currentContent->GetNextSibling();
    702      }
    703    }
    704  }
    705 
    706  return {slot, Nothing()};
    707 }
    708 
    709 void ShadowRoot::MaybeReassignContent(nsIContent& aElementOrText) {
    710  MOZ_ASSERT(aElementOrText.GetParent() == GetHost());
    711  MOZ_ASSERT(aElementOrText.IsElement() || aElementOrText.IsText());
    712  HTMLSlotElement* oldSlot = aElementOrText.GetAssignedSlot();
    713 
    714  SlotInsertionPoint assignment = SlotInsertionPointFor(aElementOrText);
    715 
    716  if (assignment.mSlot == oldSlot) {
    717    // Nothing to do here.
    718    return;
    719  }
    720 
    721  // The layout invalidation piece for Manual slots is handled in
    722  // HTMLSlotElement::Assign
    723  if (aElementOrText.IsElement() &&
    724      SlotAssignment() == SlotAssignmentMode::Named) {
    725    if (Document* doc = GetComposedDoc()) {
    726      if (RefPtr<PresShell> presShell = doc->GetPresShell()) {
    727        presShell->SlotAssignmentWillChange(*aElementOrText.AsElement(),
    728                                            oldSlot, assignment.mSlot);
    729      }
    730    }
    731  }
    732 
    733  if (oldSlot) {
    734    if (SlotAssignment() == SlotAssignmentMode::Named) {
    735      oldSlot->RemoveAssignedNode(aElementOrText);
    736      // Don't need to EnqueueSlotChangeEvent for Manual slots because it
    737      // needs to be done in tree order, so
    738      // HTMLSlotElement::Assign will handle it explicitly.
    739      oldSlot->EnqueueSlotChangeEvent();
    740    } else {
    741      oldSlot->RemoveManuallyAssignedNode(aElementOrText);
    742    }
    743  }
    744 
    745  if (assignment.mSlot) {
    746    if (assignment.mIndex) {
    747      assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aElementOrText);
    748    } else {
    749      assignment.mSlot->AppendAssignedNode(aElementOrText);
    750    }
    751    // Similar as above, HTMLSlotElement::Assign handles enqueuing
    752    // slotchange event.
    753    if (SlotAssignment() == SlotAssignmentMode::Named) {
    754      assignment.mSlot->EnqueueSlotChangeEvent();
    755    }
    756  }
    757 }
    758 
    759 void ShadowRoot::MaybeReassignMainSummary(SummaryChangeReason aReason) {
    760  MOZ_ASSERT(mIsDetailsShadowTree);
    761  if (aReason == SummaryChangeReason::Insertion) {
    762    // We've inserted a summary element, may need to remove the existing one.
    763    SlotArray* array = mSlotMap.Get(u"internal-main-summary"_ns);
    764    MOZ_RELEASE_ASSERT(array && (*array).Length() == 1);
    765    HTMLSlotElement* slot = (*array).ElementAt(0);
    766    auto assigned = slot->AssignedNodes();
    767    if (assigned.IsEmpty()) {
    768      return;
    769    }
    770    if (auto* summary = HTMLSummaryElement::FromNode(assigned[0])) {
    771      MaybeReassignContent(*summary);
    772    }
    773  } else if (MOZ_LIKELY(GetHost())) {
    774    // We need to null-check GetHost() in case we're unlinking already.
    775    auto* details = HTMLDetailsElement::FromNode(Host());
    776    MOZ_DIAGNOSTIC_ASSERT(details);
    777    // We've removed a summary element, we may need to assign the new one.
    778    if (HTMLSummaryElement* newMainSummary = details->GetFirstSummary()) {
    779      MaybeReassignContent(*newMainSummary);
    780    }
    781  }
    782 }
    783 
    784 Element* ShadowRoot::GetActiveElement() {
    785  return GetRetargetedFocusedElement();
    786 }
    787 
    788 nsINode* ShadowRoot::ImportNodeAndAppendChildAt(nsINode& aParentNode,
    789                                                nsINode& aNode, bool aDeep,
    790                                                mozilla::ErrorResult& rv) {
    791  MOZ_ASSERT(IsUAWidget());
    792 
    793  if (aParentNode.SubtreeRoot() != this) {
    794    rv.Throw(NS_ERROR_INVALID_ARG);
    795    return nullptr;
    796  }
    797 
    798  RefPtr<nsINode> node = OwnerDoc()->ImportNode(aNode, aDeep, rv);
    799  if (rv.Failed()) {
    800    return nullptr;
    801  }
    802 
    803  return aParentNode.AppendChild(*node, rv);
    804 }
    805 
    806 nsINode* ShadowRoot::CreateElementAndAppendChildAt(nsINode& aParentNode,
    807                                                   const nsAString& aTagName,
    808                                                   mozilla::ErrorResult& rv) {
    809  MOZ_ASSERT(IsUAWidget());
    810 
    811  if (aParentNode.SubtreeRoot() != this) {
    812    rv.Throw(NS_ERROR_INVALID_ARG);
    813    return nullptr;
    814  }
    815 
    816  // This option is not exposed to UA Widgets
    817  ElementCreationOptionsOrString options;
    818 
    819  RefPtr<nsINode> node = OwnerDoc()->CreateElement(aTagName, options, rv);
    820  if (rv.Failed()) {
    821    return nullptr;
    822  }
    823 
    824  return aParentNode.AppendChild(*node, rv);
    825 }
    826 
    827 void ShadowRoot::MaybeUnslotHostChild(nsIContent& aChild) {
    828  // Need to null-check the host because we may be unlinked already.
    829  MOZ_ASSERT(!GetHost() || aChild.GetParent() == GetHost());
    830 
    831  HTMLSlotElement* slot = aChild.GetAssignedSlot();
    832  if (!slot) {
    833    return;
    834  }
    835 
    836  MOZ_DIAGNOSTIC_ASSERT(!aChild.IsRootOfNativeAnonymousSubtree(),
    837                        "How did aChild end up assigned to a slot?");
    838  // If the slot is going to start showing fallback content, we need to tell
    839  // layout about it.
    840  if (slot->AssignedNodes().Length() == 1 && slot->HasChildren()) {
    841    InvalidateStyleAndLayoutOnSubtree(slot);
    842  }
    843 
    844  slot->EnqueueSlotChangeEvent();
    845  slot->RemoveAssignedNode(aChild);
    846  if (mIsDetailsShadowTree && aChild.IsHTMLElement(nsGkAtoms::summary)) {
    847    MaybeReassignMainSummary(SummaryChangeReason::Deletion);
    848  }
    849 }
    850 
    851 void ShadowRoot::MaybeSlotHostChild(nsIContent& aChild) {
    852  MOZ_ASSERT(aChild.GetParent() == GetHost());
    853  // Check to ensure that the child not an anonymous subtree root because even
    854  // though its parent could be the host it may not be in the host's child
    855  // list.
    856  if (aChild.IsRootOfNativeAnonymousSubtree()) {
    857    return;
    858  }
    859 
    860  if (!aChild.IsSlotable()) {
    861    return;
    862  }
    863 
    864  if (mIsDetailsShadowTree && aChild.IsHTMLElement(nsGkAtoms::summary)) {
    865    MaybeReassignMainSummary(SummaryChangeReason::Insertion);
    866  }
    867 
    868  SlotInsertionPoint assignment = SlotInsertionPointFor(aChild);
    869  if (!assignment.mSlot) {
    870    return;
    871  }
    872 
    873  // Fallback content will go away, let layout know.
    874  if (assignment.mSlot->AssignedNodes().IsEmpty() &&
    875      assignment.mSlot->HasChildren()) {
    876    InvalidateStyleAndLayoutOnSubtree(assignment.mSlot);
    877  }
    878 
    879  if (assignment.mIndex) {
    880    assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aChild);
    881  } else {
    882    assignment.mSlot->AppendAssignedNode(aChild);
    883  }
    884  assignment.mSlot->EnqueueSlotChangeEvent();
    885 }
    886 
    887 ServoStyleRuleMap& ShadowRoot::ServoStyleRuleMap() {
    888  if (!mStyleRuleMap) {
    889    mStyleRuleMap = MakeUnique<mozilla::ServoStyleRuleMap>();
    890  }
    891  mStyleRuleMap->EnsureTable(*this);
    892  return *mStyleRuleMap;
    893 }
    894 
    895 nsresult ShadowRoot::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
    896  *aResult = nullptr;
    897  return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    898 }
    899 
    900 void ShadowRoot::SetHTML(const nsAString& aHTML, const SetHTMLOptions& aOptions,
    901                         ErrorResult& aError) {
    902  RefPtr<Element> host = GetHost();
    903  nsContentUtils::SetHTML(this, host, aHTML, aOptions, aError);
    904 }
    905 
    906 void ShadowRoot::SetHTMLUnsafe(const TrustedHTMLOrString& aHTML,
    907                               const SetHTMLUnsafeOptions& aOptions,
    908                               nsIPrincipal* aSubjectPrincipal,
    909                               ErrorResult& aError) {
    910  RefPtr<Element> host = GetHost();
    911  nsContentUtils::SetHTMLUnsafe(this, host, aHTML, aOptions,
    912                                true /*aIsShadowRoot*/, aSubjectPrincipal,
    913                                aError);
    914 }
    915 
    916 void ShadowRoot::GetInnerHTML(
    917    OwningTrustedHTMLOrNullIsEmptyString& aInnerHTML) {
    918  DocumentFragment::GetInnerHTML(aInnerHTML.SetAsNullIsEmptyString());
    919 }
    920 
    921 MOZ_CAN_RUN_SCRIPT void ShadowRoot::SetInnerHTML(
    922    const TrustedHTMLOrNullIsEmptyString& aInnerHTML,
    923    nsIPrincipal* aSubjectPrincipal, ErrorResult& aError) {
    924  constexpr nsLiteralString sink = u"ShadowRoot innerHTML"_ns;
    925 
    926  Maybe<nsAutoString> compliantStringHolder;
    927  const nsAString* compliantString =
    928      TrustedTypeUtils::GetTrustedTypesCompliantString(
    929          aInnerHTML, sink, kTrustedTypesOnlySinkGroup, *this,
    930          aSubjectPrincipal, compliantStringHolder, aError);
    931  if (aError.Failed()) {
    932    return;
    933  }
    934 
    935  SetInnerHTMLInternal(*compliantString, aError);
    936 }
    937 
    938 void ShadowRoot::GetHTML(const GetHTMLOptions& aOptions, nsAString& aResult) {
    939  nsContentUtils::SerializeNodeToMarkup<SerializeShadowRoots::Yes>(
    940      this, true, aResult, aOptions.mSerializableShadowRoots,
    941      aOptions.mShadowRoots);
    942 }
    943 void ShadowRoot::SetReferenceTarget(RefPtr<nsAtom> aTarget) {
    944  MOZ_ASSERT(aTarget);
    945  mReferenceTarget = std::move(aTarget);
    946 }