Changeset 286169 in webkit
- Timestamp:
- Nov 25, 2021 1:14:17 PM (8 months ago)
- Location:
- trunk
- Files:
-
- 10 edited
-
LayoutTests/imported/w3c/ChangeLog (modified) (1 diff)
-
LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/attribute-or-elemental-selectors-in-has-expected.txt (modified) (1 diff)
-
LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/attribute-or-elemental-selectors-in-has.html (modified) (1 diff)
-
Source/WebCore/ChangeLog (modified) (1 diff)
-
Source/WebCore/style/ChildChangeInvalidation.cpp (modified) (3 diffs)
-
Source/WebCore/style/ChildChangeInvalidation.h (modified) (2 diffs)
-
Source/WebCore/style/RuleFeature.cpp (modified) (10 diffs)
-
Source/WebCore/style/RuleFeature.h (modified) (5 diffs)
-
Source/WebCore/style/StyleScopeRuleSets.cpp (modified) (2 diffs)
-
Source/WebCore/style/StyleScopeRuleSets.h (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/imported/w3c/ChangeLog
r286168 r286169 1 2021-11-25 Antti Koivisto <antti@apple.com> 2 3 [:has() pseudo-class] Invalidation support for adding and removing nodes 4 https://bugs.webkit.org/show_bug.cgi?id=233489 5 6 Reviewed by Alan Bujtas. 7 8 Add some additional cases to an existing test. 9 10 * web-platform-tests/css/selectors/invalidation/attribute-or-elemental-selectors-in-has-expected.txt: 11 * web-platform-tests/css/selectors/invalidation/attribute-or-elemental-selectors-in-has.html: 12 1 13 2021-11-25 Sam Weinig <weinig@apple.com> 2 14 -
trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/attribute-or-elemental-selectors-in-has-expected.txt
r286135 r286169 15 15 FAIL change #div_grandchild to #div_descendant: div#div_subject.color assert_equals: expected "rgb(255, 255, 0)" but got "rgb(128, 128, 128)" 16 16 PASS change #div_descendant to #div_grandchild: div#div_subject.color 17 FAIL add descendant to #div_subject: div#div_subject.color assert_equals: expected "rgb(154, 205, 50)" but got "rgb(128, 128, 128)" 17 PASS add descendant to #div_subject: div#div_subject.color 18 18 PASS remove descendant from #div_subject: div#div_subject.color 19 FAIL add "div > descendant" to #div_subject: div#div_subject.color assert_equals: expected "rgb(154, 205, 50)" but got "rgb(128, 128, 128)" 19 PASS add "div > descendant" to #div_subject: div#div_subject.color 20 20 PASS remove "div > descendant" from #div_subject: div#div_subject.color 21 PASS add div.child to #div_subject: div#div_subject.color 22 PASS remove div.child from #div_subject: div#div_subject.color 23 PASS add "div > div.descendant" to #div_subject: div#div_subject.color 24 PASS remove "div > div.descendant" from #div_subject: div#div_subject.color 25 FAIL add div#div_descendant to #div_subject: div#div_subject.color assert_equals: expected "rgb(255, 255, 0)" but got "rgb(128, 128, 128)" 26 PASS remove div#div_descendant from #div_subject: div#div_subject.color 27 FAIL add "div#div_descendant" to #div_subject: div#div_subject.color assert_equals: expected "rgb(255, 255, 0)" but got "rgb(128, 128, 128)" 28 PASS remove "div#div_descendant" from #div_subject: div#div_subject.color 29 PASS add div[attrname] to #div_subject: div#div_subject.color 30 PASS remove div[attrname] from #div_subject: div#div_subject.color 31 PASS add "div > div[attrname]" to #div_subject: div#div_subject.color 32 PASS remove "div > div[attrname]" from #div_subject: div#div_subject.color 21 33 -
trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/attribute-or-elemental-selectors-in-has.html
r285921 r286169 69 69 test_div('change #div_descendant to #div_grandchild', div_subject, grey); 70 70 71 descendant = document.createElement('descendant'); 72 div_subject.appendChild(descendant); 73 test_div('add descendant to #div_subject', div_subject, yellowgreen); 74 div_subject.removeChild(descendant); 75 test_div('remove descendant from #div_subject', div_subject, grey); 71 { 72 const descendant = document.createElement('descendant'); 73 div_subject.appendChild(descendant); 74 test_div('add descendant to #div_subject', div_subject, yellowgreen); 75 div_subject.removeChild(descendant); 76 test_div('remove descendant from #div_subject', div_subject, grey); 77 } 76 78 77 div = document.createElement('div'); 78 div.appendChild(descendant); 79 div_subject.appendChild(div); 80 test_div('add "div > descendant" to #div_subject', div_subject, yellowgreen); 81 div_subject.removeChild(div); 82 test_div('remove "div > descendant" from #div_subject', div_subject, grey); 79 { 80 const div = document.createElement('div'); 81 const descendant = document.createElement('descendant'); 82 div.appendChild(descendant); 83 div_subject.appendChild(div); 84 test_div('add "div > descendant" to #div_subject', div_subject, yellowgreen); 85 div_subject.removeChild(div); 86 test_div('remove "div > descendant" from #div_subject', div_subject, grey); 87 } 88 89 { 90 const child = document.createElement('div'); 91 child.classList.add('child'); 92 div_subject.appendChild(child); 93 test_div('add div.child to #div_subject', div_subject, red); 94 div_subject.removeChild(child); 95 test_div('remove div.child from #div_subject', div_subject, grey); 96 } 97 98 { 99 const descendant = document.createElement('div'); 100 descendant.classList.add('descendant'); 101 const div = document.createElement('div'); 102 div.appendChild(descendant); 103 div_subject.appendChild(div); 104 test_div('add "div > div.descendant" to #div_subject', div_subject, green); 105 div_subject.removeChild(div); 106 test_div('remove "div > div.descendant" from #div_subject', div_subject, grey); 107 } 108 109 { 110 const child = document.createElement('div'); 111 child.id = 'div_descendant'; 112 div_subject.appendChild(child); 113 test_div('add div#div_descendant to #div_subject', div_subject, yellow); 114 div_subject.removeChild(child); 115 test_div('remove div#div_descendant from #div_subject', div_subject, grey); 116 } 117 118 { 119 const descendant = document.createElement('div'); 120 descendant.id = 'div_descendant'; 121 const div = document.createElement('div'); 122 div.appendChild(descendant); 123 div_subject.appendChild(div); 124 test_div('add "div#div_descendant" to #div_subject', div_subject, yellow); 125 div_subject.removeChild(div); 126 test_div('remove "div#div_descendant" from #div_subject', div_subject, grey); 127 } 128 129 { 130 const child = document.createElement('div'); 131 child.setAttribute('attrname', 'descendant'); 132 div_subject.appendChild(child); 133 test_div('add div[attrname] to #div_subject', div_subject, blue); 134 div_subject.removeChild(child); 135 test_div('remove div[attrname] from #div_subject', div_subject, grey); 136 } 137 138 { 139 const descendant = document.createElement('div'); 140 descendant.setAttribute('attrname', 'descendant'); 141 const div = document.createElement('div'); 142 div.appendChild(descendant); 143 div_subject.appendChild(div); 144 test_div('add "div > div[attrname]" to #div_subject', div_subject, blue); 145 div_subject.removeChild(div); 146 test_div('remove "div > div[attrname]" from #div_subject', div_subject, grey); 147 } 83 148 84 149 </script> -
trunk/Source/WebCore/ChangeLog
r286168 r286169 1 2021-11-25 Antti Koivisto <antti@apple.com> 2 3 [:has() pseudo-class] Invalidation support for adding and removing nodes 4 https://bugs.webkit.org/show_bug.cgi?id=233489 5 6 Reviewed by Alan Bujtas. 7 8 We need to invalidate style when added or removed elements match :has() arguments. 9 10 This patch adds tag name invalidation rulesets for :has() case, along with the existing class 11 and attribute rulesets. It is still missing support for #id invalidation. 12 13 * style/ChildChangeInvalidation.cpp: 14 (WebCore::Style::ChildChangeInvalidation::ChildChangeInvalidation): 15 16 See if we need to invalidate for the elements being removed before the mutation. 17 18 (WebCore::Style::ChildChangeInvalidation::~ChildChangeInvalidation): 19 20 See if we need to invalidate for the elements being added after the mutation. 21 22 (WebCore::Style::ChildChangeInvalidation::invalidateForChangedElement): 23 24 Find the relevant invalidation rulesets for attributes, classes and tags and invalidate using them. 25 26 (WebCore::Style::needsTraversal): 27 28 We only need to do anything in the presence of :has() rules. 29 30 (WebCore::Style::needsDescendantTraversal): 31 32 Full traversal is only needed in case there are :has() rules using descendant combinator. 33 34 (WebCore::Style::ChildChangeInvalidation::traverseRemovedElements): 35 (WebCore::Style::ChildChangeInvalidation::traverseAddedElements): 36 * style/ChildChangeInvalidation.h: 37 * style/RuleFeature.cpp: 38 (WebCore::Style::isHasPseudoClassMatchElement): 39 (WebCore::Style::RuleFeatureSet::recursivelyCollectFeaturesFromSelector): 40 (WebCore::Style::RuleFeatureSet::collectFeatures): 41 42 Collect tag invalidation rulesets for :has case only. 43 44 (WebCore::Style::RuleFeatureSet::add): 45 (WebCore::Style::RuleFeatureSet::clear): 46 (WebCore::Style::RuleFeatureSet::shrinkToFit): 47 * style/RuleFeature.h: 48 (WebCore::Style::RuleFeatureSet::usesMatchElement const): 49 (WebCore::Style::RuleFeatureSet::setUsesMatchElement): 50 51 Keep track of what sort of match elements are being used. 52 53 * style/StyleScopeRuleSets.cpp: 54 (WebCore::Style::ScopeRuleSets::collectFeatures const): 55 (WebCore::Style::ScopeRuleSets::tagInvalidationRuleSets const): 56 * style/StyleScopeRuleSets.h: 57 1 58 2021-11-25 Sam Weinig <weinig@apple.com> 2 59 -
trunk/Source/WebCore/style/ChildChangeInvalidation.cpp
r286138 r286169 31 31 #include "ShadowRoot.h" 32 32 #include "SlotAssignment.h" 33 #include "StyleResolver.h" 34 #include "StyleScopeRuleSets.h" 35 #include "TypedElementDescendantIterator.h" 33 36 34 37 namespace WebCore::Style { … … 39 42 , m_childChange(childChange) 40 43 { 41 // FIXME: Do smarter invalidation similar to ClassChangeInvalidation. 44 if (!m_isEnabled) 45 return; 46 47 traverseRemovedElements([&](auto& changedElement) { 48 invalidateForChangedElement(changedElement); 49 }); 42 50 } 43 51 … … 47 55 return; 48 56 57 traverseAddedElements([&](auto& changedElement) { 58 invalidateForChangedElement(changedElement); 59 }); 60 49 61 invalidateAfterChange(); 62 } 63 64 void ChildChangeInvalidation::invalidateForChangedElement(Element& changedElement) 65 { 66 auto& ruleSets = parentElement().styleResolver().ruleSets(); 67 68 Invalidator::MatchElementRuleSets matchElementRuleSets; 69 70 auto addHasInvalidation = [&](const Vector<InvalidationRuleSet>* invalidationRuleSets) { 71 if (!invalidationRuleSets) 72 return; 73 for (auto& invalidationRuleSet : *invalidationRuleSets) { 74 if (isHasPseudoClassMatchElement(invalidationRuleSet.matchElement)) 75 Invalidator::addToMatchElementRuleSets(matchElementRuleSets, invalidationRuleSet); 76 } 77 }; 78 79 auto tagName = changedElement.localName().convertToASCIILowercase(); 80 addHasInvalidation(ruleSets.tagInvalidationRuleSets(tagName)); 81 82 if (changedElement.hasAttributes()) { 83 for (auto& attribute : changedElement.attributesIterator()) { 84 auto attributeName = attribute.localName().convertToASCIILowercase(); 85 addHasInvalidation(ruleSets.attributeInvalidationRuleSets(attributeName)); 86 } 87 } 88 89 if (changedElement.hasClass()) { 90 auto count = changedElement.classNames().size(); 91 for (size_t i = 0; i < count; ++i) { 92 auto& className = changedElement.classNames()[i]; 93 addHasInvalidation(ruleSets.classInvalidationRuleSets(className)); 94 } 95 } 96 97 Invalidator::invalidateWithMatchElementRuleSets(changedElement, matchElementRuleSets); 98 } 99 100 static bool needsTraversal(const RuleFeatureSet& features, const ContainerNode::ChildChange& childChange) 101 { 102 if (features.usesMatchElement(MatchElement::HasChild)) 103 return true; 104 if (features.usesMatchElement(MatchElement::HasDescendant)) 105 return true; 106 return features.usesMatchElement(MatchElement::HasSibling) && childChange.previousSiblingElement; 107 }; 108 109 static bool needsDescendantTraversal(const RuleFeatureSet& features) 110 { 111 return features.usesMatchElement(MatchElement::HasDescendant); 112 }; 113 114 template<typename Function> 115 void ChildChangeInvalidation::traverseRemovedElements(Function&& function) 116 { 117 if (m_childChange.isInsertion() && m_childChange.type != ContainerNode::ChildChange::Type::AllChildrenReplaced) 118 return; 119 120 auto& features = parentElement().styleResolver().ruleSets().features(); 121 if (!needsTraversal(features, m_childChange)) 122 return; 123 124 bool needsDescendantTraversal = Style::needsDescendantTraversal(features); 125 126 auto* toRemove = m_childChange.previousSiblingElement ? m_childChange.previousSiblingElement->nextElementSibling() : parentElement().firstElementChild(); 127 for (; toRemove != m_childChange.nextSiblingElement; toRemove = toRemove->nextElementSibling()) { 128 function(*toRemove); 129 130 if (!needsDescendantTraversal) 131 continue; 132 133 for (auto& descendant : descendantsOfType<Element>(*toRemove)) 134 function(descendant); 135 } 136 } 137 138 template<typename Function> 139 void ChildChangeInvalidation::traverseAddedElements(Function&& function) 140 { 141 if (!m_childChange.isInsertion()) 142 return; 143 144 auto* newElement = [&] { 145 auto* previous = m_childChange.previousSiblingElement; 146 auto* candidate = previous ? ElementTraversal::nextSibling(*previous) : ElementTraversal::firstChild(parentElement()); 147 if (candidate == m_childChange.nextSiblingElement) 148 candidate = nullptr; 149 return candidate; 150 }(); 151 152 if (!newElement) 153 return; 154 155 auto& features = parentElement().styleResolver().ruleSets().features(); 156 if (!needsTraversal(features, m_childChange)) 157 return; 158 159 function(*newElement); 160 161 if (!needsDescendantTraversal(features)) 162 return; 163 164 for (auto& descendant : descendantsOfType<Element>(*newElement)) 165 function(descendant); 50 166 } 51 167 -
trunk/Source/WebCore/style/ChildChangeInvalidation.h
r286112 r286169 27 27 28 28 #include "Element.h" 29 #include "StyleInvalidator.h" 29 30 30 31 namespace WebCore { … … 41 42 void invalidateAfterChange(); 42 43 void checkForSiblingStyleChanges(); 44 void invalidateForChangedElement(Element&); 45 46 template<typename Function> void traverseRemovedElements(Function&&); 47 template<typename Function> void traverseAddedElements(Function&&); 43 48 44 49 Element& parentElement() { return *m_parentElement; } -
trunk/Source/WebCore/style/RuleFeature.cpp
r286135 r286169 59 59 } 60 60 61 staticbool isHasPseudoClassMatchElement(MatchElement matchElement)61 bool isHasPseudoClassMatchElement(MatchElement matchElement) 62 62 { 63 63 switch (matchElement) { … … 173 173 } else if (selector->match() == CSSSelector::Class) 174 174 selectorFeatures.classes.append(std::make_pair(selector->value(), matchElement)); 175 else if (selector->isAttributeSelector()) { 175 else if (selector->match() == CSSSelector::Tag) { 176 if (isHasPseudoClassMatchElement(matchElement)) 177 selectorFeatures.tags.append(std::make_pair(selector->tagLowercaseLocalName(), matchElement)); 178 } else if (selector->isAttributeSelector()) { 176 179 auto& canonicalLocalName = selector->attributeCanonicalLocalName(); 177 180 auto& localName = selector->attribute().localName(); … … 220 223 uncommonAttributeRules.append({ ruleData }); 221 224 225 for (auto& nameAndMatch : selectorFeatures.tags) { 226 tagRules.ensure(nameAndMatch.first, [] { 227 return makeUnique<RuleFeatureVector>(); 228 }).iterator->value->append({ ruleData, nameAndMatch.second }); 229 setUsesMatchElement(nameAndMatch.second); 230 } 222 231 for (auto& nameAndMatch : selectorFeatures.classes) { 223 232 classRules.ensure(nameAndMatch.first, [] { … … 226 235 if (nameAndMatch.second == MatchElement::Host) 227 236 classesAffectingHost.add(nameAndMatch.first); 237 setUsesMatchElement(nameAndMatch.second); 228 238 } 229 239 for (auto& selectorAndMatch : selectorFeatures.attributes) { … … 235 245 if (matchElement == MatchElement::Host) 236 246 attributesAffectingHost.add(selector->attribute().localName().convertToASCIILowercase()); 247 setUsesMatchElement(matchElement); 237 248 } 238 249 for (auto& keyAndMatch : selectorFeatures.pseudoClasses) { … … 242 253 if (keyAndMatch.second == MatchElement::Host) 243 254 pseudoClassesAffectingHost.add(keyAndMatch.first); 255 setUsesMatchElement(keyAndMatch.second); 244 256 } 245 257 } … … 254 266 siblingRules.appendVector(other.siblingRules); 255 267 uncommonAttributeRules.appendVector(other.uncommonAttributeRules); 268 269 for (auto& keyValuePair : other.tagRules) { 270 tagRules.ensure(keyValuePair.key, [] { 271 return makeUnique<RuleFeatureVector>(); 272 }).iterator->value->appendVector(*keyValuePair.value); 273 } 274 256 275 for (auto& keyValuePair : other.classRules) { 257 276 classRules.ensure(keyValuePair.key, [] { … … 274 293 } 275 294 pseudoClassesAffectingHost.add(other.pseudoClassesAffectingHost.begin(), other.pseudoClassesAffectingHost.end()); 295 296 for (size_t i = 0; i < usedMatchElements.size(); ++i) 297 usedMatchElements[i] = usedMatchElements[i] || other.usedMatchElements[i]; 276 298 277 299 usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules; … … 295 317 siblingRules.clear(); 296 318 uncommonAttributeRules.clear(); 319 tagRules.clear(); 297 320 classRules.clear(); 298 321 classesAffectingHost.clear(); … … 309 332 siblingRules.shrinkToFit(); 310 333 uncommonAttributeRules.shrinkToFit(); 334 for (auto& rules : tagRules.values()) 335 rules->shrinkToFit(); 311 336 for (auto& rules : classRules.values()) 312 337 rules->shrinkToFit(); -
trunk/Source/WebCore/style/RuleFeature.h
r286135 r286169 68 68 void registerContentAttribute(const AtomString&); 69 69 70 bool usesMatchElement(MatchElement matchElement) const { return usedMatchElements[static_cast<uint8_t>(matchElement)]; } 71 void setUsesMatchElement(MatchElement matchElement) { usedMatchElements[static_cast<uint8_t>(matchElement)] = true; } 72 70 73 HashSet<AtomString> idsInRules; 71 74 HashSet<AtomString> idsMatchingAncestorsInRules; … … 75 78 RuleFeatureVector siblingRules; 76 79 RuleFeatureVector uncommonAttributeRules; 77 80 81 HashMap<AtomString, std::unique_ptr<RuleFeatureVector>> tagRules; 78 82 HashMap<AtomString, std::unique_ptr<RuleFeatureVector>> classRules; 79 83 HashMap<AtomString, std::unique_ptr<Vector<RuleFeatureWithInvalidationSelector>>> attributeRules; … … 82 86 HashSet<AtomString> attributesAffectingHost; 83 87 HashSet<CSSSelector::PseudoClassType, IntHash<CSSSelector::PseudoClassType>, WTF::StrongEnumHashTraits<CSSSelector::PseudoClassType>> pseudoClassesAffectingHost; 88 89 std::array<bool, matchElementCount> usedMatchElements; 84 90 85 91 bool usesFirstLineRules { false }; … … 93 99 bool hasSiblingSelector { false }; 94 100 101 Vector<std::pair<AtomString, MatchElement>, 32> tags; 95 102 Vector<std::pair<AtomString, MatchElement>, 32> classes; 96 103 Vector<std::pair<const CSSSelector*, MatchElement>, 32> attributes; … … 100 107 }; 101 108 109 bool isHasPseudoClassMatchElement(MatchElement); 110 102 111 } // namespace Style 103 112 } // namespace WebCore -
trunk/Source/WebCore/style/StyleScopeRuleSets.cpp
r284078 r286169 221 221 m_uncommonAttributeRuleSet = makeRuleSet(m_features.uncommonAttributeRules); 222 222 223 m_tagInvalidationRuleSets.clear(); 223 224 m_classInvalidationRuleSets.clear(); 224 225 m_attributeInvalidationRuleSets.clear(); … … 274 275 } 275 276 277 const Vector<InvalidationRuleSet>* ScopeRuleSets::tagInvalidationRuleSets(const AtomString& tagName) const 278 { 279 return ensureInvalidationRuleSets(tagName, m_tagInvalidationRuleSets, m_features.tagRules); 280 } 281 276 282 const Vector<InvalidationRuleSet>* ScopeRuleSets::classInvalidationRuleSets(const AtomString& className) const 277 283 { -
trunk/Source/WebCore/style/StyleScopeRuleSets.h
r284857 r286169 64 64 RuleSet* uncommonAttribute() const { return m_uncommonAttributeRuleSet.get(); } 65 65 66 const Vector<InvalidationRuleSet>* tagInvalidationRuleSets(const AtomString& tagName) const; 66 67 const Vector<InvalidationRuleSet>* classInvalidationRuleSets(const AtomString& className) const; 67 68 const Vector<InvalidationRuleSet>* attributeInvalidationRuleSets(const AtomString& attributeName) const; 68 69 const Vector<InvalidationRuleSet>* pseudoClassInvalidationRuleSets(CSSSelector::PseudoClassType) const; 70 71 const Vector<InvalidationRuleSet>* invalidationRuleSetsForChildChange(const Element&); 69 72 70 73 bool hasComplexSelectorsForStyleAttribute() const; … … 99 102 mutable RefPtr<RuleSet> m_siblingRuleSet; 100 103 mutable RefPtr<RuleSet> m_uncommonAttributeRuleSet; 104 mutable HashMap<AtomString, std::unique_ptr<Vector<InvalidationRuleSet>>> m_tagInvalidationRuleSets; 101 105 mutable HashMap<AtomString, std::unique_ptr<Vector<InvalidationRuleSet>>> m_classInvalidationRuleSets; 102 106 mutable HashMap<AtomString, std::unique_ptr<Vector<InvalidationRuleSet>>> m_attributeInvalidationRuleSets;
Note: See TracChangeset
for help on using the changeset viewer.