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 331437363377ec23e6146f20400e17cbe95f9bb0..60d3f9af017cfc972d97f87f2dab3fd7464c40cc 100644 |
| --- a/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp |
| +++ b/third_party/WebKit/Source/core/html/HTMLSlotElement.cpp |
| @@ -38,6 +38,7 @@ |
| #include "core/dom/StyleEngine.h" |
| #include "core/dom/shadow/ElementShadow.h" |
| #include "core/dom/shadow/InsertionPoint.h" |
| +#include "core/dom/shadow/SlotAssignment.h" |
| #include "core/events/Event.h" |
| #include "core/html/AssignedNodesOptions.h" |
| @@ -47,15 +48,25 @@ using namespace HTMLNames; |
| inline HTMLSlotElement::HTMLSlotElement(Document& document) |
| : HTMLElement(slotTag, document) |
| - , m_distributionState(DistributionDone) |
| - , m_assignmentState(AssignmentDone) |
| - , m_slotchangeEventAdded(false) |
| { |
| setHasCustomStyleCallbacks(); |
| } |
| DEFINE_NODE_FACTORY(HTMLSlotElement); |
| +// static |
| +AtomicString HTMLSlotElement::normalizeSlotName(const AtomicString& name) |
| +{ |
| + return (name.isNull() || name.isEmpty()) ? emptyAtom : name; |
| +} |
| + |
| +const HeapVector<Member<Node>>& HTMLSlotElement::assignedNodes() |
| +{ |
| + DCHECK(!needsDistributionRecalc()); |
| + DCHECK(isInShadowTree() || m_assignedNodes.isEmpty()); |
| + return m_assignedNodes; |
| +} |
| + |
| const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const AssignedNodesOptions& options) |
| { |
| updateDistribution(); |
| @@ -66,7 +77,7 @@ const HeapVector<Member<Node>> HTMLSlotElement::assignedNodesForBinding(const As |
| const HeapVector<Member<Node>>& HTMLSlotElement::getDistributedNodes() |
| { |
| - ASSERT(!needsDistributionRecalc()); |
| + DCHECK(!needsDistributionRecalc()); |
| if (isInShadowTree()) |
| return m_distributedNodes; |
| @@ -75,77 +86,66 @@ const HeapVector<Member<Node>>& HTMLSlotElement::getDistributedNodes() |
| // TODO(hayato): If this path causes a performance issue, we should move |
| // ShadowRootRaraDate::m_descendantSlots into TreeScopreRareData-ish and |
| // update the distribution code so it considers a document tree too. |
| - willUpdateDistribution(); |
| + clearDistribution(); |
| for (Node& child : NodeTraversal::childrenOf(*this)) { |
| - if (!child.isSlotAssignable()) |
| + if (!child.isSlotable()) |
| continue; |
| if (isHTMLSlotElement(child)) |
| m_distributedNodes.appendVector(toHTMLSlotElement(child).getDistributedNodes()); |
| else |
| m_distributedNodes.append(&child); |
| } |
| - didUpdateDistribution(); |
| return m_distributedNodes; |
| } |
| -void HTMLSlotElement::appendAssignedNode(Node& node) |
| +void HTMLSlotElement::appendAssignedNode(Node& hostChild) |
| { |
| - ASSERT(m_assignmentState == AssignmentOnGoing); |
| - m_assignedNodes.append(&node); |
| + DCHECK(hostChild.isSlotable()); |
| + m_assignedNodes.append(&hostChild); |
| +} |
| + |
| +void HTMLSlotElement::resolveDistributedNodes() |
| +{ |
| + for (auto& node : m_assignedNodes) { |
| + DCHECK(node->isSlotable()); |
| + if (isHTMLSlotElement(*node)) |
| + appendDistributedNodesFrom(toHTMLSlotElement(*node)); |
| + else |
| + appendDistributedNode(*node); |
| + |
| + if (isChildOfV1ShadowHost()) |
| + parentElementShadow()->setNeedsDistributionRecalc(); |
| + } |
| } |
| void HTMLSlotElement::appendDistributedNode(Node& node) |
| { |
| - ASSERT(m_distributionState == DistributionOnGoing); |
| size_t size = m_distributedNodes.size(); |
| m_distributedNodes.append(&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); |
| size_t index = m_distributedNodes.size(); |
| m_distributedNodes.appendVector(other.m_distributedNodes); |
| for (const auto& node : other.m_distributedNodes) |
| m_distributedIndices.set(node.get(), index++); |
| } |
| -void HTMLSlotElement::willUpdateAssignment() |
| +void HTMLSlotElement::clearDistribution() |
| { |
| - 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_oldDistributedNodes.swap(m_distributedNodes); |
| m_distributedNodes.clear(); |
| m_distributedIndices.clear(); |
| } |
| -void HTMLSlotElement::willUpdateFallback() |
| -{ |
| - m_oldFallbackNodes.swap(m_fallbackNodes); |
| - m_fallbackNodes.clear(); |
| -} |
| - |
| void HTMLSlotElement::dispatchSlotChangeEvent() |
| { |
| + m_slotchangeEventEnqueued = false; |
| Event* event = Event::create(EventTypeNames::slotchange); |
| event->setTarget(this); |
| dispatchScopedEvent(event); |
| - m_slotchangeEventAdded = false; |
| } |
| Node* HTMLSlotElement::distributedNodeNextTo(const Node& node) const |
| @@ -197,24 +197,20 @@ void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicSt |
| { |
| if (name == nameAttr) { |
| if (ShadowRoot* root = containingShadowRoot()) { |
| - root->owner()->willAffectSelector(); |
| if (root->isV1() && oldValue != newValue) |
| - root->assignV1(); |
| + root->ensureSlotAssignment().slotRenamed(normalizeSlotName(oldValue), *this); |
| } |
| } |
| HTMLElement::attributeChanged(name, oldValue, newValue, reason); |
| } |
| -void HTMLSlotElement::childrenChanged(const ChildrenChange& change) |
| +static bool wasInShadowTreeBeforeInserted(HTMLSlotElement& slot, ContainerNode& insertionPoint) |
| { |
| - HTMLElement::childrenChanged(change); |
| - if (ShadowRoot* root = containingShadowRoot()) { |
| - if (ElementShadow* rootOwner = root->owner()) { |
| - rootOwner->setNeedsDistributionRecalc(); |
| - } |
| - if (m_assignedNodes.isEmpty() && root->isV1()) |
| - root->assignV1(); |
| - } |
| + ShadowRoot* root1 = slot.containingShadowRoot(); |
| + ShadowRoot* root2 = insertionPoint.containingShadowRoot(); |
| + if (root1 && root2 && root1 == root2) |
| + return false; |
| + return root1; |
| } |
| Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* insertionPoint) |
| @@ -222,27 +218,36 @@ Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* |
| HTMLElement::insertedInto(insertionPoint); |
| ShadowRoot* root = containingShadowRoot(); |
| if (root) { |
| - if (ElementShadow* rootOwner = root->owner()) |
| - rootOwner->setNeedsDistributionRecalc(); |
| - if (root == insertionPoint->treeScope().rootNode()) |
| - root->didAddSlot(); |
| + DCHECK(root->owner()); |
| + root->owner()->setNeedsDistributionRecalc(); |
| + // Relevant DOM Standard: https://dom.spec.whatwg.org/#concept-node-insert |
| + // - 6.4: Run assign slotables for a tree with node's tree and a set containing each inclusive descendant of node that is a slot. |
| + if (!wasInShadowTreeBeforeInserted(*this, *insertionPoint)) |
| + root->ensureSlotAssignment().slotAdded(*this); |
| } |
| // We could have been distributed into in a detached subtree, make sure to |
| // clear the distribution when inserted again to avoid cycles. |
| clearDistribution(); |
| - if (root && root->isV1()) |
| - root->assignV1(); |
| - |
| return InsertionDone; |
| } |
| +static ShadowRoot* containingShadowRootBeforeRemoved(Node& removedDescendant, ContainerNode& insertionPoint) |
| +{ |
| + if (ShadowRoot* root = removedDescendant.containingShadowRoot()) |
| + return root; |
| + return insertionPoint.containingShadowRoot(); |
| +} |
| + |
| void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) |
| { |
| - ShadowRoot* root = containingShadowRoot(); |
| - if (!root) |
| - root = insertionPoint->containingShadowRoot(); |
| + // `removedFrom` is called after the node is removed from the tree. |
| + // That means: |
| + // 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 originally removed from its parent. |
| + // 2. Or (this slot is now not in a tree scope), this slot's inclusive ancestor was orginally removed from its parent (== insertion point). This slot and the originally removed node was in the same tree. |
| + |
| + ShadowRoot* root = containingShadowRootBeforeRemoved(*this, *insertionPoint); |
| if (root) { |
| if (ElementShadow* rootOwner = root->owner()) |
| rootOwner->setNeedsDistributionRecalc(); |
| @@ -251,14 +256,11 @@ void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) |
| // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up. |
| clearDistribution(); |
| - 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 == &insertionPoint->treeScope().rootNode()) { |
|
kochi
2016/05/25 06:40:26
nit: no need for &
hayato
2016/05/26 04:46:07
Done
|
| + // This slot was in a shadow tree and got disconnected from the shadow root. |
| + root->ensureSlotAssignment().slotRemoved(*this); |
| + } |
| - if (root && root->isV1()) |
| - root->assignV1(); |
| HTMLElement::removedFrom(insertionPoint); |
| } |
| @@ -271,93 +273,56 @@ void HTMLSlotElement::willRecalcStyle(StyleRecalcChange change) |
| node->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::PropagateInheritChangeToDistributedNodes)); |
| } |
| -void HTMLSlotElement::updateFallbackNodes() |
| -{ |
| - if (!m_fallbackNodes.isEmpty()) |
| - return; |
| - for (auto& child : NodeTraversal::childrenOf(*this)) { |
| - if (!child.isSlotAssignable()) |
| - continue; |
| - // Insertion points are not supported as slots fallback |
| - if (isActiveInsertionPoint(child)) |
| - continue; |
| - appendFallbackNode(child); |
| - } |
| -} |
| - |
| void HTMLSlotElement::updateDistributedNodesWithFallback() |
| { |
| if (!m_distributedNodes.isEmpty()) |
| return; |
| - for (auto node : m_fallbackNodes) { |
| - if (isHTMLSlotElement(node)) |
| - appendDistributedNodesFrom(*toHTMLSlotElement(node)); |
| + for (auto& child : NodeTraversal::childrenOf(*this)) { |
| + if (!child.isSlotable()) |
| + continue; |
| + if (isHTMLSlotElement(child)) |
| + appendDistributedNodesFrom(toHTMLSlotElement(child)); |
| else |
| - appendDistributedNode(*node); |
| + appendDistributedNode(child); |
| } |
| } |
| -bool HTMLSlotElement::assignmentChanged() |
| +void HTMLSlotElement::enqueueSlotChangeEvent() |
| { |
| - 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); |
| - if (m_distributionState == DistributionDone) |
| - m_distributionState = m_oldDistributedNodes == m_distributedNodes ? DistributionUnchanged : DistributionChanged; |
| - return m_distributionState == DistributionChanged; |
| -} |
| - |
| -bool HTMLSlotElement::fallbackChanged() |
| -{ |
| - return m_oldFallbackNodes == m_fallbackNodes; |
| -} |
| + if (!m_slotchangeEventEnqueued) { |
| + Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEvent, Persistent<HTMLSlotElement>(this))); |
| + m_slotchangeEventEnqueued = true; |
| + } |
| -void HTMLSlotElement::didUpdateAssignment() |
| -{ |
| - ASSERT(m_assignmentState == AssignmentOnGoing); |
| - m_assignmentState = AssignmentDone; |
| - if ((assignmentChanged() || fallbackChanged()) && !m_slotchangeEventAdded) |
| - fireSlotChangeEvent(); |
| -} |
| + ShadowRoot* root = containingShadowRoot(); |
| + DCHECK(root); |
| + DCHECK(root->isV1()); |
| + root->owner()->setNeedsDistributionRecalc(); |
| -void HTMLSlotElement::didUpdateDistribution() |
| -{ |
| - ASSERT(m_distributionState == DistributionOnGoing); |
| - m_distributionState = DistributionDone; |
| - if (isChildOfV1ShadowHost()) { |
| - ElementShadow* shadow = parentElementShadow(); |
| - ASSERT(shadow); |
| - if (!shadow->needsDistributionRecalc() && distributionChanged()) |
| - shadow->setNeedsDistributionRecalc(); |
| + if (ShadowRoot* parentShadowRoot = v1ShadowRootOfParent()) { |
| + if (HTMLSlotElement* next = parentShadowRoot->ensureSlotAssignment().findSlot(*this)) |
| + next->enqueueSlotChangeEvent(); |
| } |
| } |
| -void HTMLSlotElement::fireSlotChangeEvent() |
| +bool HTMLSlotElement::hasAssignedNodesSlow() const |
| { |
| - ASSERT(!m_slotchangeEventAdded); |
| - Microtask::enqueueMicrotask(WTF::bind(&HTMLSlotElement::dispatchSlotChangeEvent, this)); |
| - m_slotchangeEventAdded = true; |
| - |
| - Element* shadowHost = isShadowHost(parentElement()) ? parentElement() : nullptr; |
| - // If this slot is assigned to another slot, fire slot change event of that slot too. |
| - if (shadowHost && shadowHost->shadowRootIfV1()) { |
| - if (HTMLSlotElement* assigned = assignedSlot()) { |
| - if (!assigned->m_slotchangeEventAdded) |
| - assigned->fireSlotChangeEvent(); |
| - } |
| - } |
| + ShadowRoot* root = containingShadowRoot(); |
| + DCHECK(root); |
| + DCHECK(root->isV1()); |
| + SlotAssignment& assignment = root->ensureSlotAssignment(); |
| + if (assignment.findSlot(*this) != this) |
| + return false; |
| + return assignment.findHostChildBySlotName(name()); |
| } |
| -void HTMLSlotElement::clearDistribution() |
| +bool HTMLSlotElement::findHostChildWithSameSlotName() const |
| { |
| - willUpdateDistribution(); |
| - didUpdateDistribution(); |
| + ShadowRoot* root = containingShadowRoot(); |
| + DCHECK(root); |
| + DCHECK(root->isV1()); |
| + SlotAssignment& assignment = root->ensureSlotAssignment(); |
| + return assignment.findHostChildBySlotName(name()); |
| } |
| short HTMLSlotElement::tabIndex() const |
| @@ -369,11 +334,7 @@ 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); |
| } |