Index: third_party/WebKit/Source/core/dom/shadow/SlotAssignment.cpp |
diff --git a/third_party/WebKit/Source/core/dom/shadow/SlotAssignment.cpp b/third_party/WebKit/Source/core/dom/shadow/SlotAssignment.cpp |
index 712a29c1bd8d06057bde55ae4acc61bca9e79007..b5fed59b881b23561c5d6f9185e3742f14139b20 100644 |
--- a/third_party/WebKit/Source/core/dom/shadow/SlotAssignment.cpp |
+++ b/third_party/WebKit/Source/core/dom/shadow/SlotAssignment.cpp |
@@ -14,101 +14,171 @@ |
namespace blink { |
-HTMLSlotElement* SlotAssignment::assignedSlotFor(const Node& node) const |
+void SlotAssignment::slotAdded(HTMLSlotElement& slot) |
{ |
- return m_assignment.get(const_cast<Node*>(&node)); |
+ // 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. |
+ |
+ ++m_slotCount; |
+ m_needsCollectSlots = true; |
+ |
+ if (!m_slotMap->contains(slot.name())) { |
+ m_slotMap->add(slot.name(), &slot); |
+ return; |
+ } |
+ |
+ HTMLSlotElement& oldActive = *findSlotByName(slot.name()); |
+ DCHECK_NE(oldActive, slot); |
+ m_slotMap->add(slot.name(), &slot); |
+ if (findSlotByName(slot.name()) == oldActive) |
+ return; |
+ // |oldActive| is no longer an active slot. |
+ if (oldActive.findHostChildWithSameSlotName()) |
+ oldActive.enqueueSlotChangeEvent(); |
+ // TODO(hayato): We should not enqeueue a slotchange event for |oldActive| |
+ // if |oldActive| was inserted together with |slot|. |
+ // This could happen if |oldActive| and |slot| are descendants of the inserted node, and |
+ // |oldActive| is preceding |slot|. |
} |
-static void detachNotAssignedNode(Node& node) |
+void SlotAssignment::slotRemoved(HTMLSlotElement& slot) |
{ |
- if (node.layoutObject()) |
- node.lazyReattachIfAttached(); |
+ DCHECK_GT(m_slotCount, 0u); |
+ --m_slotCount; |
+ m_needsCollectSlots = true; |
+ |
+ DCHECK(m_slotMap->contains(slot.name())); |
+ HTMLSlotElement* oldActive = findSlotByName(slot.name()); |
+ m_slotMap->remove(slot.name(), &slot); |
+ HTMLSlotElement* newActive = findSlotByName(slot.name()); |
+ if (newActive && newActive != oldActive) { |
+ // |newActive| slot becomes an active slot. |
+ if (newActive->findHostChildWithSameSlotName()) |
+ newActive->enqueueSlotChangeEvent(); |
+ // TODO(hayato): Prevent a false-positive slotchange. |
+ // This could happen if more than one slots which have the same name are descendants of the removed node. |
+ } |
} |
-void SlotAssignment::resolveAssignment(ShadowRoot& shadowRoot) |
+bool SlotAssignment::findHostChildBySlotName(const AtomicString& slotName) const |
{ |
- m_assignment.clear(); |
+ // TODO(hayato): Avoid traversing children every time. |
+ for (Node& child : NodeTraversal::childrenOf(m_owner->host())) { |
+ if (!child.isSlotable()) |
+ continue; |
+ if (child.slotName() == slotName) |
+ return true; |
+ } |
+ return false; |
+} |
- using Name2Slot = HeapHashMap<AtomicString, Member<HTMLSlotElement>>; |
- Name2Slot name2slot; |
+void SlotAssignment::slotRenamed(const AtomicString& oldSlotName, HTMLSlotElement& slot) |
+{ |
+ // |slot| has already new name. Thus, we can not use slot.hasAssignedNodesSynchronously. |
+ bool hasAssignedNodesBefore = (findSlotByName(oldSlotName) == &slot) && findHostChildBySlotName(oldSlotName); |
- const HeapVector<Member<HTMLSlotElement>>& slots = shadowRoot.descendantSlots(); |
+ m_slotMap->remove(oldSlotName, &slot); |
+ m_slotMap->add(slot.name(), &slot); |
- for (Member<HTMLSlotElement> slot : slots) { |
- slot->willUpdateAssignment(); |
- slot->willUpdateFallback(); |
- name2slot.add(slot->name(), slot.get()); |
+ bool hasAssignedNodesAfter = slot.hasAssignedNodesSlow(); |
+ |
+ if (hasAssignedNodesBefore || hasAssignedNodesAfter) |
+ slot.enqueueSlotChangeEvent(); |
+} |
+ |
+void SlotAssignment::hostChildSlotNameChanged(const AtomicString& oldValue, const AtomicString& newValue) |
+{ |
+ if (HTMLSlotElement* slot = findSlotByName(HTMLSlotElement::normalizeSlotName(oldValue))) { |
+ slot->enqueueSlotChangeEvent(); |
+ m_owner->owner()->setNeedsDistributionRecalc(); |
+ } |
+ if (HTMLSlotElement* slot = findSlotByName(HTMLSlotElement::normalizeSlotName(newValue))) { |
+ slot->enqueueSlotChangeEvent(); |
+ m_owner->owner()->setNeedsDistributionRecalc(); |
} |
+} |
- for (Node& child : NodeTraversal::childrenOf(shadowRoot.host())) { |
- if (child.isInsertionPoint()) { |
- // A re-distribution across v0 and v1 shadow trees is not supported. |
- detachNotAssignedNode(child); |
- continue; |
- } |
- if (!child.slottable()) { |
+SlotAssignment::SlotAssignment(ShadowRoot& owner) |
+ : m_slotMap(DocumentOrderedMap::create()) |
+ , m_owner(&owner) |
+ , m_needsCollectSlots(false) |
+ , m_slotCount(0) |
+{ |
+} |
+ |
+static void detachNotAssignedNode(Node& node) |
+{ |
+ if (node.layoutObject()) |
+ node.lazyReattachIfAttached(); |
+} |
+ |
+void SlotAssignment::resolveAssignment() |
+{ |
+ for (Member<HTMLSlotElement> slot : slots()) |
+ slot->clearDistribution(); |
+ |
+ for (Node& child : NodeTraversal::childrenOf(m_owner->host())) { |
+ if (!child.isSlotable()) { |
detachNotAssignedNode(child); |
continue; |
} |
- AtomicString slotName = child.slotName(); |
- HTMLSlotElement* slot = name2slot.get(slotName); |
+ HTMLSlotElement* slot = findSlotByName(child.slotName()); |
if (slot) |
- assign(child, *slot); |
+ slot->appendAssignedNode(child); |
else |
detachNotAssignedNode(child); |
} |
- |
- for (auto slot = slots.rbegin(); slot != slots.rend(); ++slot) |
- (*slot)->updateFallbackNodes(); |
- |
- // For each slot, check if assigned nodes have changed |
- // If so, call fireSlotchange function |
- for (const auto& slot : slots) |
- slot->didUpdateAssignment(); |
} |
-void SlotAssignment::resolveDistribution(ShadowRoot& shadowRoot) |
+void SlotAssignment::resolveDistribution() |
{ |
- const HeapVector<Member<HTMLSlotElement>>& slots = shadowRoot.descendantSlots(); |
- for (Member<HTMLSlotElement> slot : slots) { |
- slot->willUpdateDistribution(); |
- } |
+ resolveAssignment(); |
+ const HeapVector<Member<HTMLSlotElement>>& slots = this->slots(); |
- for (auto slot : slots) { |
- for (auto node : slot->assignedNodes()) |
- distribute(*node, *slot); |
- } |
+ for (auto slot : slots) |
+ slot->resolveDistributedNodes(); |
// Update each slot's distribution in reverse tree order so that a child slot is visited before its parent slot. |
for (auto slot = slots.rbegin(); slot != slots.rend(); ++slot) |
(*slot)->updateDistributedNodesWithFallback(); |
- for (const auto& slot : slots) |
- slot->didUpdateDistribution(); |
} |
-void SlotAssignment::assign(Node& hostChild, HTMLSlotElement& slot) |
+const HeapVector<Member<HTMLSlotElement>>& SlotAssignment::slots() |
+{ |
+ if (m_needsCollectSlots) |
+ collectSlots(); |
+ return m_slots; |
+} |
+ |
+HTMLSlotElement* SlotAssignment::findSlot(const Node& node) |
{ |
- DCHECK(hostChild.isSlotAssignable()); |
- m_assignment.add(&hostChild, &slot); |
- slot.appendAssignedNode(hostChild); |
+ return node.isSlotable() ? findSlotByName(node.slotName()) : nullptr; |
} |
-void SlotAssignment::distribute(Node& hostChild, HTMLSlotElement& slot) |
+HTMLSlotElement* SlotAssignment::findSlotByName(const AtomicString& slotName) |
{ |
- DCHECK(hostChild.isSlotAssignable()); |
- if (isHTMLSlotElement(hostChild)) |
- slot.appendDistributedNodesFrom(toHTMLSlotElement(hostChild)); |
- else |
- slot.appendDistributedNode(hostChild); |
- |
- if (slot.isChildOfV1ShadowHost()) |
- slot.parentElementShadow()->setNeedsDistributionRecalc(); |
+ return m_slotMap->getSlotByName(slotName, m_owner.get()); |
+} |
+ |
+void SlotAssignment::collectSlots() |
+{ |
+ DCHECK(m_needsCollectSlots); |
+ m_slots.clear(); |
+ |
+ m_slots.reserveCapacity(m_slotCount); |
+ for (HTMLSlotElement& slot : Traversal<HTMLSlotElement>::descendantsOf(*m_owner)) { |
+ m_slots.append(&slot); |
+ } |
+ m_needsCollectSlots = false; |
+ DCHECK_EQ(m_slots.size(), m_slotCount); |
} |
DEFINE_TRACE(SlotAssignment) |
{ |
- visitor->trace(m_descendantSlots); |
- visitor->trace(m_assignment); |
+ visitor->trace(m_slots); |
+ visitor->trace(m_slotMap); |
+ visitor->trace(m_owner); |
} |
} // namespace blink |