Changeset 271584 in webkit
- Timestamp:
- Jan 18, 2021 12:26:24 PM (18 months ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 12 edited
-
ChangeLog (modified) (1 diff)
-
dom/Document.cpp (modified) (4 diffs)
-
dom/Element.cpp (modified) (2 diffs)
-
dom/Element.h (modified) (1 diff)
-
html/HTMLAnchorElement.cpp (modified) (2 diffs)
-
html/HTMLAnchorElement.h (modified) (1 diff)
-
html/HTMLLabelElement.cpp (modified) (4 diffs)
-
html/HTMLLabelElement.h (modified) (1 diff)
-
html/shadow/SpinButtonElement.cpp (modified) (1 diff)
-
html/shadow/SpinButtonElement.h (modified) (1 diff)
-
style/PseudoClassChangeInvalidation.cpp (modified) (2 diffs)
-
style/PseudoClassChangeInvalidation.h (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r271583 r271584 1 2021-01-18 Antti Koivisto <antti@apple.com> 2 3 Optimize :hover/:active style invalidation for deep trees and descendant selectors 4 https://bugs.webkit.org/show_bug.cgi?id=220711 5 6 Reviewed by Zalan Bujtas. 7 8 Hover and active states are flipped for the entire ancestor chain. We compute invalidation for each flipped 9 element separately. If the selectors are of form ':active .descendant' then each of these invalidations needs 10 to traverse the whole subtree, leading to O(n^2) behavior. 11 12 We really only need to traverse the descendants once, starting from the element closest to the root that changes state. 13 14 * dom/Document.cpp: 15 (WebCore::Document::updateHoverActiveState): 16 17 Compute the change root and pass the information to setActive/Hover. 18 Reorganize the function a bit to allow this, and for general readability. 19 20 * dom/Element.cpp: 21 (WebCore::Element::setActive): 22 (WebCore::Element::setHovered): 23 * dom/Element.h: 24 * html/HTMLAnchorElement.cpp: 25 (WebCore::HTMLAnchorElement::setActive): 26 * html/HTMLAnchorElement.h: 27 * html/HTMLLabelElement.cpp: 28 (WebCore::HTMLLabelElement::setActive): 29 (WebCore::HTMLLabelElement::setHovered): 30 * html/HTMLLabelElement.h: 31 * html/shadow/SpinButtonElement.cpp: 32 (WebCore::SpinButtonElement::setHovered): 33 * html/shadow/SpinButtonElement.h: 34 * style/PseudoClassChangeInvalidation.cpp: 35 (WebCore::Style::PseudoClassChangeInvalidation::computeInvalidation): 36 37 Only include descendant traversing rulesets for the change root. 38 39 * style/PseudoClassChangeInvalidation.h: 40 (WebCore::Style::PseudoClassChangeInvalidation::PseudoClassChangeInvalidation): 41 1 42 2021-01-18 Fujii Hironori <Hironori.Fujii@sony.com> 2 43 -
trunk/Source/WebCore/dom/Document.cpp
r271514 r271584 7129 7129 ASSERT(!request.readOnly()); 7130 7130 7131 Vector<RefPtr<Element>, 32> elementsToClearActive; 7132 Vector<RefPtr<Element>, 32> elementsToSetActive; 7133 Vector<RefPtr<Element>, 32> elementsToClearHover; 7134 Vector<RefPtr<Element>, 32> elementsToSetHover; 7135 7131 7136 Element* innerElementInDocument = innerElement; 7132 7137 while (innerElementInDocument && &innerElementInDocument->document() != this) { … … 7138 7143 if (oldActiveElement && !request.active()) { 7139 7144 // We are clearing the :active chain because the mouse has been released. 7140 for ( Element* currentElement = oldActiveElement; currentElement; currentElement = currentElement->parentElementInComposedTree()) {7141 currentElement->setActive(false);7145 for (auto* currentElement = oldActiveElement; currentElement; currentElement = currentElement->parentElementInComposedTree()) { 7146 elementsToClearActive.append(currentElement); 7142 7147 m_userActionElements.setInActiveChain(*currentElement, false); 7143 7148 } … … 7184 7189 auto* commonAncestor = findNearestCommonComposedAncestor(oldHoveredElement.get(), newHoveredElement); 7185 7190 7186 Vector<RefPtr<Element>, 32> elementsToRemoveFromChain;7187 Vector<RefPtr<Element>, 32> elementsToAddToChain;7188 7189 7191 if (oldHoveredElement != newHoveredElement) { 7190 7192 for (auto* element = oldHoveredElement.get(); element; element = element->parentElementInComposedTree()) { 7191 7193 if (element == commonAncestor) 7192 7194 break; 7193 if (!mustBeInActiveChain || element->isInActiveChain()) 7194 elementsToRemoveFromChain.append(element); 7195 if (mustBeInActiveChain && !element->isInActiveChain()) 7196 continue; 7197 elementsToClearHover.append(element); 7195 7198 } 7196 7199 // Unset hovered nodes in sub frame documents if the old hovered node was a frame owner. … … 7201 7204 } 7202 7205 7206 bool sawCommonAncestor = false; 7203 7207 for (auto* element = newHoveredElement; element; element = element->parentElementInComposedTree()) { 7204 if (!mustBeInActiveChain || element->isInActiveChain()) 7205 elementsToAddToChain.append(element); 7206 } 7207 7208 for (auto& element : elementsToRemoveFromChain) 7209 element->setHovered(false); 7210 7211 bool sawCommonAncestor = false; 7212 for (auto& element : elementsToAddToChain) { 7208 if (mustBeInActiveChain && !element->isInActiveChain()) 7209 continue; 7213 7210 if (allowActiveChanges) 7214 element ->setActive(true);7211 elementsToSetActive.append(element); 7215 7212 if (element == commonAncestor) 7216 7213 sawCommonAncestor = true; 7217 if (!sawCommonAncestor) { 7218 // Elements after the common hover ancestor does not change hover state, but are iterated over because they may change active state. 7219 element->setHovered(true); 7220 } 7221 } 7214 if (!sawCommonAncestor) 7215 elementsToSetHover.append(element); 7216 } 7217 7218 for (auto& element : elementsToClearActive) 7219 element->setActive(false, false, element == elementsToClearActive.last() ? Element::IsUserActionStateChangeRoot::Yes : Element::IsUserActionStateChangeRoot::No); 7220 for (auto& element : elementsToSetActive) 7221 element->setActive(true, false, element == elementsToSetActive.last() ? Element::IsUserActionStateChangeRoot::Yes : Element::IsUserActionStateChangeRoot::No); 7222 for (auto& element : elementsToClearHover) 7223 element->setHovered(false, element == elementsToClearHover.last() ? Element::IsUserActionStateChangeRoot::Yes : Element::IsUserActionStateChangeRoot::No); 7224 for (auto& element : elementsToSetHover) 7225 element->setHovered(true, element == elementsToSetHover.last() ? Element::IsUserActionStateChangeRoot::Yes : Element::IsUserActionStateChangeRoot::No); 7222 7226 } 7223 7227 -
trunk/Source/WebCore/dom/Element.cpp
r271446 r271584 679 679 } 680 680 681 void Element::setActive(bool flag, bool pause )681 void Element::setActive(bool flag, bool pause, IsUserActionStateChangeRoot isUserActionStateChangeRoot) 682 682 { 683 683 if (flag == active()) 684 684 return; 685 685 { 686 Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassActive );686 Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassActive, isUserActionStateChangeRoot); 687 687 document().userActionElements().setActive(*this, flag); 688 688 } … … 756 756 } 757 757 758 void Element::setHovered(bool flag )758 void Element::setHovered(bool flag, IsUserActionStateChangeRoot isUserActionStateChangeRoot) 759 759 { 760 760 if (flag == hovered()) 761 761 return; 762 762 { 763 Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassHover );763 Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassHover, isUserActionStateChangeRoot); 764 764 document().userActionElements().setHovered(*this, flag); 765 765 } -
trunk/Source/WebCore/dom/Element.h
r271439 r271584 322 322 bool hasFocusWithin() const { return hasNodeFlag(NodeFlag::HasFocusWithin); }; 323 323 324 virtual void setActive(bool = true, bool pause = false); 325 virtual void setHovered(bool = true); 324 enum class IsUserActionStateChangeRoot { Yes, No }; 325 virtual void setActive(bool = true, bool pause = false, IsUserActionStateChangeRoot = IsUserActionStateChangeRoot::Yes); 326 virtual void setHovered(bool = true, IsUserActionStateChangeRoot = IsUserActionStateChangeRoot::Yes); 326 327 virtual void setFocus(bool); 327 328 void setBeingDragged(bool); -
trunk/Source/WebCore/html/HTMLAnchorElement.cpp
r271124 r271584 212 212 } 213 213 214 void HTMLAnchorElement::setActive(bool down, bool pause )214 void HTMLAnchorElement::setActive(bool down, bool pause, IsUserActionStateChangeRoot isUserActionStateChangeRoot) 215 215 { 216 216 if (hasEditableStyle()) { … … 233 233 } 234 234 235 HTMLElement::setActive(down, pause );235 HTMLElement::setActive(down, pause, isUserActionStateChangeRoot); 236 236 } 237 237 -
trunk/Source/WebCore/html/HTMLAnchorElement.h
r269712 r271584 88 88 bool isKeyboardFocusable(KeyboardEvent*) const override; 89 89 void defaultEventHandler(Event&) final; 90 void setActive(bool active = true, bool pause = false) final;90 void setActive(bool active, bool pause, IsUserActionStateChangeRoot) final; 91 91 bool accessKeyAction(bool sendMouseEvents) final; 92 92 bool isURLAttribute(const Attribute&) const final; -
trunk/Source/WebCore/html/HTMLLabelElement.cpp
r269587 r271584 86 86 } 87 87 88 void HTMLLabelElement::setActive(bool down, bool pause )88 void HTMLLabelElement::setActive(bool down, bool pause, IsUserActionStateChangeRoot isUserActionStateChangeRoot) 89 89 { 90 90 if (down == active()) … … 92 92 93 93 // Update our status first. 94 HTMLElement::setActive(down, pause );94 HTMLElement::setActive(down, pause, isUserActionStateChangeRoot); 95 95 96 96 // Also update our corresponding control. … … 99 99 } 100 100 101 void HTMLLabelElement::setHovered(bool over )101 void HTMLLabelElement::setHovered(bool over, IsUserActionStateChangeRoot isUserActionStateChangeRoot) 102 102 { 103 103 if (over == hovered()) … … 105 105 106 106 // Update our status first. 107 HTMLElement::setHovered(over );107 HTMLElement::setHovered(over, isUserActionStateChangeRoot); 108 108 109 109 // Also update our corresponding control. -
trunk/Source/WebCore/html/HTMLLabelElement.h
r269587 r271584 46 46 47 47 // Overridden to update the hover/active state of the corresponding control. 48 void setActive(bool = true, bool pause = false) final;49 void setHovered(bool = true) final;48 void setActive(bool, bool pause, IsUserActionStateChangeRoot) final; 49 void setHovered(bool, IsUserActionStateChangeRoot) final; 50 50 51 51 // Overridden to either click() or focus() the corresponding control. -
trunk/Source/WebCore/html/shadow/SpinButtonElement.cpp
r267074 r271584 250 250 } 251 251 252 void SpinButtonElement::setHovered(bool flag )252 void SpinButtonElement::setHovered(bool flag, IsUserActionStateChangeRoot isUserActionStateChangeRoot) 253 253 { 254 254 if (!flag) 255 255 m_upDownState = Indeterminate; 256 HTMLDivElement::setHovered(flag );256 HTMLDivElement::setHovered(flag, isUserActionStateChangeRoot); 257 257 } 258 258 -
trunk/Source/WebCore/html/shadow/SpinButtonElement.h
r229694 r271584 80 80 void stopRepeatingTimer(); 81 81 void repeatingTimerFired(); 82 void setHovered(bool = true) override;82 void setHovered(bool, IsUserActionStateChangeRoot) override; 83 83 bool shouldRespondToMouseEvents(); 84 84 bool isMouseFocusable() const override { return false; } -
trunk/Source/WebCore/style/PseudoClassChangeInvalidation.cpp
r258321 r271584 33 33 namespace Style { 34 34 35 void PseudoClassChangeInvalidation::computeInvalidation(CSSSelector::PseudoClassType pseudoClass )35 void PseudoClassChangeInvalidation::computeInvalidation(CSSSelector::PseudoClassType pseudoClass, Element::IsUserActionStateChangeRoot isUserActionStateChangeRoot) 36 36 { 37 37 bool shouldInvalidateCurrent = false; … … 55 55 auto& ruleSets = m_element.styleResolver().ruleSets(); 56 56 if (auto* invalidationRuleSets = ruleSets.pseudoClassInvalidationRuleSets(pseudoClass)) { 57 for (auto& invalidationRuleSet : *invalidationRuleSets) 57 for (auto& invalidationRuleSet : *invalidationRuleSets) { 58 // For focus/hover we flip the whole ancestor chain. We only need to do deep invalidation traversal in the change root. 59 auto shouldInvalidate = [&] { 60 if (isUserActionStateChangeRoot == Element::IsUserActionStateChangeRoot::Yes) 61 return true; 62 return invalidationRuleSet.matchElement != MatchElement::Ancestor; 63 }(); 64 if (!shouldInvalidate) 65 continue; 58 66 Invalidator::addToMatchElementRuleSets(m_matchElementRuleSets, invalidationRuleSet); 67 } 59 68 } 60 69 } -
trunk/Source/WebCore/style/PseudoClassChangeInvalidation.h
r258321 r271584 36 36 class PseudoClassChangeInvalidation { 37 37 public: 38 PseudoClassChangeInvalidation(Element&, CSSSelector::PseudoClassType );38 PseudoClassChangeInvalidation(Element&, CSSSelector::PseudoClassType, Element::IsUserActionStateChangeRoot = Element::IsUserActionStateChangeRoot::Yes); 39 39 ~PseudoClassChangeInvalidation(); 40 40 41 41 private: 42 void computeInvalidation(CSSSelector::PseudoClassType );42 void computeInvalidation(CSSSelector::PseudoClassType, Element::IsUserActionStateChangeRoot); 43 43 void invalidateStyleWithRuleSets(); 44 44 … … 49 49 }; 50 50 51 inline PseudoClassChangeInvalidation::PseudoClassChangeInvalidation(Element& element, CSSSelector::PseudoClassType pseudoClassType )51 inline PseudoClassChangeInvalidation::PseudoClassChangeInvalidation(Element& element, CSSSelector::PseudoClassType pseudoClassType, Element::IsUserActionStateChangeRoot isUserActionStateChangeRoot) 52 52 : m_isEnabled(element.needsStyleInvalidation()) 53 53 , m_element(element) … … 56 56 if (!m_isEnabled) 57 57 return; 58 computeInvalidation(pseudoClassType );58 computeInvalidation(pseudoClassType, isUserActionStateChangeRoot); 59 59 invalidateStyleWithRuleSets(); 60 60 }
Note: See TracChangeset
for help on using the changeset viewer.