mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-03 06:18:38 +09:00
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:
parent
a7ba34c672
commit
cab78760e5
49 changed files with 775 additions and 429 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue