Title: [271851] branches/safari-611-branch/Source/WebCore
Revision
271851
Author
alanc...@apple.com
Date
2021-01-25 14:13:14 -0800 (Mon, 25 Jan 2021)

Log Message

Cherry-pick r271584. rdar://problem/73473616

    Optimize :hover/:active style invalidation for deep trees and descendant selectors
    https://bugs.webkit.org/show_bug.cgi?id=220711

    Reviewed by Zalan Bujtas.

    Hover and active states are flipped for the entire ancestor chain. We compute invalidation for each flipped
    element separately. If the selectors are of form ':active .descendant' then each of these invalidations needs
    to traverse the whole subtree, leading to O(n^2) behavior.

    We really only need to traverse the descendants once, starting from the element closest to the root that changes state.

    * dom/Document.cpp:
    (WebCore::Document::updateHoverActiveState):

    Compute the change root and pass the information to setActive/Hover.
    Reorganize the function a bit to allow this, and for general readability.

    * dom/Element.cpp:
    (WebCore::Element::setActive):
    (WebCore::Element::setHovered):
    * dom/Element.h:
    * html/HTMLAnchorElement.cpp:
    (WebCore::HTMLAnchorElement::setActive):
    * html/HTMLAnchorElement.h:
    * html/HTMLLabelElement.cpp:
    (WebCore::HTMLLabelElement::setActive):
    (WebCore::HTMLLabelElement::setHovered):
    * html/HTMLLabelElement.h:
    * html/shadow/SpinButtonElement.cpp:
    (WebCore::SpinButtonElement::setHovered):
    * html/shadow/SpinButtonElement.h:
    * style/PseudoClassChangeInvalidation.cpp:
    (WebCore::Style::PseudoClassChangeInvalidation::computeInvalidation):

    Only include descendant traversing rulesets for the change root.

    * style/PseudoClassChangeInvalidation.h:
    (WebCore::Style::PseudoClassChangeInvalidation::PseudoClassChangeInvalidation):

    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@271584 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Modified Paths

Diff

Modified: branches/safari-611-branch/Source/WebCore/ChangeLog (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/ChangeLog	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/ChangeLog	2021-01-25 22:13:14 UTC (rev 271851)
@@ -1,5 +1,92 @@
 2021-01-25  Alan Coon  <alanc...@apple.com>
 
+        Cherry-pick r271584. rdar://problem/73473616
+
+    Optimize :hover/:active style invalidation for deep trees and descendant selectors
+    https://bugs.webkit.org/show_bug.cgi?id=220711
+    
+    Reviewed by Zalan Bujtas.
+    
+    Hover and active states are flipped for the entire ancestor chain. We compute invalidation for each flipped
+    element separately. If the selectors are of form ':active .descendant' then each of these invalidations needs
+    to traverse the whole subtree, leading to O(n^2) behavior.
+    
+    We really only need to traverse the descendants once, starting from the element closest to the root that changes state.
+    
+    * dom/Document.cpp:
+    (WebCore::Document::updateHoverActiveState):
+    
+    Compute the change root and pass the information to setActive/Hover.
+    Reorganize the function a bit to allow this, and for general readability.
+    
+    * dom/Element.cpp:
+    (WebCore::Element::setActive):
+    (WebCore::Element::setHovered):
+    * dom/Element.h:
+    * html/HTMLAnchorElement.cpp:
+    (WebCore::HTMLAnchorElement::setActive):
+    * html/HTMLAnchorElement.h:
+    * html/HTMLLabelElement.cpp:
+    (WebCore::HTMLLabelElement::setActive):
+    (WebCore::HTMLLabelElement::setHovered):
+    * html/HTMLLabelElement.h:
+    * html/shadow/SpinButtonElement.cpp:
+    (WebCore::SpinButtonElement::setHovered):
+    * html/shadow/SpinButtonElement.h:
+    * style/PseudoClassChangeInvalidation.cpp:
+    (WebCore::Style::PseudoClassChangeInvalidation::computeInvalidation):
+    
+    Only include descendant traversing rulesets for the change root.
+    
+    * style/PseudoClassChangeInvalidation.h:
+    (WebCore::Style::PseudoClassChangeInvalidation::PseudoClassChangeInvalidation):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@271584 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-01-18  Antti Koivisto  <an...@apple.com>
+
+            Optimize :hover/:active style invalidation for deep trees and descendant selectors
+            https://bugs.webkit.org/show_bug.cgi?id=220711
+
+            Reviewed by Zalan Bujtas.
+
+            Hover and active states are flipped for the entire ancestor chain. We compute invalidation for each flipped
+            element separately. If the selectors are of form ':active .descendant' then each of these invalidations needs
+            to traverse the whole subtree, leading to O(n^2) behavior.
+
+            We really only need to traverse the descendants once, starting from the element closest to the root that changes state.
+
+            * dom/Document.cpp:
+            (WebCore::Document::updateHoverActiveState):
+
+            Compute the change root and pass the information to setActive/Hover.
+            Reorganize the function a bit to allow this, and for general readability.
+
+            * dom/Element.cpp:
+            (WebCore::Element::setActive):
+            (WebCore::Element::setHovered):
+            * dom/Element.h:
+            * html/HTMLAnchorElement.cpp:
+            (WebCore::HTMLAnchorElement::setActive):
+            * html/HTMLAnchorElement.h:
+            * html/HTMLLabelElement.cpp:
+            (WebCore::HTMLLabelElement::setActive):
+            (WebCore::HTMLLabelElement::setHovered):
+            * html/HTMLLabelElement.h:
+            * html/shadow/SpinButtonElement.cpp:
+            (WebCore::SpinButtonElement::setHovered):
+            * html/shadow/SpinButtonElement.h:
+            * style/PseudoClassChangeInvalidation.cpp:
+            (WebCore::Style::PseudoClassChangeInvalidation::computeInvalidation):
+
+            Only include descendant traversing rulesets for the change root.
+
+            * style/PseudoClassChangeInvalidation.h:
+            (WebCore::Style::PseudoClassChangeInvalidation::PseudoClassChangeInvalidation):
+
+2021-01-25  Alan Coon  <alanc...@apple.com>
+
         Cherry-pick r271542. rdar://problem/73477309
 
     (REGRESSION) Playback pauses upon entering PiP via media controls on trailers.apple.com

Modified: branches/safari-611-branch/Source/WebCore/dom/Document.cpp (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/dom/Document.cpp	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/dom/Document.cpp	2021-01-25 22:13:14 UTC (rev 271851)
@@ -7123,6 +7123,11 @@
 {
     ASSERT(!request.readOnly());
 
+    Vector<RefPtr<Element>, 32> elementsToClearActive;
+    Vector<RefPtr<Element>, 32> elementsToSetActive;
+    Vector<RefPtr<Element>, 32> elementsToClearHover;
+    Vector<RefPtr<Element>, 32> elementsToSetHover;
+
     Element* innerElementInDocument = innerElement;
     while (innerElementInDocument && &innerElementInDocument->document() != this) {
         innerElementInDocument->document().updateHoverActiveState(request, innerElementInDocument);
@@ -7132,8 +7137,8 @@
     Element* oldActiveElement = m_activeElement.get();
     if (oldActiveElement && !request.active()) {
         // We are clearing the :active chain because the mouse has been released.
-        for (Element* currentElement = oldActiveElement; currentElement; currentElement = currentElement->parentElementInComposedTree()) {
-            currentElement->setActive(false);
+        for (auto* currentElement = oldActiveElement; currentElement; currentElement = currentElement->parentElementInComposedTree()) {
+            elementsToClearActive.append(currentElement);
             m_userActionElements.setInActiveChain(*currentElement, false);
         }
         m_activeElement = nullptr;
@@ -7178,15 +7183,13 @@
 
     auto* commonAncestor = findNearestCommonComposedAncestor(oldHoveredElement.get(), newHoveredElement);
 
-    Vector<RefPtr<Element>, 32> elementsToRemoveFromChain;
-    Vector<RefPtr<Element>, 32> elementsToAddToChain;
-
     if (oldHoveredElement != newHoveredElement) {
         for (auto* element = oldHoveredElement.get(); element; element = element->parentElementInComposedTree()) {
             if (element == commonAncestor)
                 break;
-            if (!mustBeInActiveChain || element->isInActiveChain())
-                elementsToRemoveFromChain.append(element);
+            if (mustBeInActiveChain && !element->isInActiveChain())
+                continue;
+            elementsToClearHover.append(element);
         }
         // Unset hovered nodes in sub frame documents if the old hovered node was a frame owner.
         if (is<HTMLFrameOwnerElement>(oldHoveredElement)) {
@@ -7195,25 +7198,26 @@
         }
     }
 
+    bool sawCommonAncestor = false;
     for (auto* element = newHoveredElement; element; element = element->parentElementInComposedTree()) {
-        if (!mustBeInActiveChain || element->isInActiveChain())
-            elementsToAddToChain.append(element);
-    }
-
-    for (auto& element : elementsToRemoveFromChain)
-        element->setHovered(false);
-
-    bool sawCommonAncestor = false;
-    for (auto& element : elementsToAddToChain) {
+        if (mustBeInActiveChain && !element->isInActiveChain())
+            continue;
         if (allowActiveChanges)
-            element->setActive(true);
+            elementsToSetActive.append(element);
         if (element == commonAncestor)
             sawCommonAncestor = true;
-        if (!sawCommonAncestor) {
-            // Elements after the common hover ancestor does not change hover state, but are iterated over because they may change active state.
-            element->setHovered(true);
-        }
+        if (!sawCommonAncestor)
+            elementsToSetHover.append(element);
     }
+
+    for (auto& element : elementsToClearActive)
+        element->setActive(false, false, element == elementsToClearActive.last() ? Element::IsUserActionStateChangeRoot::Yes : Element::IsUserActionStateChangeRoot::No);
+    for (auto& element : elementsToSetActive)
+        element->setActive(true, false, element == elementsToSetActive.last() ? Element::IsUserActionStateChangeRoot::Yes : Element::IsUserActionStateChangeRoot::No);
+    for (auto& element : elementsToClearHover)
+        element->setHovered(false, element == elementsToClearHover.last() ? Element::IsUserActionStateChangeRoot::Yes : Element::IsUserActionStateChangeRoot::No);
+    for (auto& element : elementsToSetHover)
+        element->setHovered(true, element == elementsToSetHover.last() ? Element::IsUserActionStateChangeRoot::Yes : Element::IsUserActionStateChangeRoot::No);
 }
 
 bool Document::haveStylesheetsLoaded() const

Modified: branches/safari-611-branch/Source/WebCore/dom/Element.cpp (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/dom/Element.cpp	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/dom/Element.cpp	2021-01-25 22:13:14 UTC (rev 271851)
@@ -678,12 +678,12 @@
     return document().userActionElements().isBeingDragged(*this);
 }
 
-void Element::setActive(bool flag, bool pause)
+void Element::setActive(bool flag, bool pause, IsUserActionStateChangeRoot isUserActionStateChangeRoot)
 {
     if (flag == active())
         return;
     {
-        Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassActive);
+        Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassActive, isUserActionStateChangeRoot);
         document().userActionElements().setActive(*this, flag);
     }
 
@@ -755,12 +755,12 @@
     }
 }
 
-void Element::setHovered(bool flag)
+void Element::setHovered(bool flag, IsUserActionStateChangeRoot isUserActionStateChangeRoot)
 {
     if (flag == hovered())
         return;
     {
-        Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassHover);
+        Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassHover, isUserActionStateChangeRoot);
         document().userActionElements().setHovered(*this, flag);
     }
 

Modified: branches/safari-611-branch/Source/WebCore/dom/Element.h (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/dom/Element.h	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/dom/Element.h	2021-01-25 22:13:14 UTC (rev 271851)
@@ -321,8 +321,9 @@
     bool isBeingDragged() const { return isUserActionElement() && isUserActionElementDragged(); }
     bool hasFocusWithin() const { return hasNodeFlag(NodeFlag::HasFocusWithin); };
 
-    virtual void setActive(bool = true, bool pause = false);
-    virtual void setHovered(bool = true);
+    enum class IsUserActionStateChangeRoot { Yes, No };
+    virtual void setActive(bool = true, bool pause = false, IsUserActionStateChangeRoot = IsUserActionStateChangeRoot::Yes);
+    virtual void setHovered(bool = true, IsUserActionStateChangeRoot = IsUserActionStateChangeRoot::Yes);
     virtual void setFocus(bool);
     void setBeingDragged(bool);
     void setHasFocusWithin(bool);

Modified: branches/safari-611-branch/Source/WebCore/html/HTMLAnchorElement.cpp (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/html/HTMLAnchorElement.cpp	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/html/HTMLAnchorElement.cpp	2021-01-25 22:13:14 UTC (rev 271851)
@@ -211,7 +211,7 @@
     HTMLElement::defaultEventHandler(event);
 }
 
-void HTMLAnchorElement::setActive(bool down, bool pause)
+void HTMLAnchorElement::setActive(bool down, bool pause, IsUserActionStateChangeRoot isUserActionStateChangeRoot)
 {
     if (hasEditableStyle()) {
         switch (document().settings().editableLinkBehavior()) {
@@ -232,7 +232,7 @@
         }
     }
     
-    HTMLElement::setActive(down, pause);
+    HTMLElement::setActive(down, pause, isUserActionStateChangeRoot);
 }
 
 void HTMLAnchorElement::parseAttribute(const QualifiedName& name, const AtomString& value)

Modified: branches/safari-611-branch/Source/WebCore/html/HTMLAnchorElement.h (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/html/HTMLAnchorElement.h	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/html/HTMLAnchorElement.h	2021-01-25 22:13:14 UTC (rev 271851)
@@ -87,7 +87,7 @@
     bool isMouseFocusable() const override;
     bool isKeyboardFocusable(KeyboardEvent*) const override;
     void defaultEventHandler(Event&) final;
-    void setActive(bool active = true, bool pause = false) final;
+    void setActive(bool active, bool pause, IsUserActionStateChangeRoot) final;
     bool accessKeyAction(bool sendMouseEvents) final;
     bool isURLAttribute(const Attribute&) const final;
     bool canStartSelection() const final;

Modified: branches/safari-611-branch/Source/WebCore/html/HTMLLabelElement.cpp (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/html/HTMLLabelElement.cpp	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/html/HTMLLabelElement.cpp	2021-01-25 22:13:14 UTC (rev 271851)
@@ -85,13 +85,13 @@
     return downcast<HTMLFormControlElement>(control.get())->form();
 }
 
-void HTMLLabelElement::setActive(bool down, bool pause)
+void HTMLLabelElement::setActive(bool down, bool pause, IsUserActionStateChangeRoot isUserActionStateChangeRoot)
 {
     if (down == active())
         return;
 
     // Update our status first.
-    HTMLElement::setActive(down, pause);
+    HTMLElement::setActive(down, pause, isUserActionStateChangeRoot);
 
     // Also update our corresponding control.
     if (auto element = control())
@@ -98,13 +98,13 @@
         element->setActive(down, pause);
 }
 
-void HTMLLabelElement::setHovered(bool over)
+void HTMLLabelElement::setHovered(bool over, IsUserActionStateChangeRoot isUserActionStateChangeRoot)
 {
     if (over == hovered())
         return;
         
     // Update our status first.
-    HTMLElement::setHovered(over);
+    HTMLElement::setHovered(over, isUserActionStateChangeRoot);
 
     // Also update our corresponding control.
     if (auto element = control())

Modified: branches/safari-611-branch/Source/WebCore/html/HTMLLabelElement.h (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/html/HTMLLabelElement.h	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/html/HTMLLabelElement.h	2021-01-25 22:13:14 UTC (rev 271851)
@@ -45,8 +45,8 @@
     bool accessKeyAction(bool sendMouseEvents) final;
 
     // Overridden to update the hover/active state of the corresponding control.
-    void setActive(bool = true, bool pause = false) final;
-    void setHovered(bool = true) final;
+    void setActive(bool, bool pause, IsUserActionStateChangeRoot) final;
+    void setHovered(bool, IsUserActionStateChangeRoot) final;
 
     // Overridden to either click() or focus() the corresponding control.
     void defaultEventHandler(Event&) final;

Modified: branches/safari-611-branch/Source/WebCore/html/shadow/SpinButtonElement.cpp (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/html/shadow/SpinButtonElement.cpp	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/html/shadow/SpinButtonElement.cpp	2021-01-25 22:13:14 UTC (rev 271851)
@@ -249,11 +249,11 @@
         step(m_upDownState == Up ? 1 : -1);
 }
 
-void SpinButtonElement::setHovered(bool flag)
+void SpinButtonElement::setHovered(bool flag, IsUserActionStateChangeRoot isUserActionStateChangeRoot)
 {
     if (!flag)
         m_upDownState = Indeterminate;
-    HTMLDivElement::setHovered(flag);
+    HTMLDivElement::setHovered(flag, isUserActionStateChangeRoot);
 }
 
 bool SpinButtonElement::shouldRespondToMouseEvents()

Modified: branches/safari-611-branch/Source/WebCore/html/shadow/SpinButtonElement.h (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/html/shadow/SpinButtonElement.h	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/html/shadow/SpinButtonElement.h	2021-01-25 22:13:14 UTC (rev 271851)
@@ -79,7 +79,7 @@
     void startRepeatingTimer();
     void stopRepeatingTimer();
     void repeatingTimerFired();
-    void setHovered(bool = true) override;
+    void setHovered(bool, IsUserActionStateChangeRoot) override;
     bool shouldRespondToMouseEvents();
     bool isMouseFocusable() const override { return false; }
 

Modified: branches/safari-611-branch/Source/WebCore/style/PseudoClassChangeInvalidation.cpp (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/style/PseudoClassChangeInvalidation.cpp	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/style/PseudoClassChangeInvalidation.cpp	2021-01-25 22:13:14 UTC (rev 271851)
@@ -32,7 +32,7 @@
 namespace WebCore {
 namespace Style {
 
-void PseudoClassChangeInvalidation::computeInvalidation(CSSSelector::PseudoClassType pseudoClass)
+void PseudoClassChangeInvalidation::computeInvalidation(CSSSelector::PseudoClassType pseudoClass, Element::IsUserActionStateChangeRoot isUserActionStateChangeRoot)
 {
     bool shouldInvalidateCurrent = false;
     bool mayAffectStyleInShadowTree = false;
@@ -54,8 +54,17 @@
 
     auto& ruleSets = m_element.styleResolver().ruleSets();
     if (auto* invalidationRuleSets = ruleSets.pseudoClassInvalidationRuleSets(pseudoClass)) {
-        for (auto& invalidationRuleSet : *invalidationRuleSets)
+        for (auto& invalidationRuleSet : *invalidationRuleSets) {
+            // For focus/hover we flip the whole ancestor chain. We only need to do deep invalidation traversal in the change root.
+            auto shouldInvalidate = [&] {
+                if (isUserActionStateChangeRoot == Element::IsUserActionStateChangeRoot::Yes)
+                    return true;
+                return invalidationRuleSet.matchElement != MatchElement::Ancestor;
+            }();
+            if (!shouldInvalidate)
+                continue;
             Invalidator::addToMatchElementRuleSets(m_matchElementRuleSets, invalidationRuleSet);
+        }
     }
 }
 

Modified: branches/safari-611-branch/Source/WebCore/style/PseudoClassChangeInvalidation.h (271850 => 271851)


--- branches/safari-611-branch/Source/WebCore/style/PseudoClassChangeInvalidation.h	2021-01-25 22:13:09 UTC (rev 271850)
+++ branches/safari-611-branch/Source/WebCore/style/PseudoClassChangeInvalidation.h	2021-01-25 22:13:14 UTC (rev 271851)
@@ -35,11 +35,11 @@
 
 class PseudoClassChangeInvalidation {
 public:
-    PseudoClassChangeInvalidation(Element&, CSSSelector::PseudoClassType);
+    PseudoClassChangeInvalidation(Element&, CSSSelector::PseudoClassType, Element::IsUserActionStateChangeRoot = Element::IsUserActionStateChangeRoot::Yes);
     ~PseudoClassChangeInvalidation();
 
 private:
-    void computeInvalidation(CSSSelector::PseudoClassType);
+    void computeInvalidation(CSSSelector::PseudoClassType, Element::IsUserActionStateChangeRoot);
     void invalidateStyleWithRuleSets();
 
     const bool m_isEnabled;
@@ -48,7 +48,7 @@
     Invalidator::MatchElementRuleSets m_matchElementRuleSets;
 };
 
-inline PseudoClassChangeInvalidation::PseudoClassChangeInvalidation(Element& element, CSSSelector::PseudoClassType pseudoClassType)
+inline PseudoClassChangeInvalidation::PseudoClassChangeInvalidation(Element& element, CSSSelector::PseudoClassType pseudoClassType, Element::IsUserActionStateChangeRoot isUserActionStateChangeRoot)
     : m_isEnabled(element.needsStyleInvalidation())
     , m_element(element)
 
@@ -55,7 +55,7 @@
 {
     if (!m_isEnabled)
         return;
-    computeInvalidation(pseudoClassType);
+    computeInvalidation(pseudoClassType, isUserActionStateChangeRoot);
     invalidateStyleWithRuleSets();
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to