gecko-dev/dom/base/ShadowRoot.cpp
Emilio Cobos Álvarez c4dbe4893c Bug 1505489 - Keep track of elements with part attributes in a shadow tree. r=bzbarsky
I need this to make style invalidation work with Shadow Parts in a way that's
fast. If something in the ancestor shadow root or any of its ancestor changes,
that makes a ::part() selector change, I don't want to walk the whole shadow
tree over and over in order to find the parts that I need to restyle.

Unfortunately we cannot just use the mutation observer setup from ShadowRoot
since, unlike for slotted elements, there's no restriction of where a part can
appear in the shadow tree.

That means that the regular nsIMutationObserver notifications don't quite cut
it, since we'd get notified only of subtree roots and we'd need to tree-walk
around in order to figure out if we have any new part.

I thought that I was going to be able to share more code with other bits if I
moved them away from nsIMutationObserver, thus bug 1554498, but in the end it
was not the case, so here's the "without bug 1554498" version of the patch.

The patch on top of that bug (that as I mention in the commit message I'm
ambivalent about whether we should land or not) should be pretty similar either
way.

Differential Revision: https://phabricator.services.mozilla.com/D32641

--HG--
extra : moz-landing-system : lando
2019-06-11 17:41:37 +00:00

729 lines
21 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Preferences.h"
#include "mozilla/dom/BindContext.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/DocumentFragment.h"
#include "ChildIterator.h"
#include "nsContentUtils.h"
#include "nsIStyleSheetLinkingElement.h"
#include "nsWindowSizes.h"
#include "nsXBLPrototypeBinding.h"
#include "mozilla/dom/DirectionalityUtils.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLSlotElement.h"
#include "mozilla/dom/TreeOrderedArrayInlines.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/IdentifierMapEntry.h"
#include "mozilla/PresShell.h"
#include "mozilla/PresShellInlines.h"
#include "mozilla/ServoStyleRuleMap.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/dom/StyleSheetList.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment)
DocumentOrShadowRoot::Traverse(tmp, cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
if (tmp->GetHost()) {
tmp->GetHost()->RemoveMutationObserver(tmp);
}
DocumentOrShadowRoot::Unlink(tmp);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer)
NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
ShadowRoot::ShadowRoot(Element* aElement, ShadowRootMode aMode,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
: DocumentFragment(std::move(aNodeInfo)),
DocumentOrShadowRoot(*this),
mMode(aMode),
mIsUAWidget(false) {
SetHost(aElement);
// Nodes in a shadow tree should never store a value
// in the subtree root pointer, nodes in the shadow tree
// track the subtree root using GetContainingShadow().
ClearSubtreeRootPointer();
SetFlags(NODE_IS_IN_SHADOW_TREE);
Bind();
ExtendedDOMSlots()->mBindingParent = aElement;
ExtendedDOMSlots()->mContainingShadow = this;
// Add the ShadowRoot as a mutation observer on the host to watch
// for mutations because the insertion points in this ShadowRoot
// may need to be updated when the host children are modified.
GetHost()->AddMutationObserver(this);
}
ShadowRoot::~ShadowRoot() {
if (auto* host = GetHost()) {
// mHost may have been unlinked.
host->RemoveMutationObserver(this);
}
if (IsInComposedDoc()) {
OwnerDoc()->RemoveComposedDocShadowRoot(*this);
}
MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->IsComposedDocShadowRoot(*this));
UnsetFlags(NODE_IS_IN_SHADOW_TREE);
// nsINode destructor expects mSubtreeRoot == this.
SetSubtreeRootPointer(this);
}
MOZ_DEFINE_MALLOC_SIZE_OF(ShadowRootAuthorStylesMallocSizeOf)
MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ShadowRootAuthorStylesMallocEnclosingSizeOf)
void ShadowRoot::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
size_t* aNodeSize) const {
DocumentFragment::AddSizeOfExcludingThis(aSizes, aNodeSize);
DocumentOrShadowRoot::AddSizeOfExcludingThis(aSizes);
aSizes.mLayoutShadowDomAuthorStyles += Servo_AuthorStyles_SizeOfIncludingThis(
ShadowRootAuthorStylesMallocSizeOf,
ShadowRootAuthorStylesMallocEnclosingSizeOf, mServoStyles.get());
}
JSObject* ShadowRoot::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return mozilla::dom::ShadowRoot_Binding::Wrap(aCx, this, aGivenProto);
}
void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
size_t sheetCount = aOther->SheetCount();
for (size_t i = 0; i < sheetCount; ++i) {
StyleSheet* sheet = aOther->SheetAt(i);
if (sheet->IsApplicable()) {
RefPtr<StyleSheet> clonedSheet =
sheet->Clone(nullptr, nullptr, this, nullptr);
if (clonedSheet) {
AppendStyleSheet(*clonedSheet.get());
}
}
}
}
nsresult ShadowRoot::Bind() {
MOZ_ASSERT(!IsInComposedDoc(), "Forgot to unbind?");
if (Host()->IsInComposedDoc()) {
SetIsConnected(true);
OwnerDoc()->AddComposedDocShadowRoot(*this);
}
BindContext context(*this);
for (nsIContent* child = GetFirstChild(); child;
child = child->GetNextSibling()) {
nsresult rv = child->BindToTree(context, *this);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
void ShadowRoot::Unbind() {
if (IsInComposedDoc()) {
SetIsConnected(false);
OwnerDoc()->RemoveComposedDocShadowRoot(*this);
}
for (nsIContent* child = GetFirstChild(); child;
child = child->GetNextSibling()) {
child->UnbindFromTree(false);
}
}
void ShadowRoot::Unattach() {
MOZ_ASSERT(!HasSlots(), "Won't work!");
if (!GetHost()) {
// It is possible that we've been unlinked already. In such case host
// should have called Unbind and ShadowRoot's own unlink
// RemoveMutationObserver.
return;
}
Unbind();
GetHost()->RemoveMutationObserver(this);
SetHost(nullptr);
}
void ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element* aElement) {
MOZ_ASSERT(aElement);
Document* doc = GetComposedDoc();
if (!doc) {
return;
}
PresShell* presShell = doc->GetPresShell();
if (!presShell) {
return;
}
presShell->DestroyFramesForAndRestyle(aElement);
}
void ShadowRoot::PartAdded(const Element& aPart) {
MOZ_ASSERT(aPart.HasPartAttribute());
MOZ_ASSERT(!mParts.Contains(&aPart));
mParts.AppendElement(&aPart);
}
void ShadowRoot::PartRemoved(const Element& aPart) {
MOZ_ASSERT(mParts.Contains(&aPart));
mParts.RemoveElement(&aPart);
MOZ_ASSERT(!mParts.Contains(&aPart));
}
void ShadowRoot::AddSlot(HTMLSlotElement* aSlot) {
MOZ_ASSERT(aSlot);
// Note that if name attribute missing, the slot is a default slot.
nsAutoString name;
aSlot->GetName(name);
SlotArray& currentSlots = *mSlotMap.LookupOrAdd(name);
size_t index = currentSlots.Insert(*aSlot);
if (index != 0) {
return;
}
HTMLSlotElement* oldSlot = currentSlots->SafeElementAt(1);
if (oldSlot) {
MOZ_DIAGNOSTIC_ASSERT(oldSlot != aSlot);
// Move assigned nodes from old slot to new slot.
InvalidateStyleAndLayoutOnSubtree(oldSlot);
const nsTArray<RefPtr<nsINode>>& assignedNodes = oldSlot->AssignedNodes();
bool doEnqueueSlotChange = false;
while (assignedNodes.Length() > 0) {
nsINode* assignedNode = assignedNodes[0];
oldSlot->RemoveAssignedNode(assignedNode);
aSlot->AppendAssignedNode(assignedNode);
doEnqueueSlotChange = true;
}
if (doEnqueueSlotChange) {
oldSlot->EnqueueSlotChangeEvent();
aSlot->EnqueueSlotChangeEvent();
SlotStateChanged(oldSlot);
SlotStateChanged(aSlot);
}
} else {
bool doEnqueueSlotChange = false;
// Otherwise add appropriate nodes to this slot from the host.
for (nsIContent* child = GetHost()->GetFirstChild(); child;
child = child->GetNextSibling()) {
nsAutoString slotName;
if (child->IsElement()) {
child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::slot,
slotName);
}
if (!child->IsSlotable() || !slotName.Equals(name)) {
continue;
}
doEnqueueSlotChange = true;
aSlot->AppendAssignedNode(child);
}
if (doEnqueueSlotChange) {
aSlot->EnqueueSlotChangeEvent();
SlotStateChanged(aSlot);
}
}
}
void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
MOZ_ASSERT(aSlot);
nsAutoString name;
aSlot->GetName(name);
MOZ_ASSERT(mSlotMap.Get(name));
SlotArray& currentSlots = *mSlotMap.Get(name);
MOZ_DIAGNOSTIC_ASSERT(currentSlots->Contains(aSlot),
"Slot to de-register wasn't found?");
if (currentSlots->Length() == 1) {
MOZ_ASSERT(currentSlots->ElementAt(0) == aSlot);
InvalidateStyleAndLayoutOnSubtree(aSlot);
mSlotMap.Remove(name);
if (!aSlot->AssignedNodes().IsEmpty()) {
aSlot->ClearAssignedNodes();
aSlot->EnqueueSlotChangeEvent();
}
return;
}
const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot;
currentSlots.RemoveElement(*aSlot);
if (!wasFirstSlot) {
return;
}
// Move assigned nodes from removed slot to the next slot in
// tree order with the same name.
InvalidateStyleAndLayoutOnSubtree(aSlot);
HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
if (assignedNodes.IsEmpty()) {
return;
}
InvalidateStyleAndLayoutOnSubtree(replacementSlot);
while (!assignedNodes.IsEmpty()) {
nsINode* assignedNode = assignedNodes[0];
aSlot->RemoveAssignedNode(assignedNode);
replacementSlot->AppendAssignedNode(assignedNode);
}
aSlot->EnqueueSlotChangeEvent();
replacementSlot->EnqueueSlotChangeEvent();
}
// FIXME(emilio): There's a bit of code duplication between this and the
// equivalent ServoStyleSet methods, it'd be nice to not duplicate it...
void ShadowRoot::RuleAdded(StyleSheet& aSheet, css::Rule& aRule) {
if (!aSheet.IsApplicable()) {
return;
}
MOZ_ASSERT(mServoStyles);
if (mStyleRuleMap) {
mStyleRuleMap->RuleAdded(aSheet, aRule);
}
Servo_AuthorStyles_ForceDirty(mServoStyles.get());
ApplicableRulesChanged();
}
void ShadowRoot::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule) {
if (!aSheet.IsApplicable()) {
return;
}
MOZ_ASSERT(mServoStyles);
if (mStyleRuleMap) {
mStyleRuleMap->RuleRemoved(aSheet, aRule);
}
Servo_AuthorStyles_ForceDirty(mServoStyles.get());
ApplicableRulesChanged();
}
void ShadowRoot::RuleChanged(StyleSheet& aSheet, css::Rule*) {
if (!aSheet.IsApplicable()) {
return;
}
MOZ_ASSERT(mServoStyles);
Servo_AuthorStyles_ForceDirty(mServoStyles.get());
ApplicableRulesChanged();
}
// We don't need to do anything else than forwarding to the document if
// necessary.
void ShadowRoot::StyleSheetCloned(StyleSheet& aSheet) {
if (Document* doc = GetComposedDoc()) {
if (PresShell* shell = doc->GetPresShell()) {
shell->StyleSet()->StyleSheetCloned(aSheet);
}
}
}
void ShadowRoot::ApplicableRulesChanged() {
if (Document* doc = GetComposedDoc()) {
doc->RecordShadowStyleChange(*this);
}
}
void ShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet);
if (aSheet.IsApplicable()) {
InsertSheetIntoAuthorData(aIndex, aSheet);
}
}
void ShadowRoot::InsertSheetIntoAuthorData(size_t aIndex, StyleSheet& aSheet) {
MOZ_ASSERT(SheetAt(aIndex) == &aSheet);
MOZ_ASSERT(aSheet.IsApplicable());
if (!mServoStyles) {
mServoStyles = Servo_AuthorStyles_Create().Consume();
}
if (mStyleRuleMap) {
mStyleRuleMap->SheetAdded(aSheet);
}
for (size_t i = aIndex + 1; i < SheetCount(); ++i) {
StyleSheet* beforeSheet = SheetAt(i);
if (!beforeSheet->IsApplicable()) {
continue;
}
Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles.get(), &aSheet,
beforeSheet);
ApplicableRulesChanged();
return;
}
Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
ApplicableRulesChanged();
}
// FIXME(emilio): This needs to notify document observers and such,
// presumably.
void ShadowRoot::StyleSheetApplicableStateChanged(StyleSheet& aSheet,
bool aApplicable) {
int32_t index = IndexOfSheet(aSheet);
if (index < 0) {
// NOTE(emilio): @import sheets are handled in the relevant RuleAdded
// notification, which only notifies after the sheet is loaded.
//
// This setup causes weirdness in other places, we may want to fix this in
// bug 1465031.
MOZ_DIAGNOSTIC_ASSERT(aSheet.GetParentSheet(),
"It'd better be an @import sheet");
return;
}
if (aApplicable) {
InsertSheetIntoAuthorData(size_t(index), aSheet);
} else {
MOZ_ASSERT(mServoStyles);
if (mStyleRuleMap) {
mStyleRuleMap->SheetRemoved(aSheet);
}
Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
ApplicableRulesChanged();
}
}
void ShadowRoot::RemoveSheet(StyleSheet* aSheet) {
MOZ_ASSERT(aSheet);
RefPtr<StyleSheet> sheet = DocumentOrShadowRoot::RemoveSheet(*aSheet);
MOZ_ASSERT(sheet);
if (sheet->IsApplicable()) {
MOZ_ASSERT(mServoStyles);
if (mStyleRuleMap) {
mStyleRuleMap->SheetRemoved(*sheet);
}
Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), sheet);
ApplicableRulesChanged();
}
}
void ShadowRoot::AddToIdTable(Element* aElement, nsAtom* aId) {
IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
if (entry) {
entry->AddIdElement(aElement);
}
}
void ShadowRoot::RemoveFromIdTable(Element* aElement, nsAtom* aId) {
IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
if (entry) {
entry->RemoveIdElement(aElement);
if (entry->IsEmpty()) {
mIdentifierMap.RemoveEntry(entry);
}
}
}
void ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
aVisitor.mCanHandle = true;
aVisitor.mRootOfClosedTree = IsClosed();
// Inform that we're about to exit the current scope.
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
// https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
if (!aVisitor.mEvent->mFlags.mComposed) {
nsCOMPtr<nsIContent> originalTarget =
do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
if (originalTarget && originalTarget->GetContainingShadow() == this) {
// If we do stop propagation, we still want to propagate
// the event to chrome (nsPIDOMWindow::GetParentTarget()).
// The load event is special in that we don't ever propagate it
// to chrome.
nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
? win->GetParentTarget()
: nullptr;
aVisitor.SetParentTarget(parentTarget, true);
return;
}
}
nsIContent* shadowHost = GetHost();
aVisitor.SetParentTarget(shadowHost, false);
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
if (content && content->GetBindingParent() == shadowHost) {
aVisitor.mEventTargetAtParent = shadowHost;
}
}
ShadowRoot::SlotAssignment ShadowRoot::SlotAssignmentFor(nsIContent* aContent) {
nsAutoString slotName;
// Note that if slot attribute is missing, assign it to the first default
// slot, if exists.
if (Element* element = Element::FromNode(aContent)) {
element->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
}
SlotArray* slots = mSlotMap.Get(slotName);
if (!slots) {
return {};
}
HTMLSlotElement* slot = (*slots)->ElementAt(0);
MOZ_ASSERT(slot);
// Find the appropriate position in the assigned node list for the
// newly assigned content.
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
nsIContent* currentContent = GetHost()->GetFirstChild();
Maybe<uint32_t> insertionIndex;
for (uint32_t i = 0; i < assignedNodes.Length(); i++) {
// Seek through the host's explicit children until the
// assigned content is found.
while (currentContent && currentContent != assignedNodes[i]) {
if (currentContent == aContent) {
insertionIndex.emplace(i);
break;
}
currentContent = currentContent->GetNextSibling();
}
if (insertionIndex) {
break;
}
}
return {slot, insertionIndex};
}
void ShadowRoot::MaybeReassignElement(Element* aElement) {
MOZ_ASSERT(aElement->GetParent() == GetHost());
HTMLSlotElement* oldSlot = aElement->GetAssignedSlot();
SlotAssignment assignment = SlotAssignmentFor(aElement);
if (assignment.mSlot == oldSlot) {
// Nothing to do here.
return;
}
if (Document* doc = GetComposedDoc()) {
if (RefPtr<PresShell> presShell = doc->GetPresShell()) {
presShell->SlotAssignmentWillChange(*aElement, oldSlot, assignment.mSlot);
}
}
if (oldSlot) {
oldSlot->RemoveAssignedNode(aElement);
oldSlot->EnqueueSlotChangeEvent();
}
if (assignment.mSlot) {
if (assignment.mIndex) {
assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aElement);
} else {
assignment.mSlot->AppendAssignedNode(aElement);
}
assignment.mSlot->EnqueueSlotChangeEvent();
}
SlotStateChanged(oldSlot);
SlotStateChanged(assignment.mSlot);
}
Element* ShadowRoot::GetActiveElement() {
return GetRetargetedFocusedElement();
}
void ShadowRoot::GetInnerHTML(nsAString& aInnerHTML) {
GetMarkup(false, aInnerHTML);
}
void ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML,
ErrorResult& aError) {
SetInnerHTMLInternal(aInnerHTML, aError);
}
nsINode* ShadowRoot::ImportNodeAndAppendChildAt(nsINode& aParentNode,
nsINode& aNode, bool aDeep,
mozilla::ErrorResult& rv) {
MOZ_ASSERT(mIsUAWidget);
if (!aParentNode.IsInUAWidget()) {
rv.Throw(NS_ERROR_INVALID_ARG);
return nullptr;
}
RefPtr<nsINode> node = OwnerDoc()->ImportNode(aNode, aDeep, rv);
if (rv.Failed()) {
return nullptr;
}
return aParentNode.AppendChild(*node, rv);
}
nsINode* ShadowRoot::CreateElementAndAppendChildAt(nsINode& aParentNode,
const nsAString& aTagName,
mozilla::ErrorResult& rv) {
MOZ_ASSERT(mIsUAWidget);
if (!aParentNode.IsInUAWidget()) {
rv.Throw(NS_ERROR_INVALID_ARG);
return nullptr;
}
// This option is not exposed to UA Widgets
ElementCreationOptionsOrString options;
RefPtr<nsINode> node = OwnerDoc()->CreateElement(aTagName, options, rv);
if (rv.Failed()) {
return nullptr;
}
return aParentNode.AppendChild(*node, rv);
}
void ShadowRoot::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
nsAtom* aAttribute, int32_t aModType,
const nsAttrValue* aOldValue) {
if (aNameSpaceID != kNameSpaceID_None || aAttribute != nsGkAtoms::slot) {
return;
}
if (aElement->GetParent() != GetHost()) {
return;
}
MaybeReassignElement(aElement);
}
void ShadowRoot::ContentAppended(nsIContent* aFirstNewContent) {
for (nsIContent* content = aFirstNewContent; content;
content = content->GetNextSibling()) {
ContentInserted(content);
}
}
void ShadowRoot::ContentInserted(nsIContent* aChild) {
// Check to ensure that the child not an anonymous subtree root because
// even though its parent could be the host it may not be in the host's child
// list.
if (aChild->IsRootOfAnonymousSubtree()) {
return;
}
if (!aChild->IsSlotable()) {
return;
}
if (aChild->GetParent() == GetHost()) {
SlotAssignment assignment = SlotAssignmentFor(aChild);
if (!assignment.mSlot) {
return;
}
// Fallback content will go away, let layout know.
if (assignment.mSlot->AssignedNodes().IsEmpty()) {
InvalidateStyleAndLayoutOnSubtree(assignment.mSlot);
}
if (assignment.mIndex) {
assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aChild);
} else {
assignment.mSlot->AppendAssignedNode(aChild);
}
assignment.mSlot->EnqueueSlotChangeEvent();
SlotStateChanged(assignment.mSlot);
return;
}
// If parent's root is a shadow root, and parent is a slot whose assigned
// nodes is the empty list, then run signal a slot change for parent.
HTMLSlotElement* slot = HTMLSlotElement::FromNodeOrNull(aChild->GetParent());
if (slot && slot->GetContainingShadow() == this &&
slot->AssignedNodes().IsEmpty()) {
slot->EnqueueSlotChangeEvent();
}
}
void ShadowRoot::ContentRemoved(nsIContent* aChild,
nsIContent* aPreviousSibling) {
// Check to ensure that the child not an anonymous subtree root because
// even though its parent could be the host it may not be in the host's child
// list.
if (aChild->IsRootOfAnonymousSubtree()) {
return;
}
if (!aChild->IsSlotable()) {
return;
}
if (aChild->GetParent() == GetHost()) {
if (HTMLSlotElement* slot = aChild->GetAssignedSlot()) {
// If the slot is going to start showing fallback content, we need to tell
// layout about it.
if (slot->AssignedNodes().Length() == 1) {
InvalidateStyleAndLayoutOnSubtree(slot);
}
slot->RemoveAssignedNode(aChild);
slot->EnqueueSlotChangeEvent();
}
return;
}
// If parent's root is a shadow root, and parent is a slot whose assigned
// nodes is the empty list, then run signal a slot change for parent.
HTMLSlotElement* slot = HTMLSlotElement::FromNodeOrNull(aChild->GetParent());
if (slot && slot->GetContainingShadow() == this &&
slot->AssignedNodes().IsEmpty()) {
slot->EnqueueSlotChangeEvent();
}
}
ServoStyleRuleMap& ShadowRoot::ServoStyleRuleMap() {
if (!mStyleRuleMap) {
mStyleRuleMap = MakeUnique<mozilla::ServoStyleRuleMap>();
}
mStyleRuleMap->EnsureTable(*this);
return *mStyleRuleMap;
}
nsresult ShadowRoot::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
*aResult = nullptr;
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}