diff options
Diffstat (limited to 'dom/base/ShadowRoot.cpp')
-rw-r--r-- | dom/base/ShadowRoot.cpp | 765 |
1 files changed, 765 insertions, 0 deletions
diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp new file mode 100644 index 000000000..9540754f7 --- /dev/null +++ b/dom/base/ShadowRoot.cpp @@ -0,0 +1,765 @@ +/* -*- 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/ShadowRoot.h" +#include "mozilla/dom/ShadowRootBinding.h" +#include "mozilla/dom/DocumentFragment.h" +#include "ChildIterator.h" +#include "nsContentUtils.h" +#include "nsDOMClassInfoID.h" +#include "nsIDOMHTMLElement.h" +#include "nsIStyleSheetLinkingElement.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLContentElement.h" +#include "mozilla/dom/HTMLShadowElement.h" +#include "nsXBLPrototypeBinding.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInlines.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, + DocumentFragment) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPoolHost) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOlderShadow) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mYoungerShadow) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAssociatedBinding) + for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); + iter.Next()) { + iter.Get()->Traverse(&cb); + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ShadowRoot, + DocumentFragment) + if (tmp->mPoolHost) { + tmp->mPoolHost->RemoveMutationObserver(tmp); + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPoolHost) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheetList) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOlderShadow) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mYoungerShadow) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAssociatedBinding) + tmp->mIdentifierMap.Clear(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRoot) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent) + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) +NS_INTERFACE_MAP_END_INHERITING(DocumentFragment) + +NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment) +NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment) + +ShadowRoot::ShadowRoot(nsIContent* aContent, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, + nsXBLPrototypeBinding* aProtoBinding) + : DocumentFragment(aNodeInfo), mPoolHost(aContent), + mProtoBinding(aProtoBinding), mShadowElement(nullptr), + mInsertionPointChanged(false), mIsComposedDocParticipant(false) +{ + SetHost(aContent); + + // 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); + + DOMSlots()->mBindingParent = aContent; + DOMSlots()->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. + mPoolHost->AddMutationObserver(this); +} + +ShadowRoot::~ShadowRoot() +{ + if (mPoolHost) { + // mPoolHost may have been unlinked or a new ShadowRoot may have been + // creating, making this one obsolete. + mPoolHost->RemoveMutationObserver(this); + } + + UnsetFlags(NODE_IS_IN_SHADOW_TREE); + + // nsINode destructor expects mSubtreeRoot == this. + SetSubtreeRootPointer(this); + + SetHost(nullptr); +} + +JSObject* +ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto); +} + +ShadowRoot* +ShadowRoot::FromNode(nsINode* aNode) +{ + if (aNode->IsInShadowTree() && !aNode->GetParentNode()) { + MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE, + "ShadowRoot is a document fragment."); + return static_cast<ShadowRoot*>(aNode); + } + + return nullptr; +} + +void +ShadowRoot::StyleSheetChanged() +{ + mProtoBinding->FlushSkinSheets(); + + nsIPresShell* shell = OwnerDoc()->GetShell(); + if (shell) { + OwnerDoc()->BeginUpdate(UPDATE_STYLE); + shell->RecordShadowStyleChange(this); + OwnerDoc()->EndUpdate(UPDATE_STYLE); + } +} + +void +ShadowRoot::InsertSheet(StyleSheet* aSheet, + nsIContent* aLinkingContent) +{ + nsCOMPtr<nsIStyleSheetLinkingElement> + linkingElement = do_QueryInterface(aLinkingContent); + MOZ_ASSERT(linkingElement, "The only styles in a ShadowRoot should come " + "from <style>."); + + linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet + + // Find the correct position to insert into the style sheet list (must + // be in tree order). + for (size_t i = 0; i <= mProtoBinding->SheetCount(); i++) { + if (i == mProtoBinding->SheetCount()) { + mProtoBinding->AppendStyleSheet(aSheet); + break; + } + + nsINode* sheetOwningNode = mProtoBinding->StyleSheetAt(i)->GetOwnerNode(); + if (nsContentUtils::PositionIsBefore(aLinkingContent, sheetOwningNode)) { + mProtoBinding->InsertStyleSheetAt(i, aSheet); + break; + } + } + + if (aSheet->IsApplicable()) { + StyleSheetChanged(); + } +} + +void +ShadowRoot::RemoveSheet(StyleSheet* aSheet) +{ + mProtoBinding->RemoveStyleSheet(aSheet); + + if (aSheet->IsApplicable()) { + StyleSheetChanged(); + } +} + +Element* +ShadowRoot::GetElementById(const nsAString& aElementId) +{ + nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); + return entry ? entry->GetIdElement() : nullptr; +} + +already_AddRefed<nsContentList> +ShadowRoot::GetElementsByTagName(const nsAString& aTagName) +{ + return NS_GetContentList(this, kNameSpaceID_Unknown, aTagName); +} + +already_AddRefed<nsContentList> +ShadowRoot::GetElementsByTagNameNS(const nsAString& aNamespaceURI, + const nsAString& aLocalName) +{ + int32_t nameSpaceId = kNameSpaceID_Wildcard; + + if (!aNamespaceURI.EqualsLiteral("*")) { + nsresult rv = + nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, + nameSpaceId); + NS_ENSURE_SUCCESS(rv, nullptr); + } + + NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!"); + + return NS_GetContentList(this, nameSpaceId, aLocalName); +} + +void +ShadowRoot::AddToIdTable(Element* aElement, nsIAtom* aId) +{ + nsIdentifierMapEntry *entry = + mIdentifierMap.PutEntry(nsDependentAtomString(aId)); + if (entry) { + entry->AddIdElement(aElement); + } +} + +void +ShadowRoot::RemoveFromIdTable(Element* aElement, nsIAtom* aId) +{ + nsIdentifierMapEntry *entry = + mIdentifierMap.GetEntry(nsDependentAtomString(aId)); + if (entry) { + entry->RemoveIdElement(aElement); + if (entry->IsEmpty()) { + mIdentifierMap.RemoveEntry(entry); + } + } +} + +already_AddRefed<nsContentList> +ShadowRoot::GetElementsByClassName(const nsAString& aClasses) +{ + return nsContentUtils::GetElementsByClassName(this, aClasses); +} + +void +ShadowRoot::AddInsertionPoint(HTMLContentElement* aInsertionPoint) +{ + TreeOrderComparator comparator; + mInsertionPoints.InsertElementSorted(aInsertionPoint, comparator); +} + +void +ShadowRoot::RemoveInsertionPoint(HTMLContentElement* aInsertionPoint) +{ + mInsertionPoints.RemoveElement(aInsertionPoint); +} + +void +ShadowRoot::SetYoungerShadow(ShadowRoot* aYoungerShadow) +{ + mYoungerShadow = aYoungerShadow; + mYoungerShadow->mOlderShadow = this; + + ChangePoolHost(mYoungerShadow->GetShadowElement()); +} + +void +ShadowRoot::RemoveDestInsertionPoint(nsIContent* aInsertionPoint, + nsTArray<nsIContent*>& aDestInsertionPoints) +{ + // Remove the insertion point from the destination insertion points. + // Also remove all succeeding insertion points because it is no longer + // possible for the content to be distributed into deeper node trees. + int32_t index = aDestInsertionPoints.IndexOf(aInsertionPoint); + + // It's possible that we already removed the insertion point while processing + // other insertion point removals. + if (index >= 0) { + aDestInsertionPoints.SetLength(index); + } +} + +void +ShadowRoot::DistributeSingleNode(nsIContent* aContent) +{ + // Find the insertion point to which the content belongs. + HTMLContentElement* insertionPoint = nullptr; + for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) { + if (mInsertionPoints[i]->Match(aContent)) { + if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) { + // Node is already matched into the insertion point. We are done. + return; + } + + // Matching may cause the insertion point to drop fallback content. + if (mInsertionPoints[i]->MatchedNodes().IsEmpty() && + static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) { + // This match will cause the insertion point to drop all fallback + // content and used matched nodes instead. Give up on the optimization + // and just distribute all nodes. + DistributeAllNodes(); + return; + } + insertionPoint = mInsertionPoints[i]; + break; + } + } + + // Find the index into the insertion point. + if (insertionPoint) { + nsCOMArray<nsIContent>& matchedNodes = insertionPoint->MatchedNodes(); + // Find the appropriate position in the matched node list for the + // newly distributed content. + bool isIndexFound = false; + MOZ_ASSERT(mPoolHost, "Where did the content come from if there is no pool host?"); + ExplicitChildIterator childIterator(mPoolHost); + for (uint32_t i = 0; i < matchedNodes.Length(); i++) { + // Seek through the host's explicit children until the inserted content + // is found or when the current matched node is reached. + if (childIterator.Seek(aContent, matchedNodes[i])) { + // aContent was found before the current matched node. + insertionPoint->InsertMatchedNode(i, aContent); + isIndexFound = true; + break; + } + } + + if (!isIndexFound) { + // We have still not found an index in the insertion point, + // thus it must be at the end. + MOZ_ASSERT(childIterator.Seek(aContent, nullptr), + "Trying to match a node that is not a candidate to be matched"); + insertionPoint->AppendMatchedNode(aContent); + } + + // Handle the case where the parent of the insertion point is a ShadowRoot + // that is projected into the younger ShadowRoot's shadow insertion point. + // The node distributed into the insertion point must be reprojected + // to the shadow insertion point. + if (insertionPoint->GetParent() == this && + mYoungerShadow && mYoungerShadow->GetShadowElement()) { + mYoungerShadow->GetShadowElement()->DistributeSingleNode(aContent); + } + + // Handle the case where the parent of the insertion point has a ShadowRoot. + // The node distributed into the insertion point must be reprojected to the + // insertion points of the parent's ShadowRoot. + ShadowRoot* parentShadow = insertionPoint->GetParent()->GetShadowRoot(); + if (parentShadow) { + parentShadow->DistributeSingleNode(aContent); + } + + // Handle the case where the parent of the insertion point is the <shadow> + // element. The node distributed into the insertion point must be reprojected + // into the older ShadowRoot's insertion points. + if (mShadowElement && mShadowElement == insertionPoint->GetParent()) { + ShadowRoot* olderShadow = mShadowElement->GetOlderShadowRoot(); + if (olderShadow) { + olderShadow->DistributeSingleNode(aContent); + } + } + } +} + +void +ShadowRoot::RemoveDistributedNode(nsIContent* aContent) +{ + // Find insertion point containing the content and remove the node. + for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) { + if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) { + // Removing the matched node may cause the insertion point to use + // fallback content. + if (mInsertionPoints[i]->MatchedNodes().Length() == 1 && + static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) { + // Removing the matched node will cause fallback content to be + // used instead. Give up optimization and distribute all nodes. + DistributeAllNodes(); + return; + } + + mInsertionPoints[i]->RemoveMatchedNode(aContent); + + // Handle the case where the parent of the insertion point is a ShadowRoot + // that is projected into the younger ShadowRoot's shadow insertion point. + // The removed node needs to be removed from the shadow insertion point. + if (mInsertionPoints[i]->GetParent() == this) { + if (mYoungerShadow && mYoungerShadow->GetShadowElement()) { + mYoungerShadow->GetShadowElement()->RemoveDistributedNode(aContent); + } + } + + // Handle the case where the parent of the insertion point has a ShadowRoot. + // The removed node needs to be removed from the insertion points of the + // parent's ShadowRoot. + ShadowRoot* parentShadow = mInsertionPoints[i]->GetParent()->GetShadowRoot(); + if (parentShadow) { + parentShadow->RemoveDistributedNode(aContent); + } + + // Handle the case where the parent of the insertion point is the <shadow> + // element. The removed node must be removed from the older ShadowRoot's + // insertion points. + if (mShadowElement && mShadowElement == mInsertionPoints[i]->GetParent()) { + ShadowRoot* olderShadow = mShadowElement->GetOlderShadowRoot(); + if (olderShadow) { + olderShadow->RemoveDistributedNode(aContent); + } + } + + break; + } + } +} + +void +ShadowRoot::DistributeAllNodes() +{ + // Create node pool. + nsTArray<nsIContent*> nodePool; + + // Make sure there is a pool host, an older shadow may not have + // one if the younger shadow does not have a <shadow> element. + if (mPoolHost) { + ExplicitChildIterator childIterator(mPoolHost); + for (nsIContent* content = childIterator.GetNextChild(); + content; + content = childIterator.GetNextChild()) { + nodePool.AppendElement(content); + } + } + + nsTArray<ShadowRoot*> shadowsToUpdate; + + for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) { + mInsertionPoints[i]->ClearMatchedNodes(); + // Assign matching nodes from node pool. + for (uint32_t j = 0; j < nodePool.Length(); j++) { + if (mInsertionPoints[i]->Match(nodePool[j])) { + mInsertionPoints[i]->AppendMatchedNode(nodePool[j]); + nodePool.RemoveElementAt(j--); + } + } + + // Keep track of instances where the content insertion point is distributed + // (parent of insertion point has a ShadowRoot). + nsIContent* insertionParent = mInsertionPoints[i]->GetParent(); + MOZ_ASSERT(insertionParent, "The only way for an insertion point to be in the" + "mInsertionPoints array is to be a descendant of a" + "ShadowRoot, in which case, it should have a parent"); + + // If the parent of the insertion point has a ShadowRoot, the nodes distributed + // to the insertion point must be reprojected to the insertion points of the + // parent's ShadowRoot. + ShadowRoot* parentShadow = insertionParent->GetShadowRoot(); + if (parentShadow && !shadowsToUpdate.Contains(parentShadow)) { + shadowsToUpdate.AppendElement(parentShadow); + } + } + + // If there is a shadow insertion point in this ShadowRoot, the children + // of the shadow insertion point needs to be distributed into the insertion + // points of the older ShadowRoot. + if (mShadowElement && mOlderShadow) { + mOlderShadow->DistributeAllNodes(); + } + + // If there is a younger ShadowRoot with a shadow insertion point, + // then the children of this ShadowRoot needs to be distributed to + // the younger ShadowRoot's shadow insertion point. + if (mYoungerShadow && mYoungerShadow->GetShadowElement()) { + mYoungerShadow->GetShadowElement()->DistributeAllNodes(); + } + + for (uint32_t i = 0; i < shadowsToUpdate.Length(); i++) { + shadowsToUpdate[i]->DistributeAllNodes(); + } +} + +void +ShadowRoot::GetInnerHTML(nsAString& aInnerHTML) +{ + GetMarkup(false, aInnerHTML); +} + +void +ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError) +{ + SetInnerHTMLInternal(aInnerHTML, aError); +} + +Element* +ShadowRoot::Host() +{ + nsIContent* host = GetHost(); + MOZ_ASSERT(host && host->IsElement(), + "ShadowRoot host should always be an element, " + "how else did we create this ShadowRoot?"); + return host->AsElement(); +} + +bool +ShadowRoot::ApplyAuthorStyles() +{ + return mProtoBinding->InheritsStyle(); +} + +void +ShadowRoot::SetApplyAuthorStyles(bool aApplyAuthorStyles) +{ + mProtoBinding->SetInheritsStyle(aApplyAuthorStyles); + + nsIPresShell* shell = OwnerDoc()->GetShell(); + if (shell) { + OwnerDoc()->BeginUpdate(UPDATE_STYLE); + shell->RecordShadowStyleChange(this); + OwnerDoc()->EndUpdate(UPDATE_STYLE); + } +} + +StyleSheetList* +ShadowRoot::StyleSheets() +{ + if (!mStyleSheetList) { + mStyleSheetList = new ShadowRootStyleSheetList(this); + } + + return mStyleSheetList; +} + +void +ShadowRoot::SetShadowElement(HTMLShadowElement* aShadowElement) +{ + // If there is already a shadow element point, remove + // the projected shadow because it is no longer an insertion + // point. + if (mShadowElement) { + mShadowElement->SetProjectedShadow(nullptr); + } + + if (mOlderShadow) { + // Nodes for distribution will come from the new shadow element. + mOlderShadow->ChangePoolHost(aShadowElement); + } + + // Set the new shadow element to project the older ShadowRoot because + // it is the current shadow insertion point. + mShadowElement = aShadowElement; + if (mShadowElement) { + mShadowElement->SetProjectedShadow(mOlderShadow); + } +} + +void +ShadowRoot::ChangePoolHost(nsIContent* aNewHost) +{ + if (mPoolHost) { + mPoolHost->RemoveMutationObserver(this); + } + + // Clear the nodes matched to content insertion points + // because it is no longer relevant. + for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) { + mInsertionPoints[i]->ClearMatchedNodes(); + } + + mPoolHost = aNewHost; + if (mPoolHost) { + mPoolHost->AddMutationObserver(this); + } +} + +bool +ShadowRoot::IsShadowInsertionPoint(nsIContent* aContent) +{ + if (!aContent) { + return false; + } + + HTMLShadowElement* shadowElem = HTMLShadowElement::FromContent(aContent); + return shadowElem && shadowElem->IsInsertionPoint(); +} + +/** + * Returns whether the web components pool population algorithm + * on the host would contain |aContent|. This function ignores + * insertion points in the pool, thus should only be used to + * test nodes that have not yet been distributed. + */ +bool +ShadowRoot::IsPooledNode(nsIContent* aContent, nsIContent* aContainer, + nsIContent* aHost) +{ + if (nsContentUtils::IsContentInsertionPoint(aContent) || + IsShadowInsertionPoint(aContent)) { + // Insertion points never end up in the pool. + return false; + } + + if (aContainer == aHost && + nsContentUtils::IsInSameAnonymousTree(aContainer, aContent)) { + // Children of the host will end up in the pool. We check to ensure + // that the content is in the same anonymous tree as the container + // because anonymous content may report its container as the host + // but it may not be in the host's child list. + return true; + } + + if (aContainer) { + // Fallback content will end up in pool if its parent is a child of the host. + HTMLContentElement* content = HTMLContentElement::FromContent(aContainer); + return content && content->IsInsertionPoint() && + content->MatchedNodes().IsEmpty() && + aContainer->GetParentNode() == aHost; + } + + return false; +} + +void +ShadowRoot::AttributeChanged(nsIDocument* aDocument, + Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue) +{ + if (!IsPooledNode(aElement, aElement->GetParent(), mPoolHost)) { + return; + } + + // Attributes may change insertion point matching, find its new distribution. + RemoveDistributedNode(aElement); + DistributeSingleNode(aElement); +} + +void +ShadowRoot::ContentAppended(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aFirstNewContent, + int32_t aNewIndexInContainer) +{ + if (mInsertionPointChanged) { + DistributeAllNodes(); + mInsertionPointChanged = false; + return; + } + + // Watch for new nodes added to the pool because the node + // may need to be added to an insertion point. + nsIContent* currentChild = aFirstNewContent; + while (currentChild) { + // Add insertion point to destination insertion points of fallback content. + if (nsContentUtils::IsContentInsertionPoint(aContainer)) { + HTMLContentElement* content = HTMLContentElement::FromContent(aContainer); + if (content->MatchedNodes().IsEmpty()) { + currentChild->DestInsertionPoints().AppendElement(aContainer); + } + } + + if (IsPooledNode(currentChild, aContainer, mPoolHost)) { + DistributeSingleNode(currentChild); + } + + currentChild = currentChild->GetNextSibling(); + } +} + +void +ShadowRoot::ContentInserted(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer) +{ + if (mInsertionPointChanged) { + DistributeAllNodes(); + mInsertionPointChanged = false; + return; + } + + // Watch for new nodes added to the pool because the node + // may need to be added to an insertion point. + if (IsPooledNode(aChild, aContainer, mPoolHost)) { + // Add insertion point to destination insertion points of fallback content. + if (nsContentUtils::IsContentInsertionPoint(aContainer)) { + HTMLContentElement* content = HTMLContentElement::FromContent(aContainer); + if (content->MatchedNodes().IsEmpty()) { + aChild->DestInsertionPoints().AppendElement(aContainer); + } + } + + DistributeSingleNode(aChild); + } +} + +void +ShadowRoot::ContentRemoved(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer, + nsIContent* aPreviousSibling) +{ + if (mInsertionPointChanged) { + DistributeAllNodes(); + mInsertionPointChanged = false; + return; + } + + // Clear destination insertion points for removed + // fallback content. + if (nsContentUtils::IsContentInsertionPoint(aContainer)) { + HTMLContentElement* content = HTMLContentElement::FromContent(aContainer); + if (content->MatchedNodes().IsEmpty()) { + aChild->DestInsertionPoints().Clear(); + } + } + + // Watch for node that is removed from the pool because + // it may need to be removed from an insertion point. + if (IsPooledNode(aChild, aContainer, mPoolHost)) { + RemoveDistributedNode(aChild); + } +} + +nsresult +ShadowRoot::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const +{ + *aResult = nullptr; + return NS_ERROR_DOM_DATA_CLONE_ERR; +} + +void +ShadowRoot::DestroyContent() +{ + if (mOlderShadow) { + mOlderShadow->DestroyContent(); + } + DocumentFragment::DestroyContent(); +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList, StyleSheetList, + mShadowRoot) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList) +NS_INTERFACE_MAP_END_INHERITING(StyleSheetList) + +NS_IMPL_ADDREF_INHERITED(ShadowRootStyleSheetList, StyleSheetList) +NS_IMPL_RELEASE_INHERITED(ShadowRootStyleSheetList, StyleSheetList) + +ShadowRootStyleSheetList::ShadowRootStyleSheetList(ShadowRoot* aShadowRoot) + : mShadowRoot(aShadowRoot) +{ + MOZ_COUNT_CTOR(ShadowRootStyleSheetList); +} + +ShadowRootStyleSheetList::~ShadowRootStyleSheetList() +{ + MOZ_COUNT_DTOR(ShadowRootStyleSheetList); +} + +StyleSheet* +ShadowRootStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound) +{ + aFound = aIndex < mShadowRoot->mProtoBinding->SheetCount(); + if (!aFound) { + return nullptr; + } + return mShadowRoot->mProtoBinding->StyleSheetAt(aIndex); +} + +uint32_t +ShadowRootStyleSheetList::Length() +{ + return mShadowRoot->mProtoBinding->SheetCount(); +} + |