Chromium Code Reviews| 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 a13e9fb731cedae1bc806c0054377f6922f66da2..f1f70b738368317d0eb166348e2f914ecd08cc94 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& oldFirst = *findSlotByName(slot.name()); |
|
kochi
2016/05/25 06:40:26
nit: maybe oldActive or oldActiveSlot is better?
I
hayato
2016/05/26 04:46:07
Done
|
| + DCHECK_NE(oldFirst, slot); |
| + m_slotMap->add(slot.name(), &slot); |
| + if (findSlotByName(slot.name()) == &oldFirst) |
|
kochi
2016/05/25 06:40:26
nit: No need for &
hayato
2016/05/26 04:46:07
Done
|
| + return; |
| + // |oldFirst| is no longer an active slot. |
| + if (oldFirst.findHostChildWithSameSlotName()) |
| + oldFirst.enqueueSlotChangeEvent(); |
| + // TODO(hayato): We should not enqeueue a slotchange event for |oldFirst| |
| + // if |oldFirst| was inserted together with |slot|. |
| + // This could happen if |oldFirst| and |slot| are descendants of the inserted node, and |
| + // |oldFirst| 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* oldFirst = findSlotByName(slot.name()); |
|
kochi
2016/05/25 06:40:26
nit: oldActive and newActive in this function?
hayato
2016/05/26 04:46:07
Done
|
| + m_slotMap->remove(slot.name(), &slot); |
| + HTMLSlotElement* newFirst = findSlotByName(slot.name()); |
| + if (newFirst && newFirst != oldFirst) { |
| + // |newFirst| slot becomes an active slot. |
| + if (newFirst->findHostChildWithSameSlotName()) |
| + newFirst->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 |