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.
ElementSee also dom/README.md
TreeScopeRepresents a tree of elements for a document or shadow root. Gives fast access to various things inside the tree of elements. Holds a
ScopedStyleResolverfor this scope. See dom/README.md
The following are short-lived objects that are used when computing a single element's style.
When changes occur in the style sheet, either in an existing
TreeScope or with the introduction of a new
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
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
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
This guide starts with the simplest operation and works backwards.
SelectorChecker::MatchSelector- Checking if a rule applies to an element
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
StyleForLayoutObject- Calculating the computed style for an element
If 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
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 splits applies the following steps (read the method docs):
CollectMatchingRulesForList- testing some rules against an element
This is at the core of all the code paths. It takes
This creates a
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
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