Chromium Code Reviews| 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 20 matching lines...) Expand all Loading... | |
| 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; | |
|
esprehn
2016/05/23 06:41:00
Why does your code care if the name is null or emp
hayato
2016/05/24 13:23:39
|name| here came from the result of Element::fastG
| |
| 61 } | |
| 62 | |
| 63 const HeapVector<Member<Node>>& HTMLSlotElement::assignedNodes() | |
| 64 { | |
| 65 #if DCHECK_IS_ON | |
| 66 DCHECK(!needsDistributionRecalc()); | |
| 67 #endif | |
| 68 DCHECK(isInShadowTree() || m_assignedNodes.isEmpty()); | |
| 69 return m_assignedNodes; | |
| 70 } | |
| 71 | |
| 59 const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const As signedNodesOptions& options) | 72 const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const As signedNodesOptions& options) |
| 60 { | 73 { |
| 61 updateDistribution(); | 74 updateDistribution(); |
| 62 if (options.hasFlatten() && options.flatten()) | 75 if (options.hasFlatten() && options.flatten()) |
| 63 return getDistributedNodes(); | 76 return getDistributedNodes(); |
| 64 return m_assignedNodes; | 77 return m_assignedNodes; |
| 65 } | 78 } |
| 66 | 79 |
| 67 const HeapVector<Member<Node>>& HTMLSlotElement::getDistributedNodes() | 80 const HeapVector<Member<Node>>& HTMLSlotElement::getDistributedNodes() |
| 68 { | 81 { |
| 69 ASSERT(!needsDistributionRecalc()); | 82 #if DCHECK_IS_ON |
|
esprehn
2016/05/23 06:41:00
why is this needed now?
hayato
2016/05/24 13:23:39
Done. Removed.
| |
| 83 DCHECK(!needsDistributionRecalc()); | |
| 84 #endif | |
| 70 if (isInShadowTree()) | 85 if (isInShadowTree()) |
| 71 return m_distributedNodes; | 86 return m_distributedNodes; |
| 72 | 87 |
| 73 // A slot is unlikely to be used outside of a shadow tree. | 88 // A slot is unlikely to be used outside of a shadow tree. |
| 74 // We do not need to optimize this case in most cases. | 89 // We do not need to optimize this case in most cases. |
| 75 // TODO(hayato): If this path causes a performance issue, we should move | 90 // TODO(hayato): If this path causes a performance issue, we should move |
| 76 // ShadowRootRaraDate::m_descendantSlots into TreeScopreRareData-ish and | 91 // ShadowRootRaraDate::m_descendantSlots into TreeScopreRareData-ish and |
| 77 // update the distribution code so it considers a document tree too. | 92 // update the distribution code so it considers a document tree too. |
| 78 willUpdateDistribution(); | 93 clearDistribution(); |
| 79 for (Node& child : NodeTraversal::childrenOf(*this)) { | 94 for (Node& child : NodeTraversal::childrenOf(*this)) { |
| 80 if (!child.isSlotAssignable()) | 95 if (!child.isSlotable()) |
| 81 continue; | 96 continue; |
| 82 if (isHTMLSlotElement(child)) | 97 if (isHTMLSlotElement(child)) |
| 83 m_distributedNodes.appendVector(toHTMLSlotElement(child).getDistribu tedNodes()); | 98 m_distributedNodes.appendVector(toHTMLSlotElement(child).getDistribu tedNodes()); |
|
esprehn
2016/05/23 06:41:00
This code doesn't make sense, if the slot itself i
hayato
2016/05/24 13:23:39
That's an expected behavior. A slot itself can be
esprehn
2016/05/25 05:45:02
If you're not in a shadow tree then none of the el
hayato
2016/05/26 04:46:06
I am afraid that it is not easy with NodeTraversal
| |
| 84 else | 99 else |
| 85 m_distributedNodes.append(&child); | 100 m_distributedNodes.append(&child); |
| 86 } | 101 } |
| 87 didUpdateDistribution(); | |
| 88 return m_distributedNodes; | 102 return m_distributedNodes; |
| 89 } | 103 } |
| 90 | 104 |
| 91 void HTMLSlotElement::appendAssignedNode(Node& node) | 105 void HTMLSlotElement::appendAssignedNode(Node& node) |
| 92 { | 106 { |
| 93 ASSERT(m_assignmentState == AssignmentOnGoing); | |
| 94 m_assignedNodes.append(&node); | 107 m_assignedNodes.append(&node); |
| 95 } | 108 } |
| 96 | 109 |
| 97 void HTMLSlotElement::appendDistributedNode(Node& node) | 110 void HTMLSlotElement::appendDistributedNode(Node& node) |
| 98 { | 111 { |
| 99 ASSERT(m_distributionState == DistributionOnGoing); | |
| 100 size_t size = m_distributedNodes.size(); | 112 size_t size = m_distributedNodes.size(); |
| 101 m_distributedNodes.append(&node); | 113 m_distributedNodes.append(&node); |
| 102 m_distributedIndices.set(&node, size); | 114 m_distributedIndices.set(&node, size); |
| 103 } | 115 } |
| 104 | 116 |
| 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) | 117 void HTMLSlotElement::appendDistributedNodesFrom(const HTMLSlotElement& other) |
| 112 { | 118 { |
| 113 ASSERT(m_distributionState == DistributionOnGoing); | |
| 114 size_t index = m_distributedNodes.size(); | 119 size_t index = m_distributedNodes.size(); |
| 115 m_distributedNodes.appendVector(other.m_distributedNodes); | 120 m_distributedNodes.appendVector(other.m_distributedNodes); |
| 116 for (const auto& node : other.m_distributedNodes) | 121 for (const auto& node : other.m_distributedNodes) |
| 117 m_distributedIndices.set(node.get(), index++); | 122 m_distributedIndices.set(node.get(), index++); |
| 118 } | 123 } |
| 119 | 124 |
| 120 void HTMLSlotElement::willUpdateAssignment() | 125 void HTMLSlotElement::clearDistribution() |
| 121 { | 126 { |
| 122 ASSERT(m_assignmentState != AssignmentOnGoing); | |
| 123 m_assignmentState = AssignmentOnGoing; | |
| 124 m_oldAssignedNodes.swap(m_assignedNodes); | |
| 125 m_assignedNodes.clear(); | 127 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(); | 128 m_distributedNodes.clear(); |
| 134 m_distributedIndices.clear(); | 129 m_distributedIndices.clear(); |
| 135 } | 130 } |
| 136 | 131 |
| 137 void HTMLSlotElement::willUpdateFallback() | |
| 138 { | |
| 139 m_oldFallbackNodes.swap(m_fallbackNodes); | |
| 140 m_fallbackNodes.clear(); | |
| 141 } | |
| 142 | |
| 143 void HTMLSlotElement::dispatchSlotChangeEvent() | 132 void HTMLSlotElement::dispatchSlotChangeEvent() |
| 144 { | 133 { |
| 145 Event* event = Event::create(EventTypeNames::slotchange); | 134 Event* event = Event::create(EventTypeNames::slotchange); |
| 146 event->setTarget(this); | 135 event->setTarget(this); |
| 147 dispatchScopedEvent(event); | 136 dispatchScopedEvent(event); |
| 148 m_slotchangeEventAdded = false; | 137 m_slotchangeEventEnqueued = false; |
|
esprehn
2016/05/23 06:41:00
you need to set this to false before you dispatch
| |
| 149 } | 138 } |
| 150 | 139 |
| 151 Node* HTMLSlotElement::distributedNodeNextTo(const Node& node) const | 140 Node* HTMLSlotElement::distributedNodeNextTo(const Node& node) const |
| 152 { | 141 { |
| 153 const auto& it = m_distributedIndices.find(&node); | 142 const auto& it = m_distributedIndices.find(&node); |
| 154 if (it == m_distributedIndices.end()) | 143 if (it == m_distributedIndices.end()) |
| 155 return nullptr; | 144 return nullptr; |
| 156 size_t index = it->value; | 145 size_t index = it->value; |
| 157 if (index + 1 == m_distributedNodes.size()) | 146 if (index + 1 == m_distributedNodes.size()) |
| 158 return nullptr; | 147 return nullptr; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 for (auto& node : m_distributedNodes) | 179 for (auto& node : m_distributedNodes) |
| 191 node->lazyReattachIfAttached(); | 180 node->lazyReattachIfAttached(); |
| 192 | 181 |
| 193 HTMLElement::detach(context); | 182 HTMLElement::detach(context); |
| 194 } | 183 } |
| 195 | 184 |
| 196 void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicSt ring& oldValue, const AtomicString& newValue, AttributeModificationReason reason ) | 185 void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicSt ring& oldValue, const AtomicString& newValue, AttributeModificationReason reason ) |
| 197 { | 186 { |
| 198 if (name == nameAttr) { | 187 if (name == nameAttr) { |
| 199 if (ShadowRoot* root = containingShadowRoot()) { | 188 if (ShadowRoot* root = containingShadowRoot()) { |
| 200 root->owner()->willAffectSelector(); | |
| 201 if (root->isV1() && oldValue != newValue) | 189 if (root->isV1() && oldValue != newValue) |
| 202 root->assignV1(); | 190 root->ensureSlotAssignment().slotRenamed(normalizeSlotName(oldVa lue), *this); |
|
esprehn
2016/05/23 06:41:00
slotRenamed shouldn't care if the string is empty
hayato
2016/05/24 13:23:39
That should be cared because a slot with name of "
| |
| 203 } | 191 } |
| 204 } | 192 } |
| 205 HTMLElement::attributeChanged(name, oldValue, newValue, reason); | 193 HTMLElement::attributeChanged(name, oldValue, newValue, reason); |
| 206 } | 194 } |
| 207 | 195 |
| 208 void HTMLSlotElement::childrenChanged(const ChildrenChange& change) | 196 static bool wasInShadowTreeBeforeInserted(HTMLSlotElement& slot, ContainerNode& insertionPoint) |
| 209 { | 197 { |
| 210 HTMLElement::childrenChanged(change); | 198 ShadowRoot* root1 = slot.containingShadowRoot(); |
| 211 if (ShadowRoot* root = containingShadowRoot()) { | 199 ShadowRoot* root2 = insertionPoint.containingShadowRoot(); |
| 212 if (ElementShadow* rootOwner = root->owner()) { | 200 if (root1 && root2 && root1 == root2) |
| 213 rootOwner->setNeedsDistributionRecalc(); | 201 return false; |
| 214 } | 202 return root1; |
|
kochi
2016/05/24 10:02:56
The caller of this is only insertedInto() below (a
hayato
2016/05/24 13:23:39
No. It looks you are focusing on the node tree's *
kochi
2016/05/25 05:37:11
Acknowledged.
| |
| 215 if (m_assignedNodes.isEmpty() && root->isV1()) | |
| 216 root->assignV1(); | |
| 217 } | |
| 218 } | 203 } |
| 219 | 204 |
| 220 Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* insertionPoint) | 205 Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* insertionPoint) |
| 221 { | 206 { |
| 222 HTMLElement::insertedInto(insertionPoint); | 207 HTMLElement::insertedInto(insertionPoint); |
| 223 ShadowRoot* root = containingShadowRoot(); | 208 ShadowRoot* root = containingShadowRoot(); |
| 224 if (root) { | 209 if (root) { |
| 225 if (ElementShadow* rootOwner = root->owner()) | 210 DCHECK(root->owner()); |
| 226 rootOwner->setNeedsDistributionRecalc(); | 211 root->owner()->setNeedsDistributionRecalc(); |
| 227 if (root == insertionPoint->treeScope().rootNode()) | 212 // Relevant DOM Standard: https://dom.spec.whatwg.org/#concept-node-inse rt |
| 228 root->didAddSlot(); | 213 // - 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. |
| 214 if (!wasInShadowTreeBeforeInserted(*this, *insertionPoint)) | |
| 215 root->ensureSlotAssignment().slotAdded(*this); | |
|
kochi
2016/05/24 10:02:56
I'm confused at following the logic here - could y
| |
| 229 } | 216 } |
| 230 | 217 |
| 231 // We could have been distributed into in a detached subtree, make sure to | 218 // We could have been distributed into in a detached subtree, make sure to |
| 232 // clear the distribution when inserted again to avoid cycles. | 219 // clear the distribution when inserted again to avoid cycles. |
| 233 clearDistribution(); | 220 clearDistribution(); |
| 234 | 221 |
| 235 if (root && root->isV1()) | 222 return InsertionDone; |
| 236 root->assignV1(); | 223 } |
| 237 | 224 |
| 238 return InsertionDone; | 225 static ShadowRoot* containingShadowRootBeforeRemoved(Node& removedDescendant, Co ntainerNode& insertionPoint) |
| 226 { | |
| 227 if (ShadowRoot* root = removedDescendant.containingShadowRoot()) | |
| 228 return root; | |
| 229 return insertionPoint.containingShadowRoot(); | |
| 239 } | 230 } |
| 240 | 231 |
| 241 void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) | 232 void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) |
| 242 { | 233 { |
| 243 ShadowRoot* root = containingShadowRoot(); | 234 // `removedFrom` is called after the node is removed from the tree. |
| 244 if (!root) | 235 // That means: |
| 245 root = insertionPoint->containingShadowRoot(); | 236 // 1. If this slot is still in a tree scope, it means the slot has been in a shadow tree. An inclusive inclusive shadow-including ancestor of the shadow hos t was originally removed from its parent. |
|
kochi
2016/05/24 10:02:56
nit: s/inclusive inclusive/inclusive/
| |
| 237 // 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. | |
| 238 | |
| 239 ShadowRoot* root = containingShadowRootBeforeRemoved(*this, *insertionPoint) ; | |
| 246 if (root) { | 240 if (root) { |
| 247 if (ElementShadow* rootOwner = root->owner()) | 241 if (ElementShadow* rootOwner = root->owner()) |
| 248 rootOwner->setNeedsDistributionRecalc(); | 242 rootOwner->setNeedsDistributionRecalc(); |
| 249 } | 243 } |
| 250 | 244 |
| 251 // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up. | 245 // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up. |
| 252 clearDistribution(); | 246 clearDistribution(); |
| 253 | 247 |
| 254 ContainerNode& rootNode = insertionPoint->treeScope().rootNode(); | 248 if (root && root->isV1() && root == &insertionPoint->treeScope().rootNode()) { |
| 255 if (root == &rootNode) | 249 // This slot was in a shadow tree and got disconnected from the shadow r oot. |
| 256 root->didRemoveSlot(); | 250 root->ensureSlotAssignment().slotRemoved(*this); |
| 257 else if (rootNode.isShadowRoot() && toShadowRoot(rootNode).isV1()) | 251 } |
| 258 toShadowRoot(rootNode).assignV1(); | |
| 259 | 252 |
| 260 if (root && root->isV1()) | |
| 261 root->assignV1(); | |
| 262 HTMLElement::removedFrom(insertionPoint); | 253 HTMLElement::removedFrom(insertionPoint); |
| 263 } | 254 } |
| 264 | 255 |
| 265 void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) | 256 void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) |
| 266 { | 257 { |
| 267 if (change < Inherit && getStyleChangeType() < SubtreeStyleChange) | 258 if (change < Inherit && getStyleChangeType() < SubtreeStyleChange) |
| 268 return; | 259 return; |
| 269 | 260 |
| 270 for (auto& node : m_distributedNodes) | 261 for (auto& node : m_distributedNodes) |
| 271 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing: :create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); | 262 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing: :create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); |
| 272 } | 263 } |
| 273 | 264 |
| 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() | 265 void HTMLSlotElement::updateDistributedNodesWithFallback() |
| 289 { | 266 { |
| 290 if (!m_distributedNodes.isEmpty()) | 267 if (!m_distributedNodes.isEmpty()) |
| 291 return; | 268 return; |
| 292 for (auto node : m_fallbackNodes) { | 269 for (auto& child : NodeTraversal::childrenOf(*this)) { |
| 293 if (isHTMLSlotElement(node)) | 270 if (!child.isSlotable()) |
| 294 appendDistributedNodesFrom(*toHTMLSlotElement(node)); | 271 continue; |
| 272 if (isHTMLSlotElement(child)) | |
| 273 appendDistributedNodesFrom(toHTMLSlotElement(child)); | |
| 295 else | 274 else |
| 296 appendDistributedNode(*node); | 275 appendDistributedNode(child); |
| 297 } | 276 } |
| 298 } | 277 } |
| 299 | 278 |
| 300 bool HTMLSlotElement::assignmentChanged() | 279 void HTMLSlotElement::enqueueSlotChangeEvent() |
| 301 { | 280 { |
| 302 ASSERT(m_assignmentState != AssignmentOnGoing); | 281 if (!m_slotchangeEventEnqueued) { |
| 303 if (m_assignmentState == AssignmentDone) | 282 Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChan geEvent, this)); |
|
esprehn
2016/05/23 06:41:00
Does a Persistent<> get magically created for |thi
hayato
2016/05/24 13:23:39
Good point! It looks it does not.
Let me use Per
| |
| 304 m_assignmentState = m_oldAssignedNodes == m_assignedNodes ? AssignmentUn changed : AssignmentChanged; | 283 m_slotchangeEventEnqueued = true; |
| 305 return m_assignmentState == AssignmentChanged; | 284 } |
| 306 } | |
| 307 | 285 |
| 308 bool HTMLSlotElement::distributionChanged() | 286 ShadowRoot* root = containingShadowRoot(); |
| 309 { | 287 DCHECK(root); |
| 310 ASSERT(m_distributionState != DistributionOnGoing); | 288 DCHECK(root->isV1()); |
| 311 if (m_distributionState == DistributionDone) | 289 root->owner()->setNeedsDistributionRecalc(); |
| 312 m_distributionState = m_oldDistributedNodes == m_distributedNodes ? Dist ributionUnchanged : DistributionChanged; | |
| 313 return m_distributionState == DistributionChanged; | |
| 314 } | |
| 315 | 290 |
| 316 bool HTMLSlotElement::fallbackChanged() | 291 if (ShadowRoot* parentShadowRoot = v1ShadowRootOfParent()) { |
| 317 { | 292 if (HTMLSlotElement* next = parentShadowRoot->ensureSlotAssignment().fin dSlot(*this)) |
| 318 return m_oldFallbackNodes == m_fallbackNodes; | 293 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 } | 294 } |
| 339 } | 295 } |
| 340 | 296 |
| 341 void HTMLSlotElement::fireSlotChangeEvent() | 297 bool HTMLSlotElement::hasAssignedNodesSynchronously() const |
| 342 { | 298 { |
| 343 ASSERT(!m_slotchangeEventAdded); | 299 ShadowRoot* root = containingShadowRoot(); |
| 344 Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEv ent, this)); | 300 DCHECK(root); |
| 345 m_slotchangeEventAdded = true; | 301 DCHECK(root->isV1()); |
| 346 | 302 SlotAssignment& assignment = root->ensureSlotAssignment(); |
| 347 Element* shadowHost = isShadowHost(parentElement()) ? parentElement() : null ptr; | 303 if (assignment.findSlot(*this) != this) |
| 348 // If this slot is assigned to another slot, fire slot change event of that slot too. | 304 return false; |
| 349 if (shadowHost && shadowHost->shadowRootIfV1()) { | 305 return assignment.findHostChildBySlotName(name()); |
| 350 if (HTMLSlotElement* assigned = assignedSlot()) { | |
| 351 if (!assigned->m_slotchangeEventAdded) | |
| 352 assigned->fireSlotChangeEvent(); | |
| 353 } | |
| 354 } | |
| 355 } | 306 } |
| 356 | 307 |
| 357 void HTMLSlotElement::clearDistribution() | 308 bool HTMLSlotElement::findHostChildWithSameSlotName() const |
| 358 { | 309 { |
| 359 willUpdateDistribution(); | 310 ShadowRoot* root = containingShadowRoot(); |
| 360 didUpdateDistribution(); | 311 DCHECK(root); |
| 312 DCHECK(root->isV1()); | |
| 313 SlotAssignment& assignment = root->ensureSlotAssignment(); | |
| 314 return assignment.findHostChildBySlotName(name()); | |
| 361 } | 315 } |
| 362 | 316 |
| 363 short HTMLSlotElement::tabIndex() const | 317 short HTMLSlotElement::tabIndex() const |
| 364 { | 318 { |
| 365 return Element::tabIndex(); | 319 return Element::tabIndex(); |
| 366 } | 320 } |
| 367 | 321 |
| 368 DEFINE_TRACE(HTMLSlotElement) | 322 DEFINE_TRACE(HTMLSlotElement) |
| 369 { | 323 { |
| 370 visitor->trace(m_assignedNodes); | 324 visitor->trace(m_assignedNodes); |
| 371 visitor->trace(m_distributedNodes); | 325 visitor->trace(m_distributedNodes); |
| 372 visitor->trace(m_fallbackNodes); | |
| 373 visitor->trace(m_distributedIndices); | 326 visitor->trace(m_distributedIndices); |
| 374 visitor->trace(m_oldAssignedNodes); | |
| 375 visitor->trace(m_oldDistributedNodes); | |
| 376 visitor->trace(m_oldFallbackNodes); | |
| 377 HTMLElement::trace(visitor); | 327 HTMLElement::trace(visitor); |
| 378 } | 328 } |
| 379 | 329 |
| 380 } // namespace blink | 330 } // namespace blink |
| OLD | NEW |