Changeset 196383 in webkit


Ignore:
Timestamp:
Feb 10, 2016 12:47:04 PM (8 years ago)
Author:
Antti Koivisto
Message:

Optimize style invalidation after class attribute change
https://bugs.webkit.org/show_bug.cgi?id=154075
rdar://problem/12526450

Reviewed by Andreas Kling.

Currently a class attribute change invalidates style for the entire element subtree for any class found in the
active stylesheet set.

This patch optimizes class changes by building a new optimization structure called ancestorClassRules. It contains
rules that have class selectors in the portion of the complex selector that matches ancestor elements. The sets
of rules are hashes by the class name.

On class attribute change the existing StyleInvalidationAnalysis mechanism is used with ancestorClassRules to invalidate
exactly those descendants that are affected by the addition or removal of the class name. This is fast because the CSS JIT
makes selector matching cheap and the number of relevant rules is typically small.

This optimization is very effective on many dynamic pages. For example when focusing and unfocusing the web inspector it
cuts down the number of resolved elements from ~1000 to ~50. Even in PLT it reduces the number of resolved elements by ~11%.

  • css/DocumentRuleSets.cpp:

(WebCore::DocumentRuleSets::collectFeatures):
(WebCore::DocumentRuleSets::ancestorClassRules):

Create optimization RuleSets on-demand when there is an actual dynamic class change.

  • css/DocumentRuleSets.h:

(WebCore::DocumentRuleSets::features):
(WebCore::DocumentRuleSets::sibling):
(WebCore::DocumentRuleSets::uncommonAttribute):

  • css/ElementRuleCollector.cpp:

(WebCore::ElementRuleCollector::ElementRuleCollector):

Add a new constructor that doesn't requires DocumentRuleSets. Only the user and author style is required.

(WebCore::ElementRuleCollector::matchAuthorRules):
(WebCore::ElementRuleCollector::matchUserRules):

  • css/ElementRuleCollector.h:
  • css/RuleFeature.cpp:

(WebCore::RuleFeatureSet::recursivelyCollectFeaturesFromSelector):

Collect class names that show up in the ancestor portion of the selector.
Make this a member.

(WebCore::RuleFeatureSet::collectFeatures):

Move this code from RuleData.
Add the rule to ancestorClassRules if needed.

(WebCore::RuleFeatureSet::add):
(WebCore::RuleFeatureSet::clear):
(WebCore::RuleFeatureSet::shrinkToFit):
(WebCore::recursivelyCollectFeaturesFromSelector): Deleted.
(WebCore::RuleFeatureSet::collectFeaturesFromSelector): Deleted.

  • css/RuleFeature.h:

(WebCore::RuleFeature::RuleFeature):
(WebCore::RuleFeatureSet::RuleFeatureSet): Deleted.

  • css/RuleSet.cpp:

(WebCore::RuleData::RuleData):
(WebCore::RuleSet::RuleSet):
(WebCore::RuleSet::~RuleSet):
(WebCore::RuleSet::addToRuleSet):
(WebCore::RuleSet::addRule):
(WebCore::RuleSet::addRulesFromSheet):
(WebCore::collectFeaturesFromRuleData): Deleted.

  • css/RuleSet.h:

(WebCore::RuleSet::tagRules):
(WebCore::RuleSet::RuleSet): Deleted.

  • css/StyleInvalidationAnalysis.cpp:

(WebCore::shouldDirtyAllStyle):
(WebCore::StyleInvalidationAnalysis::StyleInvalidationAnalysis):

Add a new constructor that takes a ready made RuleSet instead of a stylesheet.

(WebCore::StyleInvalidationAnalysis::invalidateIfNeeded):
(WebCore::StyleInvalidationAnalysis::invalidateStyleForTree):
(WebCore::StyleInvalidationAnalysis::invalidateStyle):
(WebCore::StyleInvalidationAnalysis::invalidateStyle):

New function for invalidating a subtree instead of the whole document.

  • css/StyleInvalidationAnalysis.h:

(WebCore::StyleInvalidationAnalysis::dirtiesAllStyle):
(WebCore::StyleInvalidationAnalysis::hasShadowPseudoElementRulesInAuthorSheet):

  • dom/Element.cpp:

(WebCore::classStringHasClassName):
(WebCore::collectClasses):
(WebCore::computeClassChange):

Factor to return the changed classes.

(WebCore::invalidateStyleForClassChange):

First filter out classes that don't show up in stylesheets. If something remains invalidate the current
element for inline style change (that is a style change that doesn't affect descendants).

Next check if there are any ancestorClassRules for the changed class. If so use the StyleInvalidationAnalysis
to find any affected descendants and invalidate them with inline style change as well.

(WebCore::Element::classAttributeChanged):

Invalidate for removed classes before setting new attribute value, invalidate for added classes afterwards.

(WebCore::Element::absoluteLinkURL):
(WebCore::checkSelectorForClassChange): Deleted.

  • dom/ElementData.h:

(WebCore::ElementData::setClassNames):
(WebCore::ElementData::classNames):
(WebCore::ElementData::classNamesMemoryOffset):
(WebCore::ElementData::clearClass): Deleted.
(WebCore::ElementData::setClass): Deleted.

Location:
trunk/Source/WebCore
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r196376 r196383  
     12016-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
    11152016-02-10  Myles C. Maxfield  <mmaxfield@apple.com>
    2116
  • trunk/Source/WebCore/css/DocumentRuleSets.cpp

    r194496 r196383  
    116116}
    117117
     118RuleSet* 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
    118128} // namespace WebCore
  • trunk/Source/WebCore/css/DocumentRuleSets.h

    r190347 r196383  
    2727#include "RuleSet.h"
    2828#include <memory>
     29#include <wtf/HashMap.h>
    2930#include <wtf/RefPtr.h>
    3031#include <wtf/Vector.h>
     
    4950    RuleSet* sibling() const { return m_siblingRuleSet.get(); }
    5051    RuleSet* uncommonAttribute() const { return m_uncommonAttributeRuleSet.get(); }
     52    RuleSet* ancestorClassRules(AtomicStringImpl* className) const;
    5153
    5254    void initUserStyle(ExtensionStyleSheets&, const MediaQueryEvaluator&, StyleResolver&);
     
    6365    std::unique_ptr<RuleSet> m_siblingRuleSet;
    6466    std::unique_ptr<RuleSet> m_uncommonAttributeRuleSet;
     67    mutable HashMap<AtomicStringImpl*, std::unique_ptr<RuleSet>> m_ancestorClassRuleSet;
    6568};
    6669
  • trunk/Source/WebCore/css/ElementRuleCollector.cpp

    r196031 r196383  
    8181    : m_element(element)
    8282    , 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
     90ElementRuleCollector::ElementRuleCollector(Element& element, const RuleSet& authorStyle, const SelectorFilter* selectorFilter)
     91    : m_element(element)
     92    , m_authorStyle(authorStyle)
    8493    , m_selectorFilter(selectorFilter)
    8594{
     
    204213
    205214    // Match global author rules.
    206     MatchRequest matchRequest(m_ruleSets.authorStyle(), includeEmptyRules);
     215    MatchRequest matchRequest(&m_authorStyle, includeEmptyRules);
    207216    StyleResolver::RuleRange ruleRange = m_result.ranges.authorRuleRange();
    208217    collectMatchingRules(matchRequest, ruleRange);
     
    237246void ElementRuleCollector::matchUserRules(bool includeEmptyRules)
    238247{
    239     if (!m_ruleSets.userStyle())
     248    if (!m_userStyle)
    240249        return;
    241250   
     
    243252
    244253    m_result.ranges.lastUserRule = m_result.matchedProperties().size() - 1;
    245     MatchRequest matchRequest(m_ruleSets.userStyle(), includeEmptyRules);
     254    MatchRequest matchRequest(m_userStyle, includeEmptyRules);
    246255    StyleResolver::RuleRange ruleRange = m_result.ranges.userRuleRange();
    247256    collectMatchingRules(matchRequest, ruleRange);
  • trunk/Source/WebCore/css/ElementRuleCollector.h

    r196031 r196383  
    4747public:
    4848    ElementRuleCollector(Element&, RenderStyle*, const DocumentRuleSets&, const SelectorFilter*);
     49    ElementRuleCollector(Element&, const RuleSet& authorStyle, const SelectorFilter*);
    4950
    5051    void matchAllRules(bool matchAuthorAndUserStyles, bool includeSMILProperties);
     
    8889
    8990    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 };
    9395
    9496    bool m_isPrintStyle { false };
  • trunk/Source/WebCore/css/RuleFeature.cpp

    r178580 r196383  
    3232#include "CSSSelector.h"
    3333#include "CSSSelectorList.h"
     34#include "RuleSet.h"
    3435
    3536namespace WebCore {
    3637
    37 static void recursivelyCollectFeaturesFromSelector(RuleFeatureSet& features, const CSSSelector& firstSelector, bool& hasSiblingSelector)
     38void RuleFeatureSet::recursivelyCollectFeaturesFromSelector(SelectorFeatures& selectorFeatures, const CSSSelector& firstSelector, bool matchesAncestor)
    3839{
    3940    const CSSSelector* selector = &firstSelector;
    4041    do {
    4142        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());
    4851        } else if (selector->match() == CSSSelector::PseudoElement) {
    4952            switch (selector->pseudoElementType()) {
    5053            case CSSSelector::PseudoElementFirstLine:
    51                 features.usesFirstLineRules = true;
     54                usesFirstLineRules = true;
    5255                break;
    5356            case CSSSelector::PseudoElementFirstLetter:
    54                 features.usesFirstLetterRules = true;
     57                usesFirstLetterRules = true;
    5558                break;
    5659            default:
     
    5962        }
    6063
    61         if (!hasSiblingSelector && selector->isSiblingSelector())
    62             hasSiblingSelector = true;
     64        if (!selectorFeatures.hasSiblingSelector && selector->isSiblingSelector())
     65            selectorFeatures.hasSiblingSelector = true;
    6366
    6467        if (const CSSSelectorList* selectorList = selector->selectorList()) {
    6568            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);
    6972            }
    7073        }
     74        if (selector->relation() == CSSSelector::Child || selector->relation() == CSSSelector::Descendant)
     75            matchesAncestor = true;
    7176
    7277        selector = selector->tagHistory();
     
    7479}
    7580
    76 void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& firstSelector, bool& hasSiblingSelector)
     81void RuleFeatureSet::collectFeatures(const RuleData& ruleData)
    7782{
    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    }
    8095}
    8196
     
    88103    siblingRules.appendVector(other.siblingRules);
    89104    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    }
    90112    usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules;
    91113    usesFirstLetterRules = usesFirstLetterRules || other.usesFirstLetterRules;
     
    100122    siblingRules.clear();
    101123    uncommonAttributeRules.clear();
     124    ancestorClassRules.clear();
    102125    usesFirstLineRules = false;
    103126    usesFirstLetterRules = false;
     
    108131    siblingRules.shrinkToFit();
    109132    uncommonAttributeRules.shrinkToFit();
     133    for (auto& rules : ancestorClassRules.values())
     134        rules->shrinkToFit();
    110135}
    111136
  • trunk/Source/WebCore/css/RuleFeature.h

    r178580 r196383  
    3030namespace WebCore {
    3131
     32class CSSSelector;
     33class RuleData;
    3234class StyleRule;
    33 class CSSSelector;
    3435
    3536struct RuleFeature {
     
    4647
    4748struct RuleFeatureSet {
    48     RuleFeatureSet()
    49         : usesFirstLineRules(false)
    50         , usesFirstLetterRules(false)
    51     { }
    52 
    5349    void add(const RuleFeatureSet&);
    5450    void clear();
    5551    void shrinkToFit();
    56     void collectFeaturesFromSelector(const CSSSelector&, bool& hasSiblingSelector);
     52    void collectFeatures(const RuleData&);
    5753
    5854    HashSet<AtomicStringImpl*> idsInRules;
     
    6258    Vector<RuleFeature> siblingRules;
    6359    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
     64private:
     65    struct SelectorFeatures {
     66        bool hasSiblingSelector { false };
     67        Vector<AtomicStringImpl*> classesMatchingAncestors;
     68    };
     69    void recursivelyCollectFeaturesFromSelector(SelectorFeatures&, const CSSSelector&, bool matchesAncestor = false);
    6670};
    6771
  • trunk/Source/WebCore/css/RuleSet.cpp

    r195309 r196383  
    173173}
    174174
    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()));
     175RuleSet::RuleSet()
     176{
     177}
     178
     179RuleSet::~RuleSet()
     180{
    184181}
    185182
     
    204201{
    205202    RuleData ruleData(rule, selectorIndex, m_ruleCount++, addRuleFlags);
    206     collectFeaturesFromRuleData(m_features, ruleData);
     203    m_features.collectFeatures(ruleData);
    207204
    208205    unsigned classBucketSize = 0;
  • trunk/Source/WebCore/css/RuleSet.h

    r195309 r196383  
    158158
    159159    RuleSet();
     160    ~RuleSet();
    160161
    161162    typedef Vector<RuleData, 1> RuleDataVector;
     
    214215    RuleDataVector m_universalRules;
    215216    Vector<StyleRulePage*> m_pageRules;
    216     unsigned m_ruleCount;
    217     bool m_autoShrinkToFitEnabled;
     217    unsigned m_ruleCount { 0 };
     218    bool m_autoShrinkToFitEnabled { false };
    218219    RuleFeatureSet m_features;
    219220    Vector<RuleSetSelectorPair> m_regionSelectorsAndRuleSets;
    220221};
    221 
    222 inline RuleSet::RuleSet()
    223     : m_ruleCount(0)
    224     , m_autoShrinkToFitEnabled(true)
    225 {
    226 }
    227222
    228223inline const RuleSet::RuleDataVector* RuleSet::tagRules(AtomicStringImpl* key, bool isHTMLName) const
  • trunk/Source/WebCore/css/StyleInvalidationAnalysis.cpp

    r194762 r196383  
    7676
    7777StyleInvalidationAnalysis::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))
    7981{
    8082    if (m_dirtiesAllStyle)
    8183        return;
    8284
    83     m_ruleSets.resetAuthorStyle();
     85    m_ownedRuleSet->disableAutoShrinkToFit();
    8486    for (auto& sheet : sheets)
    85         m_ruleSets.authorStyle()->addRulesFromSheet(*sheet, mediaQueryEvaluator);
     87        m_ownedRuleSet->addRulesFromSheet(*sheet, mediaQueryEvaluator);
    8688
    87     m_hasShadowPseudoElementRulesInAuthorSheet = m_ruleSets.authorStyle()->hasShadowPseudoElementRules();
     89    m_hasShadowPseudoElementRulesInAuthorSheet = m_ruleSet.hasShadowPseudoElementRules();
    8890}
    8991
    90 StyleInvalidationAnalysis::CheckDescendants StyleInvalidationAnalysis::invalidateIfNeeded(Element& element, SelectorFilter& filter)
     92StyleInvalidationAnalysis::StyleInvalidationAnalysis(const RuleSet& ruleSet)
     93    : m_ruleSet(ruleSet)
     94    , m_hasShadowPseudoElementRulesInAuthorSheet(ruleSet.hasShadowPseudoElementRules())
     95{
     96}
     97
     98StyleInvalidationAnalysis::CheckDescendants StyleInvalidationAnalysis::invalidateIfNeeded(Element& element, const SelectorFilter* filter)
    9199{
    92100    if (m_hasShadowPseudoElementRulesInAuthorSheet) {
     
    98106    switch (element.styleChangeType()) {
    99107    case NoStyleChange: {
    100         ElementRuleCollector ruleCollector(element, nullptr, m_ruleSets, &filter);
     108        ElementRuleCollector ruleCollector(element, m_ruleSet, filter);
    101109        ruleCollector.setMode(SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements);
    102110        ruleCollector.matchAuthorRules(false);
     
    117125}
    118126
    119 void StyleInvalidationAnalysis::invalidateStyleForTree(Element& root, SelectorFilter& filter)
     127void StyleInvalidationAnalysis::invalidateStyleForTree(Element& root, SelectorFilter* filter)
    120128{
    121129    if (invalidateIfNeeded(root, filter) == CheckDescendants::No)
     
    131139            if (parent == previousElement) {
    132140                parentStack.append(parent);
    133                 filter.pushParent(parent);
     141                if (filter)
     142                    filter->pushParent(parent);
    134143            } else {
    135144                while (parentStack.last() != parent) {
    136145                    parentStack.removeLast();
    137                     filter.popParent();
     146                    if (filter)
     147                        filter->popParent();
    138148                }
    139149            }
     
    151161{
    152162    ASSERT(!m_dirtiesAllStyle);
    153     if (!m_ruleSets.authorStyle())
    154         return;
    155163
    156164    Element* documentElement = document.documentElement();
     
    159167
    160168    SelectorFilter filter;
    161     invalidateStyleForTree(*documentElement, filter);
     169    invalidateStyleForTree(*documentElement, &filter);
     170}
     171
     172void 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);
    162178}
    163179
  • trunk/Source/WebCore/css/StyleInvalidationAnalysis.h

    r190347 r196383  
    2727#define StyleInvalidationAnalysis_h
    2828
    29 #include "DocumentRuleSets.h"
    3029#include <wtf/HashSet.h>
    3130#include <wtf/text/AtomicStringImpl.h>
     
    3433
    3534class Document;
     35class Element;
     36class MediaQueryEvaluator;
     37class RuleSet;
    3638class SelectorFilter;
    3739class StyleSheetContents;
     
    4042public:
    4143    StyleInvalidationAnalysis(const Vector<StyleSheetContents*>&, const MediaQueryEvaluator&);
     44    StyleInvalidationAnalysis(const RuleSet&);
    4245
    4346    bool dirtiesAllStyle() const { return m_dirtiesAllStyle; }
    4447    bool hasShadowPseudoElementRulesInAuthorSheet() const { return m_hasShadowPseudoElementRulesInAuthorSheet; }
    4548    void invalidateStyle(Document&);
     49    void invalidateStyle(Element&);
    4650
    4751private:
    4852    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*);
    5155
     56    std::unique_ptr<RuleSet> m_ownedRuleSet;
     57    const RuleSet& m_ruleSet;
    5258    bool m_dirtiesAllStyle { false };
    5359    bool m_hasShadowPseudoElementRulesInAuthorSheet { false };
    54     DocumentRuleSets m_ruleSets;
    5560};
    5661
  • trunk/Source/WebCore/dom/Element.cpp

    r195951 r196383  
    7777#include "SelectorQuery.h"
    7878#include "Settings.h"
     79#include "StyleInvalidationAnalysis.h"
    7980#include "StyleProperties.h"
    8081#include "StyleResolver.h"
     
    13001301}
    13011302
    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 {
     1303static 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
     1312struct ClassChange {
     1313    Vector<AtomicStringImpl*, 4> added;
     1314    Vector<AtomicStringImpl*, 4> removed;
     1315};
     1316
     1317static ClassChange computeClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses)
     1318{
     1319    ClassChange classChange;
     1320
    13141321    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
    13171333    BitVector remainingClassBits;
    13181334    remainingClassBits.ensureSize(oldSize);
    13191335    // Class vectors tend to be very short. This is faster than using a hash table.
    1320     unsigned newSize = newClasses.size();
    13211336    for (unsigned i = 0; i < newSize; ++i) {
    13221337        bool foundFromBoth = false;
     
    13291344        if (foundFromBoth)
    13301345            continue;
    1331         if (styleResolver.hasSelectorForClass(newClasses[i]))
    1332             return true;
     1346        classChange.added.append(newClasses[i].impl());
    13331347    }
    13341348    for (unsigned i = 0; i < oldSize; ++i) {
     
    13361350        if (remainingClassBits.quickGet(i))
    13371351            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
     1358static 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    }
    13421386}
    13431387
    13441388void Element::classAttributeChanged(const AtomicString& newClassString)
    13451389{
     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
    13461400    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());
    13631415    }
    13641416
     
    13671419            classList->attributeValueChanged(newClassString);
    13681420    }
    1369 
    1370     if (shouldInvalidateStyle)
    1371         setNeedsStyleRecalc();
    13721421}
    13731422
  • trunk/Source/WebCore/dom/ElementData.h

    r179497 r196383  
    8686    static const unsigned attributeNotFound = static_cast<unsigned>(-1);
    8787
    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; }
    9089    const SpaceSplitString& classNames() const { return m_classNames; }
    9190    static ptrdiff_t classNamesMemoryOffset() { return OBJECT_OFFSETOF(ElementData, m_classNames); }
Note: See TracChangeset for help on using the changeset viewer.