Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Side by Side Diff: third_party/WebKit/Source/core/dom/shadow/SlotAssignment.cpp

Issue 1995203002: Rewrite Shadow DOM distribution engine to support partial synchronous distribution for v1 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: wip Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 SlotEntry::add(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 if (m_slots.isEmpty()) {
23 m_slots.add(&slot);
24 return;
25 }
26 HTMLSlotElement& first= *firstSlot();
27 DCHECK_NE(first, slot);
28 m_slots.add(&slot);
29 if (firstSlot() == &first)
30 return;
31 // |first| is no longer an active slot.
32 if (first.findHostChildWithSameSlotName())
kochi 2016/05/24 10:02:56 Can this be simply "if (findHostChildBySlotName(fi
33 first.enqueueSlotChangeEvent();
34 // TODO(hayato): We should not eneueue a slotchange event for |first|
35 // if |first| was inserted together with |slot|.
36 // This could happen if |first| and |slot| are descendants of the inserted n ode, and
37 // |first| is preceding |slot|.
kochi 2016/05/23 06:08:42 Maybe you can add a test case for this case, and n
hayato 2016/05/24 13:23:38 Yes, we should have a test. Let me add a test late
38 }
39
40 void SlotEntry::remove(HTMLSlotElement& slot)
41 {
42 DCHECK(m_slots.contains(&slot));
43 m_slots.remove(&slot);
44 HTMLSlotElement* first = firstSlot();
45 if (first && first != &slot) {
kochi 2016/05/23 06:08:42 Instead of |first != &slot|, you should check |old
esprehn 2016/05/23 06:41:00 No need for the &, you can compare references and
hayato 2016/05/24 13:23:38 Done
46 // |first| slot becomes an active slot.
47 if (first->findHostChildWithSameSlotName())
48 first->enqueueSlotChangeEvent();
49 // TODO(hayato): Prevent a false-positve slotchange.
50 // This could happen if more than one slots which have the same name are descendants of the removed node.
51 }
52 }
53
54 DEFINE_TRACE(SlotEntry)
55 {
56 visitor->trace(m_slots);
57 }
58
59 HTMLSlotElement* SlotEntry::firstSlot() const
60 {
61 return size() ? toHTMLSlotElement(*m_slots.begin()) : nullptr;
esprehn 2016/05/23 06:41:00 You want .first()
hayato 2016/05/24 13:23:39 Done
62 }
63
64 SlotEntry& SlotAssignment::entry(const AtomicString& name)
65 {
66 auto addResult = m_slotEntryMap.add(name, nullptr);
67 if (addResult.isNewEntry)
68 addResult.storedValue->value = SlotEntry::create();
69 return *addResult.storedValue->value;
70 }
71
72 void SlotAssignment::slotAdded(HTMLSlotElement& slot)
73 {
74 ++m_slotCount;
75 entry(slot.name()).add(slot);
esprehn 2016/05/23 06:41:00 findEntryByName ?
hayato 2016/05/24 13:23:39 Done. Because this method creates and return a new
76 m_needsCollectSlots = true;
77 }
78
79 void SlotAssignment::slotRemoved(HTMLSlotElement& slot)
80 {
81 DCHECK_GT(m_slotCount, 0u);
82 --m_slotCount;
83 entry(slot.name()).remove(slot);
84 m_needsCollectSlots = true;
85 }
86
87 bool SlotAssignment::findHostChildBySlotName(const AtomicString& slotName) const
88 {
89 // TODO(hayato): Avoid traversing children every time.
kochi 2016/05/23 06:08:42 Can we shortcut the traverse if any slot with |slo
hayato 2016/05/24 13:23:38 No. Slot's assigned nodes are not updated at this
kochi 2016/05/25 05:37:11 Acknowledged.
90 for (Node& child : NodeTraversal::childrenOf(*m_owner->host())) {
91 if (!child.isSlotable())
92 continue;
93 if (child.slotName() == slotName)
94 return true;
95 }
96 return false;
97 }
98
99 void SlotAssignment::slotRenamed(const AtomicString& oldSlotName, HTMLSlotElemen t& slot)
100 {
101 // |slot| has already new name. Thus, we can not use slot.hasAssignedNodesSy nchronously.
102 bool hasAssignedNodesBefore = (findSlotByName(oldSlotName) == &slot) && find HostChildBySlotName(oldSlotName);
103
104 entry(oldSlotName).remove(slot);
kochi 2016/05/23 06:08:42 Shall we remove the entry from |m_slotEntryMap| wh
hayato 2016/05/24 13:23:39 We can. However, it is very rare. I am afraid that
kochi 2016/05/25 05:37:11 Even it's rare, do we (kind of) leak empty slot en
kochi 2016/05/25 06:40:26 Ah, it is no longer necessary because now we use D
105 entry(slot.name()).add(slot);
106
107 bool hasAssignedNodesAfter = slot.hasAssignedNodesSynchronously();
108
109 if (hasAssignedNodesBefore || hasAssignedNodesAfter)
110 slot.enqueueSlotChangeEvent();
111 }
112
113 void SlotAssignment::hostChildSlotNameChanged(const AtomicString& oldValue, cons t AtomicString& newValue)
114 {
115 if (HTMLSlotElement* slot = findSlotByName(HTMLSlotElement::normalizeSlotNam e(oldValue))) {
116 slot->enqueueSlotChangeEvent();
117 m_owner->owner()->setNeedsDistributionRecalc();
118 }
119 if (HTMLSlotElement* slot = findSlotByName(HTMLSlotElement::normalizeSlotNam e(newValue))) {
120 slot->enqueueSlotChangeEvent();
121 m_owner->owner()->setNeedsDistributionRecalc();
122 }
123 }
124
125 SlotAssignment::SlotAssignment(ShadowRoot& owner)
126 : m_owner(&owner)
127 {
20 } 128 }
21 129
22 static void detachNotAssignedNode(Node& node) 130 static void detachNotAssignedNode(Node& node)
23 { 131 {
24 if (node.layoutObject()) 132 if (node.layoutObject())
25 node.lazyReattachIfAttached(); 133 node.lazyReattachIfAttached();
26 } 134 }
27 135
28 void SlotAssignment::resolveAssignment(ShadowRoot& shadowRoot) 136 void SlotAssignment::resolveAssignment()
29 { 137 {
30 m_assignment.clear(); 138 for (Member<HTMLSlotElement> slot : slots())
139 slot->clearDistribution();
31 140
32 using Name2Slot = HeapHashMap<AtomicString, Member<HTMLSlotElement>>; 141 for (Node& child : NodeTraversal::childrenOf(*m_owner->host())) {
33 Name2Slot name2slot; 142 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); 143 detachNotAssignedNode(child);
47 continue; 144 continue;
48 } 145 }
49 if (!child.slottable()) { 146 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) 147 if (slot)
56 assign(child, *slot); 148 assignTo(child, *slot);
57 else 149 else
58 detachNotAssignedNode(child); 150 detachNotAssignedNode(child);
59 } 151 }
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 } 152 }
69 153
70 void SlotAssignment::resolveDistribution(ShadowRoot& shadowRoot) 154 void SlotAssignment::resolveDistribution()
71 { 155 {
72 const HeapVector<Member<HTMLSlotElement>>& slots = shadowRoot.descendantSlot s(); 156 resolveAssignment();
73 for (Member<HTMLSlotElement> slot : slots) { 157 const HeapVector<Member<HTMLSlotElement>>& slots = this->slots();
74 slot->willUpdateDistribution();
75 }
76
77 for (auto slot : slots) { 158 for (auto slot : slots) {
78 for (auto node : slot->assignedNodes()) 159 for (auto node : slot->assignedNodes())
79 distribute(*node, *slot); 160 distributeTo(*node, *slot);
80 } 161 }
81
82 // Update each slot's distribution in reverse tree order so that a child slo t is visited before its parent slot. 162 // 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) 163 for (auto slot = slots.rbegin(); slot != slots.rend(); ++slot)
84 (*slot)->updateDistributedNodesWithFallback(); 164 (*slot)->updateDistributedNodesWithFallback();
85 for (const auto& slot : slots)
86 slot->didUpdateDistribution();
87 } 165 }
88 166
89 void SlotAssignment::assign(Node& hostChild, HTMLSlotElement& slot) 167 void SlotAssignment::assignTo(Node& hostChild, HTMLSlotElement& slot)
90 { 168 {
91 DCHECK(hostChild.isSlotAssignable()); 169 DCHECK(hostChild.isSlotable());
92 m_assignment.add(&hostChild, &slot);
93 slot.appendAssignedNode(hostChild); 170 slot.appendAssignedNode(hostChild);
94 } 171 }
95 172
96 void SlotAssignment::distribute(Node& hostChild, HTMLSlotElement& slot) 173 void SlotAssignment::distributeTo(Node& hostChild, HTMLSlotElement& slot)
97 { 174 {
98 DCHECK(hostChild.isSlotAssignable()); 175 DCHECK(hostChild.isSlotable());
99 if (isHTMLSlotElement(hostChild)) 176 if (isHTMLSlotElement(hostChild))
100 slot.appendDistributedNodesFrom(toHTMLSlotElement(hostChild)); 177 slot.appendDistributedNodesFrom(toHTMLSlotElement(hostChild));
101 else 178 else
102 slot.appendDistributedNode(hostChild); 179 slot.appendDistributedNode(hostChild);
103 180
104 if (slot.isChildOfV1ShadowHost()) 181 if (slot.isChildOfV1ShadowHost())
105 slot.parentElementShadow()->setNeedsDistributionRecalc(); 182 slot.parentElementShadow()->setNeedsDistributionRecalc();
106 } 183 }
107 184
185 const HeapVector<Member<HTMLSlotElement>>& SlotAssignment::slots()
186 {
187 if (m_needsCollectSlots)
188 collectSlots();
189 return m_slots;
190 }
191
192 HTMLSlotElement* SlotAssignment::findSlot(const Node& node)
193 {
194 return node.isSlotable() ? findSlotByName(node.slotName()) : nullptr;
195 }
196
197 HTMLSlotElement* SlotAssignment::findSlotByName(const AtomicString& slotName)
198 {
199 return entry(slotName).firstSlot();
esprehn 2016/05/23 06:41:00 Hmm, we should just reuse the same map machinery w
hayato 2016/05/24 13:23:39 Ops. Thank you for letting me notice that. It look
200 }
201
202 void SlotAssignment::collectSlots()
203 {
204 DCHECK(m_needsCollectSlots);
205 m_slots.clear();
206
207 m_slots.reserveCapacity(m_slotCount);
208 for (HTMLSlotElement& slot : Traversal<HTMLSlotElement>::descendantsOf(*m_ow ner)) {
esprehn 2016/05/23 06:41:00 we can just use HTMLSlotElement::insertedInto and
hayato 2016/05/24 13:23:38 Yeah, but we need an ordered list of *all* slots,
209 m_slots.append(&slot);
210 }
211 m_needsCollectSlots = false;
212 DCHECK_EQ(m_slots.size(), m_slotCount);
213 }
214
108 DEFINE_TRACE(SlotAssignment) 215 DEFINE_TRACE(SlotAssignment)
109 { 216 {
110 visitor->trace(m_descendantSlots); 217 visitor->trace(m_slots);
111 visitor->trace(m_assignment); 218 visitor->trace(m_slotEntryMap);
219 visitor->trace(m_owner);
112 } 220 }
113 221
114 } // namespace blink 222 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698