Title: [281878] trunk/Source/WebCore
Revision
281878
Author
[email protected]
Date
2021-09-01 14:30:55 -0700 (Wed, 01 Sep 2021)

Log Message

Eagerly resolve slot elements to simply the code in SlotAssignment
https://bugs.webkit.org/show_bug.cgi?id=229748

Reviewed by Chris Dumez.

This patch makes the resolution of slot elements eager. Lazily resolution stopped making any sense
once slotchange event was required whenever a slot element was inserted or removed per r235650.

Right now, this lazy optimization only applies when scripts repeatedly inserts & removes a slot element
in such a manner that there are multiple slot elements of the same name in a single shadow tree,
and there are assigned nodes to the slot. There is no reason to overcomplicate the slot assignment code
for this insane edge case.

No new tests since there should be no observable behavior change.

* dom/ShadowRoot.cpp:
(WebCore::ShadowRoot::findAssignedSlot):
* dom/SlotAssignment.cpp:
(WebCore::SlotAssignment::findAssignedSlot):
(WebCore::SlotAssignment::addSlotElementByName): Always call resolveSlotsAfterSlotMutation when there
is an ambiguity as to whether this is the first slot element or not.
(WebCore::SlotAssignment::removeSlotElementByName): Ditto for removal case.
(WebCore::SlotAssignment::resolveSlotsAfterSlotMutation):
(WebCore::SlotAssignment::resolveSlotsBeforeNodeInsertionOrRemoval): Moved to the header file.
(WebCore::SlotAssignment::willRemoveAllChildren): Ditto.
(WebCore::SlotAssignment::didChangeSlot):
(WebCore::SlotAssignment::assignedNodesForSlot):
(WebCore::SlotAssignment::findFirstSlotElement):
(WebCore::SlotAssignment::resolveAllSlotElements): Deleted.
* dom/SlotAssignment.h:
(WebCore::SlotAssignment::resolveSlotsBeforeNodeInsertionOrRemoval): Moved from cpp file now that it
doesn't optionally call resolveAllSlotElements. I'm not renaming this function for now since I want to
remove it altogether in a followup.
(WebCore::SlotAssignment::willRemoveAllChildren): Ditto.
(WebCore::ShadowRoot::resolveSlotsBeforeNodeInsertionOrRemoval): Always update the slot mutation version
and clear m_willBeRemovingAllChildren for simplicity. This extra condition will only apply for
the deatils element, which nobody cares about it. Meanwhile, we're wasting many IC entries for all other
node types whenever a node is inserted or removed. It's a pure madness.
(WebCore::ShadowRoot::willRemoveAllChildren): Ditto.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (281877 => 281878)


--- trunk/Source/WebCore/ChangeLog	2021-09-01 21:28:01 UTC (rev 281877)
+++ trunk/Source/WebCore/ChangeLog	2021-09-01 21:30:55 UTC (rev 281878)
@@ -1,3 +1,45 @@
+2021-09-01  Ryosuke Niwa  <[email protected]>
+
+        Eagerly resolve slot elements to simply the code in SlotAssignment
+        https://bugs.webkit.org/show_bug.cgi?id=229748
+
+        Reviewed by Chris Dumez.
+
+        This patch makes the resolution of slot elements eager. Lazily resolution stopped making any sense
+        once slotchange event was required whenever a slot element was inserted or removed per r235650.
+
+        Right now, this lazy optimization only applies when scripts repeatedly inserts & removes a slot element
+        in such a manner that there are multiple slot elements of the same name in a single shadow tree,
+        and there are assigned nodes to the slot. There is no reason to overcomplicate the slot assignment code
+        for this insane edge case.
+
+        No new tests since there should be no observable behavior change.
+
+        * dom/ShadowRoot.cpp:
+        (WebCore::ShadowRoot::findAssignedSlot):
+        * dom/SlotAssignment.cpp:
+        (WebCore::SlotAssignment::findAssignedSlot):
+        (WebCore::SlotAssignment::addSlotElementByName): Always call resolveSlotsAfterSlotMutation when there
+        is an ambiguity as to whether this is the first slot element or not.
+        (WebCore::SlotAssignment::removeSlotElementByName): Ditto for removal case.
+        (WebCore::SlotAssignment::resolveSlotsAfterSlotMutation):
+        (WebCore::SlotAssignment::resolveSlotsBeforeNodeInsertionOrRemoval): Moved to the header file.
+        (WebCore::SlotAssignment::willRemoveAllChildren): Ditto.
+        (WebCore::SlotAssignment::didChangeSlot):
+        (WebCore::SlotAssignment::assignedNodesForSlot):
+        (WebCore::SlotAssignment::findFirstSlotElement):
+        (WebCore::SlotAssignment::resolveAllSlotElements): Deleted.
+        * dom/SlotAssignment.h:
+        (WebCore::SlotAssignment::resolveSlotsBeforeNodeInsertionOrRemoval): Moved from cpp file now that it
+        doesn't optionally call resolveAllSlotElements. I'm not renaming this function for now since I want to
+        remove it altogether in a followup.
+        (WebCore::SlotAssignment::willRemoveAllChildren): Ditto.
+        (WebCore::ShadowRoot::resolveSlotsBeforeNodeInsertionOrRemoval): Always update the slot mutation version
+        and clear m_willBeRemovingAllChildren for simplicity. This extra condition will only apply for
+        the deatils element, which nobody cares about it. Meanwhile, we're wasting many IC entries for all other
+        node types whenever a node is inserted or removed. It's a pure madness.
+        (WebCore::ShadowRoot::willRemoveAllChildren): Ditto.
+
 2021-09-01  Jean-Yves Avenard  <[email protected]>
 
          'Show Next Frame' and 'Show Previous Frame' keyboard shortcuts seem out of context and only pause video

Modified: trunk/Source/WebCore/dom/ShadowRoot.cpp (281877 => 281878)


--- trunk/Source/WebCore/dom/ShadowRoot.cpp	2021-09-01 21:28:01 UTC (rev 281877)
+++ trunk/Source/WebCore/dom/ShadowRoot.cpp	2021-09-01 21:30:55 UTC (rev 281878)
@@ -226,7 +226,7 @@
     ASSERT(node.parentNode() == host());
     if (!m_slotAssignment)
         return nullptr;
-    return m_slotAssignment->findAssignedSlot(node, *this);
+    return m_slotAssignment->findAssignedSlot(node);
 }
 
 void ShadowRoot::renameSlotElement(HTMLSlotElement& slot, const AtomString& oldName, const AtomString& newName)

Modified: trunk/Source/WebCore/dom/SlotAssignment.cpp (281877 => 281878)


--- trunk/Source/WebCore/dom/SlotAssignment.cpp	2021-09-01 21:28:01 UTC (rev 281877)
+++ trunk/Source/WebCore/dom/SlotAssignment.cpp	2021-09-01 21:30:55 UTC (rev 281878)
@@ -75,7 +75,7 @@
 
 SlotAssignment::~SlotAssignment() = default;
 
-HTMLSlotElement* SlotAssignment::findAssignedSlot(const Node& node, ShadowRoot& shadowRoot)
+HTMLSlotElement* SlotAssignment::findAssignedSlot(const Node& node)
 {
     if (!is<Text>(node) && !is<Element>(node))
         return nullptr;
@@ -84,7 +84,7 @@
     if (!slot)
         return nullptr;
 
-    return findFirstSlotElement(*slot, shadowRoot);
+    return findFirstSlotElement(*slot);
 }
 
 inline bool SlotAssignment::hasAssignedNodes(ShadowRoot& shadowRoot, Slot& slot)
@@ -114,6 +114,10 @@
     // FIXME: We should be able to do a targeted reconstruction.
     shadowRoot.host()->invalidateStyleAndRenderersForSubtree();
 
+    if (!m_slotElementCount)
+        shadowRoot.host()->setHasShadowRootContainingSlots(true);
+    m_slotElementCount++;
+
     auto& slotName = slotNameFromAttributeValue(name);
     auto addResult = m_slots.ensure(slotName, [&] {
         m_slotAssignmentsIsValid = false;
@@ -121,29 +125,14 @@
     });
     auto& slot = *addResult.iterator->value;
 
-    if (!m_slotAssignmentsIsValid)
-        assignSlots(shadowRoot);
-
-    shadowRoot.host()->setHasShadowRootContainingSlots(true);
-    m_slotElementCount++;
-
-    bool needsSlotchangeEvent = shadowRoot.shouldFireSlotchangeEvent() && hasAssignedNodes(shadowRoot, slot);
-
     slot.elementCount++;
     if (slot.elementCount == 1) {
         slot.element = makeWeakPtr(slotElement);
-        if (needsSlotchangeEvent)
+        if (shadowRoot.shouldFireSlotchangeEvent() && hasAssignedNodes(shadowRoot, slot))
             slotElement.enqueueSlotChangeEvent();
         return;
     }
 
-    if (!needsSlotchangeEvent) {
-        ASSERT(slot.element || m_needsToResolveSlotElements);
-        slot.element = nullptr;
-        m_needsToResolveSlotElements = true;
-        return;
-    }
-
     resolveSlotsAfterSlotMutation(shadowRoot, SlotMutationType::Insertion);
 }
 
@@ -166,35 +155,30 @@
 
     auto* slot = m_slots.get(slotNameFromAttributeValue(name));
     RELEASE_ASSERT(slot && slot->hasSlotElements());
-    bool needsSlotchangeEvent = shadowRoot.shouldFireSlotchangeEvent() && hasAssignedNodes(shadowRoot, *slot);
 
     slot->elementCount--;
     if (!slot->elementCount) {
         slot->element = nullptr;
-        if (needsSlotchangeEvent && m_slotResolutionVersion != m_slotMutationVersion)
+        bool hasNotResolvedAllSlots = m_slotResolutionVersion != m_slotMutationVersion;
+        if (shadowRoot.shouldFireSlotchangeEvent() && hasAssignedNodes(shadowRoot, *slot) && hasNotResolvedAllSlots)
             slotElement.enqueueSlotChangeEvent();
         return;
     }
 
-    if (!needsSlotchangeEvent) {
-        ASSERT(slot->element || m_needsToResolveSlotElements);
-        slot->element = nullptr;
-        m_needsToResolveSlotElements = true;
-        return;
-    }
-
     bool elementWasRenamed = !oldParentOfRemovedTreeForRemoval;
     if (elementWasRenamed && slot->element == &slotElement)
         slotElement.enqueueSlotChangeEvent();
 
-    // A previous invocation to resolveSlotsAfterSlotMutation during this removal has updated this slot.
-    ASSERT(slot->element || (m_slotResolutionVersion == m_slotMutationVersion && !findSlotElement(shadowRoot, name)));
     if (slot->element) {
         resolveSlotsAfterSlotMutation(shadowRoot, elementWasRenamed ? SlotMutationType::Insertion : SlotMutationType::Removal,
             m_willBeRemovingAllChildren ? oldParentOfRemovedTreeForRemoval : nullptr);
+    } else {
+        // A previous invocation to resolveSlotsAfterSlotMutation during this removal has updated this slot.
+        ASSERT(m_slotResolutionVersion == m_slotMutationVersion && !findSlotElement(shadowRoot, name));
     }
 
     if (slot->oldElement == &slotElement) {
+        ASSERT(shadowRoot.shouldFireSlotchangeEvent());
         slotElement.enqueueSlotChangeEvent();
         slot->oldElement = nullptr;
     }
@@ -207,7 +191,6 @@
     m_slotResolutionVersion = m_slotMutationVersion;
 
     ASSERT(!subtreeToSkip || mutationType == SlotMutationType::Removal);
-    m_needsToResolveSlotElements = false;
 
     for (auto& slot : m_slots.values())
         slot->seenFirstElement = false;
@@ -226,6 +209,7 @@
         }
         if (currentSlot->seenFirstElement) {
             if (mutationType == SlotMutationType::Insertion && currentSlot->oldElement == currentElement) {
+                ASSERT(shadowRoot.shouldFireSlotchangeEvent());
                 currentElement->enqueueSlotChangeEvent();
                 currentSlot->oldElement = nullptr;
             }
@@ -233,9 +217,8 @@
         }
         currentSlot->seenFirstElement = true;
         slotCount++;
-        ASSERT(currentSlot->element || !hasAssignedNodes(shadowRoot, *currentSlot));
         if (currentSlot->element != currentElement) {
-            if (hasAssignedNodes(shadowRoot, *currentSlot)) {
+            if (shadowRoot.shouldFireSlotchangeEvent() && hasAssignedNodes(shadowRoot, *currentSlot)) {
                 currentSlot->oldElement = WTFMove(currentSlot->element);
                 currentElement->enqueueSlotChangeEvent();
             }
@@ -283,23 +266,6 @@
         slotElement.enqueueSlotChangeEvent();
 }
 
-void SlotAssignment::resolveSlotsBeforeNodeInsertionOrRemoval(ShadowRoot& shadowRoot)
-{
-    ASSERT(shadowRoot.shouldFireSlotchangeEvent());
-    m_slotMutationVersion++;
-    m_willBeRemovingAllChildren = false;
-    if (m_needsToResolveSlotElements)
-        resolveAllSlotElements(shadowRoot);
-}
-
-void SlotAssignment::willRemoveAllChildren(ShadowRoot& shadowRoot)
-{
-    m_slotMutationVersion++;
-    m_willBeRemovingAllChildren = true;
-    if (m_needsToResolveSlotElements)
-        resolveAllSlotElements(shadowRoot);
-}
-
 void SlotAssignment::didChangeSlot(const AtomString& slotAttrValue, ShadowRoot& shadowRoot)
 {
     auto& slotName = slotNameFromAttributeValue(slotAttrValue);
@@ -313,7 +279,7 @@
     slot->assignedNodes.clear();
     m_slotAssignmentsIsValid = false;
 
-    auto slotElement = makeRefPtr(findFirstSlotElement(*slot, shadowRoot));
+    auto slotElement = makeRefPtr(findFirstSlotElement(*slot));
     if (!slotElement)
         return;
 
@@ -344,7 +310,7 @@
         return nullptr;
 
     RELEASE_ASSERT(slot->hasSlotElements());
-    if (slot->hasDuplicatedSlotElements() && findFirstSlotElement(*slot, shadowRoot) != &slotElement)
+    if (slot->hasDuplicatedSlotElements() && findFirstSlotElement(*slot) != &slotElement)
         return nullptr;
 
     return &slot->assignedNodes;
@@ -372,10 +338,9 @@
     return slotNameFromSlotAttribute(child);
 }
 
-HTMLSlotElement* SlotAssignment::findFirstSlotElement(Slot& slot, ShadowRoot& shadowRoot)
+HTMLSlotElement* SlotAssignment::findFirstSlotElement(Slot& slot)
 {
-    if (slot.shouldResolveSlotElement())
-        resolveAllSlotElements(shadowRoot);
+    RELEASE_ASSERT(!slot.shouldResolveSlotElement());
 
     ASSERT(!slot.element || m_slotElementsForConsistencyCheck.contains(slot.element.get()));
     ASSERT(!!slot.element == !!slot.elementCount);
@@ -383,33 +348,6 @@
     return slot.element.get();
 }
 
-void SlotAssignment::resolveAllSlotElements(ShadowRoot& shadowRoot)
-{
-    ASSERT(m_needsToResolveSlotElements);
-    m_needsToResolveSlotElements = false;
-
-    // FIXME: It's inefficient to reset all values. We should be able to void this in common case.
-    for (auto& entry : m_slots)
-        entry.value->seenFirstElement = false;
-
-    unsigned slotCount = m_slots.size();
-    for (auto& slotElement : descendantsOfType<HTMLSlotElement>(shadowRoot)) {
-        auto& slotName = slotNameFromAttributeValue(slotElement.attributeWithoutSynchronization(nameAttr));
-
-        auto* slot = m_slots.get(slotName);
-        RELEASE_ASSERT(slot); // slot must have been created when a slot was inserted.
-
-        if (slot->seenFirstElement)
-            continue;
-        slot->seenFirstElement = true;
-
-        slot->element = makeWeakPtr(slotElement);
-        slotCount--;
-        if (!slotCount)
-            break;
-    }
-}
-
 void SlotAssignment::assignSlots(ShadowRoot& shadowRoot)
 {
     ASSERT(!m_slotAssignmentsIsValid);

Modified: trunk/Source/WebCore/dom/SlotAssignment.h (281877 => 281878)


--- trunk/Source/WebCore/dom/SlotAssignment.h	2021-09-01 21:28:01 UTC (rev 281877)
+++ trunk/Source/WebCore/dom/SlotAssignment.h	2021-09-01 21:30:55 UTC (rev 281878)
@@ -48,15 +48,16 @@
 
     static const AtomString& defaultSlotName() { return emptyAtom(); }
 
-    HTMLSlotElement* findAssignedSlot(const Node&, ShadowRoot&);
+    HTMLSlotElement* findAssignedSlot(const Node&);
 
     void renameSlotElement(HTMLSlotElement&, const AtomString& oldName, const AtomString& newName, ShadowRoot&);
     void addSlotElementByName(const AtomString&, HTMLSlotElement&, ShadowRoot&);
     void removeSlotElementByName(const AtomString&, HTMLSlotElement&, ContainerNode* oldParentOfRemovedTreeForRemoval, ShadowRoot&);
     void slotFallbackDidChange(HTMLSlotElement&, ShadowRoot&);
-    void resolveSlotsBeforeNodeInsertionOrRemoval(ShadowRoot&);
-    void willRemoveAllChildren(ShadowRoot&);
 
+    void resolveSlotsBeforeNodeInsertionOrRemoval();
+    void willRemoveAllChildren();
+
     void didChangeSlot(const AtomString&, ShadowRoot&);
 
     const Vector<WeakPtr<Node>>* assignedNodesForSlot(const HTMLSlotElement&, ShadowRoot&);
@@ -75,9 +76,9 @@
         bool shouldResolveSlotElement() { return !element && elementCount; }
 
         WeakPtr<HTMLSlotElement> element;
-        WeakPtr<HTMLSlotElement> oldElement;
+        WeakPtr<HTMLSlotElement> oldElement; // Set by resolveSlotsAfterSlotMutation to dispatch slotchange in tree order.
         unsigned elementCount { 0 };
-        bool seenFirstElement { false };
+        bool seenFirstElement { false }; // Used in resolveSlotsAfterSlotMutation.
         Vector<WeakPtr<Node>> assignedNodes;
     };
 
@@ -87,8 +88,7 @@
 
     virtual const AtomString& slotNameForHostChild(const Node&) const;
 
-    HTMLSlotElement* findFirstSlotElement(Slot&, ShadowRoot&);
-    void resolveAllSlotElements(ShadowRoot&);
+    HTMLSlotElement* findFirstSlotElement(Slot&);
 
     void assignSlots(ShadowRoot&);
     void assignToSlot(Node& child, const AtomString& slotName);
@@ -99,7 +99,6 @@
     HashSet<HTMLSlotElement*> m_slotElementsForConsistencyCheck;
 #endif
 
-    bool m_needsToResolveSlotElements { false };
     bool m_slotAssignmentsIsValid { false };
     bool m_willBeRemovingAllChildren { false };
     unsigned m_slotMutationVersion { 0 };
@@ -107,16 +106,28 @@
     unsigned m_slotElementCount { 0 };
 };
 
+inline void SlotAssignment::resolveSlotsBeforeNodeInsertionOrRemoval()
+{
+    m_slotMutationVersion++;
+    m_willBeRemovingAllChildren = false;
+}
+
+inline void SlotAssignment::willRemoveAllChildren()
+{
+    m_slotMutationVersion++;
+    m_willBeRemovingAllChildren = true;
+}
+
 inline void ShadowRoot::resolveSlotsBeforeNodeInsertionOrRemoval()
 {
-    if (UNLIKELY(shouldFireSlotchangeEvent() && m_slotAssignment))
-        m_slotAssignment->resolveSlotsBeforeNodeInsertionOrRemoval(*this);
+    if (UNLIKELY(m_slotAssignment))
+        m_slotAssignment->resolveSlotsBeforeNodeInsertionOrRemoval();
 }
 
 inline void ShadowRoot::willRemoveAllChildren(ContainerNode&)
 {
-    if (UNLIKELY(shouldFireSlotchangeEvent() && m_slotAssignment))
-        m_slotAssignment->willRemoveAllChildren(*this);
+    if (UNLIKELY(m_slotAssignment))
+        m_slotAssignment->willRemoveAllChildren();
 }
 
 inline void ShadowRoot::didRemoveAllChildrenOfShadowHost()
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to