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..b968766a96d3fec8e657a75e850a683ac118f06d 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 |
@@ -179,6 +198,7 @@ void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicSt |
if (name == nameAttr) { |
if (ShadowRoot* root = containingShadowRoot()) |
root->owner()->willAffectSelector(); |
+ document().updateAssignment(); |
} |
HTMLElement::attributeChanged(name, oldValue, newValue, reason); |
} |
@@ -187,8 +207,11 @@ 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 (document().shadowCascadeOrder() == ShadowCascadeOrder::ShadowCascadeV1) |
+ document().updateAssignment(); |
+ } |
} |
} |
@@ -206,6 +229,8 @@ Node::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode* |
// clear the distribution when inserted again to avoid cycles. |
clearDistribution(); |
+ if (document().shadowCascadeOrder() == ShadowCascadeOrder::ShadowCascadeV1) |
+ document().updateAssignment(); |
return InsertionDone; |
} |
@@ -224,7 +249,9 @@ void HTMLSlotElement::removedFrom(ContainerNode* insertionPoint) |
if (root == insertionPoint->treeScope().rootNode()) |
root->didRemoveSlot(); |
- |
+ if (document().shadowCascadeOrder() == ShadowCascadeOrder::ShadowCascadeV1) { |
+ document().updateAssignment(); |
+ } |
HTMLElement::removedFrom(insertionPoint); |
} |
@@ -237,9 +264,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 +274,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 +306,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 +329,28 @@ 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(); |
+ if (shadowHost && shadowRoot) { |
+ // If this slot is assigned to another slot, fire slot change event of that slot too. |
+ const AtomicString& assignedSlot = normalizeSlotName(fastGetAttribute(HTMLNames::slotAttr)); |
+ if (assignedSlot != emptyAtom) { |
+ const HeapVector<Member<HTMLSlotElement>>& slots = shadowRoot->descendantSlots(); |
+ for (Member<HTMLSlotElement> slot : slots) { |
+ if (slot->name() == assignedSlot && !slot->hasSlotChangeEventInMicrotask()) { |
+ slot->fireSlotChangeEvent(); |
+ break; |
+ } |
+ } |
+ } |
} |
} |
@@ -293,8 +369,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); |
} |