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

Side by Side Diff: third_party/WebKit/Source/core/html/HTMLSlotElement.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: clean up Created 4 years, 6 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 /* 1 /*
2 * Copyright (C) 2015 Google Inc. All rights reserved. 2 * Copyright (C) 2015 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 20 matching lines...) Expand all
31 #include "core/html/HTMLSlotElement.h" 31 #include "core/html/HTMLSlotElement.h"
32 32
33 #include "bindings/core/v8/Microtask.h" 33 #include "bindings/core/v8/Microtask.h"
34 #include "core/HTMLNames.h" 34 #include "core/HTMLNames.h"
35 #include "core/dom/ElementTraversal.h" 35 #include "core/dom/ElementTraversal.h"
36 #include "core/dom/NodeTraversal.h" 36 #include "core/dom/NodeTraversal.h"
37 #include "core/dom/StyleChangeReason.h" 37 #include "core/dom/StyleChangeReason.h"
38 #include "core/dom/StyleEngine.h" 38 #include "core/dom/StyleEngine.h"
39 #include "core/dom/shadow/ElementShadow.h" 39 #include "core/dom/shadow/ElementShadow.h"
40 #include "core/dom/shadow/InsertionPoint.h" 40 #include "core/dom/shadow/InsertionPoint.h"
41 #include "core/dom/shadow/SlotAssignment.h"
41 #include "core/events/Event.h" 42 #include "core/events/Event.h"
42 #include "core/html/AssignedNodesOptions.h" 43 #include "core/html/AssignedNodesOptions.h"
43 44
44 namespace blink { 45 namespace blink {
45 46
46 using namespace HTMLNames; 47 using namespace HTMLNames;
47 48
48 inline HTMLSlotElement::HTMLSlotElement(Document& document) 49 inline HTMLSlotElement::HTMLSlotElement(Document& document)
49 : HTMLElement(slotTag, document) 50 : HTMLElement(slotTag, document)
50 , m_distributionState(DistributionDone)
51 , m_assignmentState(AssignmentDone)
52 , m_slotchangeEventAdded(false)
53 { 51 {
54 setHasCustomStyleCallbacks(); 52 setHasCustomStyleCallbacks();
55 } 53 }
56 54
57 DEFINE_NODE_FACTORY(HTMLSlotElement); 55 DEFINE_NODE_FACTORY(HTMLSlotElement);
58 56
57 // static
58 AtomicString HTMLSlotElement::normalizeSlotName(const AtomicString& name)
59 {
60 return (name.isNull() || name.isEmpty()) ? emptyAtom : name;
61 }
62
63 const HeapVector<Member<Node>>& HTMLSlotElement::assignedNodes()
64 {
65 DCHECK(!needsDistributionRecalc());
66 DCHECK(isInShadowTree() || m_assignedNodes.isEmpty());
67 return m_assignedNodes;
68 }
69
59 const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const As signedNodesOptions& options) 70 const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const As signedNodesOptions& options)
60 { 71 {
61 updateDistribution(); 72 updateDistribution();
62 if (options.hasFlatten() && options.flatten()) 73 if (options.hasFlatten() && options.flatten())
63 return getDistributedNodes(); 74 return getDistributedNodes();
64 return m_assignedNodes; 75 return m_assignedNodes;
65 } 76 }
66 77
67 const HeapVector<Member<Node>>& HTMLSlotElement::getDistributedNodes() 78 const HeapVector<Member<Node>>& HTMLSlotElement::getDistributedNodes()
68 { 79 {
69 ASSERT(!needsDistributionRecalc()); 80 DCHECK(!needsDistributionRecalc());
70 if (isInShadowTree()) 81 if (isInShadowTree())
71 return m_distributedNodes; 82 return m_distributedNodes;
72 83
73 // A slot is unlikely to be used outside of a shadow tree. 84 // A slot is unlikely to be used outside of a shadow tree.
74 // We do not need to optimize this case in most cases. 85 // We do not need to optimize this case in most cases.
75 // TODO(hayato): If this path causes a performance issue, we should move 86 // TODO(hayato): If this path causes a performance issue, we should move
76 // ShadowRootRaraDate::m_descendantSlots into TreeScopreRareData-ish and 87 // ShadowRootRaraDate::m_descendantSlots into TreeScopreRareData-ish and
77 // update the distribution code so it considers a document tree too. 88 // update the distribution code so it considers a document tree too.
78 willUpdateDistribution(); 89 clearDistribution();
79 for (Node& child : NodeTraversal::childrenOf(*this)) { 90 for (Node& child : NodeTraversal::childrenOf(*this)) {
80 if (!child.isSlotAssignable()) 91 if (!child.isSlotable())
81 continue; 92 continue;
82 if (isHTMLSlotElement(child)) 93 if (isHTMLSlotElement(child))
83 m_distributedNodes.appendVector(toHTMLSlotElement(child).getDistribu tedNodes()); 94 m_distributedNodes.appendVector(toHTMLSlotElement(child).getDistribu tedNodes());
84 else 95 else
85 m_distributedNodes.append(&child); 96 m_distributedNodes.append(&child);
86 } 97 }
87 didUpdateDistribution();
88 return m_distributedNodes; 98 return m_distributedNodes;
89 } 99 }
90 100
91 void HTMLSlotElement::appendAssignedNode(Node& node) 101 void HTMLSlotElement::appendAssignedNode(Node& hostChild)
92 { 102 {
93 ASSERT(m_assignmentState == AssignmentOnGoing); 103 DCHECK(hostChild.isSlotable());
94 m_assignedNodes.append(&node); 104 m_assignedNodes.append(&hostChild);
105 }
106
107 void HTMLSlotElement::resolveDistributedNodes()
108 {
109 for (auto& node : m_assignedNodes) {
110 DCHECK(node->isSlotable());
111 if (isHTMLSlotElement(*node))
112 appendDistributedNodesFrom(toHTMLSlotElement(*node));
113 else
114 appendDistributedNode(*node);
115
116 if (isChildOfV1ShadowHost())
117 parentElementShadow()->setNeedsDistributionRecalc();
118 }
95 } 119 }
96 120
97 void HTMLSlotElement::appendDistributedNode(Node& node) 121 void HTMLSlotElement::appendDistributedNode(Node& node)
98 { 122 {
99 ASSERT(m_distributionState == DistributionOnGoing);
100 size_t size = m_distributedNodes.size(); 123 size_t size = m_distributedNodes.size();
101 m_distributedNodes.append(&node); 124 m_distributedNodes.append(&node);
102 m_distributedIndices.set(&node, size); 125 m_distributedIndices.set(&node, size);
103 } 126 }
104 127
105 void HTMLSlotElement::appendFallbackNode(Node& node)
106 {
107 ASSERT(m_assignmentState == AssignmentOnGoing);
108 m_fallbackNodes.append(&node);
109 }
110
111 void HTMLSlotElement::appendDistributedNodesFrom(const HTMLSlotElement& other) 128 void HTMLSlotElement::appendDistributedNodesFrom(const HTMLSlotElement& other)
112 { 129 {
113 ASSERT(m_distributionState == DistributionOnGoing);
114 size_t index = m_distributedNodes.size(); 130 size_t index = m_distributedNodes.size();
115 m_distributedNodes.appendVector(other.m_distributedNodes); 131 m_distributedNodes.appendVector(other.m_distributedNodes);
116 for (const auto& node : other.m_distributedNodes) 132 for (const auto& node : other.m_distributedNodes)
117 m_distributedIndices.set(node.get(), index++); 133 m_distributedIndices.set(node.get(), index++);
118 } 134 }
119 135
120 void HTMLSlotElement::willUpdateAssignment() 136 void HTMLSlotElement::clearDistribution()
121 { 137 {
122 ASSERT(m_assignmentState != AssignmentOnGoing);
123 m_assignmentState = AssignmentOnGoing;
124 m_oldAssignedNodes.swap(m_assignedNodes);
125 m_assignedNodes.clear(); 138 m_assignedNodes.clear();
126 }
127
128 void HTMLSlotElement::willUpdateDistribution()
129 {
130 ASSERT(m_distributionState != DistributionOnGoing);
131 m_distributionState = DistributionOnGoing;
132 m_oldDistributedNodes.swap(m_distributedNodes);
133 m_distributedNodes.clear(); 139 m_distributedNodes.clear();
134 m_distributedIndices.clear(); 140 m_distributedIndices.clear();
135 } 141 }
136 142
137 void HTMLSlotElement::willUpdateFallback()
138 {
139 m_oldFallbackNodes.swap(m_fallbackNodes);
140 m_fallbackNodes.clear();
141 }
142
143 void HTMLSlotElement::dispatchSlotChangeEvent() 143 void HTMLSlotElement::dispatchSlotChangeEvent()
144 { 144 {
145 m_slotchangeEventEnqueued = false;
145 Event* event = Event::create(EventTypeNames::slotchange); 146 Event* event = Event::create(EventTypeNames::slotchange);
146 event->setTarget(this); 147 event->setTarget(this);
147 dispatchScopedEvent(event); 148 dispatchScopedEvent(event);
148 m_slotchangeEventAdded = false;
149 } 149 }
150 150
151 Node* HTMLSlotElement::distributedNodeNextTo(const Node& node) const 151 Node* HTMLSlotElement::distributedNodeNextTo(const Node& node) const
152 { 152 {
153 const auto& it = m_distributedIndices.find(&node); 153 const auto& it = m_distributedIndices.find(&node);
154 if (it == m_distributedIndices.end()) 154 if (it == m_distributedIndices.end())
155 return nullptr; 155 return nullptr;
156 size_t index = it->value; 156 size_t index = it->value;
157 if (index + 1 == m_distributedNodes.size()) 157 if (index + 1 == m_distributedNodes.size())
158 return nullptr; 158 return nullptr;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 for (auto& node : m_distributedNodes) 190 for (auto& node : m_distributedNodes)
191 node->lazyReattachIfAttached(); 191 node->lazyReattachIfAttached();
192 192
193 HTMLElement::detach(context); 193 HTMLElement::detach(context);
194 } 194 }
195 195
196 void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicSt ring& oldValue, const AtomicString& newValue, AttributeModificationReason reason ) 196 void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicSt ring& oldValue, const AtomicString& newValue, AttributeModificationReason reason )
197 { 197 {
198 if (name == nameAttr) { 198 if (name == nameAttr) {
199 if (ShadowRoot* root = containingShadowRoot()) { 199 if (ShadowRoot* root = containingShadowRoot()) {
200 root->owner()->willAffectSelector();
201 if (root->isV1() && oldValue != newValue) 200 if (root->isV1() && oldValue != newValue)
202 root->assignV1(); 201 root->ensureSlotAssignment().slotRenamed(normalizeSlotName(oldVa lue), *this);
203 } 202 }
204 } 203 }
205 HTMLElement::attributeChanged(name, oldValue, newValue, reason); 204 HTMLElement::attributeChanged(name, oldValue, newValue, reason);
206 } 205 }
207 206
208 void HTMLSlotElement::childrenChanged(const ChildrenChange& change) 207 static bool wasInShadowTreeBeforeInserted(HTMLSlotElement& slot, ContainerNode& insertionPoint)
209 { 208 {
210 HTMLElement::childrenChanged(change); 209 ShadowRoot* root1 = slot.containingShadowRoot();
211 if (ShadowRoot* root = containingShadowRoot()) { 210 ShadowRoot* root2 = insertionPoint.containingShadowRoot();
212 if (ElementShadow* rootOwner = root->owner()) { 211 if (root1 && root2 && root1 == root2)
213 rootOwner->setNeedsDistributionRecalc(); 212 return false;
214 } 213 return root1;
215 if (m_assignedNodes.isEmpty() && root->isV1())
216 root->assignV1();
217 }
218 } 214 }
219 215
220 Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* insertionPoint) 216 Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* insertionPoint)
221 { 217 {
222 HTMLElement::insertedInto(insertionPoint); 218 HTMLElement::insertedInto(insertionPoint);
223 ShadowRoot* root = containingShadowRoot(); 219 ShadowRoot* root = containingShadowRoot();
224 if (root) { 220 if (root) {
225 if (ElementShadow* rootOwner = root->owner()) 221 DCHECK(root->owner());
226 rootOwner->setNeedsDistributionRecalc(); 222 root->owner()->setNeedsDistributionRecalc();
227 if (root == insertionPoint->treeScope().rootNode()) 223 // Relevant DOM Standard: https://dom.spec.whatwg.org/#concept-node-inse rt
228 root->didAddSlot(); 224 // - 6.4: Run assign slotables for a tree with node's tree and a set co ntaining each inclusive descendant of node that is a slot.
225 if (!wasInShadowTreeBeforeInserted(*this, *insertionPoint))
226 root->ensureSlotAssignment().slotAdded(*this);
229 } 227 }
230 228
231 // We could have been distributed into in a detached subtree, make sure to 229 // We could have been distributed into in a detached subtree, make sure to
232 // clear the distribution when inserted again to avoid cycles. 230 // clear the distribution when inserted again to avoid cycles.
233 clearDistribution(); 231 clearDistribution();
234 232
235 if (root && root->isV1()) 233 return InsertionDone;
236 root->assignV1(); 234 }
237 235
238 return InsertionDone; 236 static ShadowRoot* containingShadowRootBeforeRemoved(Node& removedDescendant, Co ntainerNode& insertionPoint)
237 {
238 if (ShadowRoot* root = removedDescendant.containingShadowRoot())
239 return root;
240 return insertionPoint.containingShadowRoot();
239 } 241 }
240 242
241 void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) 243 void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint)
242 { 244 {
243 ShadowRoot* root = containingShadowRoot(); 245 // `removedFrom` is called after the node is removed from the tree.
244 if (!root) 246 // That means:
245 root = insertionPoint->containingShadowRoot(); 247 // 1. If this slot is still in a tree scope, it means the slot has been in a shadow tree. An inclusive shadow-including ancestor of the shadow host was orig inally removed from its parent.
248 // 2. Or (this slot is now not in a tree scope), this slot's inclusive ances tor was orginally removed from its parent (== insertion point). This slot and th e originally removed node was in the same tree.
249
250 ShadowRoot* root = containingShadowRootBeforeRemoved(*this, *insertionPoint) ;
246 if (root) { 251 if (root) {
247 if (ElementShadow* rootOwner = root->owner()) 252 if (ElementShadow* rootOwner = root->owner())
248 rootOwner->setNeedsDistributionRecalc(); 253 rootOwner->setNeedsDistributionRecalc();
249 } 254 }
250 255
251 // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up. 256 // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up.
252 clearDistribution(); 257 clearDistribution();
253 258
254 ContainerNode& rootNode = insertionPoint->treeScope().rootNode(); 259 if (root && root->isV1() && root == &insertionPoint->treeScope().rootNode()) {
kochi 2016/05/25 06:40:26 nit: no need for &
hayato 2016/05/26 04:46:07 Done
255 if (root == &rootNode) 260 // This slot was in a shadow tree and got disconnected from the shadow r oot.
256 root->didRemoveSlot(); 261 root->ensureSlotAssignment().slotRemoved(*this);
257 else if (rootNode.isShadowRoot() && toShadowRoot(rootNode).isV1()) 262 }
258 toShadowRoot(rootNode).assignV1();
259 263
260 if (root && root->isV1())
261 root->assignV1();
262 HTMLElement::removedFrom(insertionPoint); 264 HTMLElement::removedFrom(insertionPoint);
263 } 265 }
264 266
265 void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) 267 void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change)
266 { 268 {
267 if (change < Inherit && getStyleChangeType() < SubtreeStyleChange) 269 if (change < Inherit && getStyleChangeType() < SubtreeStyleChange)
268 return; 270 return;
269 271
270 for (auto& node : m_distributedNodes) 272 for (auto& node : m_distributedNodes)
271 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing: :create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); 273 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing: :create(StyleChangeReason::PropagateInheritChangeToDistributedNodes));
272 } 274 }
273 275
274 void HTMLSlotElement::updateFallbackNodes()
275 {
276 if (!m_fallbackNodes.isEmpty())
277 return;
278 for (auto& child : NodeTraversal::childrenOf(*this)) {
279 if (!child.isSlotAssignable())
280 continue;
281 // Insertion points are not supported as slots fallback
282 if (isActiveInsertionPoint(child))
283 continue;
284 appendFallbackNode(child);
285 }
286 }
287
288 void HTMLSlotElement::updateDistributedNodesWithFallback() 276 void HTMLSlotElement::updateDistributedNodesWithFallback()
289 { 277 {
290 if (!m_distributedNodes.isEmpty()) 278 if (!m_distributedNodes.isEmpty())
291 return; 279 return;
292 for (auto node : m_fallbackNodes) { 280 for (auto& child : NodeTraversal::childrenOf(*this)) {
293 if (isHTMLSlotElement(node)) 281 if (!child.isSlotable())
294 appendDistributedNodesFrom(*toHTMLSlotElement(node)); 282 continue;
283 if (isHTMLSlotElement(child))
284 appendDistributedNodesFrom(toHTMLSlotElement(child));
295 else 285 else
296 appendDistributedNode(*node); 286 appendDistributedNode(child);
297 } 287 }
298 } 288 }
299 289
300 bool HTMLSlotElement::assignmentChanged() 290 void HTMLSlotElement::enqueueSlotChangeEvent()
301 { 291 {
302 ASSERT(m_assignmentState != AssignmentOnGoing); 292 if (!m_slotchangeEventEnqueued) {
303 if (m_assignmentState == AssignmentDone) 293 Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChan geEvent, Persistent<HTMLSlotElement>(this)));
304 m_assignmentState = m_oldAssignedNodes == m_assignedNodes ? AssignmentUn changed : AssignmentChanged; 294 m_slotchangeEventEnqueued = true;
305 return m_assignmentState == AssignmentChanged; 295 }
306 }
307 296
308 bool HTMLSlotElement::distributionChanged() 297 ShadowRoot* root = containingShadowRoot();
309 { 298 DCHECK(root);
310 ASSERT(m_distributionState != DistributionOnGoing); 299 DCHECK(root->isV1());
311 if (m_distributionState == DistributionDone) 300 root->owner()->setNeedsDistributionRecalc();
312 m_distributionState = m_oldDistributedNodes == m_distributedNodes ? Dist ributionUnchanged : DistributionChanged;
313 return m_distributionState == DistributionChanged;
314 }
315 301
316 bool HTMLSlotElement::fallbackChanged() 302 if (ShadowRoot* parentShadowRoot = v1ShadowRootOfParent()) {
317 { 303 if (HTMLSlotElement* next = parentShadowRoot->ensureSlotAssignment().fin dSlot(*this))
318 return m_oldFallbackNodes == m_fallbackNodes; 304 next->enqueueSlotChangeEvent();
319 }
320
321 void HTMLSlotElement::didUpdateAssignment()
322 {
323 ASSERT(m_assignmentState == AssignmentOnGoing);
324 m_assignmentState = AssignmentDone;
325 if ((assignmentChanged() || fallbackChanged()) && !m_slotchangeEventAdded)
326 fireSlotChangeEvent();
327 }
328
329 void HTMLSlotElement::didUpdateDistribution()
330 {
331 ASSERT(m_distributionState == DistributionOnGoing);
332 m_distributionState = DistributionDone;
333 if (isChildOfV1ShadowHost()) {
334 ElementShadow* shadow = parentElementShadow();
335 ASSERT(shadow);
336 if (!shadow->needsDistributionRecalc() && distributionChanged())
337 shadow->setNeedsDistributionRecalc();
338 } 305 }
339 } 306 }
340 307
341 void HTMLSlotElement::fireSlotChangeEvent() 308 bool HTMLSlotElement::hasAssignedNodesSlow() const
342 { 309 {
343 ASSERT(!m_slotchangeEventAdded); 310 ShadowRoot* root = containingShadowRoot();
344 Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEv ent, this)); 311 DCHECK(root);
345 m_slotchangeEventAdded = true; 312 DCHECK(root->isV1());
346 313 SlotAssignment& assignment = root->ensureSlotAssignment();
347 Element* shadowHost = isShadowHost(parentElement()) ? parentElement() : null ptr; 314 if (assignment.findSlot(*this) != this)
348 // If this slot is assigned to another slot, fire slot change event of that slot too. 315 return false;
349 if (shadowHost && shadowHost->shadowRootIfV1()) { 316 return assignment.findHostChildBySlotName(name());
350 if (HTMLSlotElement* assigned = assignedSlot()) {
351 if (!assigned->m_slotchangeEventAdded)
352 assigned->fireSlotChangeEvent();
353 }
354 }
355 } 317 }
356 318
357 void HTMLSlotElement::clearDistribution() 319 bool HTMLSlotElement::findHostChildWithSameSlotName() const
358 { 320 {
359 willUpdateDistribution(); 321 ShadowRoot* root = containingShadowRoot();
360 didUpdateDistribution(); 322 DCHECK(root);
323 DCHECK(root->isV1());
324 SlotAssignment& assignment = root->ensureSlotAssignment();
325 return assignment.findHostChildBySlotName(name());
361 } 326 }
362 327
363 short HTMLSlotElement::tabIndex() const 328 short HTMLSlotElement::tabIndex() const
364 { 329 {
365 return Element::tabIndex(); 330 return Element::tabIndex();
366 } 331 }
367 332
368 DEFINE_TRACE(HTMLSlotElement) 333 DEFINE_TRACE(HTMLSlotElement)
369 { 334 {
370 visitor->trace(m_assignedNodes); 335 visitor->trace(m_assignedNodes);
371 visitor->trace(m_distributedNodes); 336 visitor->trace(m_distributedNodes);
372 visitor->trace(m_fallbackNodes);
373 visitor->trace(m_distributedIndices); 337 visitor->trace(m_distributedIndices);
374 visitor->trace(m_oldAssignedNodes);
375 visitor->trace(m_oldDistributedNodes);
376 visitor->trace(m_oldFallbackNodes);
377 HTMLElement::trace(visitor); 338 HTMLElement::trace(visitor);
378 } 339 }
379 340
380 } // namespace blink 341 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698