| 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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 document().updateAssignment(); |
| 182 } | 202 } |
| 183 HTMLElement::attributeChanged(name, oldValue, newValue, reason); | 203 HTMLElement::attributeChanged(name, oldValue, newValue, reason); |
| 184 } | 204 } |
| 185 | 205 |
| 186 void HTMLSlotElement::childrenChanged(const ChildrenChange& change) | 206 void HTMLSlotElement::childrenChanged(const ChildrenChange& change) |
| 187 { | 207 { |
| 188 HTMLElement::childrenChanged(change); | 208 HTMLElement::childrenChanged(change); |
| 189 if (ShadowRoot* root = containingShadowRoot()) { | 209 if (ShadowRoot* root = containingShadowRoot()) { |
| 190 if (ElementShadow* rootOwner = root->owner()) | 210 if (ElementShadow* rootOwner = root->owner()) { |
| 191 rootOwner->setNeedsDistributionRecalc(); | 211 rootOwner->setNeedsDistributionRecalc(); |
| 212 if (document().shadowCascadeOrder() == ShadowCascadeOrder::ShadowCas
cadeV1) |
| 213 document().updateAssignment(); |
| 214 } |
| 192 } | 215 } |
| 193 } | 216 } |
| 194 | 217 |
| 195 Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode*
insertionPoint) | 218 Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode*
insertionPoint) |
| 196 { | 219 { |
| 197 HTMLElement::insertedInto(insertionPoint); | 220 HTMLElement::insertedInto(insertionPoint); |
| 198 if (ShadowRoot* root = containingShadowRoot()) { | 221 if (ShadowRoot* root = containingShadowRoot()) { |
| 199 if (ElementShadow* rootOwner = root->owner()) | 222 if (ElementShadow* rootOwner = root->owner()) |
| 200 rootOwner->setNeedsDistributionRecalc(); | 223 rootOwner->setNeedsDistributionRecalc(); |
| 201 if (root == insertionPoint->treeScope().rootNode()) | 224 if (root == insertionPoint->treeScope().rootNode()) |
| 202 root->didAddSlot(); | 225 root->didAddSlot(); |
| 203 } | 226 } |
| 204 | 227 |
| 205 // We could have been distributed into in a detached subtree, make sure to | 228 // We could have been distributed into in a detached subtree, make sure to |
| 206 // clear the distribution when inserted again to avoid cycles. | 229 // clear the distribution when inserted again to avoid cycles. |
| 207 clearDistribution(); | 230 clearDistribution(); |
| 208 | 231 |
| 232 if (document().shadowCascadeOrder() == ShadowCascadeOrder::ShadowCascadeV1) |
| 233 document().updateAssignment(); |
| 209 return InsertionDone; | 234 return InsertionDone; |
| 210 } | 235 } |
| 211 | 236 |
| 212 void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) | 237 void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) |
| 213 { | 238 { |
| 214 ShadowRoot* root = containingShadowRoot(); | 239 ShadowRoot* root = containingShadowRoot(); |
| 215 if (!root) | 240 if (!root) |
| 216 root = insertionPoint->containingShadowRoot(); | 241 root = insertionPoint->containingShadowRoot(); |
| 217 if (root) { | 242 if (root) { |
| 218 if (ElementShadow* rootOwner = root->owner()) | 243 if (ElementShadow* rootOwner = root->owner()) |
| 219 rootOwner->setNeedsDistributionRecalc(); | 244 rootOwner->setNeedsDistributionRecalc(); |
| 220 } | 245 } |
| 221 | 246 |
| 222 // Since this insertion point is no longer visible from the shadow subtree,
it need to clean itself up. | 247 // Since this insertion point is no longer visible from the shadow subtree,
it need to clean itself up. |
| 223 clearDistribution(); | 248 clearDistribution(); |
| 224 | 249 |
| 225 if (root == insertionPoint->treeScope().rootNode()) | 250 if (root == insertionPoint->treeScope().rootNode()) |
| 226 root->didRemoveSlot(); | 251 root->didRemoveSlot(); |
| 227 | 252 if (document().shadowCascadeOrder() == ShadowCascadeOrder::ShadowCascadeV1)
{ |
| 253 document().updateAssignment(); |
| 254 } |
| 228 HTMLElement::removedFrom(insertionPoint); | 255 HTMLElement::removedFrom(insertionPoint); |
| 229 } | 256 } |
| 230 | 257 |
| 231 void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) | 258 void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) |
| 232 { | 259 { |
| 233 if (change < Inherit && getStyleChangeType() < SubtreeStyleChange) | 260 if (change < Inherit && getStyleChangeType() < SubtreeStyleChange) |
| 234 return; | 261 return; |
| 235 | 262 |
| 236 for (auto& node : m_distributedNodes) | 263 for (auto& node : m_distributedNodes) |
| 237 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing:
:create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); | 264 node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing:
:create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); |
| 238 } | 265 } |
| 239 | 266 |
| 240 void HTMLSlotElement::updateDistributedNodesWithFallback() | 267 void HTMLSlotElement::updateFallbackNodes() |
| 241 { | 268 { |
| 242 if (!m_distributedNodes.isEmpty()) | 269 if (!m_fallbackNodes.isEmpty()) |
| 243 return; | 270 return; |
| 244 for (auto& child : NodeTraversal::childrenOf(*this)) { | 271 for (auto& child : NodeTraversal::childrenOf(*this)) { |
| 245 if (!child.isSlotAssignable()) | 272 if (!child.isSlotAssignable()) |
| 246 continue; | 273 continue; |
| 247 // Insertion points are not supported as slots fallback | 274 // Insertion points are not supported as slots fallback |
| 248 if (isActiveInsertionPoint(child)) | 275 if (isActiveInsertionPoint(child)) |
| 249 continue; | 276 continue; |
| 250 if (isHTMLSlotElement(child)) | 277 appendFallbackNode(child); |
| 251 appendDistributedNodesFrom(toHTMLSlotElement(child)); | 278 } |
| 279 } |
| 280 |
| 281 void HTMLSlotElement::updateDistributedNodesWithFallback() |
| 282 { |
| 283 if (!m_distributedNodes.isEmpty()) |
| 284 return; |
| 285 for (auto node : m_fallbackNodes) { |
| 286 if (isHTMLSlotElement(node)) |
| 287 appendDistributedNodesFrom(*toHTMLSlotElement(node)); |
| 252 else | 288 else |
| 253 appendDistributedNode(child); | 289 appendDistributedNode(*node); |
| 254 } | 290 } |
| 255 } | 291 } |
| 256 | 292 |
| 293 bool HTMLSlotElement::assignmentChanged() |
| 294 { |
| 295 ASSERT(m_assignmentState != AssignmentOnGoing); |
| 296 if (m_assignmentState == AssignmentDone) |
| 297 m_assignmentState = m_oldAssignedNodes == m_assignedNodes ? AssignmentUn
changed : AssignmentChanged; |
| 298 return m_assignmentState == AssignmentChanged; |
| 299 } |
| 300 |
| 257 bool HTMLSlotElement::distributionChanged() | 301 bool HTMLSlotElement::distributionChanged() |
| 258 { | 302 { |
| 259 ASSERT(m_distributionState != DistributionOnGoing); | 303 ASSERT(m_distributionState != DistributionOnGoing); |
| 260 if (m_distributionState == DistributionDone) | 304 if (m_distributionState == DistributionDone) |
| 261 m_distributionState = m_oldDistributedNodes == m_distributedNodes ? Dist
ributionUnchanged : DistributionChanged; | 305 m_distributionState = m_oldDistributedNodes == m_distributedNodes ? Dist
ributionUnchanged : DistributionChanged; |
| 262 return m_distributionState == DistributionChanged; | 306 return m_distributionState == DistributionChanged; |
| 263 } | 307 } |
| 264 | 308 |
| 309 bool HTMLSlotElement::fallbackChanged() |
| 310 { |
| 311 return m_oldFallbackNodes == m_fallbackNodes; |
| 312 } |
| 313 |
| 314 void HTMLSlotElement::didUpdateAssignment() |
| 315 { |
| 316 ASSERT(m_assignmentState == AssignmentOnGoing); |
| 317 m_assignmentState = AssignmentDone; |
| 318 if ((assignmentChanged() || fallbackChanged()) && !m_slotchangeEventAdded) |
| 319 fireSlotChangeEvent(); |
| 320 } |
| 321 |
| 265 void HTMLSlotElement::didUpdateDistribution() | 322 void HTMLSlotElement::didUpdateDistribution() |
| 266 { | 323 { |
| 267 ASSERT(m_distributionState == DistributionOnGoing); | 324 ASSERT(m_distributionState == DistributionOnGoing); |
| 268 m_distributionState = DistributionDone; | 325 m_distributionState = DistributionDone; |
| 269 if (isChildOfV1ShadowHost()) { | 326 if (isChildOfV1ShadowHost()) { |
| 270 ElementShadow* shadow = parentElementShadow(); | 327 ElementShadow* shadow = parentElementShadow(); |
| 271 ASSERT(shadow); | 328 ASSERT(shadow); |
| 272 if (!shadow->needsDistributionRecalc() && distributionChanged()) | 329 if (!shadow->needsDistributionRecalc() && distributionChanged()) |
| 273 shadow->setNeedsDistributionRecalc(); | 330 shadow->setNeedsDistributionRecalc(); |
| 274 } | 331 } |
| 275 if (hasSlotChangeEventListener() && distributionChanged()) { | 332 } |
| 276 // TODO(hayato): Do not enqueue a slotchange event for the same slot twi
ce in the microtask queue | 333 |
| 277 Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChan
geEvent, this)); | 334 void HTMLSlotElement::fireSlotChangeEvent() |
| 335 { |
| 336 ASSERT(!m_slotchangeEventAdded); |
| 337 Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEv
ent, this)); |
| 338 m_slotchangeEventAdded = true; |
| 339 |
| 340 Element* shadowHost = isShadowHost(parentElement()) ? parentElement() : null
ptr; |
| 341 ShadowRoot* shadowRoot = shadowRootIfV1(); |
| 342 if (shadowHost && shadowRoot) { |
| 343 // If this slot is assigned to another slot, fire slot change event of t
hat slot too. |
| 344 const AtomicString& assignedSlot = normalizeSlotName(fastGetAttribute(HT
MLNames::slotAttr)); |
| 345 if (assignedSlot != emptyAtom) { |
| 346 const HeapVector<Member<HTMLSlotElement>>& slots = shadowRoot->desce
ndantSlots(); |
| 347 for (Member<HTMLSlotElement> slot : slots) { |
| 348 if (slot->name() == assignedSlot && !slot->hasSlotChangeEventInM
icrotask()) { |
| 349 slot->fireSlotChangeEvent(); |
| 350 break; |
| 351 } |
| 352 } |
| 353 } |
| 278 } | 354 } |
| 279 } | 355 } |
| 280 | 356 |
| 281 void HTMLSlotElement::clearDistribution() | 357 void HTMLSlotElement::clearDistribution() |
| 282 { | 358 { |
| 283 willUpdateDistribution(); | 359 willUpdateDistribution(); |
| 284 didUpdateDistribution(); | 360 didUpdateDistribution(); |
| 285 } | 361 } |
| 286 | 362 |
| 287 short HTMLSlotElement::tabIndex() const | 363 short HTMLSlotElement::tabIndex() const |
| 288 { | 364 { |
| 289 return Element::tabIndex(); | 365 return Element::tabIndex(); |
| 290 } | 366 } |
| 291 | 367 |
| 292 DEFINE_TRACE(HTMLSlotElement) | 368 DEFINE_TRACE(HTMLSlotElement) |
| 293 { | 369 { |
| 294 visitor->trace(m_assignedNodes); | 370 visitor->trace(m_assignedNodes); |
| 295 visitor->trace(m_distributedNodes); | 371 visitor->trace(m_distributedNodes); |
| 372 visitor->trace(m_fallbackNodes); |
| 296 visitor->trace(m_distributedIndices); | 373 visitor->trace(m_distributedIndices); |
| 374 visitor->trace(m_oldAssignedNodes); |
| 297 visitor->trace(m_oldDistributedNodes); | 375 visitor->trace(m_oldDistributedNodes); |
| 376 visitor->trace(m_oldFallbackNodes); |
| 298 HTMLElement::trace(visitor); | 377 HTMLElement::trace(visitor); |
| 299 } | 378 } |
| 300 | 379 |
| 301 } // namespace blink | 380 } // namespace blink |
| OLD | NEW |