Changeset 196383 in webkit
- Timestamp:
- Feb 10, 2016 12:47:04 PM (8 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r196376 r196383 1 2016-02-10 Antti Koivisto <antti@apple.com> 2 3 Optimize style invalidation after class attribute change 4 https://bugs.webkit.org/show_bug.cgi?id=154075 5 rdar://problem/12526450 6 7 Reviewed by Andreas Kling. 8 9 Currently a class attribute change invalidates style for the entire element subtree for any class found in the 10 active stylesheet set. 11 12 This patch optimizes class changes by building a new optimization structure called ancestorClassRules. It contains 13 rules that have class selectors in the portion of the complex selector that matches ancestor elements. The sets 14 of rules are hashes by the class name. 15 16 On class attribute change the existing StyleInvalidationAnalysis mechanism is used with ancestorClassRules to invalidate 17 exactly those descendants that are affected by the addition or removal of the class name. This is fast because the CSS JIT 18 makes selector matching cheap and the number of relevant rules is typically small. 19 20 This optimization is very effective on many dynamic pages. For example when focusing and unfocusing the web inspector it 21 cuts down the number of resolved elements from ~1000 to ~50. Even in PLT it reduces the number of resolved elements by ~11%. 22 23 * css/DocumentRuleSets.cpp: 24 (WebCore::DocumentRuleSets::collectFeatures): 25 (WebCore::DocumentRuleSets::ancestorClassRules): 26 27 Create optimization RuleSets on-demand when there is an actual dynamic class change. 28 29 * css/DocumentRuleSets.h: 30 (WebCore::DocumentRuleSets::features): 31 (WebCore::DocumentRuleSets::sibling): 32 (WebCore::DocumentRuleSets::uncommonAttribute): 33 * css/ElementRuleCollector.cpp: 34 (WebCore::ElementRuleCollector::ElementRuleCollector): 35 36 Add a new constructor that doesn't requires DocumentRuleSets. Only the user and author style is required. 37 38 (WebCore::ElementRuleCollector::matchAuthorRules): 39 (WebCore::ElementRuleCollector::matchUserRules): 40 * css/ElementRuleCollector.h: 41 * css/RuleFeature.cpp: 42 (WebCore::RuleFeatureSet::recursivelyCollectFeaturesFromSelector): 43 44 Collect class names that show up in the ancestor portion of the selector. 45 Make this a member. 46 47 (WebCore::RuleFeatureSet::collectFeatures): 48 49 Move this code from RuleData. 50 Add the rule to ancestorClassRules if needed. 51 52 (WebCore::RuleFeatureSet::add): 53 (WebCore::RuleFeatureSet::clear): 54 (WebCore::RuleFeatureSet::shrinkToFit): 55 (WebCore::recursivelyCollectFeaturesFromSelector): Deleted. 56 (WebCore::RuleFeatureSet::collectFeaturesFromSelector): Deleted. 57 * css/RuleFeature.h: 58 (WebCore::RuleFeature::RuleFeature): 59 (WebCore::RuleFeatureSet::RuleFeatureSet): Deleted. 60 * css/RuleSet.cpp: 61 (WebCore::RuleData::RuleData): 62 (WebCore::RuleSet::RuleSet): 63 (WebCore::RuleSet::~RuleSet): 64 (WebCore::RuleSet::addToRuleSet): 65 (WebCore::RuleSet::addRule): 66 (WebCore::RuleSet::addRulesFromSheet): 67 (WebCore::collectFeaturesFromRuleData): Deleted. 68 * css/RuleSet.h: 69 (WebCore::RuleSet::tagRules): 70 (WebCore::RuleSet::RuleSet): Deleted. 71 * css/StyleInvalidationAnalysis.cpp: 72 (WebCore::shouldDirtyAllStyle): 73 (WebCore::StyleInvalidationAnalysis::StyleInvalidationAnalysis): 74 75 Add a new constructor that takes a ready made RuleSet instead of a stylesheet. 76 77 (WebCore::StyleInvalidationAnalysis::invalidateIfNeeded): 78 (WebCore::StyleInvalidationAnalysis::invalidateStyleForTree): 79 (WebCore::StyleInvalidationAnalysis::invalidateStyle): 80 (WebCore::StyleInvalidationAnalysis::invalidateStyle): 81 82 New function for invalidating a subtree instead of the whole document. 83 84 * css/StyleInvalidationAnalysis.h: 85 (WebCore::StyleInvalidationAnalysis::dirtiesAllStyle): 86 (WebCore::StyleInvalidationAnalysis::hasShadowPseudoElementRulesInAuthorSheet): 87 * dom/Element.cpp: 88 (WebCore::classStringHasClassName): 89 (WebCore::collectClasses): 90 (WebCore::computeClassChange): 91 92 Factor to return the changed classes. 93 94 (WebCore::invalidateStyleForClassChange): 95 96 First filter out classes that don't show up in stylesheets. If something remains invalidate the current 97 element for inline style change (that is a style change that doesn't affect descendants). 98 99 Next check if there are any ancestorClassRules for the changed class. If so use the StyleInvalidationAnalysis 100 to find any affected descendants and invalidate them with inline style change as well. 101 102 (WebCore::Element::classAttributeChanged): 103 104 Invalidate for removed classes before setting new attribute value, invalidate for added classes afterwards. 105 106 (WebCore::Element::absoluteLinkURL): 107 (WebCore::checkSelectorForClassChange): Deleted. 108 * dom/ElementData.h: 109 (WebCore::ElementData::setClassNames): 110 (WebCore::ElementData::classNames): 111 (WebCore::ElementData::classNamesMemoryOffset): 112 (WebCore::ElementData::clearClass): Deleted. 113 (WebCore::ElementData::setClass): Deleted. 114 1 115 2016-02-10 Myles C. Maxfield <mmaxfield@apple.com> 2 116 -
trunk/Source/WebCore/css/DocumentRuleSets.cpp
r194496 r196383 116 116 } 117 117 118 RuleSet* DocumentRuleSets::ancestorClassRules(AtomicStringImpl* className) const 119 { 120 auto addResult = m_ancestorClassRuleSet.add(className, nullptr); 121 if (addResult.isNewEntry) { 122 if (auto* rules = m_features.ancestorClassRules.get(className)) 123 addResult.iterator->value = makeRuleSet(*rules); 124 } 125 return addResult.iterator->value.get(); 126 } 127 118 128 } // namespace WebCore -
trunk/Source/WebCore/css/DocumentRuleSets.h
r190347 r196383 27 27 #include "RuleSet.h" 28 28 #include <memory> 29 #include <wtf/HashMap.h> 29 30 #include <wtf/RefPtr.h> 30 31 #include <wtf/Vector.h> … … 49 50 RuleSet* sibling() const { return m_siblingRuleSet.get(); } 50 51 RuleSet* uncommonAttribute() const { return m_uncommonAttributeRuleSet.get(); } 52 RuleSet* ancestorClassRules(AtomicStringImpl* className) const; 51 53 52 54 void initUserStyle(ExtensionStyleSheets&, const MediaQueryEvaluator&, StyleResolver&); … … 63 65 std::unique_ptr<RuleSet> m_siblingRuleSet; 64 66 std::unique_ptr<RuleSet> m_uncommonAttributeRuleSet; 67 mutable HashMap<AtomicStringImpl*, std::unique_ptr<RuleSet>> m_ancestorClassRuleSet; 65 68 }; 66 69 -
trunk/Source/WebCore/css/ElementRuleCollector.cpp
r196031 r196383 81 81 : m_element(element) 82 82 , m_style(style) 83 , m_ruleSets(ruleSets) 83 , m_authorStyle(*ruleSets.authorStyle()) 84 , m_userStyle(ruleSets.userStyle()) 85 , m_selectorFilter(selectorFilter) 86 { 87 ASSERT(!m_selectorFilter || m_selectorFilter->parentStackIsConsistent(element.parentNode())); 88 } 89 90 ElementRuleCollector::ElementRuleCollector(Element& element, const RuleSet& authorStyle, const SelectorFilter* selectorFilter) 91 : m_element(element) 92 , m_authorStyle(authorStyle) 84 93 , m_selectorFilter(selectorFilter) 85 94 { … … 204 213 205 214 // Match global author rules. 206 MatchRequest matchRequest( m_ruleSets.authorStyle(), includeEmptyRules);215 MatchRequest matchRequest(&m_authorStyle, includeEmptyRules); 207 216 StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange(); 208 217 collectMatchingRules(matchRequest, ruleRange); … … 237 246 void ElementRuleCollector::matchUserRules(bool includeEmptyRules) 238 247 { 239 if (!m_ ruleSets.userStyle())248 if (!m_userStyle) 240 249 return; 241 250 … … 243 252 244 253 m_result.ranges.lastUserRule = m_result.matchedProperties().size() - 1; 245 MatchRequest matchRequest(m_ ruleSets.userStyle(), includeEmptyRules);254 MatchRequest matchRequest(m_userStyle, includeEmptyRules); 246 255 StyleResolver::RuleRange ruleRange = m_result.ranges.userRuleRange(); 247 256 collectMatchingRules(matchRequest, ruleRange); -
trunk/Source/WebCore/css/ElementRuleCollector.h
r196031 r196383 47 47 public: 48 48 ElementRuleCollector(Element&, RenderStyle*, const DocumentRuleSets&, const SelectorFilter*); 49 ElementRuleCollector(Element&, const RuleSet& authorStyle, const SelectorFilter*); 49 50 50 51 void matchAllRules(bool matchAuthorAndUserStyles, bool includeSMILProperties); … … 88 89 89 90 Element& m_element; 90 RenderStyle* m_style; 91 const DocumentRuleSets& m_ruleSets; 92 const SelectorFilter* m_selectorFilter; 91 RenderStyle* m_style { nullptr }; 92 const RuleSet& m_authorStyle; 93 const RuleSet* m_userStyle { nullptr }; 94 const SelectorFilter* m_selectorFilter { nullptr }; 93 95 94 96 bool m_isPrintStyle { false }; -
trunk/Source/WebCore/css/RuleFeature.cpp
r178580 r196383 32 32 #include "CSSSelector.h" 33 33 #include "CSSSelectorList.h" 34 #include "RuleSet.h" 34 35 35 36 namespace WebCore { 36 37 37 static void recursivelyCollectFeaturesFromSelector(RuleFeatureSet& features, const CSSSelector& firstSelector, bool& hasSiblingSelector)38 void RuleFeatureSet::recursivelyCollectFeaturesFromSelector(SelectorFeatures& selectorFeatures, const CSSSelector& firstSelector, bool matchesAncestor) 38 39 { 39 40 const CSSSelector* selector = &firstSelector; 40 41 do { 41 42 if (selector->match() == CSSSelector::Id) 42 features.idsInRules.add(selector->value().impl()); 43 else if (selector->match() == CSSSelector::Class) 44 features.classesInRules.add(selector->value().impl()); 45 else if (selector->isAttributeSelector()) { 46 features.attributeCanonicalLocalNamesInRules.add(selector->attributeCanonicalLocalName().impl()); 47 features.attributeLocalNamesInRules.add(selector->attribute().localName().impl()); 43 idsInRules.add(selector->value().impl()); 44 else if (selector->match() == CSSSelector::Class) { 45 classesInRules.add(selector->value().impl()); 46 if (matchesAncestor) 47 selectorFeatures.classesMatchingAncestors.append(selector->value().impl()); 48 } else if (selector->isAttributeSelector()) { 49 attributeCanonicalLocalNamesInRules.add(selector->attributeCanonicalLocalName().impl()); 50 attributeLocalNamesInRules.add(selector->attribute().localName().impl()); 48 51 } else if (selector->match() == CSSSelector::PseudoElement) { 49 52 switch (selector->pseudoElementType()) { 50 53 case CSSSelector::PseudoElementFirstLine: 51 features.usesFirstLineRules = true;54 usesFirstLineRules = true; 52 55 break; 53 56 case CSSSelector::PseudoElementFirstLetter: 54 features.usesFirstLetterRules = true;57 usesFirstLetterRules = true; 55 58 break; 56 59 default: … … 59 62 } 60 63 61 if (! hasSiblingSelector && selector->isSiblingSelector())62 hasSiblingSelector = true;64 if (!selectorFeatures.hasSiblingSelector && selector->isSiblingSelector()) 65 selectorFeatures.hasSiblingSelector = true; 63 66 64 67 if (const CSSSelectorList* selectorList = selector->selectorList()) { 65 68 for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) { 66 if (! hasSiblingSelector && selector->isSiblingSelector())67 hasSiblingSelector = true;68 recursivelyCollectFeaturesFromSelector( features, *subSelector, hasSiblingSelector);69 if (!selectorFeatures.hasSiblingSelector && selector->isSiblingSelector()) 70 selectorFeatures.hasSiblingSelector = true; 71 recursivelyCollectFeaturesFromSelector(selectorFeatures, *subSelector, matchesAncestor); 69 72 } 70 73 } 74 if (selector->relation() == CSSSelector::Child || selector->relation() == CSSSelector::Descendant) 75 matchesAncestor = true; 71 76 72 77 selector = selector->tagHistory(); … … 74 79 } 75 80 76 void RuleFeatureSet::collectFeatures FromSelector(const CSSSelector& firstSelector, bool& hasSiblingSelector)81 void RuleFeatureSet::collectFeatures(const RuleData& ruleData) 77 82 { 78 hasSiblingSelector = false; 79 recursivelyCollectFeaturesFromSelector(*this, firstSelector, hasSiblingSelector); 83 SelectorFeatures selectorFeatures; 84 recursivelyCollectFeaturesFromSelector(selectorFeatures, *ruleData.selector()); 85 if (selectorFeatures.hasSiblingSelector) 86 siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin())); 87 if (ruleData.containsUncommonAttributeSelector()) 88 uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin())); 89 for (auto* className : selectorFeatures.classesMatchingAncestors) { 90 auto addResult = ancestorClassRules.add(className, nullptr); 91 if (addResult.isNewEntry) 92 addResult.iterator->value = std::make_unique<Vector<RuleFeature>>(); 93 addResult.iterator->value->append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin())); 94 } 80 95 } 81 96 … … 88 103 siblingRules.appendVector(other.siblingRules); 89 104 uncommonAttributeRules.appendVector(other.uncommonAttributeRules); 105 for (auto& keyValuePair : other.ancestorClassRules) { 106 auto addResult = ancestorClassRules.add(keyValuePair.key, nullptr); 107 if (addResult.isNewEntry) 108 addResult.iterator->value = std::make_unique<Vector<RuleFeature>>(*keyValuePair.value); 109 else 110 addResult.iterator->value->appendVector(*keyValuePair.value); 111 } 90 112 usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules; 91 113 usesFirstLetterRules = usesFirstLetterRules || other.usesFirstLetterRules; … … 100 122 siblingRules.clear(); 101 123 uncommonAttributeRules.clear(); 124 ancestorClassRules.clear(); 102 125 usesFirstLineRules = false; 103 126 usesFirstLetterRules = false; … … 108 131 siblingRules.shrinkToFit(); 109 132 uncommonAttributeRules.shrinkToFit(); 133 for (auto& rules : ancestorClassRules.values()) 134 rules->shrinkToFit(); 110 135 } 111 136 -
trunk/Source/WebCore/css/RuleFeature.h
r178580 r196383 30 30 namespace WebCore { 31 31 32 class CSSSelector; 33 class RuleData; 32 34 class StyleRule; 33 class CSSSelector;34 35 35 36 struct RuleFeature { … … 46 47 47 48 struct RuleFeatureSet { 48 RuleFeatureSet()49 : usesFirstLineRules(false)50 , usesFirstLetterRules(false)51 { }52 53 49 void add(const RuleFeatureSet&); 54 50 void clear(); 55 51 void shrinkToFit(); 56 void collectFeatures FromSelector(const CSSSelector&, bool& hasSiblingSelector);52 void collectFeatures(const RuleData&); 57 53 58 54 HashSet<AtomicStringImpl*> idsInRules; … … 62 58 Vector<RuleFeature> siblingRules; 63 59 Vector<RuleFeature> uncommonAttributeRules; 64 bool usesFirstLineRules; 65 bool usesFirstLetterRules; 60 HashMap<AtomicStringImpl*, std::unique_ptr<Vector<RuleFeature>>> ancestorClassRules; 61 bool usesFirstLineRules { false }; 62 bool usesFirstLetterRules { false }; 63 64 private: 65 struct SelectorFeatures { 66 bool hasSiblingSelector { false }; 67 Vector<AtomicStringImpl*> classesMatchingAncestors; 68 }; 69 void recursivelyCollectFeaturesFromSelector(SelectorFeatures&, const CSSSelector&, bool matchesAncestor = false); 66 70 }; 67 71 -
trunk/Source/WebCore/css/RuleSet.cpp
r195309 r196383 173 173 } 174 174 175 static void collectFeaturesFromRuleData(RuleFeatureSet& features, const RuleData& ruleData) 176 { 177 bool hasSiblingSelector; 178 features.collectFeaturesFromSelector(*ruleData.selector(), hasSiblingSelector); 179 180 if (hasSiblingSelector) 181 features.siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin())); 182 if (ruleData.containsUncommonAttributeSelector()) 183 features.uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin())); 175 RuleSet::RuleSet() 176 { 177 } 178 179 RuleSet::~RuleSet() 180 { 184 181 } 185 182 … … 204 201 { 205 202 RuleData ruleData(rule, selectorIndex, m_ruleCount++, addRuleFlags); 206 collectFeaturesFromRuleData(m_features,ruleData);203 m_features.collectFeatures(ruleData); 207 204 208 205 unsigned classBucketSize = 0; -
trunk/Source/WebCore/css/RuleSet.h
r195309 r196383 158 158 159 159 RuleSet(); 160 ~RuleSet(); 160 161 161 162 typedef Vector<RuleData, 1> RuleDataVector; … … 214 215 RuleDataVector m_universalRules; 215 216 Vector<StyleRulePage*> m_pageRules; 216 unsigned m_ruleCount ;217 bool m_autoShrinkToFitEnabled ;217 unsigned m_ruleCount { 0 }; 218 bool m_autoShrinkToFitEnabled { false }; 218 219 RuleFeatureSet m_features; 219 220 Vector<RuleSetSelectorPair> m_regionSelectorsAndRuleSets; 220 221 }; 221 222 inline RuleSet::RuleSet()223 : m_ruleCount(0)224 , m_autoShrinkToFitEnabled(true)225 {226 }227 222 228 223 inline const RuleSet::RuleDataVector* RuleSet::tagRules(AtomicStringImpl* key, bool isHTMLName) const -
trunk/Source/WebCore/css/StyleInvalidationAnalysis.cpp
r194762 r196383 76 76 77 77 StyleInvalidationAnalysis::StyleInvalidationAnalysis(const Vector<StyleSheetContents*>& sheets, const MediaQueryEvaluator& mediaQueryEvaluator) 78 : m_dirtiesAllStyle(shouldDirtyAllStyle(sheets)) 78 : m_ownedRuleSet(std::make_unique<RuleSet>()) 79 , m_ruleSet(*m_ownedRuleSet) 80 , m_dirtiesAllStyle(shouldDirtyAllStyle(sheets)) 79 81 { 80 82 if (m_dirtiesAllStyle) 81 83 return; 82 84 83 m_ ruleSets.resetAuthorStyle();85 m_ownedRuleSet->disableAutoShrinkToFit(); 84 86 for (auto& sheet : sheets) 85 m_ ruleSets.authorStyle()->addRulesFromSheet(*sheet, mediaQueryEvaluator);87 m_ownedRuleSet->addRulesFromSheet(*sheet, mediaQueryEvaluator); 86 88 87 m_hasShadowPseudoElementRulesInAuthorSheet = m_ruleSet s.authorStyle()->hasShadowPseudoElementRules();89 m_hasShadowPseudoElementRulesInAuthorSheet = m_ruleSet.hasShadowPseudoElementRules(); 88 90 } 89 91 90 StyleInvalidationAnalysis::CheckDescendants StyleInvalidationAnalysis::invalidateIfNeeded(Element& element, SelectorFilter& filter) 92 StyleInvalidationAnalysis::StyleInvalidationAnalysis(const RuleSet& ruleSet) 93 : m_ruleSet(ruleSet) 94 , m_hasShadowPseudoElementRulesInAuthorSheet(ruleSet.hasShadowPseudoElementRules()) 95 { 96 } 97 98 StyleInvalidationAnalysis::CheckDescendants StyleInvalidationAnalysis::invalidateIfNeeded(Element& element, const SelectorFilter* filter) 91 99 { 92 100 if (m_hasShadowPseudoElementRulesInAuthorSheet) { … … 98 106 switch (element.styleChangeType()) { 99 107 case NoStyleChange: { 100 ElementRuleCollector ruleCollector(element, nullptr, m_ruleSets, &filter);108 ElementRuleCollector ruleCollector(element, m_ruleSet, filter); 101 109 ruleCollector.setMode(SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements); 102 110 ruleCollector.matchAuthorRules(false); … … 117 125 } 118 126 119 void StyleInvalidationAnalysis::invalidateStyleForTree(Element& root, SelectorFilter &filter)127 void StyleInvalidationAnalysis::invalidateStyleForTree(Element& root, SelectorFilter* filter) 120 128 { 121 129 if (invalidateIfNeeded(root, filter) == CheckDescendants::No) … … 131 139 if (parent == previousElement) { 132 140 parentStack.append(parent); 133 filter.pushParent(parent); 141 if (filter) 142 filter->pushParent(parent); 134 143 } else { 135 144 while (parentStack.last() != parent) { 136 145 parentStack.removeLast(); 137 filter.popParent(); 146 if (filter) 147 filter->popParent(); 138 148 } 139 149 } … … 151 161 { 152 162 ASSERT(!m_dirtiesAllStyle); 153 if (!m_ruleSets.authorStyle())154 return;155 163 156 164 Element* documentElement = document.documentElement(); … … 159 167 160 168 SelectorFilter filter; 161 invalidateStyleForTree(*documentElement, filter); 169 invalidateStyleForTree(*documentElement, &filter); 170 } 171 172 void StyleInvalidationAnalysis::invalidateStyle(Element& element) 173 { 174 ASSERT(!m_dirtiesAllStyle); 175 176 // Don't use SelectorFilter as the rule sets here tend to be small and the filter would have setup cost deep in the tree. 177 invalidateStyleForTree(element, nullptr); 162 178 } 163 179 -
trunk/Source/WebCore/css/StyleInvalidationAnalysis.h
r190347 r196383 27 27 #define StyleInvalidationAnalysis_h 28 28 29 #include "DocumentRuleSets.h"30 29 #include <wtf/HashSet.h> 31 30 #include <wtf/text/AtomicStringImpl.h> … … 34 33 35 34 class Document; 35 class Element; 36 class MediaQueryEvaluator; 37 class RuleSet; 36 38 class SelectorFilter; 37 39 class StyleSheetContents; … … 40 42 public: 41 43 StyleInvalidationAnalysis(const Vector<StyleSheetContents*>&, const MediaQueryEvaluator&); 44 StyleInvalidationAnalysis(const RuleSet&); 42 45 43 46 bool dirtiesAllStyle() const { return m_dirtiesAllStyle; } 44 47 bool hasShadowPseudoElementRulesInAuthorSheet() const { return m_hasShadowPseudoElementRulesInAuthorSheet; } 45 48 void invalidateStyle(Document&); 49 void invalidateStyle(Element&); 46 50 47 51 private: 48 52 enum class CheckDescendants { Yes, No }; 49 CheckDescendants invalidateIfNeeded(Element&, SelectorFilter&);50 void invalidateStyleForTree(Element&, SelectorFilter &);53 CheckDescendants invalidateIfNeeded(Element&, const SelectorFilter*); 54 void invalidateStyleForTree(Element&, SelectorFilter*); 51 55 56 std::unique_ptr<RuleSet> m_ownedRuleSet; 57 const RuleSet& m_ruleSet; 52 58 bool m_dirtiesAllStyle { false }; 53 59 bool m_hasShadowPseudoElementRulesInAuthorSheet { false }; 54 DocumentRuleSets m_ruleSets;55 60 }; 56 61 -
trunk/Source/WebCore/dom/Element.cpp
r195951 r196383 77 77 #include "SelectorQuery.h" 78 78 #include "Settings.h" 79 #include "StyleInvalidationAnalysis.h" 79 80 #include "StyleProperties.h" 80 81 #include "StyleResolver.h" … … 1300 1301 } 1301 1302 1302 static bool checkSelectorForClassChange(const SpaceSplitString& changedClasses, const StyleResolver& styleResolver) 1303 { 1304 unsigned changedSize = changedClasses.size(); 1305 for (unsigned i = 0; i < changedSize; ++i) { 1306 if (styleResolver.hasSelectorForClass(changedClasses[i])) 1307 return true; 1308 } 1309 return false; 1310 } 1311 1312 static bool checkSelectorForClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses, const StyleResolver& styleResolver) 1313 { 1303 static Vector<AtomicStringImpl*, 4> collectClasses(const SpaceSplitString& classes) 1304 { 1305 Vector<AtomicStringImpl*, 4> result; 1306 result.reserveCapacity(classes.size()); 1307 for (unsigned i = 0; i < classes.size(); ++i) 1308 result.uncheckedAppend(classes[i].impl()); 1309 return result; 1310 } 1311 1312 struct ClassChange { 1313 Vector<AtomicStringImpl*, 4> added; 1314 Vector<AtomicStringImpl*, 4> removed; 1315 }; 1316 1317 static ClassChange computeClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses) 1318 { 1319 ClassChange classChange; 1320 1314 1321 unsigned oldSize = oldClasses.size(); 1315 if (!oldSize) 1316 return checkSelectorForClassChange(newClasses, styleResolver); 1322 unsigned newSize = newClasses.size(); 1323 1324 if (!oldSize) { 1325 classChange.added = collectClasses(newClasses); 1326 return classChange; 1327 } 1328 if (!newSize) { 1329 classChange.removed = collectClasses(oldClasses); 1330 return classChange; 1331 } 1332 1317 1333 BitVector remainingClassBits; 1318 1334 remainingClassBits.ensureSize(oldSize); 1319 1335 // Class vectors tend to be very short. This is faster than using a hash table. 1320 unsigned newSize = newClasses.size();1321 1336 for (unsigned i = 0; i < newSize; ++i) { 1322 1337 bool foundFromBoth = false; … … 1329 1344 if (foundFromBoth) 1330 1345 continue; 1331 if (styleResolver.hasSelectorForClass(newClasses[i])) 1332 return true; 1346 classChange.added.append(newClasses[i].impl()); 1333 1347 } 1334 1348 for (unsigned i = 0; i < oldSize; ++i) { … … 1336 1350 if (remainingClassBits.quickGet(i)) 1337 1351 continue; 1338 if (styleResolver.hasSelectorForClass(oldClasses[i])) 1339 return true; 1340 } 1341 return false; 1352 classChange.removed.append(oldClasses[i].impl()); 1353 } 1354 1355 return classChange; 1356 } 1357 1358 static void invalidateStyleForClassChange(Element& element, const Vector<AtomicStringImpl*, 4>& changedClasses, const DocumentRuleSets& ruleSets) 1359 { 1360 Vector<AtomicStringImpl*, 4> changedClassesAffectingStyle; 1361 for (auto* changedClass : changedClasses) { 1362 if (ruleSets.features().classesInRules.contains(changedClass)) 1363 changedClassesAffectingStyle.append(changedClass); 1364 }; 1365 1366 if (changedClassesAffectingStyle.isEmpty()) 1367 return; 1368 1369 if (element.shadowRoot() && ruleSets.authorStyle()->hasShadowPseudoElementRules()) { 1370 element.setNeedsStyleRecalc(FullStyleChange); 1371 return; 1372 } 1373 1374 element.setNeedsStyleRecalc(InlineStyleChange); 1375 1376 if (!element.firstElementChild()) 1377 return; 1378 1379 for (auto* changedClass : changedClassesAffectingStyle) { 1380 auto* ancestorClassRules = ruleSets.ancestorClassRules(changedClass); 1381 if (!ancestorClassRules) 1382 continue; 1383 StyleInvalidationAnalysis invalidationAnalysis(*ancestorClassRules); 1384 invalidationAnalysis.invalidateStyle(element); 1385 } 1342 1386 } 1343 1387 1344 1388 void Element::classAttributeChanged(const AtomicString& newClassString) 1345 1389 { 1390 // Note: We'll need ElementData, but it doesn't have to be UniqueElementData. 1391 if (!elementData()) 1392 ensureUniqueElementData(); 1393 1394 bool shouldFoldCase = document().inQuirksMode(); 1395 bool newStringHasClasses = classStringHasClassName(newClassString); 1396 1397 auto oldClassNames = elementData()->classNames(); 1398 auto newClassNames = newStringHasClasses ? SpaceSplitString(newClassString, shouldFoldCase) : SpaceSplitString(); 1399 1346 1400 StyleResolver* styleResolver = document().styleResolverIfExists(); 1347 bool testShouldInvalidateStyle = inRenderedDocument() && styleResolver && styleChangeType() < FullStyleChange; 1348 bool shouldInvalidateStyle = false; 1349 1350 if (classStringHasClassName(newClassString)) { 1351 const bool shouldFoldCase = document().inQuirksMode(); 1352 // Note: We'll need ElementData, but it doesn't have to be UniqueElementData. 1353 if (!elementData()) 1354 ensureUniqueElementData(); 1355 const SpaceSplitString oldClasses = elementData()->classNames(); 1356 elementData()->setClass(newClassString, shouldFoldCase); 1357 const SpaceSplitString& newClasses = elementData()->classNames(); 1358 shouldInvalidateStyle = testShouldInvalidateStyle && checkSelectorForClassChange(oldClasses, newClasses, *styleResolver); 1359 } else if (elementData()) { 1360 const SpaceSplitString& oldClasses = elementData()->classNames(); 1361 shouldInvalidateStyle = testShouldInvalidateStyle && checkSelectorForClassChange(oldClasses, *styleResolver); 1362 elementData()->clearClass(); 1401 bool shouldInvalidateStyle = inRenderedDocument() && styleResolver && styleChangeType() < FullStyleChange; 1402 1403 ClassChange classChange; 1404 if (shouldInvalidateStyle) { 1405 classChange = computeClassChange(oldClassNames, newClassNames); 1406 if (!classChange.removed.isEmpty()) 1407 invalidateStyleForClassChange(*this, classChange.removed, styleResolver->ruleSets()); 1408 } 1409 1410 elementData()->setClassNames(newClassNames); 1411 1412 if (shouldInvalidateStyle) { 1413 if (!classChange.added.isEmpty()) 1414 invalidateStyleForClassChange(*this, classChange.added, styleResolver->ruleSets()); 1363 1415 } 1364 1416 … … 1367 1419 classList->attributeValueChanged(newClassString); 1368 1420 } 1369 1370 if (shouldInvalidateStyle)1371 setNeedsStyleRecalc();1372 1421 } 1373 1422 -
trunk/Source/WebCore/dom/ElementData.h
r179497 r196383 86 86 static const unsigned attributeNotFound = static_cast<unsigned>(-1); 87 87 88 void clearClass() const { m_classNames.clear(); } 89 void setClass(const AtomicString& className, bool shouldFoldCase) const { m_classNames.set(className, shouldFoldCase); } 88 void setClassNames(const SpaceSplitString& classNames) const { m_classNames = classNames; } 90 89 const SpaceSplitString& classNames() const { return m_classNames; } 91 90 static ptrdiff_t classNamesMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_classNames); }
Note: See TracChangeset
for help on using the changeset viewer.