Bug 1409975 - Implement node distribution for shadow tree slots

* Implementation for assignedNodes
* Include slots in the flat tree
* Fix event get-the-parent algorithm for a node
* Update and add reftests for Shadow DOM v1
* Update web platform tests expectations

Tag #1375
This commit is contained in:
Matt A. Tobin 2020-04-17 07:10:54 -04:00 committed by Roy Tam
commit cab78760e5
49 changed files with 775 additions and 429 deletions

View file

@ -6,6 +6,7 @@
#include "ChildIterator.h"
#include "nsContentUtils.h"
#include "mozilla/dom/HTMLSlotElement.h"
#include "mozilla/dom/XBLChildrenElement.h"
#include "mozilla/dom/ShadowRoot.h"
#include "nsIAnonymousContentCreator.h"
@ -57,15 +58,34 @@ GetMatchedNodesForPoint(nsIContent* aContent)
// XXX handle <slot> element?
}
ExplicitChildIterator::ExplicitChildIterator(const nsIContent* aParent,
bool aStartAtBeginning)
: mParent(aParent),
mChild(nullptr),
mDefaultChild(nullptr),
mIsFirst(aStartAtBeginning),
mIndexInInserted(0)
{
mParentAsSlot = HTMLSlotElement::FromContent(mParent);
}
nsIContent*
ExplicitChildIterator::GetNextChild()
{
// If we're already in the inserted-children array, look there first
if (mIndexInInserted) {
MOZ_ASSERT(mChild);
MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
MOZ_ASSERT(!mDefaultChild);
if (mParentAsSlot) {
const nsTArray<RefPtr<nsINode>>& assignedNodes =
mParentAsSlot->AssignedNodes();
mChild = (mIndexInInserted < assignedNodes.Length()) ?
assignedNodes[mIndexInInserted++]->AsContent() : nullptr;
return mChild;
}
MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
if (mIndexInInserted < assignedChildren.Length()) {
return assignedChildren[mIndexInInserted++];
@ -84,6 +104,19 @@ ExplicitChildIterator::GetNextChild()
mChild = mChild->GetNextSibling();
} else if (mIsFirst) { // at the beginning of the child list
// For slot parent, iterate over assigned nodes if not empty, otherwise
// fall through and iterate over direct children (fallback content).
if (mParentAsSlot) {
const nsTArray<RefPtr<nsINode>>& assignedNodes =
mParentAsSlot->AssignedNodes();
if (!assignedNodes.IsEmpty()) {
mIndexInInserted = 1;
mChild = assignedNodes[0]->AsContent();
mIsFirst = false;
return mChild;
}
}
mChild = mParent->GetFirstChild();
mIsFirst = false;
} else if (mChild) { // in the middle of the child list
@ -185,6 +218,12 @@ ExplicitChildIterator::Get() const
{
MOZ_ASSERT(!mIsFirst);
// When mParentAsSlot is set, mChild is always set to the current child. It
// does not matter whether mChild is an assigned node or a fallback content.
if (mParentAsSlot) {
return mChild;
}
if (mIndexInInserted) {
MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
return assignedChildren[mIndexInInserted - 1];
@ -198,6 +237,20 @@ ExplicitChildIterator::GetPreviousChild()
{
// If we're already in the inserted-children array, look there first
if (mIndexInInserted) {
if (mParentAsSlot) {
const nsTArray<RefPtr<nsINode>>& assignedNodes =
mParentAsSlot->AssignedNodes();
mChild = (--mIndexInInserted) ?
assignedNodes[mIndexInInserted - 1]->AsContent() : nullptr;
if (!mChild) {
mIsFirst = true;
}
return mChild;
}
// NB: mIndexInInserted points one past the last returned child so we need
// to look *two* indices back in order to return the previous child.
MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
@ -218,6 +271,18 @@ ExplicitChildIterator::GetPreviousChild()
} else if (mChild) { // in the middle of the child list
mChild = mChild->GetPreviousSibling();
} else { // at the end of the child list
// For slot parent, iterate over assigned nodes if not empty, otherwise
// fall through and iterate over direct children (fallback content).
if (mParentAsSlot) {
const nsTArray<RefPtr<nsINode>>& assignedNodes =
mParentAsSlot->AssignedNodes();
if (!assignedNodes.IsEmpty()) {
mIndexInInserted = assignedNodes.Length();
mChild = assignedNodes[mIndexInInserted - 1]->AsContent();
return mChild;
}
}
mChild = mParent->GetLastChild();
}

View file

@ -36,22 +36,19 @@ class ExplicitChildIterator
{
public:
explicit ExplicitChildIterator(const nsIContent* aParent,
bool aStartAtBeginning = true)
: mParent(aParent),
mChild(nullptr),
mDefaultChild(nullptr),
mIndexInInserted(0),
mIsFirst(aStartAtBeginning)
{
}
bool aStartAtBeginning = true);
ExplicitChildIterator(const ExplicitChildIterator& aOther)
: mParent(aOther.mParent), mChild(aOther.mChild),
: mParent(aOther.mParent),
mParentAsSlot(aOther.mParentAsSlot),
mChild(aOther.mChild),
mDefaultChild(aOther.mDefaultChild),
mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {}
ExplicitChildIterator(ExplicitChildIterator&& aOther)
: mParent(aOther.mParent), mChild(aOther.mChild),
: mParent(aOther.mParent),
mParentAsSlot(aOther.mParentAsSlot),
mChild(aOther.mChild),
mDefaultChild(aOther.mDefaultChild),
mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {}
@ -98,6 +95,10 @@ protected:
// the <xbl:content> element for the binding.
const nsIContent* mParent;
// If parent is a slot element, this points to the parent as HTMLSlotElement,
// otherwise, it's null.
const HTMLSlotElement* mParentAsSlot;
// 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

View file

@ -182,6 +182,24 @@ nsIContent::GetAssignedSlotByMode() const
return slot;
}
nsINode*
nsIContent::GetFlattenedTreeParentForMaybeAssignedNode() const
{
if (HTMLSlotElement* assignedSlot = GetAssignedSlot()) {
return assignedSlot;
}
HTMLSlotElement* parentSlot = HTMLSlotElement::FromContent(GetParent());
if (!parentSlot) {
return nullptr;
}
// If this is not an unassigned node, then it must be a fallback content.
MOZ_ASSERT(parentSlot->AssignedNodes().IsEmpty());
return parentSlot;
}
nsINode*
nsIContent::GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const
{
@ -233,16 +251,10 @@ nsIContent::GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const
if (parent && nsContentUtils::HasDistributedChildren(parent) &&
nsContentUtils::IsInSameAnonymousTree(parent, this)) {
// This node is distributed to insertion points, thus we
// need to consult the destination insertion points list to
// figure out where this node was inserted in the flattened tree.
// It may be the case that |parent| distributes its children
// but the child does not match any insertion points, thus
// the flattened tree parent is nullptr.
nsTArray<nsIContent*>* destInsertionPoints = GetExistingDestInsertionPoints();
parent = destInsertionPoints && !destInsertionPoints->IsEmpty() ?
destInsertionPoints->LastElement()->GetParent() : nullptr;
} else if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
return GetFlattenedTreeParentForMaybeAssignedNode();
}
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
nsIContent* insertionParent = GetXBLInsertionParent();
if (insertionParent) {
parent = insertionParent;
@ -890,42 +902,10 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
}
}
nsIContent* parent = GetParent();
// Web components have a special event chain that need to account
// for destination insertion points where nodes have been distributed.
nsTArray<nsIContent*>* destPoints = GetExistingDestInsertionPoints();
if (destPoints && !destPoints->IsEmpty()) {
// Push destination insertion points to aVisitor.mDestInsertionPoints.
for (uint32_t i = 0; i < destPoints->Length(); i++) {
nsIContent* point = destPoints->ElementAt(i);
aVisitor.mDestInsertionPoints.AppendElement(point);
}
}
ShadowRoot* thisShadowRoot = ShadowRoot::FromNode(this);
if (thisShadowRoot) {
if (!aVisitor.mEvent->mFlags.mComposed) {
// If we do stop propagation, we still want to propagate
// the event to chrome (nsPIDOMWindow::GetParentTarget()).
// The load event is special in that we don't ever propagate it
// to chrome.
nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
? win->GetParentTarget() : nullptr;
aVisitor.mParentTarget = parentTarget;
return NS_OK;
}
if (!aVisitor.mDestInsertionPoints.IsEmpty()) {
parent = aVisitor.mDestInsertionPoints.LastElement();
aVisitor.mDestInsertionPoints.SetLength(
aVisitor.mDestInsertionPoints.Length() - 1);
} else {
parent = thisShadowRoot->GetHost();
}
}
// Event parent is the assigned slot, if node is assigned, or node's parent
// otherwise.
HTMLSlotElement* slot = GetAssignedSlot();
nsIContent* parent = slot ? slot : GetParent();
// Event may need to be retargeted if this is the root of a native
// anonymous content subtree or event is dispatched somewhere inside XBL.

View file

@ -14,7 +14,9 @@
#include "nsIDOMHTMLElement.h"
#include "nsIStyleSheetLinkingElement.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLSlotElement.h"
#include "nsXBLPrototypeBinding.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
@ -109,6 +111,87 @@ ShadowRoot::FromNode(nsINode* aNode)
return nullptr;
}
void
ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
{
MOZ_ASSERT(aSlot);
// Note that if name attribute missing, the slot is a default slot.
nsAutoString name;
aSlot->GetName(name);
nsTArray<HTMLSlotElement*>* currentSlots = mSlotMap.LookupOrAdd(name);
MOZ_ASSERT(currentSlots);
HTMLSlotElement* oldSlot = currentSlots->IsEmpty() ?
nullptr : currentSlots->ElementAt(0);
TreeOrderComparator comparator;
currentSlots->InsertElementSorted(aSlot, comparator);
HTMLSlotElement* currentSlot = currentSlots->ElementAt(0);
if (currentSlot != aSlot) {
return;
}
if (oldSlot && oldSlot != currentSlot) {
// Move assigned nodes from old slot to new slot.
const nsTArray<RefPtr<nsINode>>& assignedNodes = oldSlot->AssignedNodes();
while (assignedNodes.Length() > 0) {
nsINode* assignedNode = assignedNodes[0];
oldSlot->RemoveAssignedNode(assignedNode);
currentSlot->AppendAssignedNode(assignedNode);
}
} else {
// Otherwise add appropriate nodes to this slot from the host.
for (nsIContent* child = GetHost()->GetFirstChild();
child;
child = child->GetNextSibling()) {
nsAutoString slotName;
child->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
if (child->IsSlotable() && slotName.Equals(name)) {
currentSlot->AppendAssignedNode(child);
}
}
}
}
void
ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot)
{
MOZ_ASSERT(aSlot);
nsAutoString name;
aSlot->GetName(name);
nsTArray<HTMLSlotElement*>* currentSlots = mSlotMap.Get(name);
if (currentSlots) {
if (currentSlots->Length() == 1) {
MOZ_ASSERT(currentSlots->ElementAt(0) == aSlot);
mSlotMap.Remove(name);
aSlot->ClearAssignedNodes();
} else {
bool doReplaceSlot = currentSlots->ElementAt(0) == aSlot;
currentSlots->RemoveElement(aSlot);
HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
// Move assigned nodes from removed slot to the next slot in
// tree order with the same name.
if (doReplaceSlot) {
const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
while (assignedNodes.Length() > 0) {
nsINode* assignedNode = assignedNodes[0];
aSlot->RemoveAssignedNode(assignedNode);
replacementSlot->AppendAssignedNode(assignedNode);
}
}
}
}
}
void
ShadowRoot::StyleSheetChanged()
{
@ -222,6 +305,128 @@ ShadowRoot::GetElementsByClassName(const nsAString& aClasses)
return nsContentUtils::GetElementsByClassName(this, aClasses);
}
nsresult
ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
{
aVisitor.mCanHandle = true;
// https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
if (!aVisitor.mEvent->mFlags.mComposed) {
nsCOMPtr<nsIContent> originalTarget =
do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
if (originalTarget->GetContainingShadow() == this) {
// If we do stop propagation, we still want to propagate
// the event to chrome (nsPIDOMWindow::GetParentTarget()).
// The load event is special in that we don't ever propagate it
// to chrome.
nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
? win->GetParentTarget() : nullptr;
aVisitor.mParentTarget = parentTarget;
return NS_OK;
}
}
nsIContent* shadowHost = GetHost();
aVisitor.mParentTarget = shadowHost;
if (aVisitor.mOriginalTargetIsInAnon) {
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
if (content && content->GetBindingParent() == shadowHost) {
aVisitor.mEventTargetAtParent = shadowHost;
}
}
return NS_OK;
}
const HTMLSlotElement*
ShadowRoot::AssignSlotFor(nsIContent* aContent)
{
nsAutoString slotName;
// Note that if slot attribute is missing, assign it to the first default
// slot, if exists.
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
nsTArray<HTMLSlotElement*>* slots = mSlotMap.Get(slotName);
if (!slots) {
return nullptr;
}
HTMLSlotElement* slot = slots->ElementAt(0);
MOZ_ASSERT(slot);
// Find the appropriate position in the assigned node list for the
// newly assigned content.
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
nsIContent* currentContent = GetHost()->GetFirstChild();
bool indexFound = false;
uint32_t insertionIndex;
for (uint32_t i = 0; i < assignedNodes.Length(); i++) {
// Seek through the host's explicit children until the
// assigned content is found.
while (currentContent && currentContent != assignedNodes[i]) {
if (currentContent == aContent) {
indexFound = true;
insertionIndex = i;
}
currentContent = currentContent->GetNextSibling();
}
if (indexFound) {
break;
}
}
if (indexFound) {
slot->InsertAssignedNode(insertionIndex, aContent);
} else {
slot->AppendAssignedNode(aContent);
}
return slot;
}
const HTMLSlotElement*
ShadowRoot::UnassignSlotFor(nsIContent* aNode, const nsAString& aSlotName)
{
// Find the insertion point to which the content belongs. Note that if slot
// attribute is missing, unassign it from the first default slot, if exists.
nsTArray<HTMLSlotElement*>* slots = mSlotMap.Get(aSlotName);
if (!slots) {
return nullptr;
}
HTMLSlotElement* slot = slots->ElementAt(0);
MOZ_ASSERT(slot);
if (!slot->AssignedNodes().Contains(aNode)) {
return nullptr;
}
slot->RemoveAssignedNode(aNode);
return slot;
}
bool
ShadowRoot::MaybeReassignElement(Element* aElement,
const nsAttrValue* aOldValue)
{
nsIContent* parent = aElement->GetParent();
if (parent && parent == GetHost()) {
const HTMLSlotElement* oldSlot = UnassignSlotFor(aElement,
aOldValue ? aOldValue->GetStringValue() : EmptyString());
const HTMLSlotElement* newSlot = AssignSlotFor(aElement);
if (oldSlot != newSlot) {
return true;
}
}
return false;
}
void
ShadowRoot::DistributionChanged()
{
@ -333,7 +538,12 @@ ShadowRoot::AttributeChanged(nsIDocument* aDocument,
int32_t aModType,
const nsAttrValue* aOldValue)
{
if (!IsPooledNode(aElement)) {
if (aNameSpaceID != kNameSpaceID_None || aAttribute != nsGkAtoms::slot) {
return;
}
// Attributes may change insertion point matching, find its new distribution.
if (!MaybeReassignElement(aElement, aOldValue)) {
return;
}
@ -369,11 +579,18 @@ ShadowRoot::ContentInserted(nsIDocument* aDocument,
nsIContent* aChild,
int32_t aIndexInContainer)
{
if (mInsertionPointChanged) {
DistributeAllNodes();
mInsertionPointChanged = false;
// Check to ensure that the content is in the same anonymous tree
// as the container because anonymous content may report its container
// as the host but it may not be in the host's child list.
if (!nsContentUtils::IsInSameAnonymousTree(aContainer, aChild)) {
return;
}
if (!aChild->IsSlotable() || aContainer != GetHost()) {
return;
}
AssignSlotFor(aChild);
}
void
@ -383,11 +600,20 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument,
int32_t aIndexInContainer,
nsIContent* aPreviousSibling)
{
if (mInsertionPointChanged) {
DistributeAllNodes();
mInsertionPointChanged = false;
// Check to ensure that the content is in the same anonymous tree
// as the container because anonymous content may report its container
// as the host but it may not be in the host's child list.
if (!nsContentUtils::IsInSameAnonymousTree(aContainer, aChild)) {
return;
}
if (!aChild->IsSlotable() || aContainer != GetHost()) {
return;
}
nsAutoString slotName;
aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
UnassignSlotFor(aChild, slotName);
}
nsresult

View file

@ -21,6 +21,9 @@ class nsIContent;
class nsXBLPrototypeBinding;
namespace mozilla {
class EventChainPreVisitor;
namespace dom {
class Element;
@ -73,10 +76,26 @@ public:
private:
/**
* Redistributes a node of the pool, and returns whether the distribution
* Try to reassign an element to a slot and returns whether the assignment
* changed.
*/
bool RedistributeElement(Element*);
bool MaybeReassignElement(Element* aElement, const nsAttrValue* aOldValue);
/**
* Try to assign aContent to a slot in the shadow tree, returns the assigned
* slot if found.
*/
const HTMLSlotElement* AssignSlotFor(nsIContent* aContent);
/**
* Unassign aContent from the assigned slot in the shadow tree, returns the
* assigned slot if found.
*
* Note: slot attribute of aContent may have changed already, so pass slot
* name explicity here.
*/
const HTMLSlotElement* UnassignSlotFor(nsIContent* aContent,
const nsAString& aSlotName);
/**
* Called when we redistribute content after insertion points have changed.
@ -86,6 +105,9 @@ private:
bool IsPooledNode(nsIContent* aChild) const;
public:
void AddSlot(HTMLSlotElement* aSlot);
void RemoveSlot(HTMLSlotElement* aSlot);
void SetInsertionPointChanged() { mInsertionPointChanged = true; }
void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
@ -113,11 +135,18 @@ public:
mIsComposedDocParticipant = aIsComposedDocParticipant;
}
nsresult GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
protected:
virtual ~ShadowRoot();
ShadowRootMode mMode;
// Map from name of slot to an array of all slots in the shadow DOM with with
// the given name. The slots are stored as a weak pointer because the elements
// are in the shadow tree and should be kept alive by its parent.
nsClassHashtable<nsStringHashKey, nsTArray<mozilla::dom::HTMLSlotElement*>> mSlotMap;
nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
nsXBLPrototypeBinding* mProtoBinding;

View file

@ -44,6 +44,7 @@
#include "mozilla/dom/Element.h"
#include "mozilla/dom/FileSystemSecurity.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/HTMLSlotElement.h"
#include "mozilla/dom/HTMLTemplateElement.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
@ -7053,6 +7054,13 @@ nsContentUtils::HasDistributedChildren(nsIContent* aContent)
return true;
}
HTMLSlotElement* slotEl = HTMLSlotElement::FromContent(aContent);
if (slotEl && slotEl->GetContainingShadow()) {
// Children of a slot are rendered if the slot does not have any assigned
// nodes (fallback content).
return slotEl->AssignedNodes().IsEmpty();
}
return false;
}

View file

@ -999,6 +999,12 @@ protected:
*/
nsIAtom* DoGetID() const;
/**
* Returns the assigned slot, if it exists, or the direct parent, if it's a
* fallback content of a slot.
*/
nsINode* GetFlattenedTreeParentForMaybeAssignedNode() const;
public:
#ifdef DEBUG
/**

View file

@ -408,6 +408,12 @@ public:
*/
virtual bool IsNodeOfType(uint32_t aFlags) const = 0;
bool
IsSlotable() const
{
return IsElement() || IsNodeOfType(eTEXT);
}
virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
/**

View file

@ -204,13 +204,6 @@ public:
* which should be used when the event is handled at mParentTarget.
*/
dom::EventTarget* mEventTargetAtParent;
/**
* An array of destination insertion points that need to be inserted
* into the event path of nodes that are distributed by the
* web components distribution algorithm.
*/
nsTArray<nsIContent*> mDestInsertionPoints;
};
class EventChainPostVisitor : public mozilla::EventChainVisitor

View file

@ -7,6 +7,7 @@
#include "mozilla/dom/HTMLSlotElement.h"
#include "mozilla/dom/HTMLSlotElementBinding.h"
#include "mozilla/dom/HTMLUnknownElement.h"
#include "mozilla/dom/ShadowRoot.h"
#include "nsGkAtoms.h"
#include "nsDocument.h"
@ -15,12 +16,10 @@ NS_NewHTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser)
{
RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
/* Disabled for now
if (nsDocument::IsWebComponentsEnabled(nodeInfo)) {
already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
return new mozilla::dom::HTMLSlotElement(nodeInfoArg);
}
*/
already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
@ -50,13 +49,161 @@ NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
NS_IMPL_ELEMENT_CLONE(HTMLSlotElement)
nsresult
HTMLSlotElement::BindToTree(nsIDocument* aDocument,
nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
ShadowRoot* containingShadow = GetContainingShadow();
if (containingShadow && !oldContainingShadow) {
containingShadow->AddSlot(this);
}
return NS_OK;
}
void
HTMLSlotElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
if (oldContainingShadow && !GetContainingShadow()) {
oldContainingShadow->RemoveSlot(this);
}
}
nsresult
HTMLSlotElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify)
{
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::name) {
if (ShadowRoot* containingShadow = GetContainingShadow()) {
containingShadow->RemoveSlot(this);
}
}
return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue,
aNotify);
}
nsresult
HTMLSlotElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify)
{
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::name) {
if (ShadowRoot* containingShadow = GetContainingShadow()) {
containingShadow->AddSlot(this);
}
}
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
aOldValue, aNotify);
}
/**
* Flatten assigned nodes given a slot, as in:
* https://dom.spec.whatwg.org/#find-flattened-slotables
*/
static void
FlattenAssignedNodes(HTMLSlotElement* aSlot, nsTArray<RefPtr<nsINode>>& aNodes)
{
if (!aSlot->GetContainingShadow()) {
return;
}
const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
// If assignedNodes is empty, use children of slot as fallback content.
if (assignedNodes.IsEmpty()) {
for (nsIContent* child = aSlot->AsContent()->GetFirstChild();
child;
child = child->GetNextSibling()) {
if (!child->IsSlotable()) {
continue;
}
if (child->IsHTMLElement(nsGkAtoms::slot)) {
FlattenAssignedNodes(HTMLSlotElement::FromContent(child), aNodes);
} else {
aNodes.AppendElement(child);
}
}
return;
}
for (uint32_t i = 0; i < assignedNodes.Length(); i++) {
nsINode* assignedNode = assignedNodes[i];
if (assignedNode->IsHTMLElement(nsGkAtoms::slot)) {
FlattenAssignedNodes(
HTMLSlotElement::FromContent(assignedNode->AsContent()), aNodes);
} else {
aNodes.AppendElement(assignedNode);
}
}
}
void
HTMLSlotElement::AssignedNodes(const AssignedNodesOptions& aOptions,
nsTArray<RefPtr<nsINode>>& aNodes)
{
if (aOptions.mFlatten) {
return FlattenAssignedNodes(this, aNodes);
}
aNodes = mAssignedNodes;
}
const nsTArray<RefPtr<nsINode>>&
HTMLSlotElement::AssignedNodes() const
{
return mAssignedNodes;
}
void
HTMLSlotElement::InsertAssignedNode(uint32_t aIndex, nsINode* aNode)
{
mAssignedNodes.InsertElementAt(aIndex, aNode);
aNode->AsContent()->SetAssignedSlot(this);
}
void
HTMLSlotElement::AppendAssignedNode(nsINode* aNode)
{
mAssignedNodes.AppendElement(aNode);
aNode->AsContent()->SetAssignedSlot(this);
}
void
HTMLSlotElement::RemoveAssignedNode(nsINode* aNode)
{
mAssignedNodes.RemoveElement(aNode);
aNode->AsContent()->SetAssignedSlot(nullptr);
}
void
HTMLSlotElement::ClearAssignedNodes()
{
for (uint32_t i = 0; i < mAssignedNodes.Length(); i++) {
mAssignedNodes[i]->AsContent()->SetAssignedSlot(nullptr);
}
mAssignedNodes.Clear();
}
JSObject*
HTMLSlotElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{

View file

@ -26,6 +26,22 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLSlotElement, nsGenericHTMLElement)
virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override;
// nsIContent
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers) override;
virtual void UnbindFromTree(bool aDeep = true,
bool aNullParent = true) override;
virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify) override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
// WebIDL
void SetName(const nsAString& aName, ErrorResult& aRv)
{
SetHTMLAttr(nsGkAtoms::name, aName, aRv);
@ -39,6 +55,13 @@ public:
void AssignedNodes(const AssignedNodesOptions& aOptions,
nsTArray<RefPtr<nsINode>>& aNodes);
// Helper methods
const nsTArray<RefPtr<nsINode>>& AssignedNodes() const;
void InsertAssignedNode(uint32_t aIndex, nsINode* aNode);
void AppendAssignedNode(nsINode* aNode);
void RemoveAssignedNode(nsINode* aNode);
void ClearAssignedNodes();
protected:
virtual ~HTMLSlotElement();
virtual JSObject*

View file

@ -1,8 +0,0 @@
<!DOCTYPE HTML>
<html>
<body>
<div>
<div style="border: 10px solid green">Hello</div>
</div>
</body>
</html>

View file

@ -1,21 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<script>
function tweak() {
// div with style "border: 10px solid green"
var shadowDiv = document.createElement("div");
shadowDiv.style.border = "10px solid green";
var insertionPoint = document.createElement("content");
shadowDiv.appendChild(insertionPoint);
var shadowRoot = document.getElementById('outer').createShadowRoot();
shadowRoot.appendChild(shadowDiv);
}
</script>
</head>
<body onload="tweak()">
<div id="outer">Hello</div>
</body>
</html>

View file

@ -1,11 +0,0 @@
<!DOCTYPE HTML>
<html>
<body>
<div>
<div style="border: 10px solid green">
<span style="background-color: purple">Hello</span>
<span style="background-color: orange">World</span>
</div>
</div>
</body>
</html>

View file

@ -1,24 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<script>
function tweak() {
// div with style "border: 10px solid green"
var shadowDiv = document.createElement("div");
shadowDiv.style.border = "10px solid green";
var insertionPoint = document.createElement("content");
shadowDiv.appendChild(insertionPoint);
var shadowRoot = document.getElementById('outer').createShadowRoot();
shadowRoot.appendChild(shadowDiv);
}
</script>
</head>
<body onload="tweak()">
<div id="outer">
<span style="background-color: purple">Hello</span>
<span style="background-color: orange">World</span>
</div>
</body>
</html>

View file

@ -9,7 +9,7 @@
shadowDiv.style.height = "100px";
shadowDiv.style.backgroundColor = "green";
var shadowRoot = document.getElementById('outer').createShadowRoot();
var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.appendChild(shadowDiv);
}
</script>

View file

@ -6,7 +6,7 @@
var shadowDiv = document.createElement("div");
shadowDiv.style.border = "10px solid green";
var shadowRoot = document.getElementById('outer').createShadowRoot();
var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.appendChild(shadowDiv);
var orangeDiv = document.createElement("div");

View file

@ -6,7 +6,7 @@
var shadowDiv = document.createElement("div");
shadowDiv.style.border = "10px solid green";
var shadowRoot = document.getElementById('outer').createShadowRoot();
var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.appendChild(shadowDiv);
var orangeDiv = document.createElement("div");

View file

@ -6,7 +6,7 @@
var shadowDiv = document.createElement("div");
shadowDiv.style.border = "10px solid green";
var shadowRoot = document.getElementById('outer').createShadowRoot();
var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.appendChild(shadowDiv);
var orangeDiv = document.createElement("div");

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<style>
body { color: green; }
</style>
</head>
<body>
This text should be green
</body>
</html>

View file

@ -0,0 +1,6 @@
<!DOCTYPE HTML>
<html>
<body>
<slot style="color: green">This text should be green</slot>
</body>
</html>

View file

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
div {
width: 100px;
height: 100px;
background: green;
}
</style>
</head>
<body>
<p>There should be a green box below.</p>
<div></div>
</body>
</html>

View file

@ -0,0 +1,7 @@
<!DOCTYPE HTML>
<html>
<body>
<p>There should be a green box below.</p>
<slot style="display: block; width: 100px; height: 100px; background: green;"></slot>
</body>
</html>

View file

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<body>
<div>
<div style="color: green">This text should be green</div>
</div>
</body>
</html>

View file

@ -0,0 +1,18 @@
<!DOCTYPE HTML>
<html>
<head>
<script>
function tweak() {
var slot = document.createElement("slot");
slot.style.color = "green";
var shadowRoot =
document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.appendChild(slot);
}
</script>
</head>
<body onload="tweak()">
<div id="outer">This text should be green</div>
</body>
</html>

View file

@ -0,0 +1,20 @@
<!DOCTYPE HTML>
<html>
<head>
<script>
function tweak() {
var slot = document.createElement("slot");
// The border shouldn't be visible, due to display: contents.
slot.style.border = "1px solid red";
slot.style.color = "green";
var shadowRoot =
document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.appendChild(slot);
}
</script>
</head>
<body onload="tweak()">
<div id="outer">This text should be green</div>
</body>
</html>

View file

@ -5,9 +5,11 @@
<body>
<div id="host"></div>
<script>
var host = document.getElementById("host");
var root = host.createShadowRoot();
root.innerHTML = 'a <content></content> c';
var host, root;
host = document.getElementById("host");
root = host.attachShadow({mode: 'open'});
root.innerHTML = 'a <slot></slot> c';
function tweak() {
var span = document.createElement("span");
@ -19,7 +21,7 @@
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", tweak, false);
window.addEventListener("MozReftestInvalidate", tweak);
</script>
</body>
</html>

View file

@ -5,8 +5,10 @@
<body>
<div id="host"></div>
<script>
var host = document.getElementById("host");
var root = host.createShadowRoot();
var host, root;
host = document.getElementById("host");
root = host.attachShadow({mode: 'open'});
root.innerHTML = "<span>a</span>";
function tweak() {
@ -20,7 +22,7 @@
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", tweak, false);
window.addEventListener("MozReftestInvalidate", tweak);
</script>
</body>
</html>

View file

@ -1,10 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<div>
<div style="background-color: green"><span>Hello</span><span> </span><span>World</span></div>
</div>
</body>
</html>

View file

@ -1,23 +0,0 @@
<!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>

View file

@ -8,13 +8,13 @@
shadowDiv.style.border = "10px solid green";
// Insertion point will match nothing and use fallback content.
var insertionPoint = document.createElement("content");
shadowDiv.appendChild(insertionPoint);
var slot = document.createElement("slot");
shadowDiv.appendChild(slot);
// Append three nodes as children to use as fallback content.
insertionPoint.innerHTML = '<span style="background-color: orange">Hello</span> <span style="background-color: green">World</span>';
slot.innerHTML = '<span style="background-color: orange">Hello</span> <span style="background-color: green">World</span>';
var shadowRoot = document.getElementById('outer').createShadowRoot();
var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.appendChild(shadowDiv);
}
</script>

View file

@ -5,19 +5,21 @@
<body>
<div id="host"></div>
<script>
var host = document.getElementById("host");
var root = host.createShadowRoot();
var host, root;
host = document.getElementById("host");
root = host.attachShadow({mode: 'open'});
root.innerHTML = '<style>input ~ div { background: red; transition: background 100ms; } input:checked ~ div { background: green; }</style><input id="one" type="checkbox"><div style="height: 50px; width: 50px;"></div>';
function tweak() {
var el = root.getElementById("one");
el.checked = true;
el.nextSibling.addEventListener("transitionend", function() {
document.documentElement.removeAttribute("class");
}, false);
var el = root.getElementById("one");
el.checked = true;
el.nextSibling.addEventListener("transitionend", function() {
setTimeout(()=>{document.documentElement.removeAttribute("class")}, 1000); // wait for the checkbox SVG image to load on Android
});
}
window.addEventListener("MozReftestInvalidate", tweak, false);
window.addEventListener("MozReftestInvalidate", tweak);
</script>
</body>
</html>

View file

@ -7,19 +7,20 @@
var outerShadow = document.createElement("div");
outerShadow.style.border = "10px solid green";
var outerInsertionPoint = document.createElement("content");
var outerInsertionPoint = document.createElement("slot");
outerShadow.appendChild(outerInsertionPoint);
// div with style "border: 10px solid orange"
var innerShadow = document.createElement("div");
innerShadow.style.border = "10px solid orange";
var innerInsertionPoint = document.createElement("content");
innerShadow.appendChild(innerInsertionPoint);
var slot = document.createElement("slot");
innerShadow.appendChild(slot);
outerShadow.createShadowRoot().appendChild(innerShadow);
outerShadow.attachShadow({mode: 'open'}).appendChild(innerShadow);
var shadowRoot = document.getElementById('outer').createShadowRoot();
var shadowRoot =
document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.appendChild(outerShadow);
}
</script>

View file

@ -0,0 +1,16 @@
<!doctype html>
<template id="tmpl">
<div style="display: table">
Some text
<span style="display: table-cell">something</span>
More text
</div>
</template>
<div id="host"></div>
<script>
let shadowRoot = document.getElementById("host").attachShadow({mode: 'open'});
let tmpl = document.getElementById("tmpl");
shadowRoot.appendChild(document.importNode(tmpl.content, true));
document.body.offsetTop;
shadowRoot.firstElementChild.querySelector("span").remove();
</script>

View file

@ -0,0 +1,15 @@
<!doctype html>
<template id="tmpl">
<div style="display: block">
Some text
More text
</div>
</template>
<div id="host"></div>
<script>
let shadowRoot = document.getElementById("host").attachShadow({mode: 'open'});
let tmpl = document.getElementById("tmpl");
shadowRoot.appendChild(document.importNode(tmpl.content, true));
document.body.offsetTop;
shadowRoot.firstElementChild.style.display = "table";
</script>

View file

@ -3,8 +3,6 @@ pref(dom.webcomponents.enabled,true) == basic-shadow-1.html basic-shadow-1-ref.h
pref(dom.webcomponents.enabled,true) == basic-shadow-2.html basic-shadow-2-ref.html
pref(dom.webcomponents.enabled,true) == basic-shadow-3.html basic-shadow-3-ref.html
pref(dom.webcomponents.enabled,true) == basic-shadow-4.html basic-shadow-4-ref.html
#bug 1421542 pref(dom.webcomponents.enabled,true) == basic-insertion-point-1.html basic-insertion-point-1-ref.html
#bug 1421542 pref(dom.webcomponents.enabled,true) == basic-insertion-point-2.html basic-insertion-point-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
#bug 1421542 pref(dom.webcomponents.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
@ -13,3 +11,9 @@ pref(dom.webcomponents.enabled,true) == input-transition-1.html input-transition
#bug 1421542 pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html
pref(dom.webcomponents.enabled,true) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
pref(dom.webcomponents.enabled,true) == basic-slot-1.html basic-slot-1-ref.html
pref(dom.webcomponents.enabled,true) == basic-slot-2.html basic-slot-2-ref.html
pref(dom.webcomponents.enabled,true) == basic-slot-3.html basic-slot-3-ref.html
pref(dom.webcomponents.enabled,true) == basic-slot-4.html basic-slot-3-ref.html
pref(dom.webcomponents.enabled,true) == basic-slot-5.html basic-slot-5-ref.html
pref(dom.webcomponents.enabled,true) == basic-slot-6.html basic-slot-6-ref.html

View file

@ -6,7 +6,7 @@
<div id="container"><div id="host"></div></div>
<script>
var host = document.getElementById("host");
var root = host.createShadowRoot();
var root = host.attachShadow({mode: 'open'});
root.innerHTML = 'inside shadow DOM';
var container = document.getElementById("container");

View file

@ -8,15 +8,15 @@
shadowDiv.style.border = "10px solid green";
// Insertion point will match nothing and use fallback content.
var insertionPoint = document.createElement("content");
shadowDiv.appendChild(insertionPoint);
var slot = document.createElement("slot");
shadowDiv.appendChild(slot);
var shadowRoot = document.getElementById('outer').createShadowRoot();
var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.appendChild(shadowDiv);
// Remove the insertion point from the ShadowRoot, "Hello" should no
// longer be rendered.
shadowDiv.removeChild(insertionPoint);
shadowDiv.removeChild(slot);
}
</script>
</head>

View file

@ -0,0 +1,22 @@
<!doctype html>
<style>
div { display: contents }
</style>
<div></div>
<div></div>
<script>
let divs = document.querySelectorAll('div');
divs[0].attachShadow({mode: 'open'}).innerHTML = `
<style>
* { color: green; }
</style>
<span>Should be green</span>
`;
divs[1].attachShadow({mode: 'open'}).innerHTML = `
<style>
* { color: initial; }
[foo] { }
</style>
<span>Should not be green</span>
`;
</script>

View file

@ -0,0 +1,14 @@
<!doctype html>
<div id="host"></div>
<script>
let root = host.attachShadow({mode: 'open'});
root.innerHTML = `
<style>
#test {
color: green;
}
</style>
<span id="test">Should be green</span>
<span id="test2">Should not be green</span>
`;
</script>

View file

@ -5,17 +5,18 @@
<body>
<div id="outer"><span id="distnode">text</span></div>
<script>
var shadowRoot = document.getElementById('outer').createShadowRoot();
shadowRoot.innerHTML = '<div><content></content></div>';
function tweak() {
var distNode = document.getElementById("distnode");
distNode.textContent = "Hello World";
var shadowRoot = document.getElementById('outer').attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<div><slot></slot></div>';
document.documentElement.removeAttribute("class");
}
function tweak() {
var distNode = document.getElementById("distnode");
distNode.textContent = "Hello World";
window.addEventListener("MozReftestInvalidate", tweak);
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", tweak);
</script>
</body>
</html>

View file

@ -471,3 +471,9 @@ div:-moz-native-anonymous.moz-custom-content-container {
width: 100%;
height: 100%;
}
/* Shadow DOM v1
* https://drafts.csswg.org/css-scoping/#slots-in-shadow-tree */
slot {
display: contents;
}

View file

@ -1,6 +1,6 @@
[interfaces.html]
type: testharness
prefs: [dom.forms.inputmode:true, dom.dialog_element.enabled:true]
prefs: [dom.forms.inputmode:true, dom.dialog_element.enabled:true, dom.webcomponents.enabled:true]
[Document interface: attribute domain]
expected: FAIL
@ -2524,21 +2524,6 @@
[HTMLSlotElement interface: operation assignedNodes(AssignedNodesOptions)]
expected: FAIL
[HTMLSlotElement must be primary interface of document.createElement("slot")]
expected: FAIL
[Stringification of document.createElement("slot")]
expected: FAIL
[HTMLSlotElement interface: document.createElement("slot") must inherit property "name" with the proper type (0)]
expected: FAIL
[HTMLSlotElement interface: document.createElement("slot") must inherit property "assignedNodes" with the proper type (1)]
expected: FAIL
[HTMLSlotElement interface: calling assignedNodes(AssignedNodesOptions) on document.createElement("slot") with too few arguments must throw TypeError]
expected: FAIL
[HTMLInputElement interface: createInput("text") must inherit property "dirName" with the proper type (6)]
expected: FAIL

View file

@ -1,6 +1,6 @@
[reflection-misc.html]
type: testharness
prefs: [dom.dialog_element.enabled: true]
prefs: [dom.dialog_element.enabled: true, dom.webcomponents.enabled:true]
[html.tabIndex: setAttribute() to object "3" followed by getAttribute()]
expected: FAIL

View file

@ -1,44 +0,0 @@
[HTMLSlotElement-interface.html]
type: testharness
[HTMLSlotElement must be defined on window]
expected: FAIL
["name" attribute on HTMLSlotElement must reflect "name" attribute]
expected: FAIL
[assignedNodes() on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree]
expected: FAIL
[assignedNodes({"flattened":false}) on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree]
expected: FAIL
[assignedNodes({"flattened":true}) on a HTMLSlotElement must return an empty array when the slot element is not in a tree or in a document tree]
expected: FAIL
[assignedNodes() must return the list of assigned nodes when none of the assigned nodes themselves are slots]
expected: FAIL
[assignedNodes({"flattened":false}) must return the list of assigned nodes when none of the assigned nodes themselves are slots]
expected: FAIL
[assignedNodes({"flattened":true}) must return the list of assigned nodes when none of the assigned nodes themselves are slots]
expected: FAIL
[assignedNodes() must update when slot and name attributes are modified]
expected: FAIL
[assignedNodes({"flattened":false}) must update when slot and name attributes are modified]
expected: FAIL
[assignedNodes({"flattened":true}) must update when slot and name attributes are modified]
expected: FAIL
[assignedNodes must update when a default slot is introduced dynamically by a slot rename]
expected: FAIL
[assignedNodes must update when slot elements are inserted or removed]
expected: FAIL
[assignedNodes({flatten: true}) must return the distributed nodes, and assignedNodes() and assignedNodes({flatten: false}) must returned the assigned nodes]
expected: FAIL

View file

@ -1,12 +0,0 @@
[Slotable-interface.html]
type: testharness
prefs: [dom.webcomponents.enabled:true]
[assignedSlot must return null when the node does not have an assigned node]
expected: FAIL
[assignedSlot must return the assigned slot]
expected: FAIL
[assignedSlot must return null when the assigned slot element is inside a closed shadow tree]
expected: FAIL

View file

@ -1,38 +0,0 @@
[event-inside-slotted-node.html]
type: testharness
[Firing an event inside a grand child of a detached open mode shadow host]
expected: FAIL
[Firing an event inside a grand child of a detached closed mode shadow host]
expected: FAIL
[Firing an event inside a grand child of an in-document open mode shadow host]
expected: FAIL
[Firing an event inside a grand child of an in-document closed mode shadow host]
expected: FAIL
[Firing an event on a node with two ancestors with a detached open and open shadow trees with an inner open shadow tree]
expected: FAIL
[Firing an event on a node with two ancestors with a detached open and open shadow trees with an inner closed shadow tree]
expected: FAIL
[Firing an event on a node with two ancestors with a detached open and closed shadow trees with an inner open shadow tree]
expected: FAIL
[Firing an event on a node with two ancestors with a detached open and closed shadow trees with an inner closed shadow tree]
expected: FAIL
[Firing an event on a node with two ancestors with a detached closed and open shadow trees with an inner open shadow tree]
expected: FAIL
[Firing an event on a node with two ancestors with a detached closed and open shadow trees with an inner closed shadow tree]
expected: FAIL
[Firing an event on a node with two ancestors with a detached closed and closed shadow trees with an inner open shadow tree]
expected: FAIL
[Firing an event on a node with two ancestors with a detached closed and closed shadow trees with an inner closed shadow tree]
expected: FAIL

View file

@ -1,32 +0,0 @@
[slots-fallback.html]
type: testharness
[Slots fallback: Basic.]
expected: FAIL
[Slots fallback: Slots in Slots.]
expected: FAIL
[Slots fallback: Fallback contents should not be used if a node is assigned.]
expected: FAIL
[Slots fallback: Slots in Slots: Assinged nodes should be used as fallback contents of another slot]
expected: FAIL
[Slots fallback: Complex case.]
expected: FAIL
[Slots fallback: Mutation. Append fallback contents.]
expected: FAIL
[Slots fallback: Mutation. Remove fallback contents.]
expected: FAIL
[Slots fallback: Mutation. Assign a node to a slot so that fallback contens are no longer used.]
expected: FAIL
[Slots fallback: Mutation. Remove an assigned node from a slot so that fallback contens will be used.]
expected: FAIL
[Slots fallback: Mutation. Remove a slot which is a fallback content of another slot.]
expected: FAIL

View file

@ -1,71 +0,0 @@
[slots.html]
type: testharness
[Slots: Basic.]
expected: FAIL
[Slots: Slots in closed.]
expected: FAIL
[Slots: Slots not in a shadow tree.]
expected: FAIL
[Slots: Distributed nooes for Slots not in a shadow tree.]
expected: FAIL
[Slots: Name matching]
expected: FAIL
[Slots: No direct host child.]
expected: FAIL
[Slots: Default Slot.]
expected: FAIL
[Slots: Slot in Slot does not matter in assignment.]
expected: FAIL
[Slots: Slot is assigned to another slot]
expected: FAIL
[Slots: Open > Closed.]
expected: FAIL
[Slots: Closed > Closed.]
expected: FAIL
[Slots: Closed > Open.]
expected: FAIL
[Slots: Complex case: Basi line.]
expected: FAIL
[Slots: Mutation: appendChild.]
expected: FAIL
[Slots: Mutation: Change slot= attribute 1.]
expected: FAIL
[Slots: Mutation: Change slot= attribute 2.]
expected: FAIL
[Slots: Mutation: Change slot= attribute 3.]
expected: FAIL
[Slots: Mutation: Remove a child.]
expected: FAIL
[Slots: Mutation: Add a slot: after.]
expected: FAIL
[Slots: Mutation: Add a slot: before.]
expected: FAIL
[Slots: Mutation: Remove a slot.]
expected: FAIL
[Slots: Mutation: Change slot name= attribute.]
expected: FAIL
[Slots: Mutation: Change slot slot= attribute.]
expected: FAIL