diff options
Diffstat (limited to 'dom/base/ShadowRoot.cpp')
-rw-r--r-- | dom/base/ShadowRoot.cpp | 240 |
1 files changed, 233 insertions, 7 deletions
diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp index 97214e050..308f57cf7 100644 --- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -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" @@ -110,6 +112,87 @@ ShadowRoot::FromNode(nsINode* aNode) } 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() { mProtoBinding->FlushSkinSheets(); @@ -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 |