summaryrefslogtreecommitdiffstats
path: root/dom/html/HTMLShadowElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/html/HTMLShadowElement.cpp')
-rw-r--r--dom/html/HTMLShadowElement.cpp373
1 files changed, 373 insertions, 0 deletions
diff --git a/dom/html/HTMLShadowElement.cpp b/dom/html/HTMLShadowElement.cpp
new file mode 100644
index 000000000..8824c2875
--- /dev/null
+++ b/dom/html/HTMLShadowElement.cpp
@@ -0,0 +1,373 @@
+/* -*- 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/dom/ShadowRoot.h"
+
+#include "ChildIterator.h"
+#include "nsContentUtils.h"
+#include "nsDocument.h"
+#include "mozilla/dom/HTMLShadowElement.h"
+#include "mozilla/dom/HTMLUnknownElement.h"
+#include "mozilla/dom/HTMLShadowElementBinding.h"
+
+// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Shadow) to add check for web components
+// being enabled.
+nsGenericHTMLElement*
+NS_NewHTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+ mozilla::dom::FromParser aFromParser)
+{
+ // When this check is removed, remove the nsDocument.h and
+ // HTMLUnknownElement.h includes. Also remove nsINode::IsHTMLShadowElement.
+ //
+ // We have to jump through some hoops to be able to produce both NodeInfo* and
+ // already_AddRefed<NodeInfo>& for our callees.
+ RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
+ if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) {
+ already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
+ return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
+ }
+
+ already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
+ return new mozilla::dom::HTMLShadowElement(nodeInfoArg);
+}
+
+using namespace mozilla::dom;
+
+HTMLShadowElement::HTMLShadowElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+ : nsGenericHTMLElement(aNodeInfo), mIsInsertionPoint(false)
+{
+}
+
+HTMLShadowElement::~HTMLShadowElement()
+{
+ if (mProjectedShadow) {
+ mProjectedShadow->RemoveMutationObserver(this);
+ }
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLShadowElement)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLShadowElement,
+ nsGenericHTMLElement)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProjectedShadow)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLShadowElement,
+ nsGenericHTMLElement)
+ if (tmp->mProjectedShadow) {
+ tmp->mProjectedShadow->RemoveMutationObserver(tmp);
+ tmp->mProjectedShadow = nullptr;
+ }
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_ADDREF_INHERITED(HTMLShadowElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLShadowElement, Element)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLShadowElement)
+NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
+
+NS_IMPL_ELEMENT_CLONE(HTMLShadowElement)
+
+JSObject*
+HTMLShadowElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return HTMLShadowElementBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+HTMLShadowElement::SetProjectedShadow(ShadowRoot* aProjectedShadow)
+{
+ if (mProjectedShadow) {
+ mProjectedShadow->RemoveMutationObserver(this);
+
+ // The currently projected ShadowRoot is going away,
+ // thus the destination insertion points need to be updated.
+ ExplicitChildIterator childIterator(mProjectedShadow);
+ for (nsIContent* content = childIterator.GetNextChild();
+ content;
+ content = childIterator.GetNextChild()) {
+ ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
+ }
+ }
+
+ mProjectedShadow = aProjectedShadow;
+ if (mProjectedShadow) {
+ // A new ShadowRoot is being projected, thus its explcit
+ // children will be distributed to this shadow insertion point.
+ ExplicitChildIterator childIterator(mProjectedShadow);
+ for (nsIContent* content = childIterator.GetNextChild();
+ content;
+ content = childIterator.GetNextChild()) {
+ content->DestInsertionPoints().AppendElement(this);
+ }
+
+ // Watch for mutations on the projected shadow because
+ // it affects the nodes that are distributed to this shadow
+ // insertion point.
+ mProjectedShadow->AddMutationObserver(this);
+ }
+}
+
+static bool
+IsInFallbackContent(nsIContent* aContent)
+{
+ nsINode* parentNode = aContent->GetParentNode();
+ while (parentNode) {
+ if (parentNode->IsHTMLElement(nsGkAtoms::content)) {
+ return true;
+ }
+ parentNode = parentNode->GetParentNode();
+ }
+
+ return false;
+}
+
+nsresult
+HTMLShadowElement::BindToTree(nsIDocument* aDocument,
+ nsIContent* aParent,
+ nsIContent* aBindingParent,
+ bool aCompileEventHandlers)
+{
+ RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
+
+ nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
+ aBindingParent,
+ aCompileEventHandlers);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ShadowRoot* containingShadow = GetContainingShadow();
+ if (containingShadow && !oldContainingShadow) {
+ // Keep track of all descendant <shadow> elements in tree order so
+ // that when the current shadow insertion point is removed, the next
+ // one can be found quickly.
+ TreeOrderComparator comparator;
+ containingShadow->ShadowDescendants().InsertElementSorted(this, comparator);
+
+ if (containingShadow->ShadowDescendants()[0] != this) {
+ // Only the first <shadow> (in tree order) of a ShadowRoot can be an insertion point.
+ return NS_OK;
+ }
+
+ if (IsInFallbackContent(this)) {
+ // If the first shadow element in tree order is invalid (in fallback content),
+ // the containing ShadowRoot will not have a shadow insertion point.
+ containingShadow->SetShadowElement(nullptr);
+ } else {
+ mIsInsertionPoint = true;
+ containingShadow->SetShadowElement(this);
+ }
+
+ containingShadow->SetInsertionPointChanged();
+ }
+
+ if (mIsInsertionPoint && containingShadow) {
+ // Propagate BindToTree calls to projected shadow root children.
+ ShadowRoot* projectedShadow = containingShadow->GetOlderShadowRoot();
+ if (projectedShadow) {
+ projectedShadow->SetIsComposedDocParticipant(IsInComposedDoc());
+
+ for (nsIContent* child = projectedShadow->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ rv = child->BindToTree(nullptr, projectedShadow,
+ projectedShadow->GetBindingParent(),
+ aCompileEventHandlers);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+HTMLShadowElement::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+ RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
+
+ if (mIsInsertionPoint && oldContainingShadow) {
+ // Propagate UnbindFromTree call to previous projected shadow
+ // root children.
+ ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadowRoot();
+ if (projectedShadow) {
+ for (nsIContent* child = projectedShadow->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ child->UnbindFromTree(true, false);
+ }
+
+ projectedShadow->SetIsComposedDocParticipant(false);
+ }
+ }
+
+ nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
+
+ if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) {
+ nsTArray<HTMLShadowElement*>& shadowDescendants =
+ oldContainingShadow->ShadowDescendants();
+ shadowDescendants.RemoveElement(this);
+ oldContainingShadow->SetShadowElement(nullptr);
+
+ // Find the next shadow insertion point.
+ if (shadowDescendants.Length() > 0 &&
+ !IsInFallbackContent(shadowDescendants[0])) {
+ oldContainingShadow->SetShadowElement(shadowDescendants[0]);
+ }
+
+ oldContainingShadow->SetInsertionPointChanged();
+
+ mIsInsertionPoint = false;
+ }
+}
+
+void
+HTMLShadowElement::DistributeSingleNode(nsIContent* aContent)
+{
+ if (aContent->DestInsertionPoints().Contains(this)) {
+ // Node has already been distrbuted this this node,
+ // we are done.
+ return;
+ }
+
+ aContent->DestInsertionPoints().AppendElement(this);
+
+ // Handle the case where the shadow element is a child of
+ // a node with a ShadowRoot. The nodes that have been distributed to
+ // this shadow insertion point will need to be reprojected into the
+ // insertion points of the parent's ShadowRoot.
+ ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
+ if (parentShadowRoot) {
+ parentShadowRoot->DistributeSingleNode(aContent);
+ return;
+ }
+
+ // Handle the case where the parent of this shadow element is a ShadowRoot
+ // that is projected into a shadow insertion point in the younger ShadowRoot.
+ ShadowRoot* containingShadow = GetContainingShadow();
+ ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
+ if (youngerShadow && GetParent() == containingShadow) {
+ HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
+ if (youngerShadowElement) {
+ youngerShadowElement->DistributeSingleNode(aContent);
+ }
+ }
+}
+
+void
+HTMLShadowElement::RemoveDistributedNode(nsIContent* aContent)
+{
+ ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints());
+
+ // Handle the case where the shadow element is a child of
+ // a node with a ShadowRoot. The nodes that have been distributed to
+ // this shadow insertion point will need to be removed from the
+ // insertion points of the parent's ShadowRoot.
+ ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
+ if (parentShadowRoot) {
+ parentShadowRoot->RemoveDistributedNode(aContent);
+ return;
+ }
+
+ // Handle the case where the parent of this shadow element is a ShadowRoot
+ // that is projected into a shadow insertion point in the younger ShadowRoot.
+ ShadowRoot* containingShadow = GetContainingShadow();
+ ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
+ if (youngerShadow && GetParent() == containingShadow) {
+ HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
+ if (youngerShadowElement) {
+ youngerShadowElement->RemoveDistributedNode(aContent);
+ }
+ }
+}
+
+void
+HTMLShadowElement::DistributeAllNodes()
+{
+ // All the explicit children of the projected ShadowRoot are distributed
+ // into this shadow insertion point so update the destination insertion
+ // points.
+ ShadowRoot* containingShadow = GetContainingShadow();
+ ShadowRoot* olderShadow = containingShadow->GetOlderShadowRoot();
+ if (olderShadow) {
+ ExplicitChildIterator childIterator(olderShadow);
+ for (nsIContent* content = childIterator.GetNextChild();
+ content;
+ content = childIterator.GetNextChild()) {
+ ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
+ content->DestInsertionPoints().AppendElement(this);
+ }
+ }
+
+ // Handle the case where the shadow element is a child of
+ // a node with a ShadowRoot. The nodes that have been distributed to
+ // this shadow insertion point will need to be reprojected into the
+ // insertion points of the parent's ShadowRoot.
+ ShadowRoot* parentShadowRoot = GetParent()->GetShadowRoot();
+ if (parentShadowRoot) {
+ parentShadowRoot->DistributeAllNodes();
+ return;
+ }
+
+ // Handle the case where the parent of this shadow element is a ShadowRoot
+ // that is projected into a shadow insertion point in the younger ShadowRoot.
+ ShadowRoot* youngerShadow = containingShadow->GetYoungerShadowRoot();
+ if (youngerShadow && GetParent() == containingShadow) {
+ HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
+ if (youngerShadowElement) {
+ youngerShadowElement->DistributeAllNodes();
+ }
+ }
+}
+
+void
+HTMLShadowElement::ContentAppended(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aFirstNewContent,
+ int32_t aNewIndexInContainer)
+{
+ // Watch for content appended to the projected shadow (the ShadowRoot that
+ // will be rendered in place of this shadow insertion point) because the
+ // nodes may need to be distributed into other insertion points.
+ nsIContent* currentChild = aFirstNewContent;
+ while (currentChild) {
+ if (ShadowRoot::IsPooledNode(currentChild, aContainer, mProjectedShadow)) {
+ DistributeSingleNode(currentChild);
+ }
+ currentChild = currentChild->GetNextSibling();
+ }
+}
+
+void
+HTMLShadowElement::ContentInserted(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t aIndexInContainer)
+{
+ // Watch for content appended to the projected shadow (the ShadowRoot that
+ // will be rendered in place of this shadow insertion point) because the
+ // nodes may need to be distributed into other insertion points.
+ if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
+ return;
+ }
+
+ DistributeSingleNode(aChild);
+}
+
+void
+HTMLShadowElement::ContentRemoved(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t aIndexInContainer,
+ nsIContent* aPreviousSibling)
+{
+ // Watch for content removed from the projected shadow (the ShadowRoot that
+ // will be rendered in place of this shadow insertion point) because the
+ // nodes may need to be removed from other insertion points.
+ if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
+ return;
+ }
+
+ RemoveDistributedNode(aChild);
+}
+