Index: third_party/WebKit/Source/core/html/HTMLSlotElement.cpp |
diff --git a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp |
index 331437363377ec23e6146f20400e17cbe95f9bb0..02e9279d0792cbb813b7d80195ed553274ce64ec 100644 |
--- a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp |
+++ b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp |
@@ -32,12 +32,12 @@ |
#include "bindings/core/v8/Microtask.h" |
#include "core/HTMLNames.h" |
-#include "core/dom/ElementTraversal.h" |
#include "core/dom/NodeTraversal.h" |
#include "core/dom/StyleChangeReason.h" |
#include "core/dom/StyleEngine.h" |
#include "core/dom/shadow/ElementShadow.h" |
#include "core/dom/shadow/InsertionPoint.h" |
+#include "core/dom/shadow/SlotAssignment.h" |
#include "core/events/Event.h" |
#include "core/html/AssignedNodesOptions.h" |
@@ -47,15 +47,25 @@ using namespace HTMLNames; |
inline HTMLSlotElement::HTMLSlotElement(Document& document) |
: HTMLElement(slotTag, document) |
- , m_distributionState(DistributionDone) |
- , m_assignmentState(AssignmentDone) |
- , m_slotchangeEventAdded(false) |
{ |
setHasCustomStyleCallbacks(); |
} |
DEFINE_NODE_FACTORY(HTMLSlotElement); |
+// static |
+AtomicString HTMLSlotElement::normalizeSlotName(const AtomicString& name) |
+{ |
+ return (name.isNull() || name.isEmpty()) ? emptyAtom : name; |
+} |
+ |
+const HeapVector<Member<Node>>& HTMLSlotElement::assignedNodes() |
+{ |
+ DCHECK(!needsDistributionRecalc()); |
+ DCHECK(isInShadowTree() || m_assignedNodes.isEmpty()); |
+ return m_assignedNodes; |
+} |
+ |
const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const AssignedNodesOptions& options) |
{ |
updateDistribution(); |
@@ -66,86 +76,80 @@ const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const As |
const HeapVector<Member<Node>>& HTMLSlotElement::getDistributedNodes() |
{ |
- ASSERT(!needsDistributionRecalc()); |
+ DCHECK(!needsDistributionRecalc()); |
if (isInShadowTree()) |
return m_distributedNodes; |
// A slot is unlikely to be used outside of a shadow tree. |
// We do not need to optimize this case in most cases. |
// TODO(hayato): If this path causes a performance issue, we should move |
- // ShadowRootRaraDate::m_descendantSlots into TreeScopreRareData-ish and |
+ // ShadowRoot::m_slotAssignment into TreeScopreRareData-ish and |
// update the distribution code so it considers a document tree too. |
- willUpdateDistribution(); |
- for (Node& child : NodeTraversal::childrenOf(*this)) { |
- if (!child.isSlotAssignable()) |
+ clearDistribution(); |
+ Node* child = NodeTraversal::firstChild(*this); |
+ while (child) { |
+ if (!child->isSlotable()) { |
+ child = NodeTraversal::nextSkippingChildren(*child, this); |
continue; |
- if (isHTMLSlotElement(child)) |
- m_distributedNodes.appendVector(toHTMLSlotElement(child).getDistributedNodes()); |
- else |
- m_distributedNodes.append(&child); |
+ } |
+ if (isHTMLSlotElement(child)) { |
+ child = NodeTraversal::next(*child, this); |
+ } else { |
+ m_distributedNodes.append(child); |
+ child = NodeTraversal::nextSkippingChildren(*child, this); |
+ } |
} |
- didUpdateDistribution(); |
return m_distributedNodes; |
} |
-void HTMLSlotElement::appendAssignedNode(Node& node) |
+void HTMLSlotElement::appendAssignedNode(Node& hostChild) |
{ |
- ASSERT(m_assignmentState == AssignmentOnGoing); |
- m_assignedNodes.append(&node); |
+ DCHECK(hostChild.isSlotable()); |
+ m_assignedNodes.append(&hostChild); |
+} |
+ |
+void HTMLSlotElement::resolveDistributedNodes() |
+{ |
+ for (auto& node : m_assignedNodes) { |
+ DCHECK(node->isSlotable()); |
+ if (isHTMLSlotElement(*node)) |
+ appendDistributedNodesFrom(toHTMLSlotElement(*node)); |
+ else |
+ appendDistributedNode(*node); |
+ |
+ if (isChildOfV1ShadowHost()) |
+ parentElementShadow()->setNeedsDistributionRecalc(); |
+ } |
} |
void HTMLSlotElement::appendDistributedNode(Node& node) |
{ |
- ASSERT(m_distributionState == DistributionOnGoing); |
size_t size = m_distributedNodes.size(); |
m_distributedNodes.append(&node); |
m_distributedIndices.set(&node, size); |
} |
-void HTMLSlotElement::appendFallbackNode(Node& node) |
-{ |
- ASSERT(m_assignmentState == AssignmentOnGoing); |
- m_fallbackNodes.append(&node); |
-} |
- |
void HTMLSlotElement::appendDistributedNodesFrom(const HTMLSlotElement& other) |
{ |
- ASSERT(m_distributionState == DistributionOnGoing); |
size_t index = m_distributedNodes.size(); |
m_distributedNodes.appendVector(other.m_distributedNodes); |
for (const auto& node : other.m_distributedNodes) |
m_distributedIndices.set(node.get(), index++); |
} |
-void HTMLSlotElement::willUpdateAssignment() |
+void HTMLSlotElement::clearDistribution() |
{ |
- ASSERT(m_assignmentState != AssignmentOnGoing); |
- m_assignmentState = AssignmentOnGoing; |
- m_oldAssignedNodes.swap(m_assignedNodes); |
m_assignedNodes.clear(); |
-} |
- |
-void HTMLSlotElement::willUpdateDistribution() |
-{ |
- ASSERT(m_distributionState != DistributionOnGoing); |
- m_distributionState = DistributionOnGoing; |
- m_oldDistributedNodes.swap(m_distributedNodes); |
m_distributedNodes.clear(); |
m_distributedIndices.clear(); |
} |
-void HTMLSlotElement::willUpdateFallback() |
-{ |
- m_oldFallbackNodes.swap(m_fallbackNodes); |
- m_fallbackNodes.clear(); |
-} |
- |
void HTMLSlotElement::dispatchSlotChangeEvent() |
{ |
+ m_slotchangeEventEnqueued = false; |
Event* event = Event::create(EventTypeNames::slotchange); |
event->setTarget(this); |
dispatchScopedEvent(event); |
- m_slotchangeEventAdded = false; |
} |
Node* HTMLSlotElement::distributedNodeNextTo(const Node& node) const |
@@ -197,24 +201,20 @@ void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicSt |
{ |
if (name == nameAttr) { |
if (ShadowRoot* root = containingShadowRoot()) { |
- root->owner()->willAffectSelector(); |
if (root->isV1() && oldValue != newValue) |
- root->assignV1(); |
+ root->ensureSlotAssignment().slotRenamed(normalizeSlotName(oldValue), *this); |
} |
} |
HTMLElement::attributeChanged(name, oldValue, newValue, reason); |
} |
-void HTMLSlotElement::childrenChanged(const ChildrenChange& change) |
+static bool wasInShadowTreeBeforeInserted(HTMLSlotElement& slot, ContainerNode& insertionPoint) |
{ |
- HTMLElement::childrenChanged(change); |
- if (ShadowRoot* root = containingShadowRoot()) { |
- if (ElementShadow* rootOwner = root->owner()) { |
- rootOwner->setNeedsDistributionRecalc(); |
- } |
- if (m_assignedNodes.isEmpty() && root->isV1()) |
- root->assignV1(); |
- } |
+ ShadowRoot* root1 = slot.containingShadowRoot(); |
+ ShadowRoot* root2 = insertionPoint.containingShadowRoot(); |
+ if (root1 && root2 && root1 == root2) |
+ return false; |
+ return root1; |
} |
Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* insertionPoint) |
@@ -222,27 +222,36 @@ Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* |
HTMLElement::insertedInto(insertionPoint); |
ShadowRoot* root = containingShadowRoot(); |
if (root) { |
- if (ElementShadow* rootOwner = root->owner()) |
- rootOwner->setNeedsDistributionRecalc(); |
- if (root == insertionPoint->treeScope().rootNode()) |
- root->didAddSlot(); |
+ DCHECK(root->owner()); |
+ root->owner()->setNeedsDistributionRecalc(); |
+ // Relevant DOM Standard: https://dom.spec.whatwg.org/#concept-node-insert |
+ // - 6.4: Run assign slotables for a tree with node's tree and a set containing each inclusive descendant of node that is a slot. |
+ if (!wasInShadowTreeBeforeInserted(*this, *insertionPoint)) |
+ root->ensureSlotAssignment().slotAdded(*this); |
} |
// We could have been distributed into in a detached subtree, make sure to |
// clear the distribution when inserted again to avoid cycles. |
clearDistribution(); |
- if (root && root->isV1()) |
- root->assignV1(); |
- |
return InsertionDone; |
} |
+static ShadowRoot* containingShadowRootBeforeRemoved(Node& removedDescendant, ContainerNode& insertionPoint) |
+{ |
+ if (ShadowRoot* root = removedDescendant.containingShadowRoot()) |
+ return root; |
+ return insertionPoint.containingShadowRoot(); |
+} |
+ |
void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) |
{ |
- ShadowRoot* root = containingShadowRoot(); |
- if (!root) |
- root = insertionPoint->containingShadowRoot(); |
+ // `removedFrom` is called after the node is removed from the tree. |
+ // That means: |
+ // 1. If this slot is still in a tree scope, it means the slot has been in a shadow tree. An inclusive shadow-including ancestor of the shadow host was originally removed from its parent. |
+ // 2. Or (this slot is now not in a tree scope), this slot's inclusive ancestor was orginally removed from its parent (== insertion point). This slot and the originally removed node was in the same tree. |
+ |
+ ShadowRoot* root = containingShadowRootBeforeRemoved(*this, *insertionPoint); |
if (root) { |
if (ElementShadow* rootOwner = root->owner()) |
rootOwner->setNeedsDistributionRecalc(); |
@@ -251,14 +260,11 @@ void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) |
// Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up. |
clearDistribution(); |
- ContainerNode& rootNode = insertionPoint->treeScope().rootNode(); |
- if (root == &rootNode) |
- root->didRemoveSlot(); |
- else if (rootNode.isShadowRoot() && toShadowRoot(rootNode).isV1()) |
- toShadowRoot(rootNode).assignV1(); |
+ if (root && root->isV1() && root == insertionPoint->treeScope().rootNode()) { |
+ // This slot was in a shadow tree and got disconnected from the shadow root. |
+ root->ensureSlotAssignment().slotRemoved(*this); |
+ } |
- if (root && root->isV1()) |
- root->assignV1(); |
HTMLElement::removedFrom(insertionPoint); |
} |
@@ -271,93 +277,56 @@ void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) |
node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); |
} |
-void HTMLSlotElement::updateFallbackNodes() |
-{ |
- if (!m_fallbackNodes.isEmpty()) |
- return; |
- for (auto& child : NodeTraversal::childrenOf(*this)) { |
- if (!child.isSlotAssignable()) |
- continue; |
- // Insertion points are not supported as slots fallback |
- if (isActiveInsertionPoint(child)) |
- continue; |
- appendFallbackNode(child); |
- } |
-} |
- |
void HTMLSlotElement::updateDistributedNodesWithFallback() |
{ |
if (!m_distributedNodes.isEmpty()) |
return; |
- for (auto node : m_fallbackNodes) { |
- if (isHTMLSlotElement(node)) |
- appendDistributedNodesFrom(*toHTMLSlotElement(node)); |
+ for (auto& child : NodeTraversal::childrenOf(*this)) { |
+ if (!child.isSlotable()) |
+ continue; |
+ if (isHTMLSlotElement(child)) |
+ appendDistributedNodesFrom(toHTMLSlotElement(child)); |
else |
- appendDistributedNode(*node); |
+ appendDistributedNode(child); |
} |
} |
-bool HTMLSlotElement::assignmentChanged() |
+void HTMLSlotElement::enqueueSlotChangeEvent() |
{ |
- ASSERT(m_assignmentState != AssignmentOnGoing); |
- if (m_assignmentState == AssignmentDone) |
- m_assignmentState = m_oldAssignedNodes == m_assignedNodes ? AssignmentUnchanged : AssignmentChanged; |
- return m_assignmentState == AssignmentChanged; |
-} |
- |
-bool HTMLSlotElement::distributionChanged() |
-{ |
- ASSERT(m_distributionState != DistributionOnGoing); |
- if (m_distributionState == DistributionDone) |
- m_distributionState = m_oldDistributedNodes == m_distributedNodes ? DistributionUnchanged : DistributionChanged; |
- return m_distributionState == DistributionChanged; |
-} |
- |
-bool HTMLSlotElement::fallbackChanged() |
-{ |
- return m_oldFallbackNodes == m_fallbackNodes; |
-} |
+ if (!m_slotchangeEventEnqueued) { |
+ Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEvent, Persistent<HTMLSlotElement>(this))); |
+ m_slotchangeEventEnqueued = true; |
+ } |
-void HTMLSlotElement::didUpdateAssignment() |
-{ |
- ASSERT(m_assignmentState == AssignmentOnGoing); |
- m_assignmentState = AssignmentDone; |
- if ((assignmentChanged() || fallbackChanged()) && !m_slotchangeEventAdded) |
- fireSlotChangeEvent(); |
-} |
+ ShadowRoot* root = containingShadowRoot(); |
+ DCHECK(root); |
+ DCHECK(root->isV1()); |
+ root->owner()->setNeedsDistributionRecalc(); |
-void HTMLSlotElement::didUpdateDistribution() |
-{ |
- ASSERT(m_distributionState == DistributionOnGoing); |
- m_distributionState = DistributionDone; |
- if (isChildOfV1ShadowHost()) { |
- ElementShadow* shadow = parentElementShadow(); |
- ASSERT(shadow); |
- if (!shadow->needsDistributionRecalc() && distributionChanged()) |
- shadow->setNeedsDistributionRecalc(); |
+ if (ShadowRoot* parentShadowRoot = v1ShadowRootOfParent()) { |
+ if (HTMLSlotElement* next = parentShadowRoot->ensureSlotAssignment().findSlot(*this)) |
+ next->enqueueSlotChangeEvent(); |
} |
} |
-void HTMLSlotElement::fireSlotChangeEvent() |
+bool HTMLSlotElement::hasAssignedNodesSlow() const |
{ |
- ASSERT(!m_slotchangeEventAdded); |
- Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEvent, this)); |
- m_slotchangeEventAdded = true; |
- |
- Element* shadowHost = isShadowHost(parentElement()) ? parentElement() : nullptr; |
- // If this slot is assigned to another slot, fire slot change event of that slot too. |
- if (shadowHost && shadowHost->shadowRootIfV1()) { |
- if (HTMLSlotElement* assigned = assignedSlot()) { |
- if (!assigned->m_slotchangeEventAdded) |
- assigned->fireSlotChangeEvent(); |
- } |
- } |
+ ShadowRoot* root = containingShadowRoot(); |
+ DCHECK(root); |
+ DCHECK(root->isV1()); |
+ SlotAssignment& assignment = root->ensureSlotAssignment(); |
+ if (assignment.findSlot(*this) != this) |
+ return false; |
+ return assignment.findHostChildBySlotName(name()); |
} |
-void HTMLSlotElement::clearDistribution() |
+bool HTMLSlotElement::findHostChildWithSameSlotName() const |
{ |
- willUpdateDistribution(); |
- didUpdateDistribution(); |
+ ShadowRoot* root = containingShadowRoot(); |
+ DCHECK(root); |
+ DCHECK(root->isV1()); |
+ SlotAssignment& assignment = root->ensureSlotAssignment(); |
+ return assignment.findHostChildBySlotName(name()); |
} |
short HTMLSlotElement::tabIndex() const |
@@ -369,11 +338,7 @@ DEFINE_TRACE(HTMLSlotElement) |
{ |
visitor->trace(m_assignedNodes); |
visitor->trace(m_distributedNodes); |
- visitor->trace(m_fallbackNodes); |
visitor->trace(m_distributedIndices); |
- visitor->trace(m_oldAssignedNodes); |
- visitor->trace(m_oldDistributedNodes); |
- visitor->trace(m_oldFallbackNodes); |
HTMLElement::trace(visitor); |
} |