OLD | NEW |
---|---|
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 14 matching lines...) Expand all Loading... | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | 30 |
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/NodeTraversal.h" | 36 #include "core/dom/NodeTraversal.h" |
36 #include "core/dom/StyleChangeReason.h" | 37 #include "core/dom/StyleChangeReason.h" |
38 #include "core/dom/StyleEngine.h" | |
37 #include "core/dom/shadow/ElementShadow.h" | 39 #include "core/dom/shadow/ElementShadow.h" |
38 #include "core/dom/shadow/InsertionPoint.h" | 40 #include "core/dom/shadow/InsertionPoint.h" |
39 #include "core/events/Event.h" | 41 #include "core/events/Event.h" |
40 #include "core/html/AssignedNodesOptions.h" | 42 #include "core/html/AssignedNodesOptions.h" |
41 | 43 |
42 namespace blink { | 44 namespace blink { |
43 | 45 |
44 using namespace HTMLNames; | 46 using namespace HTMLNames; |
45 | 47 |
46 inline HTMLSlotElement::HTMLSlotElement(Document& document) | 48 inline HTMLSlotElement::HTMLSlotElement(Document& document) |
47 : HTMLElement(slotTag, document) | 49 : HTMLElement(slotTag, document) |
48 , m_distributionState(DistributionDone) | 50 , m_distributionState(DistributionDone) |
51 , m_assignmentState(AssignmentDone) | |
52 , m_slotchangeEventAdded(false) | |
49 { | 53 { |
50 setHasCustomStyleCallbacks(); | 54 setHasCustomStyleCallbacks(); |
51 } | 55 } |
52 | 56 |
53 DEFINE_NODE_FACTORY(HTMLSlotElement); | 57 DEFINE_NODE_FACTORY(HTMLSlotElement); |
54 | 58 |
55 const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const As signedNodesOptions& options) | 59 const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const As signedNodesOptions& options) |
56 { | 60 { |
57 updateDistribution(); | 61 updateDistribution(); |
58 if (options.hasFlatten() && options.flatten()) | 62 if (options.hasFlatten() && options.flatten()) |
(...skipping 20 matching lines...) Expand all Loading... | |
79 m_distributedNodes.appendVector(toHTMLSlotElement(child).getDistribu tedNodes()); | 83 m_distributedNodes.appendVector(toHTMLSlotElement(child).getDistribu tedNodes()); |
80 else | 84 else |
81 m_distributedNodes.append(&child); | 85 m_distributedNodes.append(&child); |
82 } | 86 } |
83 didUpdateDistribution(); | 87 didUpdateDistribution(); |
84 return m_distributedNodes; | 88 return m_distributedNodes; |
85 } | 89 } |
86 | 90 |
87 void HTMLSlotElement::appendAssignedNode(Node& node) | 91 void HTMLSlotElement::appendAssignedNode(Node& node) |
88 { | 92 { |
89 ASSERT(m_distributionState == DistributionOnGoing); | 93 ASSERT(m_assignmentState == AssignmentOnGoing); |
90 m_assignedNodes.append(&node); | 94 m_assignedNodes.append(&node); |
91 } | 95 } |
92 | 96 |
93 void HTMLSlotElement::appendDistributedNode(Node& node) | 97 void HTMLSlotElement::appendDistributedNode(Node& node) |
94 { | 98 { |
95 ASSERT(m_distributionState == DistributionOnGoing); | 99 ASSERT(m_distributionState == DistributionOnGoing); |
96 size_t size = m_distributedNodes.size(); | 100 size_t size = m_distributedNodes.size(); |
97 m_distributedNodes.append(&node); | 101 m_distributedNodes.append(&node); |
98 m_distributedIndices.set(&node, size); | 102 m_distributedIndices.set(&node, size); |
99 } | 103 } |
100 | 104 |
105 void HTMLSlotElement::appendFallbackNode(Node& node) | |
106 { | |
107 ASSERT(m_assignmentState == AssignmentOnGoing); | |
108 m_fallbackNodes.append(&node); | |
109 } | |
110 | |
101 void HTMLSlotElement::appendDistributedNodesFrom(const HTMLSlotElement& other) | 111 void HTMLSlotElement::appendDistributedNodesFrom(const HTMLSlotElement& other) |
102 { | 112 { |
103 ASSERT(m_distributionState == DistributionOnGoing); | 113 ASSERT(m_distributionState == DistributionOnGoing); |
104 size_t index = m_distributedNodes.size(); | 114 size_t index = m_distributedNodes.size(); |
105 m_distributedNodes.appendVector(other.m_distributedNodes); | 115 m_distributedNodes.appendVector(other.m_distributedNodes); |
106 for (const auto& node : other.m_distributedNodes) | 116 for (const auto& node : other.m_distributedNodes) |
107 m_distributedIndices.set(node.get(), index++); | 117 m_distributedIndices.set(node.get(), index++); |
108 } | 118 } |
109 | 119 |
120 void HTMLSlotElement::willUpdateAssignment() | |
121 { | |
122 ASSERT(m_assignmentState != AssignmentOnGoing); | |
123 m_assignmentState = AssignmentOnGoing; | |
124 m_oldAssignedNodes.swap(m_assignedNodes); | |
125 m_assignedNodes.clear(); | |
126 } | |
127 | |
110 void HTMLSlotElement::willUpdateDistribution() | 128 void HTMLSlotElement::willUpdateDistribution() |
111 { | 129 { |
112 ASSERT(m_distributionState != DistributionOnGoing); | 130 ASSERT(m_distributionState != DistributionOnGoing); |
113 m_distributionState = DistributionOnGoing; | 131 m_distributionState = DistributionOnGoing; |
114 m_assignedNodes.clear(); | |
115 m_oldDistributedNodes.swap(m_distributedNodes); | 132 m_oldDistributedNodes.swap(m_distributedNodes); |
116 m_distributedNodes.clear(); | 133 m_distributedNodes.clear(); |
117 m_distributedIndices.clear(); | 134 m_distributedIndices.clear(); |
118 } | 135 } |
119 | 136 |
120 bool HTMLSlotElement::hasSlotChangeEventListener() | 137 void HTMLSlotElement::willUpdateFallback() |
121 { | 138 { |
122 return eventTargetData() && eventTargetData()->eventListenerMap.find(EventTy peNames::slotchange); | 139 m_oldFallbackNodes.swap(m_fallbackNodes); |
140 m_fallbackNodes.clear(); | |
123 } | 141 } |
124 | 142 |
125 void HTMLSlotElement::dispatchSlotChangeEvent() | 143 void HTMLSlotElement::dispatchSlotChangeEvent() |
126 { | 144 { |
127 Event* event = Event::create(EventTypeNames::slotchange); | 145 Event* event = Event::create(EventTypeNames::slotchange); |
128 event->setTarget(this); | 146 event->setTarget(this); |
129 dispatchScopedEvent(event); | 147 dispatchScopedEvent(event); |
148 m_slotchangeEventAdded = false; | |
130 } | 149 } |
131 | 150 |
132 Node* HTMLSlotElement::distributedNodeNextTo(const Node& node) const | 151 Node* HTMLSlotElement::distributedNodeNextTo(const Node& node) const |
133 { | 152 { |
134 const auto& it = m_distributedIndices.find(&node); | 153 const auto& it = m_distributedIndices.find(&node); |
135 if (it == m_distributedIndices.end()) | 154 if (it == m_distributedIndices.end()) |
136 return nullptr; | 155 return nullptr; |
137 size_t index = it->value; | 156 size_t index = it->value; |
138 if (index + 1 == m_distributedNodes.size()) | 157 if (index + 1 == m_distributedNodes.size()) |
139 return nullptr; | 158 return nullptr; |
(...skipping 30 matching lines...) Expand all Loading... | |
170 { | 189 { |
171 for (auto& node : m_distributedNodes) | 190 for (auto& node : m_distributedNodes) |
172 node->lazyReattachIfAttached(); | 191 node->lazyReattachIfAttached(); |
173 | 192 |
174 HTMLElement::detach(context); | 193 HTMLElement::detach(context); |
175 } | 194 } |
176 | 195 |
177 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 ) |
178 { | 197 { |
179 if (name == nameAttr) { | 198 if (name == nameAttr) { |
180 if (ShadowRoot* root = containingShadowRoot()) | 199 if (ShadowRoot* root = containingShadowRoot()) { |
181 root->owner()->willAffectSelector(); | 200 root->owner()->willAffectSelector(); |
201 if (root->isV1() && oldValue != newValue) | |
202 root->assignV1(); | |
203 } | |
182 } | 204 } |
183 HTMLElement::attributeChanged(name, oldValue, newValue, reason); | 205 HTMLElement::attributeChanged(name, oldValue, newValue, reason); |
184 } | 206 } |
185 | 207 |
186 void HTMLSlotElement::childrenChanged(const ChildrenChange& change) | 208 void HTMLSlotElement::childrenChanged(const ChildrenChange& change) |
187 { | 209 { |
188 HTMLElement::childrenChanged(change); | 210 HTMLElement::childrenChanged(change); |
189 if (ShadowRoot* root = containingShadowRoot()) { | 211 if (ShadowRoot* root = containingShadowRoot()) { |
190 if (ElementShadow* rootOwner = root->owner()) | 212 if (ElementShadow* rootOwner = root->owner()) { |
191 rootOwner->setNeedsDistributionRecalc(); | 213 rootOwner->setNeedsDistributionRecalc(); |
214 } | |
215 if (m_assignedNodes.isEmpty() && root->isV1()) | |
216 root->assignV1(); | |
192 } | 217 } |
193 } | 218 } |
194 | 219 |
195 Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* insertionPoint) | 220 Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* insertionPoint) |
196 { | 221 { |
197 HTMLElement::insertedInto(insertionPoint); | 222 HTMLElement::insertedInto(insertionPoint); |
198 if (ShadowRoot* root = containingShadowRoot()) { | 223 ShadowRoot* root = containingShadowRoot(); |
224 if (root) { | |
199 if (ElementShadow* rootOwner = root->owner()) | 225 if (ElementShadow* rootOwner = root->owner()) |
200 rootOwner->setNeedsDistributionRecalc(); | 226 rootOwner->setNeedsDistributionRecalc(); |
201 if (root == insertionPoint->treeScope().rootNode()) | 227 if (root == insertionPoint->treeScope().rootNode()) |
202 root->didAddSlot(); | 228 root->didAddSlot(); |
203 } | 229 } |
204 | 230 |
205 // We could have been distributed into in a detached subtree, make sure to | 231 // We could have been distributed into in a detached subtree, make sure to |
206 // clear the distribution when inserted again to avoid cycles. | 232 // clear the distribution when inserted again to avoid cycles. |
207 clearDistribution(); | 233 clearDistribution(); |
208 | 234 |
235 if (root && root->isV1()) | |
236 root->assignV1(); | |
237 | |
209 return InsertionDone; | 238 return InsertionDone; |
210 } | 239 } |
211 | 240 |
212 void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) | 241 void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) |
213 { | 242 { |
214 ShadowRoot* root = containingShadowRoot(); | 243 ShadowRoot* root = containingShadowRoot(); |
215 if (!root) | 244 if (!root) |
216 root = insertionPoint->containingShadowRoot(); | 245 root = insertionPoint->containingShadowRoot(); |
217 if (root) { | 246 if (root) { |
218 if (ElementShadow* rootOwner = root->owner()) | 247 if (ElementShadow* rootOwner = root->owner()) |
219 rootOwner->setNeedsDistributionRecalc(); | 248 rootOwner->setNeedsDistributionRecalc(); |
220 } | 249 } |
221 | 250 |
222 // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up. | 251 // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up. |
223 clearDistribution(); | 252 clearDistribution(); |
224 | 253 |
225 if (root == insertionPoint->treeScope().rootNode()) | 254 ContainerNode& rootNode = insertionPoint->treeScope().rootNode(); |
255 if (root == &rootNode) | |
226 root->didRemoveSlot(); | 256 root->didRemoveSlot(); |
257 else if (rootNode.isShadowRoot() && toShadowRoot(rootNode).isV1()) | |
258 toShadowRoot(rootNode).assignV1(); | |
227 | 259 |
260 if (root && root->isV1()) | |
261 root->assignV1(); | |
228 HTMLElement::removedFrom(insertionPoint); | 262 HTMLElement::removedFrom(insertionPoint); |
229 } | 263 } |
230 | 264 |
231 void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) | 265 void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) |
232 { | 266 { |
233 if (change < Inherit && getStyleChangeType() < SubtreeStyleChange) | 267 if (change < Inherit && getStyleChangeType() < SubtreeStyleChange) |
234 return; | 268 return; |
235 | 269 |
236 for (auto& node : m_distributedNodes) | 270 for (auto& node : m_distributedNodes) |
237 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing: :create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); | 271 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing: :create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); |
238 } | 272 } |
239 | 273 |
240 void HTMLSlotElement::updateDistributedNodesWithFallback() | 274 void HTMLSlotElement::updateFallbackNodes() |
241 { | 275 { |
242 if (!m_distributedNodes.isEmpty()) | 276 if (!m_fallbackNodes.isEmpty()) |
243 return; | 277 return; |
244 for (auto& child : NodeTraversal::childrenOf(*this)) { | 278 for (auto& child : NodeTraversal::childrenOf(*this)) { |
245 if (!child.isSlotAssignable()) | 279 if (!child.isSlotAssignable()) |
246 continue; | 280 continue; |
247 // Insertion points are not supported as slots fallback | 281 // Insertion points are not supported as slots fallback |
248 if (isActiveInsertionPoint(child)) | 282 if (isActiveInsertionPoint(child)) |
249 continue; | 283 continue; |
250 if (isHTMLSlotElement(child)) | 284 appendFallbackNode(child); |
251 appendDistributedNodesFrom(toHTMLSlotElement(child)); | 285 } |
286 } | |
287 | |
288 void HTMLSlotElement::updateDistributedNodesWithFallback() | |
289 { | |
290 if (!m_distributedNodes.isEmpty()) | |
291 return; | |
292 for (auto node : m_fallbackNodes) { | |
293 if (isHTMLSlotElement(node)) | |
294 appendDistributedNodesFrom(*toHTMLSlotElement(node)); | |
252 else | 295 else |
253 appendDistributedNode(child); | 296 appendDistributedNode(*node); |
254 } | 297 } |
255 } | 298 } |
256 | 299 |
300 bool HTMLSlotElement::assignmentChanged() | |
301 { | |
302 ASSERT(m_assignmentState != AssignmentOnGoing); | |
303 if (m_assignmentState == AssignmentDone) | |
304 m_assignmentState = m_oldAssignedNodes == m_assignedNodes ? AssignmentUn changed : AssignmentChanged; | |
305 return m_assignmentState == AssignmentChanged; | |
306 } | |
307 | |
257 bool HTMLSlotElement::distributionChanged() | 308 bool HTMLSlotElement::distributionChanged() |
258 { | 309 { |
259 ASSERT(m_distributionState != DistributionOnGoing); | 310 ASSERT(m_distributionState != DistributionOnGoing); |
260 if (m_distributionState == DistributionDone) | 311 if (m_distributionState == DistributionDone) |
261 m_distributionState = m_oldDistributedNodes == m_distributedNodes ? Dist ributionUnchanged : DistributionChanged; | 312 m_distributionState = m_oldDistributedNodes == m_distributedNodes ? Dist ributionUnchanged : DistributionChanged; |
262 return m_distributionState == DistributionChanged; | 313 return m_distributionState == DistributionChanged; |
263 } | 314 } |
264 | 315 |
316 bool HTMLSlotElement::fallbackChanged() | |
317 { | |
318 return m_oldFallbackNodes == m_fallbackNodes; | |
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 | |
265 void HTMLSlotElement::didUpdateDistribution() | 329 void HTMLSlotElement::didUpdateDistribution() |
266 { | 330 { |
267 ASSERT(m_distributionState == DistributionOnGoing); | 331 ASSERT(m_distributionState == DistributionOnGoing); |
268 m_distributionState = DistributionDone; | 332 m_distributionState = DistributionDone; |
269 if (isChildOfV1ShadowHost()) { | 333 if (isChildOfV1ShadowHost()) { |
270 ElementShadow* shadow = parentElementShadow(); | 334 ElementShadow* shadow = parentElementShadow(); |
271 ASSERT(shadow); | 335 ASSERT(shadow); |
272 if (!shadow->needsDistributionRecalc() && distributionChanged()) | 336 if (!shadow->needsDistributionRecalc() && distributionChanged()) |
273 shadow->setNeedsDistributionRecalc(); | 337 shadow->setNeedsDistributionRecalc(); |
274 } | 338 } |
275 if (hasSlotChangeEventListener() && distributionChanged()) { | 339 } |
276 // TODO(hayato): Do not enqueue a slotchange event for the same slot twi ce in the microtask queue | 340 |
277 Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChan geEvent, this)); | 341 void HTMLSlotElement::fireSlotChangeEvent() |
342 { | |
343 ASSERT(!m_slotchangeEventAdded); | |
344 Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEv ent, this)); | |
345 m_slotchangeEventAdded = true; | |
346 | |
347 Element* shadowHost = isShadowHost(parentElement()) ? parentElement() : null ptr; | |
348 ShadowRoot* shadowRoot = shadowRootIfV1(); | |
hayato
2016/04/22 05:33:22
This would be:
shadowHost.shadowRootIfV1()
yuzuchan
2016/04/22 06:59:11
Done.
| |
349 // If this slot is assigned to another slot, fire slot change event of that slot too. | |
350 if (shadowHost && shadowRoot) { | |
351 if (HTMLSlotElement* assigned = assignedSlot()) | |
352 assigned->fireSlotChangeEvent(); | |
278 } | 353 } |
279 } | 354 } |
280 | 355 |
281 void HTMLSlotElement::clearDistribution() | 356 void HTMLSlotElement::clearDistribution() |
282 { | 357 { |
283 willUpdateDistribution(); | 358 willUpdateDistribution(); |
284 didUpdateDistribution(); | 359 didUpdateDistribution(); |
285 } | 360 } |
286 | 361 |
287 short HTMLSlotElement::tabIndex() const | 362 short HTMLSlotElement::tabIndex() const |
288 { | 363 { |
289 return Element::tabIndex(); | 364 return Element::tabIndex(); |
290 } | 365 } |
291 | 366 |
292 DEFINE_TRACE(HTMLSlotElement) | 367 DEFINE_TRACE(HTMLSlotElement) |
293 { | 368 { |
294 visitor->trace(m_assignedNodes); | 369 visitor->trace(m_assignedNodes); |
295 visitor->trace(m_distributedNodes); | 370 visitor->trace(m_distributedNodes); |
371 visitor->trace(m_fallbackNodes); | |
296 visitor->trace(m_distributedIndices); | 372 visitor->trace(m_distributedIndices); |
373 visitor->trace(m_oldAssignedNodes); | |
297 visitor->trace(m_oldDistributedNodes); | 374 visitor->trace(m_oldDistributedNodes); |
375 visitor->trace(m_oldFallbackNodes); | |
298 HTMLElement::trace(visitor); | 376 HTMLElement::trace(visitor); |
299 } | 377 } |
300 | 378 |
301 } // namespace blink | 379 } // namespace blink |
OLD | NEW |