Chromium Code Reviews| Index: third_party/WebKit/Source/core/html/HTMLSlotElement.cpp | 
| diff --git a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp | 
| index 156689e76873104ff6fec7eaa50a98d24490e829..a73134d6feb0896dabb3857829d0be31502e538a 100644 | 
| --- a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp | 
| +++ b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp | 
| @@ -32,8 +32,10 @@ | 
| #include "bindings/core/v8/Microtask.h" | 
| #include "core/HTMLNames.h" | 
| +#include "core/dom/ElementTraversal.h" | 
| #include "core/dom/NodeTraversal.h" | 
| #include "core/dom/StyleChangeReason.h" | 
| +#include "core/dom/StyleEngine.h" | 
| #include "core/dom/shadow/ElementShadow.h" | 
| #include "core/dom/shadow/InsertionPoint.h" | 
| #include "core/events/Event.h" | 
| @@ -46,6 +48,8 @@ using namespace HTMLNames; | 
| inline HTMLSlotElement::HTMLSlotElement(Document& document) | 
| : HTMLElement(slotTag, document) | 
| , m_distributionState(DistributionDone) | 
| + , m_assignmentState(AssignmentDone) | 
| + , m_slotchangeEventAdded(false) | 
| { | 
| setHasCustomStyleCallbacks(); | 
| } | 
| @@ -86,7 +90,7 @@ const HeapVector<Member<Node>>& HTMLSlotElement::getDistributedNodes() | 
| void HTMLSlotElement::appendAssignedNode(Node& node) | 
| { | 
| - ASSERT(m_distributionState == DistributionOnGoing); | 
| + ASSERT(m_assignmentState == AssignmentOnGoing); | 
| m_assignedNodes.append(&node); | 
| } | 
| @@ -98,6 +102,12 @@ void HTMLSlotElement::appendDistributedNode(Node& node) | 
| m_distributedIndices.set(&node, size); | 
| } | 
| +void HTMLSlotElement::appendFallbackNode(Node& node) | 
| +{ | 
| + ASSERT(m_assignmentState == AssignmentOnGoing); | 
| + m_fallbackNodes.append(&node); | 
| +} | 
| + | 
| void HTMLSlotElement::appendDistributedNodesFrom(const HTMLSlotElement& other) | 
| { | 
| ASSERT(m_distributionState == DistributionOnGoing); | 
| @@ -107,19 +117,27 @@ void HTMLSlotElement::appendDistributedNodesFrom(const HTMLSlotElement& other) | 
| m_distributedIndices.set(node.get(), index++); | 
| } | 
| +void HTMLSlotElement::willUpdateAssignment() | 
| +{ | 
| + ASSERT(m_assignmentState != AssignmentOnGoing); | 
| + m_assignmentState = AssignmentOnGoing; | 
| + m_oldAssignedNodes.swap(m_assignedNodes); | 
| + m_assignedNodes.clear(); | 
| +} | 
| + | 
| void HTMLSlotElement::willUpdateDistribution() | 
| { | 
| ASSERT(m_distributionState != DistributionOnGoing); | 
| m_distributionState = DistributionOnGoing; | 
| - m_assignedNodes.clear(); | 
| m_oldDistributedNodes.swap(m_distributedNodes); | 
| m_distributedNodes.clear(); | 
| m_distributedIndices.clear(); | 
| } | 
| -bool HTMLSlotElement::hasSlotChangeEventListener() | 
| +void HTMLSlotElement::willUpdateFallback() | 
| { | 
| - return eventTargetData() && eventTargetData()->eventListenerMap.find(EventTypeNames::slotchange); | 
| + m_oldFallbackNodes.swap(m_fallbackNodes); | 
| + m_fallbackNodes.clear(); | 
| } | 
| void HTMLSlotElement::dispatchSlotChangeEvent() | 
| @@ -127,6 +145,7 @@ void HTMLSlotElement::dispatchSlotChangeEvent() | 
| Event* event = Event::create(EventTypeNames::slotchange); | 
| event->setTarget(this); | 
| dispatchScopedEvent(event); | 
| + m_slotchangeEventAdded = false; | 
| } | 
| Node* HTMLSlotElement::distributedNodeNextTo(const Node& node) const | 
| @@ -177,8 +196,11 @@ void HTMLSlotElement::detach(const AttachContext& context) | 
| void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason) | 
| { | 
| if (name == nameAttr) { | 
| - if (ShadowRoot* root = containingShadowRoot()) | 
| + if (ShadowRoot* root = containingShadowRoot()) { | 
| root->owner()->willAffectSelector(); | 
| + if (root->isV1() && oldValue != newValue) | 
| + root->assignV1(); | 
| + } | 
| } | 
| HTMLElement::attributeChanged(name, oldValue, newValue, reason); | 
| } | 
| @@ -187,15 +209,19 @@ void HTMLSlotElement::childrenChanged(const ChildrenChange& change) | 
| { | 
| HTMLElement::childrenChanged(change); | 
| if (ShadowRoot* root = containingShadowRoot()) { | 
| - if (ElementShadow* rootOwner = root->owner()) | 
| + if (ElementShadow* rootOwner = root->owner()) { | 
| rootOwner->setNeedsDistributionRecalc(); | 
| + } | 
| + if (m_assignedNodes.isEmpty() && root->isV1()) | 
| + root->assignV1(); | 
| } | 
| } | 
| Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* insertionPoint) | 
| { | 
| HTMLElement::insertedInto(insertionPoint); | 
| - if (ShadowRoot* root = containingShadowRoot()) { | 
| + ShadowRoot* root = containingShadowRoot(); | 
| + if (root) { | 
| if (ElementShadow* rootOwner = root->owner()) | 
| rootOwner->setNeedsDistributionRecalc(); | 
| if (root == insertionPoint->treeScope().rootNode()) | 
| @@ -206,6 +232,9 @@ Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* | 
| // clear the distribution when inserted again to avoid cycles. | 
| clearDistribution(); | 
| + if (root && root->isV1()) | 
| + root->assignV1(); | 
| + | 
| return InsertionDone; | 
| } | 
| @@ -222,9 +251,14 @@ void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) | 
| // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up. | 
| clearDistribution(); | 
| - if (root == insertionPoint->treeScope().rootNode()) | 
| + ContainerNode& rootNode = insertionPoint->treeScope().rootNode(); | 
| + if (root == &rootNode) | 
| root->didRemoveSlot(); | 
| + else if (rootNode.isShadowRoot() && toShadowRoot(rootNode).isV1()) | 
| + toShadowRoot(rootNode).assignV1(); | 
| + if (root && root->isV1()) | 
| + root->assignV1(); | 
| HTMLElement::removedFrom(insertionPoint); | 
| } | 
| @@ -237,9 +271,9 @@ void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) | 
| node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); | 
| } | 
| -void HTMLSlotElement::updateDistributedNodesWithFallback() | 
| +void HTMLSlotElement::updateFallbackNodes() | 
| { | 
| - if (!m_distributedNodes.isEmpty()) | 
| + if (!m_fallbackNodes.isEmpty()) | 
| return; | 
| for (auto& child : NodeTraversal::childrenOf(*this)) { | 
| if (!child.isSlotAssignable()) | 
| @@ -247,13 +281,30 @@ void HTMLSlotElement::updateDistributedNodesWithFallback() | 
| // Insertion points are not supported as slots fallback | 
| if (isActiveInsertionPoint(child)) | 
| continue; | 
| - if (isHTMLSlotElement(child)) | 
| - appendDistributedNodesFrom(toHTMLSlotElement(child)); | 
| + appendFallbackNode(child); | 
| + } | 
| +} | 
| + | 
| +void HTMLSlotElement::updateDistributedNodesWithFallback() | 
| +{ | 
| + if (!m_distributedNodes.isEmpty()) | 
| + return; | 
| + for (auto node : m_fallbackNodes) { | 
| + if (isHTMLSlotElement(node)) | 
| + appendDistributedNodesFrom(*toHTMLSlotElement(node)); | 
| else | 
| - appendDistributedNode(child); | 
| + appendDistributedNode(*node); | 
| } | 
| } | 
| +bool HTMLSlotElement::assignmentChanged() | 
| +{ | 
| + ASSERT(m_assignmentState != AssignmentOnGoing); | 
| + if (m_assignmentState == AssignmentDone) | 
| + m_assignmentState = m_oldAssignedNodes == m_assignedNodes ? AssignmentUnchanged : AssignmentChanged; | 
| + return m_assignmentState == AssignmentChanged; | 
| +} | 
| + | 
| bool HTMLSlotElement::distributionChanged() | 
| { | 
| ASSERT(m_distributionState != DistributionOnGoing); | 
| @@ -262,6 +313,19 @@ bool HTMLSlotElement::distributionChanged() | 
| return m_distributionState == DistributionChanged; | 
| } | 
| +bool HTMLSlotElement::fallbackChanged() | 
| +{ | 
| + return m_oldFallbackNodes == m_fallbackNodes; | 
| +} | 
| + | 
| +void HTMLSlotElement::didUpdateAssignment() | 
| +{ | 
| + ASSERT(m_assignmentState == AssignmentOnGoing); | 
| + m_assignmentState = AssignmentDone; | 
| + if ((assignmentChanged() || fallbackChanged()) && !m_slotchangeEventAdded) | 
| + fireSlotChangeEvent(); | 
| +} | 
| + | 
| void HTMLSlotElement::didUpdateDistribution() | 
| { | 
| ASSERT(m_distributionState == DistributionOnGoing); | 
| @@ -272,9 +336,20 @@ void HTMLSlotElement::didUpdateDistribution() | 
| if (!shadow->needsDistributionRecalc() && distributionChanged()) | 
| shadow->setNeedsDistributionRecalc(); | 
| } | 
| - if (hasSlotChangeEventListener() && distributionChanged()) { | 
| - // TODO(hayato): Do not enqueue a slotchange event for the same slot twice in the microtask queue | 
| - Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEvent, this)); | 
| +} | 
| + | 
| +void HTMLSlotElement::fireSlotChangeEvent() | 
| +{ | 
| + ASSERT(!m_slotchangeEventAdded); | 
| + Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEvent, this)); | 
| + m_slotchangeEventAdded = true; | 
| + | 
| + Element* shadowHost = isShadowHost(parentElement()) ? parentElement() : nullptr; | 
| + ShadowRoot* shadowRoot = shadowRootIfV1(); | 
| 
 
hayato
2016/04/22 05:33:22
This would be:
  shadowHost.shadowRootIfV1()
 
yuzuchan
2016/04/22 06:59:11
Done.
 
 | 
| + // If this slot is assigned to another slot, fire slot change event of that slot too. | 
| + if (shadowHost && shadowRoot) { | 
| + if (HTMLSlotElement* assigned = assignedSlot()) | 
| + assigned->fireSlotChangeEvent(); | 
| } | 
| } | 
| @@ -293,8 +368,11 @@ DEFINE_TRACE(HTMLSlotElement) | 
| { | 
| visitor->trace(m_assignedNodes); | 
| visitor->trace(m_distributedNodes); | 
| + visitor->trace(m_fallbackNodes); | 
| visitor->trace(m_distributedIndices); | 
| + visitor->trace(m_oldAssignedNodes); | 
| visitor->trace(m_oldDistributedNodes); | 
| + visitor->trace(m_oldFallbackNodes); | 
| HTMLElement::trace(visitor); | 
| } |