OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/dom/shadow/SlotAssignment.h" | 5 #include "core/dom/shadow/SlotAssignment.h" |
6 | 6 |
7 #include "core/HTMLNames.h" | 7 #include "core/HTMLNames.h" |
8 #include "core/dom/ElementTraversal.h" | 8 #include "core/dom/ElementTraversal.h" |
9 #include "core/dom/NodeTraversal.h" | 9 #include "core/dom/NodeTraversal.h" |
10 #include "core/dom/shadow/ElementShadow.h" | 10 #include "core/dom/shadow/ElementShadow.h" |
11 #include "core/dom/shadow/InsertionPoint.h" | 11 #include "core/dom/shadow/InsertionPoint.h" |
12 #include "core/dom/shadow/ShadowRoot.h" | 12 #include "core/dom/shadow/ShadowRoot.h" |
13 #include "core/html/HTMLSlotElement.h" | 13 #include "core/html/HTMLSlotElement.h" |
14 | 14 |
15 namespace blink { | 15 namespace blink { |
16 | 16 |
17 HTMLSlotElement* SlotAssignment::assignedSlotFor(const Node& node) const | 17 void SlotAssignment::slotAdded(HTMLSlotElement& slot) |
18 { | 18 { |
19 return m_assignment.get(const_cast<Node*>(&node)); | 19 // Relevant DOM Standard: |
| 20 // https://dom.spec.whatwg.org/#concept-node-insert |
| 21 // 6.4: Run assign slotables for a tree with node's tree and a set containi
ng each inclusive descendant of node that is a slot. |
| 22 |
| 23 ++m_slotCount; |
| 24 m_needsCollectSlots = true; |
| 25 |
| 26 if (!m_slotMap->contains(slot.name())) { |
| 27 m_slotMap->add(slot.name(), &slot); |
| 28 return; |
| 29 } |
| 30 |
| 31 HTMLSlotElement& oldActive = *findSlotByName(slot.name()); |
| 32 DCHECK_NE(oldActive, slot); |
| 33 m_slotMap->add(slot.name(), &slot); |
| 34 if (findSlotByName(slot.name()) == oldActive) |
| 35 return; |
| 36 // |oldActive| is no longer an active slot. |
| 37 if (oldActive.findHostChildWithSameSlotName()) |
| 38 oldActive.enqueueSlotChangeEvent(); |
| 39 // TODO(hayato): We should not enqeueue a slotchange event for |oldActive| |
| 40 // if |oldActive| was inserted together with |slot|. |
| 41 // This could happen if |oldActive| and |slot| are descendants of the insert
ed node, and |
| 42 // |oldActive| is preceding |slot|. |
| 43 } |
| 44 |
| 45 void SlotAssignment::slotRemoved(HTMLSlotElement& slot) |
| 46 { |
| 47 DCHECK_GT(m_slotCount, 0u); |
| 48 --m_slotCount; |
| 49 m_needsCollectSlots = true; |
| 50 |
| 51 DCHECK(m_slotMap->contains(slot.name())); |
| 52 HTMLSlotElement* oldActive = findSlotByName(slot.name()); |
| 53 m_slotMap->remove(slot.name(), &slot); |
| 54 HTMLSlotElement* newActive = findSlotByName(slot.name()); |
| 55 if (newActive && newActive != oldActive) { |
| 56 // |newActive| slot becomes an active slot. |
| 57 if (newActive->findHostChildWithSameSlotName()) |
| 58 newActive->enqueueSlotChangeEvent(); |
| 59 // TODO(hayato): Prevent a false-positive slotchange. |
| 60 // This could happen if more than one slots which have the same name are
descendants of the removed node. |
| 61 } |
| 62 } |
| 63 |
| 64 bool SlotAssignment::findHostChildBySlotName(const AtomicString& slotName) const |
| 65 { |
| 66 // TODO(hayato): Avoid traversing children every time. |
| 67 for (Node& child : NodeTraversal::childrenOf(m_owner->host())) { |
| 68 if (!child.isSlotable()) |
| 69 continue; |
| 70 if (child.slotName() == slotName) |
| 71 return true; |
| 72 } |
| 73 return false; |
| 74 } |
| 75 |
| 76 void SlotAssignment::slotRenamed(const AtomicString& oldSlotName, HTMLSlotElemen
t& slot) |
| 77 { |
| 78 // |slot| has already new name. Thus, we can not use slot.hasAssignedNodesSy
nchronously. |
| 79 bool hasAssignedNodesBefore = (findSlotByName(oldSlotName) == &slot) && find
HostChildBySlotName(oldSlotName); |
| 80 |
| 81 m_slotMap->remove(oldSlotName, &slot); |
| 82 m_slotMap->add(slot.name(), &slot); |
| 83 |
| 84 bool hasAssignedNodesAfter = slot.hasAssignedNodesSlow(); |
| 85 |
| 86 if (hasAssignedNodesBefore || hasAssignedNodesAfter) |
| 87 slot.enqueueSlotChangeEvent(); |
| 88 } |
| 89 |
| 90 void SlotAssignment::hostChildSlotNameChanged(const AtomicString& oldValue, cons
t AtomicString& newValue) |
| 91 { |
| 92 if (HTMLSlotElement* slot = findSlotByName(HTMLSlotElement::normalizeSlotNam
e(oldValue))) { |
| 93 slot->enqueueSlotChangeEvent(); |
| 94 m_owner->owner()->setNeedsDistributionRecalc(); |
| 95 } |
| 96 if (HTMLSlotElement* slot = findSlotByName(HTMLSlotElement::normalizeSlotNam
e(newValue))) { |
| 97 slot->enqueueSlotChangeEvent(); |
| 98 m_owner->owner()->setNeedsDistributionRecalc(); |
| 99 } |
| 100 } |
| 101 |
| 102 SlotAssignment::SlotAssignment(ShadowRoot& owner) |
| 103 : m_slotMap(DocumentOrderedMap::create()) |
| 104 , m_owner(&owner) |
| 105 , m_needsCollectSlots(false) |
| 106 , m_slotCount(0) |
| 107 { |
20 } | 108 } |
21 | 109 |
22 static void detachNotAssignedNode(Node& node) | 110 static void detachNotAssignedNode(Node& node) |
23 { | 111 { |
24 if (node.layoutObject()) | 112 if (node.layoutObject()) |
25 node.lazyReattachIfAttached(); | 113 node.lazyReattachIfAttached(); |
26 } | 114 } |
27 | 115 |
28 void SlotAssignment::resolveAssignment(ShadowRoot& shadowRoot) | 116 void SlotAssignment::resolveAssignment() |
29 { | 117 { |
30 m_assignment.clear(); | 118 for (Member<HTMLSlotElement> slot : slots()) |
| 119 slot->clearDistribution(); |
31 | 120 |
32 using Name2Slot = HeapHashMap<AtomicString, Member<HTMLSlotElement>>; | 121 for (Node& child : NodeTraversal::childrenOf(m_owner->host())) { |
33 Name2Slot name2slot; | 122 if (!child.isSlotable()) { |
34 | |
35 const HeapVector<Member<HTMLSlotElement>>& slots = shadowRoot.descendantSlot
s(); | |
36 | |
37 for (Member<HTMLSlotElement> slot : slots) { | |
38 slot->willUpdateAssignment(); | |
39 slot->willUpdateFallback(); | |
40 name2slot.add(slot->name(), slot.get()); | |
41 } | |
42 | |
43 for (Node& child : NodeTraversal::childrenOf(shadowRoot.host())) { | |
44 if (child.isInsertionPoint()) { | |
45 // A re-distribution across v0 and v1 shadow trees is not supported. | |
46 detachNotAssignedNode(child); | 123 detachNotAssignedNode(child); |
47 continue; | 124 continue; |
48 } | 125 } |
49 if (!child.slottable()) { | 126 HTMLSlotElement* slot = findSlotByName(child.slotName()); |
50 detachNotAssignedNode(child); | |
51 continue; | |
52 } | |
53 AtomicString slotName = child.slotName(); | |
54 HTMLSlotElement* slot = name2slot.get(slotName); | |
55 if (slot) | 127 if (slot) |
56 assign(child, *slot); | 128 slot->appendAssignedNode(child); |
57 else | 129 else |
58 detachNotAssignedNode(child); | 130 detachNotAssignedNode(child); |
59 } | 131 } |
60 | |
61 for (auto slot = slots.rbegin(); slot != slots.rend(); ++slot) | |
62 (*slot)->updateFallbackNodes(); | |
63 | |
64 // For each slot, check if assigned nodes have changed | |
65 // If so, call fireSlotchange function | |
66 for (const auto& slot : slots) | |
67 slot->didUpdateAssignment(); | |
68 } | 132 } |
69 | 133 |
70 void SlotAssignment::resolveDistribution(ShadowRoot& shadowRoot) | 134 void SlotAssignment::resolveDistribution() |
71 { | 135 { |
72 const HeapVector<Member<HTMLSlotElement>>& slots = shadowRoot.descendantSlot
s(); | 136 resolveAssignment(); |
73 for (Member<HTMLSlotElement> slot : slots) { | 137 const HeapVector<Member<HTMLSlotElement>>& slots = this->slots(); |
74 slot->willUpdateDistribution(); | |
75 } | |
76 | 138 |
77 for (auto slot : slots) { | 139 for (auto slot : slots) |
78 for (auto node : slot->assignedNodes()) | 140 slot->resolveDistributedNodes(); |
79 distribute(*node, *slot); | |
80 } | |
81 | 141 |
82 // Update each slot's distribution in reverse tree order so that a child slo
t is visited before its parent slot. | 142 // Update each slot's distribution in reverse tree order so that a child slo
t is visited before its parent slot. |
83 for (auto slot = slots.rbegin(); slot != slots.rend(); ++slot) | 143 for (auto slot = slots.rbegin(); slot != slots.rend(); ++slot) |
84 (*slot)->updateDistributedNodesWithFallback(); | 144 (*slot)->updateDistributedNodesWithFallback(); |
85 for (const auto& slot : slots) | |
86 slot->didUpdateDistribution(); | |
87 } | 145 } |
88 | 146 |
89 void SlotAssignment::assign(Node& hostChild, HTMLSlotElement& slot) | 147 const HeapVector<Member<HTMLSlotElement>>& SlotAssignment::slots() |
90 { | 148 { |
91 DCHECK(hostChild.isSlotAssignable()); | 149 if (m_needsCollectSlots) |
92 m_assignment.add(&hostChild, &slot); | 150 collectSlots(); |
93 slot.appendAssignedNode(hostChild); | 151 return m_slots; |
94 } | 152 } |
95 | 153 |
96 void SlotAssignment::distribute(Node& hostChild, HTMLSlotElement& slot) | 154 HTMLSlotElement* SlotAssignment::findSlot(const Node& node) |
97 { | 155 { |
98 DCHECK(hostChild.isSlotAssignable()); | 156 return node.isSlotable() ? findSlotByName(node.slotName()) : nullptr; |
99 if (isHTMLSlotElement(hostChild)) | 157 } |
100 slot.appendDistributedNodesFrom(toHTMLSlotElement(hostChild)); | |
101 else | |
102 slot.appendDistributedNode(hostChild); | |
103 | 158 |
104 if (slot.isChildOfV1ShadowHost()) | 159 HTMLSlotElement* SlotAssignment::findSlotByName(const AtomicString& slotName) |
105 slot.parentElementShadow()->setNeedsDistributionRecalc(); | 160 { |
| 161 return m_slotMap->getSlotByName(slotName, m_owner.get()); |
| 162 } |
| 163 |
| 164 void SlotAssignment::collectSlots() |
| 165 { |
| 166 DCHECK(m_needsCollectSlots); |
| 167 m_slots.clear(); |
| 168 |
| 169 m_slots.reserveCapacity(m_slotCount); |
| 170 for (HTMLSlotElement& slot : Traversal<HTMLSlotElement>::descendantsOf(*m_ow
ner)) { |
| 171 m_slots.append(&slot); |
| 172 } |
| 173 m_needsCollectSlots = false; |
| 174 DCHECK_EQ(m_slots.size(), m_slotCount); |
106 } | 175 } |
107 | 176 |
108 DEFINE_TRACE(SlotAssignment) | 177 DEFINE_TRACE(SlotAssignment) |
109 { | 178 { |
110 visitor->trace(m_descendantSlots); | 179 visitor->trace(m_slots); |
111 visitor->trace(m_assignment); | 180 visitor->trace(m_slotMap); |
| 181 visitor->trace(m_owner); |
112 } | 182 } |
113 | 183 |
114 } // namespace blink | 184 } // namespace blink |
OLD | NEW |