зеркало из https://github.com/mozilla/gecko-dev.git
Bug 999271 - Implement web components getDestinationInsertionPoints() extension to Element interface. r=mrbkap
This commit is contained in:
Родитель
a7a3cbd2ad
Коммит
ccc506dc38
|
@ -114,11 +114,12 @@ class Link;
|
||||||
class UndoManager;
|
class UndoManager;
|
||||||
class DOMRect;
|
class DOMRect;
|
||||||
class DOMRectList;
|
class DOMRectList;
|
||||||
|
class DestinationInsertionPointList;
|
||||||
|
|
||||||
// IID for the dom::Element interface
|
// IID for the dom::Element interface
|
||||||
#define NS_ELEMENT_IID \
|
#define NS_ELEMENT_IID \
|
||||||
{ 0xf7c18f0f, 0xa8fd, 0x4a95, \
|
{ 0xd123f791, 0x124a, 0x43f3, \
|
||||||
{ 0x91, 0x72, 0xd3, 0xa7, 0x4a, 0xb8, 0xc4, 0xbe } }
|
{ 0x84, 0xe3, 0x55, 0x81, 0x0b, 0x6c, 0xf3, 0x08 } }
|
||||||
|
|
||||||
class Element : public FragmentOrElement
|
class Element : public FragmentOrElement
|
||||||
{
|
{
|
||||||
|
@ -693,6 +694,7 @@ public:
|
||||||
already_AddRefed<DOMRect> GetBoundingClientRect();
|
already_AddRefed<DOMRect> GetBoundingClientRect();
|
||||||
|
|
||||||
already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
|
already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
|
||||||
|
already_AddRefed<DestinationInsertionPointList> GetDestinationInsertionPoints();
|
||||||
|
|
||||||
void ScrollIntoView()
|
void ScrollIntoView()
|
||||||
{
|
{
|
||||||
|
@ -1185,6 +1187,29 @@ private:
|
||||||
EventStates mState;
|
EventStates mState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DestinationInsertionPointList : public nsINodeList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DestinationInsertionPointList(Element* aElement);
|
||||||
|
virtual ~DestinationInsertionPointList();
|
||||||
|
|
||||||
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
|
NS_DECL_CYCLE_COLLECTION_CLASS(DestinationInsertionPointList)
|
||||||
|
|
||||||
|
// nsIDOMNodeList
|
||||||
|
NS_DECL_NSIDOMNODELIST
|
||||||
|
|
||||||
|
// nsINodeList
|
||||||
|
virtual nsIContent* Item(uint32_t aIndex);
|
||||||
|
virtual int32_t IndexOf(nsIContent* aContent);
|
||||||
|
virtual nsINode* GetParentObject() { return mParent; }
|
||||||
|
virtual uint32_t Length() const;
|
||||||
|
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||||
|
protected:
|
||||||
|
nsRefPtr<Element> mParent;
|
||||||
|
nsCOMArray<nsIContent> mDestinationPoints;
|
||||||
|
};
|
||||||
|
|
||||||
NS_DEFINE_STATIC_IID_ACCESSOR(Element, NS_ELEMENT_IID)
|
NS_DEFINE_STATIC_IID_ACCESSOR(Element, NS_ELEMENT_IID)
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
|
|
|
@ -209,6 +209,8 @@ public:
|
||||||
nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
|
nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
|
||||||
virtual ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
|
virtual ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
|
||||||
virtual ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
|
virtual ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
|
||||||
|
virtual nsTArray<nsIContent*> &DestInsertionPoints() MOZ_OVERRIDE;
|
||||||
|
virtual nsTArray<nsIContent*> *GetExistingDestInsertionPoints() const MOZ_OVERRIDE;
|
||||||
virtual void SetShadowRoot(ShadowRoot* aBinding) MOZ_OVERRIDE;
|
virtual void SetShadowRoot(ShadowRoot* aBinding) MOZ_OVERRIDE;
|
||||||
virtual nsIContent *GetXBLInsertionParent() const MOZ_OVERRIDE;
|
virtual nsIContent *GetXBLInsertionParent() const MOZ_OVERRIDE;
|
||||||
virtual void SetXBLInsertionParent(nsIContent* aContent) MOZ_OVERRIDE;
|
virtual void SetXBLInsertionParent(nsIContent* aContent) MOZ_OVERRIDE;
|
||||||
|
@ -375,6 +377,12 @@ public:
|
||||||
*/
|
*/
|
||||||
nsRefPtr<ShadowRoot> mContainingShadow;
|
nsRefPtr<ShadowRoot> mContainingShadow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of web component insertion points to which this element
|
||||||
|
* is distributed.
|
||||||
|
*/
|
||||||
|
nsTArray<nsIContent*> mDestInsertionPoints;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XBL binding installed on the element.
|
* XBL binding installed on the element.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -39,8 +39,8 @@ enum nsLinkState {
|
||||||
|
|
||||||
// IID for the nsIContent interface
|
// IID for the nsIContent interface
|
||||||
#define NS_ICONTENT_IID \
|
#define NS_ICONTENT_IID \
|
||||||
{ 0x1329e5b7, 0x4bcd, 0x450c, \
|
{ 0xc534a378, 0x7b5f, 0x43a4, \
|
||||||
{ 0xa2, 0x3a, 0x98, 0xc5, 0x85, 0xcd, 0x73, 0xf9 } }
|
{ 0xaf, 0x65, 0x5f, 0xfe, 0xea, 0xd6, 0x00, 0xfb } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A node of content in a document's content model. This interface
|
* A node of content in a document's content model. This interface
|
||||||
|
@ -668,6 +668,20 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual mozilla::dom::ShadowRoot *GetContainingShadow() const = 0;
|
virtual mozilla::dom::ShadowRoot *GetContainingShadow() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of destination insertion points where this content
|
||||||
|
* is distributed by web component distribution algorithms.
|
||||||
|
* The array is created if it does not already exist.
|
||||||
|
*/
|
||||||
|
virtual nsTArray<nsIContent*> &DestInsertionPoints() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as DestInsertionPoints except that this method will return
|
||||||
|
* null if the array of destination insertion points does not already
|
||||||
|
* exist.
|
||||||
|
*/
|
||||||
|
virtual nsTArray<nsIContent*> *GetExistingDestInsertionPoints() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the insertion parent element of the XBL binding.
|
* Gets the insertion parent element of the XBL binding.
|
||||||
* The insertion parent is our one true parent in the transformed DOM.
|
* The insertion parent is our one true parent in the transformed DOM.
|
||||||
|
|
|
@ -125,6 +125,7 @@
|
||||||
|
|
||||||
#include "mozilla/CORSMode.h"
|
#include "mozilla/CORSMode.h"
|
||||||
#include "mozilla/dom/ShadowRoot.h"
|
#include "mozilla/dom/ShadowRoot.h"
|
||||||
|
#include "mozilla/dom/NodeListBinding.h"
|
||||||
|
|
||||||
#include "nsStyledElement.h"
|
#include "nsStyledElement.h"
|
||||||
#include "nsXBLService.h"
|
#include "nsXBLService.h"
|
||||||
|
@ -826,6 +827,83 @@ Element::CreateShadowRoot(ErrorResult& aError)
|
||||||
return shadowRoot.forget();
|
return shadowRoot.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION(DestinationInsertionPointList, mParent, mDestinationPoints)
|
||||||
|
|
||||||
|
NS_INTERFACE_TABLE_HEAD(DestinationInsertionPointList)
|
||||||
|
NS_INTERFACE_TABLE(DestinationInsertionPointList, nsINodeList)
|
||||||
|
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(DestinationInsertionPointList)
|
||||||
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(DestinationInsertionPointList)
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(DestinationInsertionPointList)
|
||||||
|
|
||||||
|
DestinationInsertionPointList::DestinationInsertionPointList(Element* aElement)
|
||||||
|
: mParent(aElement)
|
||||||
|
{
|
||||||
|
SetIsDOMBinding();
|
||||||
|
|
||||||
|
nsTArray<nsIContent*>* destPoints = aElement->GetExistingDestInsertionPoints();
|
||||||
|
if (destPoints) {
|
||||||
|
for (uint32_t i = 0; i < destPoints->Length(); i++) {
|
||||||
|
mDestinationPoints.AppendElement(destPoints->ElementAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DestinationInsertionPointList::~DestinationInsertionPointList()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIContent*
|
||||||
|
DestinationInsertionPointList::Item(uint32_t aIndex)
|
||||||
|
{
|
||||||
|
return mDestinationPoints.SafeElementAt(aIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
DestinationInsertionPointList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
|
||||||
|
{
|
||||||
|
nsIContent* item = Item(aIndex);
|
||||||
|
if (!item) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CallQueryInterface(item, aReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
DestinationInsertionPointList::Length() const
|
||||||
|
{
|
||||||
|
return mDestinationPoints.Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
DestinationInsertionPointList::GetLength(uint32_t* aLength)
|
||||||
|
{
|
||||||
|
*aLength = Length();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
DestinationInsertionPointList::IndexOf(nsIContent* aContent)
|
||||||
|
{
|
||||||
|
return mDestinationPoints.IndexOf(aContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
DestinationInsertionPointList::WrapObject(JSContext* aCx)
|
||||||
|
{
|
||||||
|
return NodeListBinding::Wrap(aCx, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<DestinationInsertionPointList>
|
||||||
|
Element::GetDestinationInsertionPoints()
|
||||||
|
{
|
||||||
|
nsRefPtr<DestinationInsertionPointList> list =
|
||||||
|
new DestinationInsertionPointList(this);
|
||||||
|
return list.forget();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Element::GetAttribute(const nsAString& aName, DOMString& aReturn)
|
Element::GetAttribute(const nsAString& aName, DOMString& aReturn)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1011,6 +1011,23 @@ FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
|
||||||
slots->mShadowRoot = aShadowRoot;
|
slots->mShadowRoot = aShadowRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsTArray<nsIContent*>&
|
||||||
|
FragmentOrElement::DestInsertionPoints()
|
||||||
|
{
|
||||||
|
nsDOMSlots *slots = DOMSlots();
|
||||||
|
return slots->mDestInsertionPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsTArray<nsIContent*>*
|
||||||
|
FragmentOrElement::GetExistingDestInsertionPoints() const
|
||||||
|
{
|
||||||
|
nsDOMSlots *slots = GetExistingDOMSlots();
|
||||||
|
if (slots) {
|
||||||
|
return &slots->mDestInsertionPoints;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
|
FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
|
||||||
{
|
{
|
||||||
|
|
|
@ -256,6 +256,22 @@ ShadowRoot::SetYoungerShadow(ShadowRoot* aYoungerShadow)
|
||||||
ChangePoolHost(mYoungerShadow->GetShadowElement());
|
ChangePoolHost(mYoungerShadow->GetShadowElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ShadowRoot::RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
|
||||||
|
nsTArray<nsIContent*>& aDestInsertionPoints)
|
||||||
|
{
|
||||||
|
// Remove the insertion point from the destination insertion points.
|
||||||
|
// Also remove all succeeding insertion points because it is no longer
|
||||||
|
// possible for the content to be distributed into deeper node trees.
|
||||||
|
int32_t index = aDestInsertionPoints.IndexOf(aInsertionPoint);
|
||||||
|
|
||||||
|
// It's possible that we already removed the insertion point while processing
|
||||||
|
// other insertion point removals.
|
||||||
|
if (index >= 0) {
|
||||||
|
aDestInsertionPoints.SetLength(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
||||||
{
|
{
|
||||||
|
@ -295,7 +311,7 @@ ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
||||||
// is found or when the current matched node is reached.
|
// is found or when the current matched node is reached.
|
||||||
if (childIterator.Seek(aContent, matchedNodes[i])) {
|
if (childIterator.Seek(aContent, matchedNodes[i])) {
|
||||||
// aContent was found before the current matched node.
|
// aContent was found before the current matched node.
|
||||||
matchedNodes.InsertElementAt(i, aContent);
|
insertionPoint->InsertMatchedNode(i, aContent);
|
||||||
isIndexFound = true;
|
isIndexFound = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -306,7 +322,7 @@ ShadowRoot::DistributeSingleNode(nsIContent* aContent)
|
||||||
// thus it must be at the end.
|
// thus it must be at the end.
|
||||||
MOZ_ASSERT(childIterator.Seek(aContent),
|
MOZ_ASSERT(childIterator.Seek(aContent),
|
||||||
"Trying to match a node that is not a candidate to be matched");
|
"Trying to match a node that is not a candidate to be matched");
|
||||||
matchedNodes.AppendElement(aContent);
|
insertionPoint->AppendMatchedNode(aContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the case where the parent of the insertion point is a ShadowRoot
|
// Handle the case where the parent of the insertion point is a ShadowRoot
|
||||||
|
@ -354,7 +370,7 @@ ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mInsertionPoints[i]->MatchedNodes().RemoveElement(aContent);
|
mInsertionPoints[i]->RemoveMatchedNode(aContent);
|
||||||
|
|
||||||
// Handle the case where the parent of the insertion point is a ShadowRoot
|
// Handle the case where the parent of the insertion point is a ShadowRoot
|
||||||
// that is projected into the younger ShadowRoot's shadow insertion point.
|
// that is projected into the younger ShadowRoot's shadow insertion point.
|
||||||
|
@ -412,8 +428,7 @@ ShadowRoot::DistributeAllNodes()
|
||||||
// Assign matching nodes from node pool.
|
// Assign matching nodes from node pool.
|
||||||
for (uint32_t j = 0; j < nodePool.Length(); j++) {
|
for (uint32_t j = 0; j < nodePool.Length(); j++) {
|
||||||
if (mInsertionPoints[i]->Match(nodePool[j])) {
|
if (mInsertionPoints[i]->Match(nodePool[j])) {
|
||||||
mInsertionPoints[i]->MatchedNodes().AppendElement(nodePool[j]);
|
mInsertionPoints[i]->AppendMatchedNode(nodePool[j]);
|
||||||
nodePool[j]->SetXBLInsertionParent(mInsertionPoints[i]);
|
|
||||||
nodePool.RemoveElementAt(j--);
|
nodePool.RemoveElementAt(j--);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,7 +440,7 @@ ShadowRoot::DistributeAllNodes()
|
||||||
"mInsertionPoints array is to be a descendant of a"
|
"mInsertionPoints array is to be a descendant of a"
|
||||||
"ShadowRoot, in which case, it should have a parent");
|
"ShadowRoot, in which case, it should have a parent");
|
||||||
|
|
||||||
// If the parent of the insertion point has as ShadowRoot, the nodes distributed
|
// If the parent of the insertion point has a ShadowRoot, the nodes distributed
|
||||||
// to the insertion point must be reprojected to the insertion points of the
|
// to the insertion point must be reprojected to the insertion points of the
|
||||||
// parent's ShadowRoot.
|
// parent's ShadowRoot.
|
||||||
ShadowRoot* parentShadow = insertionParent->GetShadowRoot();
|
ShadowRoot* parentShadow = insertionParent->GetShadowRoot();
|
||||||
|
@ -562,6 +577,11 @@ ShadowRoot::IsPooledNode(nsIContent* aContent, nsIContent* aContainer,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aContainer == aHost) {
|
||||||
|
// Any other child nodes of the host will end up in the pool.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (aContainer->IsHTML(nsGkAtoms::content)) {
|
if (aContainer->IsHTML(nsGkAtoms::content)) {
|
||||||
// Fallback content will end up in pool if its parent is a child of the host.
|
// Fallback content will end up in pool if its parent is a child of the host.
|
||||||
HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
|
HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
|
||||||
|
@ -569,11 +589,6 @@ ShadowRoot::IsPooledNode(nsIContent* aContent, nsIContent* aContainer,
|
||||||
aContainer->GetParentNode() == aHost;
|
aContainer->GetParentNode() == aHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aContainer == aHost) {
|
|
||||||
// Any other child nodes of the host will end up in the pool.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,9 +624,18 @@ ShadowRoot::ContentAppended(nsIDocument* aDocument,
|
||||||
// may need to be added to an insertion point.
|
// may need to be added to an insertion point.
|
||||||
nsIContent* currentChild = aFirstNewContent;
|
nsIContent* currentChild = aFirstNewContent;
|
||||||
while (currentChild) {
|
while (currentChild) {
|
||||||
|
// Add insertion point to destination insertion points of fallback content.
|
||||||
|
if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
|
||||||
|
HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
|
||||||
|
if (content->MatchedNodes().IsEmpty()) {
|
||||||
|
currentChild->DestInsertionPoints().AppendElement(aContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (IsPooledNode(currentChild, aContainer, mPoolHost)) {
|
if (IsPooledNode(currentChild, aContainer, mPoolHost)) {
|
||||||
DistributeSingleNode(currentChild);
|
DistributeSingleNode(currentChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentChild = currentChild->GetNextSibling();
|
currentChild = currentChild->GetNextSibling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -631,6 +655,14 @@ ShadowRoot::ContentInserted(nsIDocument* aDocument,
|
||||||
// Watch for new nodes added to the pool because the node
|
// Watch for new nodes added to the pool because the node
|
||||||
// may need to be added to an insertion point.
|
// may need to be added to an insertion point.
|
||||||
if (IsPooledNode(aChild, aContainer, mPoolHost)) {
|
if (IsPooledNode(aChild, aContainer, mPoolHost)) {
|
||||||
|
// Add insertion point to destination insertion points of fallback content.
|
||||||
|
if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
|
||||||
|
HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
|
||||||
|
if (content->MatchedNodes().IsEmpty()) {
|
||||||
|
aChild->DestInsertionPoints().AppendElement(aContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DistributeSingleNode(aChild);
|
DistributeSingleNode(aChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -648,6 +680,15 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear destination insertion points for removed
|
||||||
|
// fallback content.
|
||||||
|
if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
|
||||||
|
HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
|
||||||
|
if (content->MatchedNodes().IsEmpty()) {
|
||||||
|
aChild->DestInsertionPoints().Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Watch for node that is removed from the pool because
|
// Watch for node that is removed from the pool because
|
||||||
// it may need to be removed from an insertion point.
|
// it may need to be removed from an insertion point.
|
||||||
if (IsPooledNode(aChild, aContainer, mPoolHost)) {
|
if (IsPooledNode(aChild, aContainer, mPoolHost)) {
|
||||||
|
|
|
@ -113,6 +113,9 @@ public:
|
||||||
static ShadowRoot* FromNode(nsINode* aNode);
|
static ShadowRoot* FromNode(nsINode* aNode);
|
||||||
static bool IsShadowInsertionPoint(nsIContent* aContent);
|
static bool IsShadowInsertionPoint(nsIContent* aContent);
|
||||||
|
|
||||||
|
static void RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
|
||||||
|
nsTArray<nsIContent*>& aDestInsertionPoints);
|
||||||
|
|
||||||
// WebIDL methods.
|
// WebIDL methods.
|
||||||
Element* GetElementById(const nsAString& aElementId);
|
Element* GetElementById(const nsAString& aElementId);
|
||||||
already_AddRefed<nsContentList>
|
already_AddRefed<nsContentList>
|
||||||
|
|
|
@ -685,6 +685,23 @@ nsGenericDOMDataNode::SetShadowRoot(ShadowRoot* aShadowRoot)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsTArray<nsIContent*>&
|
||||||
|
nsGenericDOMDataNode::DestInsertionPoints()
|
||||||
|
{
|
||||||
|
nsDataSlots *slots = DataSlots();
|
||||||
|
return slots->mDestInsertionPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsTArray<nsIContent*>*
|
||||||
|
nsGenericDOMDataNode::GetExistingDestInsertionPoints() const
|
||||||
|
{
|
||||||
|
nsDataSlots *slots = GetExistingDataSlots();
|
||||||
|
if (slots) {
|
||||||
|
return &slots->mDestInsertionPoints;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
nsXBLBinding *
|
nsXBLBinding *
|
||||||
nsGenericDOMDataNode::GetXBLBinding() const
|
nsGenericDOMDataNode::GetXBLBinding() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -159,6 +159,8 @@ public:
|
||||||
nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
|
nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
|
||||||
virtual mozilla::dom::ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
|
virtual mozilla::dom::ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
|
||||||
virtual mozilla::dom::ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
|
virtual mozilla::dom::ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
|
||||||
|
virtual nsTArray<nsIContent*> &DestInsertionPoints() MOZ_OVERRIDE;
|
||||||
|
virtual nsTArray<nsIContent*> *GetExistingDestInsertionPoints() const MOZ_OVERRIDE;
|
||||||
virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) MOZ_OVERRIDE;
|
virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) MOZ_OVERRIDE;
|
||||||
virtual nsIContent *GetXBLInsertionParent() const MOZ_OVERRIDE;
|
virtual nsIContent *GetXBLInsertionParent() const MOZ_OVERRIDE;
|
||||||
virtual void SetXBLInsertionParent(nsIContent* aContent) MOZ_OVERRIDE;
|
virtual void SetXBLInsertionParent(nsIContent* aContent) MOZ_OVERRIDE;
|
||||||
|
@ -266,6 +268,11 @@ protected:
|
||||||
* @see nsIContent::GetContainingShadow
|
* @see nsIContent::GetContainingShadow
|
||||||
*/
|
*/
|
||||||
nsRefPtr<mozilla::dom::ShadowRoot> mContainingShadow;
|
nsRefPtr<mozilla::dom::ShadowRoot> mContainingShadow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see nsIContent::GetDestInsertionPoints
|
||||||
|
*/
|
||||||
|
nsTArray<nsIContent*> mDestInsertionPoints;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Override from nsINode
|
// Override from nsINode
|
||||||
|
|
|
@ -92,9 +92,8 @@ HTMLContentElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||||
if (containingShadow) {
|
if (containingShadow) {
|
||||||
containingShadow->RemoveInsertionPoint(this);
|
containingShadow->RemoveInsertionPoint(this);
|
||||||
|
|
||||||
// Remove all the assigned nodes now that the
|
// Remove all the matched nodes now that the
|
||||||
// insertion point now that the insertion point is
|
// insertion point is no longer an insertion point.
|
||||||
// no longer a descendant of a ShadowRoot.
|
|
||||||
ClearMatchedNodes();
|
ClearMatchedNodes();
|
||||||
containingShadow->SetInsertionPointChanged();
|
containingShadow->SetInsertionPointChanged();
|
||||||
}
|
}
|
||||||
|
@ -105,13 +104,71 @@ HTMLContentElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HTMLContentElement::AppendMatchedNode(nsIContent* aContent)
|
||||||
|
{
|
||||||
|
mMatchedNodes.AppendElement(aContent);
|
||||||
|
nsTArray<nsIContent*>& destInsertionPoint = aContent->DestInsertionPoints();
|
||||||
|
destInsertionPoint.AppendElement(this);
|
||||||
|
|
||||||
|
if (mMatchedNodes.Length() == 1) {
|
||||||
|
// Fallback content gets dropped so we need to updated fallback
|
||||||
|
// content distribution.
|
||||||
|
UpdateFallbackDistribution();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HTMLContentElement::UpdateFallbackDistribution()
|
||||||
|
{
|
||||||
|
for (nsIContent* child = nsINode::GetFirstChild();
|
||||||
|
child;
|
||||||
|
child = child->GetNextSibling()) {
|
||||||
|
nsTArray<nsIContent*>& destInsertionPoint = child->DestInsertionPoints();
|
||||||
|
destInsertionPoint.Clear();
|
||||||
|
if (mMatchedNodes.IsEmpty()) {
|
||||||
|
destInsertionPoint.AppendElement(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HTMLContentElement::RemoveMatchedNode(nsIContent* aContent)
|
||||||
|
{
|
||||||
|
mMatchedNodes.RemoveElement(aContent);
|
||||||
|
ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints());
|
||||||
|
|
||||||
|
if (mMatchedNodes.IsEmpty()) {
|
||||||
|
// Fallback content is activated so we need to update fallback
|
||||||
|
// content distribution.
|
||||||
|
UpdateFallbackDistribution();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HTMLContentElement::InsertMatchedNode(uint32_t aIndex, nsIContent* aContent)
|
||||||
|
{
|
||||||
|
mMatchedNodes.InsertElementAt(aIndex, aContent);
|
||||||
|
nsTArray<nsIContent*>& destInsertionPoint = aContent->DestInsertionPoints();
|
||||||
|
destInsertionPoint.AppendElement(this);
|
||||||
|
|
||||||
|
if (mMatchedNodes.Length() == 1) {
|
||||||
|
// Fallback content gets dropped so we need to updated fallback
|
||||||
|
// content distribution.
|
||||||
|
UpdateFallbackDistribution();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
HTMLContentElement::ClearMatchedNodes()
|
HTMLContentElement::ClearMatchedNodes()
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < mMatchedNodes.Length(); i++) {
|
for (uint32_t i = 0; i < mMatchedNodes.Length(); i++) {
|
||||||
mMatchedNodes[i]->SetXBLInsertionParent(nullptr);
|
ShadowRoot::RemoveDestInsertionPoint(this, mMatchedNodes[i]->DestInsertionPoints());
|
||||||
}
|
}
|
||||||
|
|
||||||
mMatchedNodes.Clear();
|
mMatchedNodes.Clear();
|
||||||
|
|
||||||
|
UpdateFallbackDistribution();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
|
@ -46,6 +46,9 @@ public:
|
||||||
bool Match(nsIContent* aContent);
|
bool Match(nsIContent* aContent);
|
||||||
bool IsInsertionPoint() const { return mIsInsertionPoint; }
|
bool IsInsertionPoint() const { return mIsInsertionPoint; }
|
||||||
nsCOMArray<nsIContent>& MatchedNodes() { return mMatchedNodes; }
|
nsCOMArray<nsIContent>& MatchedNodes() { return mMatchedNodes; }
|
||||||
|
void AppendMatchedNode(nsIContent* aContent);
|
||||||
|
void RemoveMatchedNode(nsIContent* aContent);
|
||||||
|
void InsertMatchedNode(uint32_t aIndex, nsIContent* aContent);
|
||||||
void ClearMatchedNodes();
|
void ClearMatchedNodes();
|
||||||
|
|
||||||
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||||
|
@ -69,6 +72,15 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual JSObject* WrapNode(JSContext *aCx) MOZ_OVERRIDE;
|
virtual JSObject* WrapNode(JSContext *aCx) MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the destination insertion points of the fallback
|
||||||
|
* content of this insertion point. If there are nodes matched
|
||||||
|
* to this insertion point, then destination insertion points
|
||||||
|
* of fallback are cleared, otherwise, this insertion point
|
||||||
|
* is a destination insertion point.
|
||||||
|
*/
|
||||||
|
void UpdateFallbackDistribution();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of nodes from the ShadowRoot host that match the
|
* An array of nodes from the ShadowRoot host that match the
|
||||||
* content insertion selector.
|
* content insertion selector.
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "mozilla/dom/ShadowRoot.h"
|
#include "mozilla/dom/ShadowRoot.h"
|
||||||
|
|
||||||
|
#include "ChildIterator.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "mozilla/dom/HTMLShadowElement.h"
|
#include "mozilla/dom/HTMLShadowElement.h"
|
||||||
#include "mozilla/dom/HTMLShadowElementBinding.h"
|
#include "mozilla/dom/HTMLShadowElementBinding.h"
|
||||||
|
@ -60,10 +61,28 @@ HTMLShadowElement::SetProjectedShadow(ShadowRoot* aProjectedShadow)
|
||||||
{
|
{
|
||||||
if (mProjectedShadow) {
|
if (mProjectedShadow) {
|
||||||
mProjectedShadow->RemoveMutationObserver(this);
|
mProjectedShadow->RemoveMutationObserver(this);
|
||||||
|
|
||||||
|
// The currently projected ShadowRoot is going away,
|
||||||
|
// thus the destination insertion points need to be updated.
|
||||||
|
ExplicitChildIterator childIterator(mProjectedShadow);
|
||||||
|
for (nsIContent* content = childIterator.GetNextChild();
|
||||||
|
content;
|
||||||
|
content = childIterator.GetNextChild()) {
|
||||||
|
ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mProjectedShadow = aProjectedShadow;
|
mProjectedShadow = aProjectedShadow;
|
||||||
if (mProjectedShadow) {
|
if (mProjectedShadow) {
|
||||||
|
// A new ShadowRoot is being projected, thus its explcit
|
||||||
|
// children will be distributed to this shadow insertion point.
|
||||||
|
ExplicitChildIterator childIterator(mProjectedShadow);
|
||||||
|
for (nsIContent* content = childIterator.GetNextChild();
|
||||||
|
content;
|
||||||
|
content = childIterator.GetNextChild()) {
|
||||||
|
content->DestInsertionPoints().AppendElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
// Watch for mutations on the projected shadow because
|
// Watch for mutations on the projected shadow because
|
||||||
// it affects the nodes that are distributed to this shadow
|
// it affects the nodes that are distributed to this shadow
|
||||||
// insertion point.
|
// insertion point.
|
||||||
|
@ -156,6 +175,14 @@ HTMLShadowElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||||
void
|
void
|
||||||
HTMLShadowElement::DistributeSingleNode(nsIContent* aContent)
|
HTMLShadowElement::DistributeSingleNode(nsIContent* aContent)
|
||||||
{
|
{
|
||||||
|
if (aContent->DestInsertionPoints().Contains(this)) {
|
||||||
|
// Node has already been distrbuted this this node,
|
||||||
|
// we are done.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aContent->DestInsertionPoints().AppendElement(this);
|
||||||
|
|
||||||
// Handle the case where the shadow element is a child of
|
// Handle the case where the shadow element is a child of
|
||||||
// a node with a ShadowRoot. The nodes that have been distributed to
|
// a node with a ShadowRoot. The nodes that have been distributed to
|
||||||
// this shadow insertion point will need to be reprojected into the
|
// this shadow insertion point will need to be reprojected into the
|
||||||
|
@ -181,6 +208,8 @@ HTMLShadowElement::DistributeSingleNode(nsIContent* aContent)
|
||||||
void
|
void
|
||||||
HTMLShadowElement::RemoveDistributedNode(nsIContent* aContent)
|
HTMLShadowElement::RemoveDistributedNode(nsIContent* aContent)
|
||||||
{
|
{
|
||||||
|
ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints());
|
||||||
|
|
||||||
// Handle the case where the shadow element is a child of
|
// Handle the case where the shadow element is a child of
|
||||||
// a node with a ShadowRoot. The nodes that have been distributed to
|
// a node with a ShadowRoot. The nodes that have been distributed to
|
||||||
// this shadow insertion point will need to be removed from the
|
// this shadow insertion point will need to be removed from the
|
||||||
|
@ -206,6 +235,21 @@ HTMLShadowElement::RemoveDistributedNode(nsIContent* aContent)
|
||||||
void
|
void
|
||||||
HTMLShadowElement::DistributeAllNodes()
|
HTMLShadowElement::DistributeAllNodes()
|
||||||
{
|
{
|
||||||
|
// All the explicit children of the projected ShadowRoot are distributed
|
||||||
|
// into this shadow insertion point so update the destination insertion
|
||||||
|
// points.
|
||||||
|
ShadowRoot* containingShadow = GetContainingShadow();
|
||||||
|
ShadowRoot* olderShadow = containingShadow->GetOlderShadow();
|
||||||
|
if (olderShadow) {
|
||||||
|
ExplicitChildIterator childIterator(olderShadow);
|
||||||
|
for (nsIContent* content = childIterator.GetNextChild();
|
||||||
|
content;
|
||||||
|
content = childIterator.GetNextChild()) {
|
||||||
|
ShadowRoot::RemoveDestInsertionPoint(this, content->DestInsertionPoints());
|
||||||
|
content->DestInsertionPoints().AppendElement(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the case where the shadow element is a child of
|
// Handle the case where the shadow element is a child of
|
||||||
// a node with a ShadowRoot. The nodes that have been distributed to
|
// a node with a ShadowRoot. The nodes that have been distributed to
|
||||||
// this shadow insertion point will need to be reprojected into the
|
// this shadow insertion point will need to be reprojected into the
|
||||||
|
@ -218,7 +262,6 @@ HTMLShadowElement::DistributeAllNodes()
|
||||||
|
|
||||||
// Handle the case where the parent of this shadow element is a ShadowRoot
|
// 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.
|
// that is projected into a shadow insertion point in the younger ShadowRoot.
|
||||||
ShadowRoot* containingShadow = GetContainingShadow();
|
|
||||||
ShadowRoot* youngerShadow = containingShadow->GetYoungerShadow();
|
ShadowRoot* youngerShadow = containingShadow->GetYoungerShadow();
|
||||||
if (youngerShadow && GetParent() == containingShadow) {
|
if (youngerShadow && GetParent() == containingShadow) {
|
||||||
HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
|
HTMLShadowElement* youngerShadowElement = youngerShadow->GetShadowElement();
|
||||||
|
@ -269,7 +312,7 @@ HTMLShadowElement::ContentRemoved(nsIDocument* aDocument,
|
||||||
int32_t aIndexInContainer,
|
int32_t aIndexInContainer,
|
||||||
nsIContent* aPreviousSibling)
|
nsIContent* aPreviousSibling)
|
||||||
{
|
{
|
||||||
// Watch for content removed to the projected shadow (the ShadowRoot that
|
// Watch for content removed from the projected shadow (the ShadowRoot that
|
||||||
// will be rendered in place of this shadow insertion point) because the
|
// will be rendered in place of this shadow insertion point) because the
|
||||||
// nodes may need to be removed from other insertion points.
|
// nodes may need to be removed from other insertion points.
|
||||||
if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
|
if (!ShadowRoot::IsPooledNode(aChild, aContainer, mProjectedShadow)) {
|
||||||
|
|
|
@ -5,6 +5,9 @@ support-files =
|
||||||
[test_bug900724.html]
|
[test_bug900724.html]
|
||||||
[test_content_element.html]
|
[test_content_element.html]
|
||||||
[test_nested_content_element.html]
|
[test_nested_content_element.html]
|
||||||
|
[test_dest_insertion_points.html]
|
||||||
|
[test_dest_insertion_points_shadow.html]
|
||||||
|
[test_fallback_dest_insertion_points.html]
|
||||||
[test_dynamic_content_element_matching.html]
|
[test_dynamic_content_element_matching.html]
|
||||||
[test_document_register.html]
|
[test_document_register.html]
|
||||||
[test_document_register_base_queue.html]
|
[test_document_register_base_queue.html]
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=999999
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 999999</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999999">Mozilla Bug 999999</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content">
|
||||||
|
<div id="shadowhost">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
</pre>
|
||||||
|
<script type="application/javascript">
|
||||||
|
|
||||||
|
/** Test for Bug 999999 **/
|
||||||
|
var host = document.getElementById("shadowhost");
|
||||||
|
|
||||||
|
// Test destination insertion points of node distributed to content element.
|
||||||
|
var shadowRoot = host.createShadowRoot();
|
||||||
|
shadowRoot.innerHTML = '<div id="innerhost"><content id="innercontent" select=".red"></content></div>';
|
||||||
|
var innerContent = shadowRoot.getElementById("innercontent");
|
||||||
|
|
||||||
|
var span = document.createElement("span");
|
||||||
|
span.setAttribute("class", "red blue");
|
||||||
|
is(host.getDestinationInsertionPoints().length, 0, "Destination insertion points should be empty when not being distributed.");
|
||||||
|
|
||||||
|
host.appendChild(span);
|
||||||
|
|
||||||
|
is(span.getDestinationInsertionPoints().length, 1, "Destination insertion points should only contain a single content insertion point.");
|
||||||
|
is(span.getDestinationInsertionPoints()[0], innerContent, "Content element should contain destination insertion point.");
|
||||||
|
|
||||||
|
// Test destination insertion points of redistributed node.
|
||||||
|
var innerHost = shadowRoot.getElementById("innerhost");
|
||||||
|
var innerShadowRoot = innerHost.createShadowRoot();
|
||||||
|
innerShadowRoot.innerHTML = '<content id="innerinnercontent" select=".blue"></content>';
|
||||||
|
|
||||||
|
var innerInnerContent = innerShadowRoot.getElementById("innerinnercontent");
|
||||||
|
|
||||||
|
is(span.getDestinationInsertionPoints().length, 2, "Redistributed node should have 2 destination insertion points.");
|
||||||
|
is(span.getDestinationInsertionPoints()[1], innerInnerContent, "Nested content insertion point should be in list of destination insertion points.");
|
||||||
|
|
||||||
|
// Test destination insertion points after removing reprojection onto second content element.
|
||||||
|
span.setAttribute("class", "red");
|
||||||
|
is(span.getDestinationInsertionPoints().length, 1, "Destination insertion points should only contain 1 insertion point after removing reprojection.");
|
||||||
|
is(span.getDestinationInsertionPoints()[0], innerContent, "First content element should be only insertion point after removing reprojection.");
|
||||||
|
|
||||||
|
// Test destination insertion points after removing the projected content from the host.
|
||||||
|
host.removeChild(span);
|
||||||
|
is(span.getDestinationInsertionPoints().length, 0, "Destination insertion points should be empty after being removed from the shadow host.");
|
||||||
|
|
||||||
|
// Test destination insertion points of distributed content after removing insertion point.
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.setAttribute("class", "red blue");
|
||||||
|
host.appendChild(div);
|
||||||
|
|
||||||
|
is(div.getDestinationInsertionPoints().length, 2, "Div should be distributed into 2 insertion points.");
|
||||||
|
|
||||||
|
innerShadowRoot.removeChild(innerInnerContent);
|
||||||
|
|
||||||
|
is(div.getDestinationInsertionPoints().length, 1, "Div should be distributed into insertion point in one ShadowRoot.");
|
||||||
|
is(div.getDestinationInsertionPoints()[0], innerContent, "Destination insertion points should only contain content insertion point in first ShadowRoot.");
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,68 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=999999
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 999999</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999999">Mozilla Bug 999999</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content">
|
||||||
|
<div id="shadowhost"></div>
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
</pre>
|
||||||
|
<script type="application/javascript">
|
||||||
|
|
||||||
|
/** Test for Bug 999999 **/
|
||||||
|
var host = document.getElementById("shadowhost");
|
||||||
|
|
||||||
|
// Test destination insertion points of node distributed to shadow element.
|
||||||
|
var olderShadowRoot = host.createShadowRoot();
|
||||||
|
var youngerShadowRoot = host.createShadowRoot();
|
||||||
|
|
||||||
|
var shadowElem = document.createElement("shadow");
|
||||||
|
youngerShadowRoot.appendChild(shadowElem);
|
||||||
|
|
||||||
|
var span = document.createElement("span");
|
||||||
|
olderShadowRoot.appendChild(span);
|
||||||
|
|
||||||
|
is(span.getDestinationInsertionPoints().length, 1, "Child of ShadowRoot should be distributed to shadow insertion point.");
|
||||||
|
is(span.getDestinationInsertionPoints()[0], shadowElem, "Shadow element should be in destination insertion point list.");
|
||||||
|
|
||||||
|
// Test destination insertion points of node removed from tree.
|
||||||
|
olderShadowRoot.removeChild(span);
|
||||||
|
is(span.getDestinationInsertionPoints().length, 0, "Node removed from tree should no longer be distributed.");
|
||||||
|
|
||||||
|
// Test destination insertion points of fallback content being reprojected into a shadow element.
|
||||||
|
var content = document.createElement("content");
|
||||||
|
var fallback = document.createElement("span");
|
||||||
|
|
||||||
|
content.appendChild(fallback);
|
||||||
|
olderShadowRoot.appendChild(content);
|
||||||
|
|
||||||
|
is(fallback.getDestinationInsertionPoints().length, 2, "The fallback content should have 2 destination insertion points, the parent content and the shadow element to which it is reprojected.");
|
||||||
|
is(fallback.getDestinationInsertionPoints()[0], content, "First destination of the fallback content should be the parent content element.");
|
||||||
|
is(fallback.getDestinationInsertionPoints()[1], shadowElem, "Second destination of the fallback content should be the shadow element to which the element is reprojected.");
|
||||||
|
|
||||||
|
// Test destination insertion points of fallback content being removed from tree.
|
||||||
|
content.removeChild(fallback);
|
||||||
|
is(fallback.getDestinationInsertionPoints().length, 0, "The content should no longer be distributed to any nodes because it is no longer fallback content.");
|
||||||
|
|
||||||
|
// Test destination insertion points of distributed content after removing shadow insertion point.
|
||||||
|
var div = document.createElement("div");
|
||||||
|
olderShadowRoot.appendChild(div);
|
||||||
|
is(div.getDestinationInsertionPoints().length, 1, "Children in older shadow root should be distributed to shadow insertion point.");
|
||||||
|
is(div.getDestinationInsertionPoints()[0], shadowElem, "Destination insertion point should include shadow element.");
|
||||||
|
|
||||||
|
youngerShadowRoot.removeChild(shadowElem);
|
||||||
|
is(div.getDestinationInsertionPoints().length, 0, "Destination insertion points should be empty after removing shadow element.");
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,71 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=999999
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 999999</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=999999">Mozilla Bug 999999</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content">
|
||||||
|
<div id="shadowhost"></div>
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
</pre>
|
||||||
|
<script type="application/javascript">
|
||||||
|
|
||||||
|
/** Test for Bug 999999 **/
|
||||||
|
var host = document.getElementById("shadowhost");
|
||||||
|
|
||||||
|
// Test destination insertion points of node distributed to content element.
|
||||||
|
var shadowRoot = host.createShadowRoot();
|
||||||
|
shadowRoot.innerHTML = '<div id="innerhost"><content id="innercontent"></content></div>';
|
||||||
|
|
||||||
|
var fallback = document.createElement("span");
|
||||||
|
var innerContent = shadowRoot.getElementById("innercontent");
|
||||||
|
|
||||||
|
innerContent.appendChild(fallback);
|
||||||
|
|
||||||
|
is(fallback.getDestinationInsertionPoints().length, 1, "Active fallback content should be distributed to insertion point.");
|
||||||
|
is(fallback.getDestinationInsertionPoints()[0], innerContent, "Insertion point should be in list of destination insertion points.");
|
||||||
|
|
||||||
|
// Test destination insertion points of reprojected fallback content.
|
||||||
|
var innerHost = shadowRoot.getElementById("innerhost");
|
||||||
|
var innerShadowRoot = innerHost.createShadowRoot();
|
||||||
|
innerShadowRoot.innerHTML = '<content id="innerinnercontent"></content>';
|
||||||
|
|
||||||
|
var innerInnerContent = innerShadowRoot.getElementById("innerinnercontent");
|
||||||
|
|
||||||
|
is(fallback.getDestinationInsertionPoints().length, 2, "Fallback content should have been distributed into parent and reprojected into another insertion point.");
|
||||||
|
is(fallback.getDestinationInsertionPoints()[1], innerInnerContent, "Destination insertion points should contain content element to which the node was reprojected.");
|
||||||
|
|
||||||
|
// Test destination insertion points of fallback content that was dropped due to content element matching a node in the host.
|
||||||
|
var span = document.createElement("span");
|
||||||
|
host.appendChild(span);
|
||||||
|
|
||||||
|
is(fallback.getDestinationInsertionPoints().length, 0, "After dropping insertion points, fallback content should not have any nodes in destination insertion points list.");
|
||||||
|
|
||||||
|
// Test destination insertion points of fallback content after reactivating by dropping matched content on host.
|
||||||
|
host.removeChild(span);
|
||||||
|
is(fallback.getDestinationInsertionPoints().length, 2, "Fallback content should have 2 destination insertion points after being reactivated.");
|
||||||
|
is(fallback.getDestinationInsertionPoints()[0], innerContent, "First destination insertion point should be the parent content");
|
||||||
|
is(fallback.getDestinationInsertionPoints()[1], innerInnerContent, "Second destination insertion point should be the content to which the node is reprojected.");
|
||||||
|
|
||||||
|
// Test destination insertion points of fallback content after removed from the tree.
|
||||||
|
innerContent.removeChild(fallback);
|
||||||
|
is(fallback.getDestinationInsertionPoints().length, 0, "Fallback content is no longer fallback content, destination insertion points should be empty.");
|
||||||
|
|
||||||
|
// Test destination insertion points of child of non-insertion point content element.
|
||||||
|
var notInsertionPointContent = document.createElement("content");
|
||||||
|
var notFallback = document.createElement("span");
|
||||||
|
notInsertionPointContent.appendChild(notFallback);
|
||||||
|
is(notFallback.getDestinationInsertionPoints().length, 0, "Child of non-insertion point content should not be distributed to any nodes.");
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -201,11 +201,13 @@ partial interface Element {
|
||||||
NodeList querySelectorAll(DOMString selectors);
|
NodeList querySelectorAll(DOMString selectors);
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#shadow-root-object
|
// http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-element-interface
|
||||||
partial interface Element {
|
partial interface Element {
|
||||||
[Throws,Pref="dom.webcomponents.enabled"]
|
[Throws,Pref="dom.webcomponents.enabled"]
|
||||||
ShadowRoot createShadowRoot();
|
ShadowRoot createShadowRoot();
|
||||||
[Pref="dom.webcomponents.enabled"]
|
[Pref="dom.webcomponents.enabled"]
|
||||||
|
NodeList getDestinationInsertionPoints();
|
||||||
|
[Pref="dom.webcomponents.enabled"]
|
||||||
readonly attribute ShadowRoot? shadowRoot;
|
readonly attribute ShadowRoot? shadowRoot;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче