зеркало из https://github.com/mozilla/gecko-dev.git
Bug 887538 - Implement web components shadow element. r=mrbkap
This commit is contained in:
Родитель
4a425922ba
Коммит
a77e9ae737
|
@ -2275,6 +2275,22 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class TreeOrderComparator {
|
||||
public:
|
||||
bool Equals(nsINode* aElem1, nsINode* aElem2) const {
|
||||
return aElem1 == aElem2;
|
||||
}
|
||||
bool LessThan(nsINode* aElem1, nsINode* aElem2) const {
|
||||
return nsContentUtils::PositionIsBefore(aElem1, aElem2);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#define NS_INTERFACE_MAP_ENTRY_TEAROFF(_interface, _allocator) \
|
||||
if (aIID.Equals(NS_GET_IID(_interface))) { \
|
||||
foundInterface = static_cast<_interface *>(_allocator); \
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/XBLChildrenElement.h"
|
||||
#include "mozilla/dom/HTMLContentElement.h"
|
||||
#include "mozilla/dom/HTMLShadowElement.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -73,6 +75,17 @@ ExplicitChildIterator::GetNextChild()
|
|||
}
|
||||
mIndexInInserted = 0;
|
||||
mChild = mChild->GetNextSibling();
|
||||
} else if (mShadowIterator) {
|
||||
// If we're inside of a <shadow> element, look through the
|
||||
// explicit children of the projected ShadowRoot via
|
||||
// the mShadowIterator.
|
||||
nsIContent* nextChild = mShadowIterator->GetNextChild();
|
||||
if (nextChild) {
|
||||
return nextChild;
|
||||
}
|
||||
|
||||
mShadowIterator = nullptr;
|
||||
mChild = mChild->GetNextSibling();
|
||||
} else if (mDefaultChild) {
|
||||
// If we're already in default content, check if there are more nodes there
|
||||
MOZ_ASSERT(mChild);
|
||||
|
@ -93,24 +106,49 @@ ExplicitChildIterator::GetNextChild()
|
|||
|
||||
// Iterate until we find a non-insertion point, or an insertion point with
|
||||
// content.
|
||||
while (mChild && nsContentUtils::IsContentInsertionPoint(mChild)) {
|
||||
MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
|
||||
if (!assignedChildren.IsEmpty()) {
|
||||
// Iterate through elements projected on insertion point.
|
||||
mIndexInInserted = 1;
|
||||
return assignedChildren[0];
|
||||
}
|
||||
while (mChild) {
|
||||
// If the current child being iterated is a shadow insertion point then
|
||||
// the iterator needs to go into the projected ShadowRoot.
|
||||
if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
|
||||
// Look for the next child in the projected ShadowRoot for the <shadow>
|
||||
// element.
|
||||
HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild);
|
||||
ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
|
||||
if (projectedShadow) {
|
||||
mShadowIterator = new ExplicitChildIterator(projectedShadow);
|
||||
nsIContent* nextChild = mShadowIterator->GetNextChild();
|
||||
if (nextChild) {
|
||||
return nextChild;
|
||||
}
|
||||
mShadowIterator = nullptr;
|
||||
}
|
||||
mChild = mChild->GetNextSibling();
|
||||
} else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
|
||||
// If the current child being iterated is a content insertion point
|
||||
// then the iterator needs to return the nodes distributed into
|
||||
// the content insertion point.
|
||||
MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
|
||||
if (!assignedChildren.IsEmpty()) {
|
||||
// Iterate through elements projected on insertion point.
|
||||
mIndexInInserted = 1;
|
||||
return assignedChildren[0];
|
||||
}
|
||||
|
||||
// Insertion points inside fallback/default content
|
||||
// are considered inactive and do not get assigned nodes.
|
||||
mDefaultChild = mChild->GetFirstChild();
|
||||
if (mDefaultChild) {
|
||||
return mDefaultChild;
|
||||
}
|
||||
// Insertion points inside fallback/default content
|
||||
// are considered inactive and do not get assigned nodes.
|
||||
mDefaultChild = mChild->GetFirstChild();
|
||||
if (mDefaultChild) {
|
||||
return mDefaultChild;
|
||||
}
|
||||
|
||||
// If we have an insertion point with no assigned nodes and
|
||||
// no default content, move on to the next node.
|
||||
mChild = mChild->GetNextSibling();
|
||||
// If we have an insertion point with no assigned nodes and
|
||||
// no default content, move on to the next node.
|
||||
mChild = mChild->GetNextSibling();
|
||||
} else {
|
||||
// mChild is not an insertion point, thus it is the next node to
|
||||
// return from this iterator.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mChild;
|
||||
|
@ -146,18 +184,22 @@ FlattenedChildIterator::FlattenedChildIterator(nsIContent* aParent)
|
|||
}
|
||||
}
|
||||
|
||||
nsIContent* FlattenedChildIterator::Get()
|
||||
nsIContent*
|
||||
ExplicitChildIterator::Get()
|
||||
{
|
||||
MOZ_ASSERT(!mIsFirst);
|
||||
|
||||
if (mIndexInInserted) {
|
||||
XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
|
||||
return point->mInsertedChildren[mIndexInInserted - 1];
|
||||
} else if (mShadowIterator) {
|
||||
return mShadowIterator->Get();
|
||||
}
|
||||
return mDefaultChild ? mDefaultChild : mChild;
|
||||
}
|
||||
|
||||
nsIContent* FlattenedChildIterator::GetPreviousChild()
|
||||
nsIContent*
|
||||
ExplicitChildIterator::GetPreviousChild()
|
||||
{
|
||||
// If we're already in the inserted-children array, look there first
|
||||
if (mIndexInInserted) {
|
||||
|
@ -168,6 +210,13 @@ nsIContent* FlattenedChildIterator::GetPreviousChild()
|
|||
return assignedChildren[mIndexInInserted - 1];
|
||||
}
|
||||
mChild = mChild->GetPreviousSibling();
|
||||
} else if (mShadowIterator) {
|
||||
nsIContent* previousChild = mShadowIterator->GetPreviousChild();
|
||||
if (previousChild) {
|
||||
return previousChild;
|
||||
}
|
||||
mShadowIterator = nullptr;
|
||||
mChild = mChild->GetPreviousSibling();
|
||||
} else if (mDefaultChild) {
|
||||
// If we're already in default content, check if there are more nodes there
|
||||
mDefaultChild = mDefaultChild->GetPreviousSibling();
|
||||
|
@ -186,19 +235,43 @@ nsIContent* FlattenedChildIterator::GetPreviousChild()
|
|||
|
||||
// Iterate until we find a non-insertion point, or an insertion point with
|
||||
// content.
|
||||
while (mChild && nsContentUtils::IsContentInsertionPoint(mChild)) {
|
||||
MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
|
||||
if (!assignedChildren.IsEmpty()) {
|
||||
mIndexInInserted = assignedChildren.Length();
|
||||
return assignedChildren[mIndexInInserted - 1];
|
||||
}
|
||||
while (mChild) {
|
||||
if (ShadowRoot::IsShadowInsertionPoint(mChild)) {
|
||||
// If the current child being iterated is a shadow insertion point then
|
||||
// the iterator needs to go into the projected ShadowRoot.
|
||||
HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(mChild);
|
||||
ShadowRoot* projectedShadow = shadowElem->GetOlderShadowRoot();
|
||||
if (projectedShadow) {
|
||||
// Create a ExplicitChildIterator that begins iterating from the end.
|
||||
mShadowIterator = new ExplicitChildIterator(projectedShadow, false);
|
||||
nsIContent* previousChild = mShadowIterator->GetPreviousChild();
|
||||
if (previousChild) {
|
||||
return previousChild;
|
||||
}
|
||||
mShadowIterator = nullptr;
|
||||
}
|
||||
mChild = mChild->GetPreviousSibling();
|
||||
} else if (nsContentUtils::IsContentInsertionPoint(mChild)) {
|
||||
// If the current child being iterated is a content insertion point
|
||||
// then the iterator needs to return the nodes distributed into
|
||||
// the content insertion point.
|
||||
MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
|
||||
if (!assignedChildren.IsEmpty()) {
|
||||
mIndexInInserted = assignedChildren.Length();
|
||||
return assignedChildren[mIndexInInserted - 1];
|
||||
}
|
||||
|
||||
mDefaultChild = mChild->GetLastChild();
|
||||
if (mDefaultChild) {
|
||||
return mDefaultChild;
|
||||
}
|
||||
mDefaultChild = mChild->GetLastChild();
|
||||
if (mDefaultChild) {
|
||||
return mDefaultChild;
|
||||
}
|
||||
|
||||
mChild = mChild->GetPreviousSibling();
|
||||
mChild = mChild->GetPreviousSibling();
|
||||
} else {
|
||||
// mChild is not an insertion point, thus it is the next node to
|
||||
// return from this iterator.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mChild) {
|
||||
|
|
|
@ -24,16 +24,18 @@ namespace dom {
|
|||
// This class iterates normal DOM child nodes of a given DOM node with
|
||||
// <xbl:children> nodes replaced by the elements that have been filtered into that
|
||||
// insertion point. Any bindings on the given element are ignored for purposes
|
||||
// of determining which insertion point children are filtered into.
|
||||
// of determining which insertion point children are filtered into. The iterator
|
||||
// can be initialized to start at the end by providing false for aStartAtBeginning
|
||||
// in order to start iterating in reverse from the last child.
|
||||
class ExplicitChildIterator
|
||||
{
|
||||
public:
|
||||
ExplicitChildIterator(nsIContent* aParent)
|
||||
ExplicitChildIterator(nsIContent* aParent, bool aStartAtBeginning = true)
|
||||
: mParent(aParent),
|
||||
mChild(nullptr),
|
||||
mDefaultChild(nullptr),
|
||||
mIndexInInserted(0),
|
||||
mIsFirst(true)
|
||||
mIsFirst(aStartAtBeginning)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -57,13 +59,22 @@ public:
|
|||
return child == aChildToFind;
|
||||
}
|
||||
|
||||
// Returns the current target of this iterator (which might be an explicit
|
||||
// child of the node, fallback content of an insertion point or
|
||||
// a node distributed to an insertion point.
|
||||
nsIContent* Get();
|
||||
|
||||
// The inverse of GetNextChild. Properly steps in and out of insertion
|
||||
// points.
|
||||
nsIContent* GetPreviousChild();
|
||||
|
||||
protected:
|
||||
// The parent of the children being iterated. For the FlattenedChildIterator,
|
||||
// if there is a binding attached to the original parent, mParent points to
|
||||
// the <xbl:content> element for the binding.
|
||||
nsIContent* mParent;
|
||||
|
||||
// The current child. When we encounter an <xbl:children> insertion point,
|
||||
// The current child. When we encounter an insertion point,
|
||||
// mChild remains as the insertion point whose content we're iterating (and
|
||||
// our state is controled by mDefaultChild or mIndexInInserted depending on
|
||||
// whether the insertion point expands to its default content or not).
|
||||
|
@ -71,10 +82,15 @@ protected:
|
|||
|
||||
// If non-null, this points to the current default content for the current
|
||||
// insertion point that we're iterating (i.e. mChild, which must be an
|
||||
// nsXBLChildrenElement). Once this transitions back to null,
|
||||
// we continue iterating at mChild's next sibling.
|
||||
// nsXBLChildrenElement or HTMLContentElement). Once this transitions back
|
||||
// to null, we continue iterating at mChild's next sibling.
|
||||
nsIContent* mDefaultChild;
|
||||
|
||||
// If non-null, this points to an iterator of the explicit children of
|
||||
// the ShadowRoot projected by the current shadow element that we're
|
||||
// iterating.
|
||||
nsAutoPtr<ExplicitChildIterator> mShadowIterator;
|
||||
|
||||
// If not zero, we're iterating inserted children for an insertion point. This
|
||||
// is an index into mChild's inserted children array (mChild must be an
|
||||
// nsXBLChildrenElement). The index is one past the "current" child (as
|
||||
|
@ -93,15 +109,6 @@ class FlattenedChildIterator : public ExplicitChildIterator
|
|||
public:
|
||||
FlattenedChildIterator(nsIContent* aParent);
|
||||
|
||||
// Returns the current target of this iterator (which might be an explicit
|
||||
// child of the node, default content for an <xbl:children> element or
|
||||
// an inserted child for an <xbl:children> element.
|
||||
nsIContent* Get();
|
||||
|
||||
// The inverse of GetNextChild. Properly steps in and out of <xbl:children>
|
||||
// elements.
|
||||
nsIContent* GetPreviousChild();
|
||||
|
||||
bool XBLInvolved() { return mXBLInvolved; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -753,7 +753,16 @@ Element::CreateShadowRoot(ErrorResult& aError)
|
|||
|
||||
nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(),
|
||||
protoBinding);
|
||||
|
||||
// Replace the old ShadowRoot with the new one and let the old
|
||||
// ShadowRoot know about the younger ShadowRoot because the old
|
||||
// ShadowRoot is projected into the younger ShadowRoot's shadow
|
||||
// insertion point (if it exists).
|
||||
ShadowRoot* olderShadow = GetShadowRoot();
|
||||
SetShadowRoot(shadowRoot);
|
||||
if (olderShadow) {
|
||||
olderShadow->SetYoungerShadow(shadowRoot);
|
||||
}
|
||||
|
||||
// xblBinding takes ownership of docInfo.
|
||||
nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "nsIStyleSheetLinkingElement.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLContentElement.h"
|
||||
#include "mozilla/dom/HTMLShadowElement.h"
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -32,19 +33,23 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot,
|
||||
DocumentFragment)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHost)
|
||||
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)
|
||||
tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ShadowRoot,
|
||||
DocumentFragment)
|
||||
if (tmp->mHost) {
|
||||
tmp->mHost->RemoveMutationObserver(tmp);
|
||||
if (tmp->mPoolHost) {
|
||||
tmp->mPoolHost->RemoveMutationObserver(tmp);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHost)
|
||||
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
|
||||
|
@ -62,8 +67,9 @@ NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
|
|||
ShadowRoot::ShadowRoot(nsIContent* aContent,
|
||||
already_AddRefed<nsINodeInfo> aNodeInfo,
|
||||
nsXBLPrototypeBinding* aProtoBinding)
|
||||
: DocumentFragment(aNodeInfo), mHost(aContent),
|
||||
mProtoBinding(aProtoBinding), mInsertionPointChanged(false)
|
||||
: DocumentFragment(aNodeInfo), mPoolHost(aContent),
|
||||
mProtoBinding(aProtoBinding), mShadowElement(nullptr),
|
||||
mInsertionPointChanged(false)
|
||||
{
|
||||
SetHost(aContent);
|
||||
SetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
|
@ -75,14 +81,15 @@ ShadowRoot::ShadowRoot(nsIContent* aContent,
|
|||
// 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.
|
||||
mHost->AddMutationObserver(this);
|
||||
mPoolHost->AddMutationObserver(this);
|
||||
}
|
||||
|
||||
ShadowRoot::~ShadowRoot()
|
||||
{
|
||||
if (mHost) {
|
||||
// mHost may have been unlinked.
|
||||
mHost->RemoveMutationObserver(this);
|
||||
if (mPoolHost) {
|
||||
// mPoolHost may have been unlinked or a new ShadowRoot may have been
|
||||
// creating, making this one obsolete.
|
||||
mPoolHost->RemoveMutationObserver(this);
|
||||
}
|
||||
|
||||
ClearInDocument();
|
||||
|
@ -233,16 +240,6 @@ ShadowRoot::PrefEnabled()
|
|||
return Preferences::GetBool("dom.webcomponents.enabled", false);
|
||||
}
|
||||
|
||||
class TreeOrderComparator {
|
||||
public:
|
||||
bool Equals(nsINode* aElem1, nsINode* aElem2) const {
|
||||
return aElem1 == aElem2;
|
||||
}
|
||||
bool LessThan(nsINode* aElem1, nsINode* aElem2) const {
|
||||
return nsContentUtils::PositionIsBefore(aElem1, aElem2);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
ShadowRoot::AddInsertionPoint(HTMLContentElement* aInsertionPoint)
|
||||
{
|
||||
|
@ -256,6 +253,15 @@ ShadowRoot::RemoveInsertionPoint(HTMLContentElement* aInsertionPoint)
|
|||
mInsertionPoints.RemoveElement(aInsertionPoint);
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::SetYoungerShadow(ShadowRoot* aYoungerShadow)
|
||||
{
|
||||
mYoungerShadow = aYoungerShadow;
|
||||
mYoungerShadow->mOlderShadow = this;
|
||||
|
||||
ChangePoolHost(mYoungerShadow->GetShadowElement());
|
||||
}
|
||||
|
||||
void
|
||||
ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
||||
{
|
||||
|
@ -263,6 +269,11 @@ ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
|||
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()) {
|
||||
|
@ -283,7 +294,8 @@ ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
|||
// Find the appropriate position in the matched node list for the
|
||||
// newly distributed content.
|
||||
bool isIndexFound = false;
|
||||
ExplicitChildIterator childIterator(GetHost());
|
||||
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.
|
||||
|
@ -303,6 +315,15 @@ ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
|||
matchedNodes.AppendElement(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.
|
||||
|
@ -310,6 +331,16 @@ ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,6 +362,15 @@ ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
|
|||
|
||||
mInsertionPoints[i]->MatchedNodes().RemoveElement(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.
|
||||
|
@ -339,6 +379,16 @@ ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
@ -349,13 +399,20 @@ ShadowRoot::DistributeAllNodes()
|
|||
{
|
||||
// Create node pool.
|
||||
nsTArray<nsIContent*> nodePool;
|
||||
ExplicitChildIterator childIterator(GetHost());
|
||||
for (nsIContent* content = childIterator.GetNextChild();
|
||||
content;
|
||||
content = childIterator.GetNextChild()) {
|
||||
nodePool.AppendElement(content);
|
||||
|
||||
// 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.
|
||||
|
@ -366,11 +423,9 @@ ShadowRoot::DistributeAllNodes()
|
|||
nodePool.RemoveElementAt(j--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Distribute nodes in outer ShadowRoot for the case of an insertion point being
|
||||
// distributed into an outer ShadowRoot.
|
||||
for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
|
||||
// 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"
|
||||
|
@ -380,10 +435,28 @@ ShadowRoot::DistributeAllNodes()
|
|||
// to the insertion point must be reprojected to the insertion points of the
|
||||
// parent's ShadowRoot.
|
||||
ShadowRoot* parentShadow = insertionParent->GetShadowRoot();
|
||||
if (parentShadow) {
|
||||
parentShadow->DistributeAllNodes();
|
||||
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
|
||||
|
@ -427,16 +500,70 @@ ShadowRoot::StyleSheets()
|
|||
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 && aContent->IsHTML(nsGkAtoms::shadow)) {
|
||||
HTMLShadowElement* shadowElem = static_cast<HTMLShadowElement*>(aContent);
|
||||
return shadowElem->IsInsertionPoint();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static bool
|
||||
IsPooledNode(nsIContent* aContent, nsIContent* aContainer, nsIContent* aHost)
|
||||
bool
|
||||
ShadowRoot::IsPooledNode(nsIContent* aContent, nsIContent* aContainer,
|
||||
nsIContent* aHost)
|
||||
{
|
||||
if (nsContentUtils::IsContentInsertionPoint(aContent)) {
|
||||
if (nsContentUtils::IsContentInsertionPoint(aContent) ||
|
||||
IsShadowInsertionPoint(aContent)) {
|
||||
// Insertion points never end up in the pool.
|
||||
return false;
|
||||
}
|
||||
|
@ -463,9 +590,7 @@ ShadowRoot::AttributeChanged(nsIDocument* aDocument,
|
|||
nsIAtom* aAttribute,
|
||||
int32_t aModType)
|
||||
{
|
||||
// Watch for attribute changes to nodes in the pool because
|
||||
// insertion points can select on attributes.
|
||||
if (!IsPooledNode(aElement, aElement->GetParent(), mHost)) {
|
||||
if (!IsPooledNode(aElement, aElement->GetParent(), mPoolHost)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -490,7 +615,7 @@ ShadowRoot::ContentAppended(nsIDocument* aDocument,
|
|||
// may need to be added to an insertion point.
|
||||
nsIContent* currentChild = aFirstNewContent;
|
||||
while (currentChild) {
|
||||
if (IsPooledNode(currentChild, aContainer, mHost)) {
|
||||
if (IsPooledNode(currentChild, aContainer, mPoolHost)) {
|
||||
DistributeSingleNode(currentChild);
|
||||
}
|
||||
currentChild = currentChild->GetNextSibling();
|
||||
|
@ -511,7 +636,7 @@ ShadowRoot::ContentInserted(nsIDocument* aDocument,
|
|||
|
||||
// Watch for new nodes added to the pool because the node
|
||||
// may need to be added to an insertion point.
|
||||
if (IsPooledNode(aChild, aContainer, mHost)) {
|
||||
if (IsPooledNode(aChild, aContainer, mPoolHost)) {
|
||||
DistributeSingleNode(aChild);
|
||||
}
|
||||
}
|
||||
|
@ -531,7 +656,7 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument,
|
|||
|
||||
// Watch for node that is removed from the pool because
|
||||
// it may need to be removed from an insertion point.
|
||||
if (IsPooledNode(aChild, aContainer, mHost)) {
|
||||
if (IsPooledNode(aChild, aContainer, mPoolHost)) {
|
||||
RemoveDistributedNode(aChild);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace dom {
|
|||
|
||||
class Element;
|
||||
class HTMLContentElement;
|
||||
class HTMLShadowElement;
|
||||
class ShadowRootStyleSheetList;
|
||||
|
||||
class ShadowRoot : public DocumentFragment,
|
||||
|
@ -53,21 +54,39 @@ public:
|
|||
bool ApplyAuthorStyles();
|
||||
void SetApplyAuthorStyles(bool aApplyAuthorStyles);
|
||||
nsIDOMStyleSheetList* StyleSheets();
|
||||
HTMLShadowElement* GetShadowElement() { return mShadowElement; }
|
||||
|
||||
/**
|
||||
* Distributes a single explicit child of the host to the content
|
||||
* Sets the current shadow insertion point where the older
|
||||
* ShadowRoot will be projected.
|
||||
*/
|
||||
void SetShadowElement(HTMLShadowElement* aShadowElement);
|
||||
|
||||
/**
|
||||
* Change the node that populates the distribution pool with
|
||||
* its children. This is distinct from the ShadowRoot host described
|
||||
* in the specifications. The ShadowRoot host is the element
|
||||
* which created this ShadowRoot and does not change. The pool host
|
||||
* is the same as the ShadowRoot host if this is the youngest
|
||||
* ShadowRoot. If this is an older ShadowRoot, the pool host is
|
||||
* the <shadow> element in the younger ShadowRoot (if it exists).
|
||||
*/
|
||||
void ChangePoolHost(nsIContent* aNewHost);
|
||||
|
||||
/**
|
||||
* Distributes a single explicit child of the pool host to the content
|
||||
* insertion points in this ShadowRoot.
|
||||
*/
|
||||
void DistributeSingleNode(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Removes a single explicit child of the host from the content
|
||||
* Removes a single explicit child of the pool host from the content
|
||||
* insertion points in this ShadowRoot.
|
||||
*/
|
||||
void RemoveDistributedNode(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Distributes all the explicit children of the host to the content
|
||||
* Distributes all the explicit children of the pool host to the content
|
||||
* insertion points in this ShadowRoot.
|
||||
*/
|
||||
void DistributeAllNodes();
|
||||
|
@ -75,23 +94,24 @@ public:
|
|||
void AddInsertionPoint(HTMLContentElement* aInsertionPoint);
|
||||
void RemoveInsertionPoint(HTMLContentElement* aInsertionPoint);
|
||||
|
||||
void SetYoungerShadow(ShadowRoot* aYoungerShadow);
|
||||
ShadowRoot* GetOlderShadow() { return mOlderShadow; }
|
||||
ShadowRoot* GetYoungerShadow() { return mYoungerShadow; }
|
||||
void SetInsertionPointChanged() { mInsertionPointChanged = true; }
|
||||
|
||||
void SetAssociatedBinding(nsXBLBinding* aBinding)
|
||||
{
|
||||
mAssociatedBinding = aBinding;
|
||||
}
|
||||
void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mHost;
|
||||
}
|
||||
nsISupports* GetParentObject() const { return mPoolHost; }
|
||||
|
||||
nsIContent* GetHost() { return mHost; }
|
||||
nsIContent* GetPoolHost() { return mPoolHost; }
|
||||
nsTArray<HTMLShadowElement*>& ShadowDescendants() { return mShadowDescendants; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
||||
static bool IsPooledNode(nsIContent* aChild, nsIContent* aContainer,
|
||||
nsIContent* aHost);
|
||||
static ShadowRoot* FromNode(nsINode* aNode);
|
||||
static bool IsShadowInsertionPoint(nsIContent* aContent);
|
||||
|
||||
// WebIDL methods.
|
||||
Element* GetElementById(const nsAString& aElementId);
|
||||
|
@ -107,7 +127,9 @@ public:
|
|||
protected:
|
||||
void Restyle();
|
||||
|
||||
nsCOMPtr<nsIContent> mHost;
|
||||
// The pool host is the parent of the nodes that will be distributed
|
||||
// into the insertion points in this ShadowRoot. See |ChangeShadowRoot|.
|
||||
nsCOMPtr<nsIContent> mPoolHost;
|
||||
|
||||
// An array of content insertion points that are a descendant of the ShadowRoot
|
||||
// sorted in tree order. Insertion points are responsible for notifying
|
||||
|
@ -116,6 +138,10 @@ protected:
|
|||
// by the array.
|
||||
nsTArray<HTMLContentElement*> mInsertionPoints;
|
||||
|
||||
// An array of the <shadow> elements that are descendant of the ShadowRoot
|
||||
// sorted in tree order. Only the first may be a shadow insertion point.
|
||||
nsTArray<HTMLShadowElement*> mShadowDescendants;
|
||||
|
||||
nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
|
||||
nsXBLPrototypeBinding* mProtoBinding;
|
||||
|
||||
|
@ -126,6 +152,17 @@ protected:
|
|||
|
||||
nsRefPtr<ShadowRootStyleSheetList> mStyleSheetList;
|
||||
|
||||
// The current shadow insertion point of this ShadowRoot.
|
||||
HTMLShadowElement* mShadowElement;
|
||||
|
||||
// The ShadowRoot that was created by the host element before
|
||||
// this ShadowRoot was created.
|
||||
nsRefPtr<ShadowRoot> mOlderShadow;
|
||||
|
||||
// The ShadowRoot that was created by the host element after
|
||||
// this ShadowRoot was created.
|
||||
nsRefPtr<ShadowRoot> mYoungerShadow;
|
||||
|
||||
// A boolean that indicates that an insertion point was added or removed
|
||||
// from this ShadowRoot and that the nodes need to be redistributed into
|
||||
// the insertion points. After this flag is set, nodes will be distributed
|
||||
|
|
|
@ -1384,6 +1384,7 @@ GK_ATOM(ry, "ry")
|
|||
GK_ATOM(saturate, "saturate")
|
||||
GK_ATOM(set, "set")
|
||||
GK_ATOM(seed, "seed")
|
||||
GK_ATOM(shadow, "shadow")
|
||||
GK_ATOM(shape_rendering, "shape-rendering")
|
||||
GK_ATOM(skewX, "skewX")
|
||||
GK_ATOM(skewY, "skewY")
|
||||
|
|
|
@ -61,7 +61,7 @@ using mozilla::AutoJSContext;
|
|||
} \
|
||||
ShadowRoot* shadow = ShadowRoot::FromNode(node); \
|
||||
if (shadow) { \
|
||||
node = shadow->GetHost(); \
|
||||
node = shadow->GetPoolHost(); \
|
||||
} else { \
|
||||
node = node->GetParentNode(); \
|
||||
} \
|
||||
|
|
|
@ -190,19 +190,6 @@ HTMLPropertiesCollection::ContentRemoved(nsIDocument *aDocument,
|
|||
mIsDirty = true;
|
||||
}
|
||||
|
||||
class TreeOrderComparator {
|
||||
public:
|
||||
bool Equals(const nsGenericHTMLElement* aElem1,
|
||||
const nsGenericHTMLElement* aElem2) const {
|
||||
return aElem1 == aElem2;
|
||||
}
|
||||
bool LessThan(const nsGenericHTMLElement* aElem1,
|
||||
const nsGenericHTMLElement* aElem2) const {
|
||||
return nsContentUtils::PositionIsBefore(const_cast<nsGenericHTMLElement*>(aElem1),
|
||||
const_cast<nsGenericHTMLElement*>(aElem2));
|
||||
}
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
MarkDirty(const nsAString& aKey, PropertyNodeList* aEntry, void* aData)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "nsContentUtils.h"
|
||||
#include "mozilla/dom/HTMLShadowElement.h"
|
||||
#include "mozilla/dom/HTMLShadowElementBinding.h"
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Shadow)
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
HTMLShadowElement::HTMLShadowElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo), mIsInsertionPoint(false)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
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*> aScope)
|
||||
{
|
||||
return HTMLShadowElementBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::SetProjectedShadow(ShadowRoot* aProjectedShadow)
|
||||
{
|
||||
if (mProjectedShadow) {
|
||||
mProjectedShadow->RemoveMutationObserver(this);
|
||||
}
|
||||
|
||||
mProjectedShadow = aProjectedShadow;
|
||||
if (mProjectedShadow) {
|
||||
// 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->IsElement() &&
|
||||
parentNode->AsElement()->IsHTML(nsGkAtoms::content)) {
|
||||
return true;
|
||||
}
|
||||
parentNode = parentNode->GetParentNode();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLShadowElement::BindToTree(nsIDocument* aDocument,
|
||||
nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ShadowRoot* containingShadow = GetContainingShadow();
|
||||
if (containingShadow) {
|
||||
// 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();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
if (mIsInsertionPoint) {
|
||||
ShadowRoot* containingShadow = GetContainingShadow();
|
||||
// Make sure that containingShadow exists, it may have been nulled
|
||||
// during unlinking in which case the ShadowRoot is going away.
|
||||
if (containingShadow) {
|
||||
nsTArray<HTMLShadowElement*>& shadowDescendants =
|
||||
containingShadow->ShadowDescendants();
|
||||
shadowDescendants.RemoveElement(this);
|
||||
containingShadow->SetShadowElement(nullptr);
|
||||
|
||||
// Find the next shadow insertion point.
|
||||
if (shadowDescendants.Length() > 0 &&
|
||||
!IsInFallbackContent(shadowDescendants[0])) {
|
||||
containingShadow->SetShadowElement(shadowDescendants[0]);
|
||||
}
|
||||
|
||||
containingShadow->SetInsertionPointChanged();
|
||||
}
|
||||
|
||||
mIsInsertionPoint = false;
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::DistributeSingleNode(nsIContent* aContent)
|
||||
{
|
||||
// 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->GetYoungerShadow();
|
||||
if (youngerShadow && GetParent() == containingShadow) {
|
||||
HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
|
||||
if (youngerShadowElement) {
|
||||
youngerShadowElement->DistributeSingleNode(aContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::RemoveDistributedNode(nsIContent* aContent)
|
||||
{
|
||||
// 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->GetYoungerShadow();
|
||||
if (youngerShadow && GetParent() == containingShadow) {
|
||||
HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
|
||||
if (youngerShadowElement) {
|
||||
youngerShadowElement->RemoveDistributedNode(aContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLShadowElement::DistributeAllNodes()
|
||||
{
|
||||
// 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* containingShadow = GetContainingShadow();
|
||||
ShadowRoot* youngerShadow = containingShadow->GetYoungerShadow();
|
||||
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 to 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_HTMLShadowElement_h__
|
||||
#define mozilla_dom_HTMLShadowElement_h__
|
||||
|
||||
#include "nsGenericHTMLElement.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class HTMLShadowElement MOZ_FINAL : public nsGenericHTMLElement,
|
||||
public nsStubMutationObserver
|
||||
{
|
||||
public:
|
||||
HTMLShadowElement(already_AddRefed<nsINodeInfo> aNodeInfo);
|
||||
virtual ~HTMLShadowElement();
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLShadowElement,
|
||||
nsGenericHTMLElement)
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers);
|
||||
|
||||
virtual void UnbindFromTree(bool aDeep = true,
|
||||
bool aNullParent = true);
|
||||
|
||||
bool IsInsertionPoint() { return mIsInsertionPoint; }
|
||||
|
||||
/**
|
||||
* Sets the ShadowRoot that will be rendered in place of
|
||||
* this shadow insertion point.
|
||||
*/
|
||||
void SetProjectedShadow(ShadowRoot* aProjectedShadow);
|
||||
|
||||
/**
|
||||
* Distributes a single explicit child of the projected ShadowRoot
|
||||
* to relevant insertion points.
|
||||
*/
|
||||
void DistributeSingleNode(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Removes a single explicit child of the projected ShadowRoot
|
||||
* from relevant insertion points.
|
||||
*/
|
||||
void RemoveDistributedNode(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Distributes all the explicit children of the projected ShadowRoot
|
||||
* to the shadow insertion point in the younger ShadowRoot and
|
||||
* the content insertion point of the parent node's ShadowRoot.
|
||||
*/
|
||||
void DistributeAllNodes();
|
||||
|
||||
// WebIDL methods.
|
||||
ShadowRoot* GetOlderShadowRoot() { return mProjectedShadow; }
|
||||
|
||||
protected:
|
||||
virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
||||
// The ShadowRoot that will be rendered in place of this shadow insertion point.
|
||||
nsRefPtr<ShadowRoot> mProjectedShadow;
|
||||
|
||||
bool mIsInsertionPoint;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_HTMLShadowElement_h__
|
||||
|
|
@ -51,6 +51,7 @@ EXPORTS.mozilla.dom += [
|
|||
'HTMLProgressElement.h',
|
||||
'HTMLScriptElement.h',
|
||||
'HTMLSelectElement.h',
|
||||
'HTMLShadowElement.h',
|
||||
'HTMLSharedElement.h',
|
||||
'HTMLSharedListElement.h',
|
||||
'HTMLSharedObjectElement.h',
|
||||
|
@ -122,6 +123,7 @@ UNIFIED_SOURCES += [
|
|||
'HTMLPropertiesCollection.cpp',
|
||||
'HTMLScriptElement.cpp',
|
||||
'HTMLSelectElement.cpp',
|
||||
'HTMLShadowElement.cpp',
|
||||
'HTMLSharedElement.cpp',
|
||||
'HTMLSharedListElement.cpp',
|
||||
'HTMLSharedObjectElement.cpp',
|
||||
|
|
|
@ -1786,6 +1786,7 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(Pre)
|
|||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Progress)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Script)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Select)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Shadow)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Source)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Span)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Style)
|
||||
|
|
|
@ -976,6 +976,12 @@ DOMInterfaces = {
|
|||
]
|
||||
},
|
||||
|
||||
'HTMLShadowElement': {
|
||||
'resultNotAddRefed': [
|
||||
'getOldShadowRoot'
|
||||
]
|
||||
},
|
||||
|
||||
'SharedWorker': {
|
||||
'nativeType': 'mozilla::dom::workers::SharedWorker',
|
||||
'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
|
||||
|
|
|
@ -305,6 +305,7 @@ var interfaceNamesInGlobalScope =
|
|||
"HTMLQuoteElement",
|
||||
"HTMLScriptElement",
|
||||
"HTMLSelectElement",
|
||||
"HTMLShadowElement",
|
||||
"HTMLSourceElement",
|
||||
"HTMLSpanElement",
|
||||
"HTMLStyleElement",
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=887538
|
||||
-->
|
||||
<head>
|
||||
<title>Test for HTMLShadowElement</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="grabme"></div>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887538">Bug 887538</a>
|
||||
<script>
|
||||
var host = document.createElement("span");
|
||||
|
||||
// Create three shadow roots on a single host and make sure that shadow elements
|
||||
// are associated with the correct shadow root.
|
||||
var firstShadow = host.createShadowRoot();
|
||||
firstShadow.innerHTML = '<shadow id="shadowone"></shadow>';
|
||||
var secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<shadow id="shadowtwo"></shadow>';
|
||||
var thirdShadow = host.createShadowRoot();
|
||||
thirdShadow.innerHTML = '<shadow id="shadowthree"></shadow>';
|
||||
|
||||
is(firstShadow.getElementById("shadowone").olderShadowRoot, null, "Shadow element in oldest ShadowRoot should not be associated with a ShadowRoot.");
|
||||
is(secondShadow.getElementById("shadowtwo").olderShadowRoot, firstShadow, "Shadow element should be associated with older ShadowRoot.");
|
||||
is(thirdShadow.getElementById("shadowthree").olderShadowRoot, secondShadow, "Shadow element should be associated with older ShadowRoot.");
|
||||
|
||||
// Only the first ShadowRoot in tree order is an insertion point.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<shadow id="shadowone"></shadow><shadow id="shadowtwo"></shadow>';
|
||||
var shadowElemOne = secondShadow.getElementById("shadowone");
|
||||
var shadowElemTwo = secondShadow.getElementById("shadowtwo");
|
||||
|
||||
is(shadowElemOne.olderShadowRoot, firstShadow, "First <shadow> in tree order should be an insertion point.");
|
||||
is(shadowElemTwo.olderShadowRoot, null, "Second <shadow> in tree order should not be an insertion point.");
|
||||
|
||||
// Remove the first <shadow> element and make sure the second <shadow> element becomes an insertion point.
|
||||
secondShadow.removeChild(shadowElemOne);
|
||||
is(shadowElemOne.olderShadowRoot, null, "<shadow> element not in a ShadowRoot is not an insertion point.");
|
||||
is(shadowElemTwo.olderShadowRoot, firstShadow, "Second <shadow> element should become insertion point after first is removed.");
|
||||
|
||||
// Insert a <shadow> element before the current shadow insertion point and make sure that it becomes an insertion point.
|
||||
secondShadow.insertBefore(shadowElemOne, shadowElemTwo);
|
||||
is(shadowElemOne.olderShadowRoot, firstShadow, "<shadow> element inserted as first in tree order should become an insertion point.");
|
||||
is(shadowElemTwo.olderShadowRoot, null, "<shadow> element should no longer be an insertion point it another is inserted before.");
|
||||
|
||||
// <shadow> element in fallback content is not an insertion point.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<content><shadow id="shadowone"></shadow></content><shadow id="shadowtwo"></shadow>';
|
||||
shadowElemOne = secondShadow.getElementById("shadowone");
|
||||
shadowElemTwo = secondShadow.getElementById("shadowtwo");
|
||||
|
||||
is(shadowElemOne.olderShadowRoot, null, "<shadow> element in fallback content is not an insertion point.");
|
||||
is(shadowElemTwo.olderShadowRoot, null, "<shadow> element preceeded by another <shadow> element is not an insertion point.");
|
||||
|
||||
// <shadow> element that is descendant of shadow element is not an insertion point.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<shadow><shadow id="shadowone"></shadow></shadow>';
|
||||
shadowElemOne = secondShadow.getElementById("shadowone");
|
||||
is(shadowElemOne.olderShadowRoot, null, "<shadow> element that is descendant of shadow element is not an insertion point.");
|
||||
|
||||
// Check projection of <content> elements through <shadow> elements.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
firstShadow.innerHTML = '<content id="firstcontent"></content>';
|
||||
secondShadow.innerHTML = '<shadow><span id="one"></span><content id="secondcontent"></content><span id="four"></span></shadow>';
|
||||
host.innerHTML = '<span id="two"></span><span id="three"></span>';
|
||||
var firstContent = firstShadow.getElementById("firstcontent");
|
||||
var secondContent = secondShadow.getElementById("secondcontent");
|
||||
var firstDistNodes = firstContent.getDistributedNodes();
|
||||
var secondDistNodes = secondContent.getDistributedNodes();
|
||||
|
||||
is(secondDistNodes.length, 2, "There should be two distributed nodes from the host.");
|
||||
ok(secondDistNodes[0].id == "two" &&
|
||||
secondDistNodes[1].id == "three", "Nodes projected from host should preserve order.");
|
||||
|
||||
is(firstDistNodes.length, 4, "There should be four distributed nodes, two from the first shadow, two from the second shadow.");
|
||||
ok(firstDistNodes[0].id == "one" &&
|
||||
firstDistNodes[1].id == "two" &&
|
||||
firstDistNodes[2].id == "three" &&
|
||||
firstDistNodes[3].id == "four", "Reprojection through shadow should preserve node order.");
|
||||
|
||||
// Remove a node from the host and make sure that it is removed from all insertion points.
|
||||
host.removeChild(host.firstChild);
|
||||
firstDistNodes = firstContent.getDistributedNodes();
|
||||
secondDistNodes = secondContent.getDistributedNodes();
|
||||
|
||||
is(secondDistNodes.length, 1, "There should be one distriubted node remaining after removing node from host.");
|
||||
ok(secondDistNodes[0].id == "three", "Span with id=two should have been removed from content element.");
|
||||
is(firstDistNodes.length, 3, "There should be three distributed nodes remaining after removing node from host.");
|
||||
ok(firstDistNodes[0].id == "one" &&
|
||||
firstDistNodes[1].id == "three" &&
|
||||
firstDistNodes[2].id == "four", "Reprojection through shadow should preserve node order.");
|
||||
|
||||
// Check projection of <shadow> elements to <content> elements.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<span id="firstspan"><shadow></shadow></span>';
|
||||
thirdShadow = secondShadow.getElementById("firstspan").createShadowRoot();
|
||||
thirdShadow.innerHTML = '<content id="firstcontent"></content>';
|
||||
firstContent = thirdShadow.getElementById("firstcontent");
|
||||
var shadowChild = document.createElement("span");
|
||||
firstShadow.appendChild(shadowChild);
|
||||
|
||||
is(firstContent.getDistributedNodes()[0], shadowChild, "Elements in shadow insertioin point should be projected into content insertion points.");
|
||||
|
||||
// Remove child of ShadowRoot and check that projected node is removed from insertion point.
|
||||
firstShadow.removeChild(firstShadow.firstChild);
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 0, "Reprojected element was removed from ShadowRoot, thus it should be removed from content insertion point.");
|
||||
|
||||
// Check deeply nested projection of <shadow> elements.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
firstShadow.innerHTML = '<content></content>';
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<shadow><content></content></shadow>';
|
||||
thirdShadow = host.createShadowRoot();
|
||||
thirdShadow.innerHTML = '<span id="firstspan"><shadow><content></content></shadow></span>';
|
||||
var fourthShadow = thirdShadow.getElementById("firstspan").createShadowRoot();
|
||||
fourthShadow.innerHTML = '<content id="firstcontent"></content>';
|
||||
firstContent = fourthShadow.getElementById("firstcontent");
|
||||
host.innerHTML = '<span></span>';
|
||||
|
||||
is(firstContent.getDistributedNodes()[0], host.firstChild, "Child of host should be projected to insertion point.");
|
||||
|
||||
// Remove node and make sure that it is removed from distributed nodes.
|
||||
host.removeChild(host.firstChild);
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 0, "Node removed from host should be removed from insertion point.");
|
||||
|
||||
// Check projection of fallback content through <shadow> elements.
|
||||
host = document.createElement("span");
|
||||
firstShadow = host.createShadowRoot();
|
||||
firstShadow.innerHTML = '<content><span id="firstspan"></span></content>';
|
||||
secondShadow = host.createShadowRoot();
|
||||
secondShadow.innerHTML = '<span id="secondspan"><shadow id="firstshadow"></shadow></span>';
|
||||
firstShadowElem = secondShadow.getElementById("firstshadow");
|
||||
thirdShadow = secondShadow.getElementById("secondspan").createShadowRoot();
|
||||
thirdShadow.innerHTML = '<content id="firstcontent"></content>';
|
||||
firstContent = thirdShadow.getElementById("firstcontent");
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 1, "There should be one node distributed from fallback content.");
|
||||
is(firstContent.getDistributedNodes()[0], firstShadow.getElementById("firstspan"), "Fallback content should be distributed.");
|
||||
|
||||
// Add some content to the host (causing the fallback content to be dropped) and make sure distribution nodes are updated.
|
||||
|
||||
var newElem = document.createElement("div");
|
||||
firstShadowElem.appendChild(newElem);
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 1, "There should be one node distributed from the host.");
|
||||
is(firstContent.getDistributedNodes()[0], newElem, "Distributed node should be from host, not fallback content.");
|
||||
|
||||
// Remove the distribution node and check that fallback content is used.
|
||||
firstShadowElem.removeChild(newElem);
|
||||
|
||||
is(firstContent.getDistributedNodes().length, 1, "There should be one node distributed from fallback content.");
|
||||
is(firstContent.getDistributedNodes()[0], firstShadow.getElementById("firstspan"), "Fallback content should be distributed after removing node from host.");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html
|
||||
*
|
||||
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
|
||||
* Opera Software ASA. You are granted a license to use, reproduce
|
||||
* and create derivative works of this document.
|
||||
*/
|
||||
|
||||
interface HTMLShadowElement : HTMLElement
|
||||
{
|
||||
readonly attribute ShadowRoot? olderShadowRoot;
|
||||
};
|
||||
|
|
@ -167,6 +167,7 @@ WEBIDL_FILES = [
|
|||
'HTMLQuoteElement.webidl',
|
||||
'HTMLScriptElement.webidl',
|
||||
'HTMLSelectElement.webidl',
|
||||
'HTMLShadowElement.webidl',
|
||||
'HTMLSourceElement.webidl',
|
||||
'HTMLSpanElement.webidl',
|
||||
'HTMLStyleElement.webidl',
|
||||
|
|
|
@ -730,6 +730,7 @@ static const nsElementInfo kElements[eHTMLTag_userdefined] = {
|
|||
GROUP_LEAF),
|
||||
ELEM(section, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
|
||||
ELEM(select, true, false, GROUP_FORMCONTROL, GROUP_SELECT_CONTENT),
|
||||
ELEM(shadow, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT),
|
||||
ELEM(small, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
|
||||
ELEM(source, false, false, GROUP_NONE, GROUP_NONE),
|
||||
ELEM(span, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<div><span>Hello</span><span>World</span></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function tweak() {
|
||||
var oldShadowRoot = document.getElementById('outer').createShadowRoot();
|
||||
oldShadowRoot.innerHTML = 'World';
|
||||
|
||||
var youngShadowRoot = document.getElementById('outer').createShadowRoot();
|
||||
youngShadowRoot.innerHTML = 'Hello<content></content><shadow></shadow>';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="tweak()">
|
||||
<div id="outer"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<div><span>Hello</span><span>World</span></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function tweak() {
|
||||
var oldShadowRoot = document.getElementById('outer').createShadowRoot();
|
||||
oldShadowRoot.innerHTML = 'Hello';
|
||||
|
||||
var youngShadowRoot = document.getElementById('outer').createShadowRoot();
|
||||
youngShadowRoot.innerHTML = '<shadow></shadow><content></content>World';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="tweak()">
|
||||
<div id="outer"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div style="width:100px; height:100px; background-color:green"></div><div style="width:100px; height:100px; background-color:orange">Hello World</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function tweak() {
|
||||
var olderShadow = document.getElementById('outer').createShadowRoot();
|
||||
olderShadow.innerHTML = '<div style="width:100px; height:100px; background-color: orange"><content></content></div>';
|
||||
|
||||
var youngerShadow = document.getElementById('outer').createShadowRoot();
|
||||
youngerShadow.innerHTML = '<div style="width:100px; height:100px; background-color: green"></div><shadow>Hello World</shadow>';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="tweak()">
|
||||
<div id="outer">
|
||||
<div style="width:300px; height:100px; background-color:red;"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div style="background-color: green"><span>Hello</span><span> </span><span>World</span></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function tweak() {
|
||||
var oldestShadow = document.getElementById('outer').createShadowRoot();
|
||||
oldestShadow.innerHTML = 'Hello';
|
||||
|
||||
var olderShadow = document.getElementById('outer').createShadowRoot();
|
||||
|
||||
var youngerShadow = document.getElementById('outer').createShadowRoot();
|
||||
youngerShadow.innerHTML = '<div style="background-color:green"><shadow></shadow></div>';
|
||||
|
||||
olderShadow.innerHTML = '<shadow></shadow> World';
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="tweak()">
|
||||
<div id="outer">
|
||||
<div style="width:300px; height:100px; background-color:red;"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div style="background-color: green"><span>Hello</span><span> </span><span>World</span></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function tweak() {
|
||||
var olderShadow = document.getElementById('outer').createShadowRoot();
|
||||
olderShadow.innerHTML = '<content></content><span>World</span>';
|
||||
|
||||
var youngerShadow = document.getElementById('outer').createShadowRoot();
|
||||
youngerShadow.innerHTML = '<div id="shadowparent"><shadow id="youngshadow"><span>Hello</span></shadow></div>';
|
||||
|
||||
var shadowParent = youngerShadow.getElementById("shadowparent");
|
||||
var nestedShadow = shadowParent.createShadowRoot();
|
||||
nestedShadow.innerHTML = '<div style="background-color: green"><content></content></div>';
|
||||
|
||||
// Dynamically append a span to the shadow element in the younger ShadowRoot to make sure
|
||||
// it is projected into the nested shadow.
|
||||
var appendedSpan = document.createElement("span");
|
||||
appendedSpan.textContent = ' ';
|
||||
youngerShadow.getElementById("youngshadow").appendChild(appendedSpan);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="tweak()">
|
||||
<div id="outer">
|
||||
<div style="width:300px; height:100px; background-color:red;"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -5,6 +5,10 @@ pref(dom.webcomponents.enabled,true) == basic-shadow-3.html basic-shadow-3-ref.h
|
|||
pref(dom.webcomponents.enabled,true) == basic-shadow-4.html basic-shadow-4-ref.html
|
||||
pref(dom.webcomponents.enabled,true) == basic-insertion-point-1.html basic-insertion-point-1-ref.html
|
||||
pref(dom.webcomponents.enabled,true) == basic-insertion-point-2.html basic-insertion-point-2-ref.html
|
||||
pref(dom.webcomponents.enabled,true) == adjacent-insertion-points-1.html adjacent-insertion-points-1-ref.html
|
||||
pref(dom.webcomponents.enabled,true) == adjacent-insertion-points-2.html adjacent-insertion-points-2-ref.html
|
||||
pref(dom.webcomponents.enabled,true) == fallback-content-1.html fallback-content-1-ref.html
|
||||
pref(dom.webcomponents.enabled,true) == remove-insertion-point-1.html remove-insertion-point-1-ref.html
|
||||
pref(dom.webcomponents.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
|
||||
pref(dom.webcomponents.enabled,true) == basic-shadow-element-1.html basic-shadow-element-1-ref.html
|
||||
pref(dom.webcomponents.enabled,true) == nested-shadow-element-1.html nested-shadow-element-1-ref.html
|
||||
|
|
|
@ -135,6 +135,7 @@ HTML_HTMLELEMENT_TAG(samp)
|
|||
HTML_TAG(script, Script)
|
||||
HTML_HTMLELEMENT_TAG(section)
|
||||
HTML_TAG(select, Select)
|
||||
HTML_TAG(shadow, Shadow)
|
||||
HTML_HTMLELEMENT_TAG(small)
|
||||
HTML_TAG(source, Source)
|
||||
HTML_TAG(span, Span)
|
||||
|
|
|
@ -419,6 +419,10 @@ const nsHTMLElement gHTMLElements[] = {
|
|||
/*tag*/ eHTMLTag_select,
|
||||
/*parent,leaf*/ kFormControl, false
|
||||
},
|
||||
{
|
||||
/*tag*/ eHTMLTag_shadow,
|
||||
/*parent,leaf*/ kFlowEntity, false
|
||||
},
|
||||
{
|
||||
/*tag*/ eHTMLTag_small,
|
||||
/*parent,leaf*/ kFontStyle, false
|
||||
|
|
|
@ -217,6 +217,8 @@ static const PRUnichar sHTMLTagUnicodeName_section[] =
|
|||
{'s', 'e', 'c', 't', 'i', 'o', 'n', '\0'};
|
||||
static const PRUnichar sHTMLTagUnicodeName_select[] =
|
||||
{'s', 'e', 'l', 'e', 'c', 't', '\0'};
|
||||
static const PRUnichar sHTMLTagUnicodeName_shadow[] =
|
||||
{'s', 'h', 'a', 'd', 'o', 'w', '\0'};
|
||||
static const PRUnichar sHTMLTagUnicodeName_small[] =
|
||||
{'s', 'm', 'a', 'l', 'l', '\0'};
|
||||
static const PRUnichar sHTMLTagUnicodeName_source[] =
|
||||
|
|
Загрузка…
Ссылка в новой задаче