Bug 887538 - Implement web components shadow element. r=mrbkap

This commit is contained in:
William Chen 2013-12-20 22:43:58 -08:00
Родитель 4a425922ba
Коммит a77e9ae737
33 изменённых файлов: 1092 добавлений и 112 удалений

Просмотреть файл

@ -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[] =