Optimize style invalidation after class attribute change
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Feb 2016 20:47:04 +0000 (20:47 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Feb 2016 20:47:04 +0000 (20:47 +0000)
commite60e0f76b7e8895a20ca1e640c0485751f7717e7
treefe2600356ecbd3bf15df6abe8376a73f721d69de
parent3fe491a0d2aedeeae825587b8ccb7c3af62f0a3b
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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@196383 268f45cc-cd09-0410-ab3c-d52691b4dbfc
13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/css/DocumentRuleSets.cpp
Source/WebCore/css/DocumentRuleSets.h
Source/WebCore/css/ElementRuleCollector.cpp
Source/WebCore/css/ElementRuleCollector.h
Source/WebCore/css/RuleFeature.cpp
Source/WebCore/css/RuleFeature.h
Source/WebCore/css/RuleSet.cpp
Source/WebCore/css/RuleSet.h
Source/WebCore/css/StyleInvalidationAnalysis.cpp
Source/WebCore/css/StyleInvalidationAnalysis.h
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/ElementData.h