Changeset 288012 in webkit
- Timestamp:
- Jan 14, 2022 3:56:51 AM (6 months ago)
- Location:
- trunk
- Files:
-
- 2 added
- 8 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/TestExpectations (modified) (1 diff)
-
LayoutTests/imported/w3c/ChangeLog (modified) (1 diff)
-
LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-complexity-expected.txt (added)
-
LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-complexity.html (added)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/css/SelectorChecker.cpp (modified) (1 diff)
-
Source/WebCore/css/SelectorChecker.h (modified) (1 diff)
-
Source/WebCore/style/ChildChangeInvalidation.cpp (modified) (6 diffs)
-
Source/WebCore/style/ChildChangeInvalidation.h (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r288009 r288012 1 2022-01-14 Antti Koivisto <antti@apple.com> 2 3 [:has() pseudo-class] Avoid O(n^2) in style invalidation with repeated DOM mutations 4 https://bugs.webkit.org/show_bug.cgi?id=234842 5 <rdar://problem/87397176> 6 7 Reviewed by Dean Jackson. 8 9 * TestExpectations: 10 1 11 2022-01-13 Antoine Quint <graouts@webkit.org> 2 12 -
trunk/LayoutTests/TestExpectations
r288009 r288012 1489 1489 webkit.org/b/223497 imported/w3c/web-platform-tests/css/selectors/nesting.html [ ImageOnlyFailure ] 1490 1490 imported/w3c/web-platform-tests/css/selectors/xml-class-selector.xml [ ImageOnlyFailure ] 1491 1492 # This test is bit heavy for debug 1493 [ Debug ] imported/w3c/web-platform-tests/css/selectors/invalidation/has-complexity.html [ Skip ] 1491 1494 1492 1495 # ref for this is under a directory that is not yet imported -
trunk/LayoutTests/imported/w3c/ChangeLog
r288009 r288012 1 2022-01-14 Antti Koivisto <antti@apple.com> 2 3 [:has() pseudo-class] Avoid O(n^2) in style invalidation with repeated DOM mutations 4 https://bugs.webkit.org/show_bug.cgi?id=234842 5 <rdar://problem/87397176> 6 7 Reviewed by Dean Jackson. 8 9 * web-platform-tests/css/selectors/invalidation/has-complexity-expected.txt: Added. 10 * web-platform-tests/css/selectors/invalidation/has-complexity.html: Added. 11 1 12 2022-01-13 Antoine Quint <graouts@webkit.org> 2 13 -
trunk/Source/WebCore/ChangeLog
r288011 r288012 1 2022-01-14 Antti Koivisto <antti@apple.com> 2 3 [:has() pseudo-class] Avoid O(n^2) in style invalidation with repeated DOM mutations 4 https://bugs.webkit.org/show_bug.cgi?id=234842 5 <rdar://problem/87397176> 6 7 Reviewed by Dean Jackson. 8 9 Use invalidation selectors to check if a given mutation needs :has() invalidation. 10 11 Test: imported/w3c/web-platform-tests/css/selectors/invalidation/has-complexity.html 12 13 * css/SelectorChecker.cpp: 14 (WebCore::SelectorChecker::checkOne const): 15 * css/SelectorChecker.h: 16 * style/ChildChangeInvalidation.cpp: 17 (WebCore::Style::ChildChangeInvalidation::invalidateForChangedElement): 18 19 Invalidate only if the invalidation ruleset has an invalidation selector that matches 20 the added/removed element. Even in that case we only need to invalidate if that selector 21 has not already matched within this parent. 22 23 As we don't have persistent state that would remember what already matched accross multiple 24 mutations, approximate this by checking if the closest sibling matched. 25 26 (WebCore::Style::ChildChangeInvalidation::invalidateForHasBeforeMutation): 27 (WebCore::Style::ChildChangeInvalidation::invalidateForHasAfterMutation): 28 * style/ChildChangeInvalidation.h: 29 1 30 2022-01-14 Nikolas Zimmermann <nzimmermann@igalia.com> 2 31 -
trunk/Source/WebCore/css/SelectorChecker.cpp
r287222 r288012 1085 1085 const Node* contextualReferenceNode = !checkingContext.scope ? element.document().documentElement() : checkingContext.scope; 1086 1086 1087 bool matches = &element == contextualReferenceNode ;1087 bool matches = &element == contextualReferenceNode || checkingContext.matchesAllScopes; 1088 1088 1089 1089 if (!matches && checkingContext.scope) { -
trunk/Source/WebCore/css/SelectorChecker.h
r286302 r288012 96 96 AtomString nameForHightlightPseudoElement; 97 97 const ContainerNode* scope { nullptr }; 98 bool matchesAllScopes { false }; 98 99 Style::ScopeOrdinal styleScopeOrdinal { Style::ScopeOrdinal::Element }; 99 100 Style::SelectorMatchingState* selectorMatchingState { nullptr }; -
trunk/Source/WebCore/style/ChildChangeInvalidation.cpp
r287973 r288012 38 38 namespace WebCore::Style { 39 39 40 void ChildChangeInvalidation::invalidateForChangedElement(Element& changedElement )40 void ChildChangeInvalidation::invalidateForChangedElement(Element& changedElement, MatchingHasSelectors& matchingHasSelectors) 41 41 { 42 42 auto& ruleSets = parentElement().styleResolver().ruleSets(); … … 44 44 Invalidator::MatchElementRuleSets matchElementRuleSets; 45 45 46 bool isDescendant = changedElement.parentElement() != &parentElement(); 47 48 auto canAffectAncestors = [&](MatchElement matchElement) { 49 if (!isDescendant) 46 bool isChild = changedElement.parentElement() == &parentElement(); 47 48 auto canAffectElementsWithStyle = [&](MatchElement matchElement) { 49 switch (matchElement) { 50 case MatchElement::HasSibling: 51 case MatchElement::HasChild: 52 return isChild; 53 case MatchElement::HasDescendant: 54 case MatchElement::HasSiblingDescendant: 55 case MatchElement::HasNonSubject: 50 56 return true; 51 return matchElement == MatchElement::HasDescendant 52 || matchElement == MatchElement::HasSiblingDescendant 53 || matchElement == MatchElement::HasNonSubject; 57 default: 58 ASSERT_NOT_REACHED(); 59 return false; 60 } 61 }; 62 63 bool isFirst = isChild && m_childChange.previousSiblingElement == changedElement.previousElementSibling(); 64 65 auto hasMatchingInvalidationSelector = [&](auto& invalidationRuleSet) { 66 SelectorChecker selectorChecker(changedElement.document()); 67 SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements); 68 checkingContext.matchesAllScopes = true; 69 70 for (auto* selector : invalidationRuleSet.invalidationSelectors) { 71 if (isFirst) { 72 // If this :has() matches ignoring this mutation, nothing actually changes and we don't need to invalidate. 73 // FIXME: We could cache this state across invalidations instead of just testing a single sibling. 74 auto* sibling = m_childChange.previousSiblingElement ? m_childChange.previousSiblingElement : m_childChange.nextSiblingElement; 75 if (sibling && selectorChecker.match(*selector, *sibling, checkingContext)) { 76 matchingHasSelectors.add(selector); 77 continue; 78 } 79 } 80 81 if (matchingHasSelectors.contains(selector)) 82 continue; 83 84 if (selectorChecker.match(*selector, changedElement, checkingContext)) { 85 matchingHasSelectors.add(selector); 86 return true; 87 } 88 } 89 return false; 54 90 }; 55 91 … … 58 94 return; 59 95 for (auto& invalidationRuleSet : *invalidationRuleSets) { 60 if (!canAffectAncestors(invalidationRuleSet.matchElement)) 96 if (!canAffectElementsWithStyle(invalidationRuleSet.matchElement)) 97 continue; 98 if (!hasMatchingInvalidationSelector(invalidationRuleSet)) 61 99 continue; 62 100 Invalidator::addToMatchElementRuleSets(matchElementRuleSets, invalidationRuleSet); … … 77 115 return; 78 116 117 MatchingHasSelectors matchingHasSelectors; 118 79 119 traverseRemovedElements([&](auto& changedElement) { 80 invalidateForChangedElement(changedElement );120 invalidateForChangedElement(changedElement, matchingHasSelectors); 81 121 }); 82 122 } … … 89 129 return; 90 130 131 MatchingHasSelectors matchingHasSelectors; 132 91 133 traverseAddedElements([&](auto& changedElement) { 92 invalidateForChangedElement(changedElement );134 invalidateForChangedElement(changedElement, matchingHasSelectors); 93 135 }); 94 136 } … … 107 149 bool needsDescendantTraversal = Style::needsDescendantTraversal(features); 108 150 109 auto* toRemove = m_childChange.previousSiblingElement ? m_childChange.previousSiblingElement->nextElementSibling() : parentElement().firstElementChild(); 110 for (; toRemove != m_childChange.nextSiblingElement; toRemove = toRemove->nextElementSibling()) { 151 auto* firstToRemove = m_childChange.previousSiblingElement ? m_childChange.previousSiblingElement->nextElementSibling() : parentElement().firstElementChild(); 152 153 for (auto* toRemove = firstToRemove; toRemove != m_childChange.nextSiblingElement; toRemove = toRemove->nextElementSibling()) { 111 154 function(*toRemove); 112 155 -
trunk/Source/WebCore/style/ChildChangeInvalidation.h
r287325 r288012 29 29 #include "StyleInvalidator.h" 30 30 #include "StyleScope.h" 31 #include <wtf/HashSet.h> 31 32 32 33 namespace WebCore { … … 45 46 void invalidateAfterChange(); 46 47 void checkForSiblingStyleChanges(); 47 void invalidateForChangedElement(Element&); 48 using MatchingHasSelectors = HashSet<const CSSSelector*>; 49 void invalidateForChangedElement(Element&, MatchingHasSelectors&); 48 50 49 51 template<typename Function> void traverseRemovedElements(Function&&);
Note: See TracChangeset
for help on using the changeset viewer.