| 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
|
|
|