This is is a description of how Blink calculates which style rules apply to each element. It is not a comprehensive guide to the whole style computation/update process but it should be accurate for what it does cover. Further additions are welcome.
It ignores all V0 Shadow DOM logic.
The process of calculating styles for the elements is broken into 3 phases:
A catalogue of the classes involved. Read their class docs.
The following are long-lived objects that remain static during the calculation of each element's style.
Element
See also dom/README.mdTreeScope
Represents a tree of elements for a document or shadow root. Gives fast access to various things inside the tree of elements. Holds a ScopedStyleResolver
for this scope. See dom/README.mdStyleEngine
StyleResolver
ScopedStyleResolver
TreeScopeStyleSheetCollection
StyleRule
RuleData
RuleSet
The following are short-lived objects that are used when computing a single element's style.
ElementResolveContext
StyleResolverState
MatchRequest
ElementRuleCollector
SelectorCheckingContext
SelectorChecker
When changes occur in the style sheet, either in an existing TreeScope
or with the introduction of a new TreeScope
, the ScopedStyleResolver
for that scope must be updated. This is done by calling AppendActiveStyleSheets
and passing in a collection of style sheets. These style sheets are appended to the list of active style sheets in the TreeScope
and also partitioned and indexed by FindBestRuleSetAndAdd
. Within each RuleSet
are several maps of maps of RuleData
objects. FindBestRuleSetAndAdd
looks at the right-most compound selector of each selector and chooses which of these maps to hold this RuleData
. E.g. the selector p.cname
's right-most simple selector matches against class="cname"
, so this is added to the ClassRules
map with a key of "cname"
.
At the end of this process, each TreeScope
in the document has a ScopedStyleResolver
containing all of the style rules defined directly in that scope, partitioned into various RuleSet
s.
This guide starts with the simplest operation and works backwards.
SelectorChecker::MatchSelector
- Checking if a rule applies to an elementMatch
is the public entrypoint and MatchSelector
is the recursive core of checking if a rule applies to an element. Read their docs and also SelectorCheckingContext
and CheckOne
.
The whole process is started by ElementRuleCollector::CollectMatchingRulesForList
which creates the initial SelectorCheckingContext
, pointing to the element we are considering and to first simple selector of the CSSSelector array. Read CSSSelector
's class description to understand how complex selectors are represented by arrays of CSSSelector
s.
StyleForLayoutObject
- Calculating the computed style for an elementIf there are no custom style callbacks or animations StyleForLayoutObject
leads to StyleResolver::ResolveStyle
which is where the bulk of the work occurs. First by finding all of the rules which match the element and then using that and other info to compute the final style.
Blink considers all of the relevant style sheets for each element by partitioning and indexing the rules in each stylesheet inside the RuleSet
object, Blink is able to avoid considering many irrelevant rules for the current element. E.g. if the element has class="cname"
then Blink looks in the RuleSet's ClassRules
map under the key “cname” and considers all of the RuleData
objects found there. This allows it to avoid considering any rules with selectors that end in ".othercname"
which would have been under "othercname"
in the ClassRules
map.
In this way, Blink works through various lists of RuleData
for the element calling CollectMatchingRulesForList
on each list, how that works is described below.
Inside this method, context is set up that is used while calculating the style.
With all of this context set up, it calls MatchAllRules
which matches the rules from the
MatchAuthorRules
MatchAuthorRules
splits applies the following steps (read the method docs):
CollectMatchingRulesForList
- testing some rules against an elementThis is at the core of all the code paths. It takes
RuleData
MatchRequest
This creates a SelectorChecker
and SelectorCheckerContext
for the element and uses it to check try match, one by one, against each RuleData
object in the input list. If checker.Match(context, result)
returns true then this rule applies to this element and it is added to the collection with DidMatchRule
.
TODO
Document::UpdateStyleAndLayoutTree
is the starting point for computing or recomputing the styles of elements in the document. This calls UpdateActiveStyle
which calls UpdateActiveStyle
and leads into the compiling and index above. Then it calls UpdateStyleInvalidationIfNeeded()
(see here) and then UpdateStyle
which is what starts the traversal of the Element tree.
The tree is traversed in shadow-including tree oreder. There are 2 recursive paths that can be taken. The simpler one is in the case where the change being applied is ComputedStyleConstants::kReattach
. It recurses through ContainerNode::RecalcDescendantStylesForReattach
and involves methods with names like RecalcFooStyleForReattach
. The more complex recursion is similar. It recurses through ContainerNode::RecalcDescendantStyles
and involves methods with names like RecalcFooStyle
but it can enter the reattach code also. In both cases, the actual style calculation is performed by Element::StyleForLayoutObject
.
NthIndexCache
MatchScopedRules
MatchSelector
CalculateBaseComputedStyle