1# CSS Style Calculation in Blink
2
3[Rendered](https://chromium.googlesource.com/chromium/src/+/HEAD/third_party/blink/renderer/core/css/style-calculation.md)
4
5
6# About this document
7
8This is is a description of how Blink calculates which style rules apply to each
9element. It is not a comprehensive guide to the whole style computation/update
10process but it should be accurate for what it does cover. Further additions are
11welcome.
12
13It ignores all V0 Shadow DOM logic.
14
15
16# High-level view
17
18
19## Process
20
21The process of calculating styles for the elements is broken into 3 phases:
22
23*   Gathering, partitioning and indexing the style rules present in all of the
24    style sheets
25*   Visiting each element and finding all of the rules which apply to that
26    element
27*   Combining those rules and other information to produce the final computed
28    style
29
30
31## Main classes
32
33A catalogue of the classes involved. Read their class docs.
34
35The following are long-lived objects that remain static during the calculation
36of each element's style.
37
38* [`Element`](https://cs.chromium.org/?q=symbol:%5Eblink::Element$) See also
39[dom/README.md](https://chromium.googlesource.com/chromium/src/+/HEAD/third_party/blink/renderer/core/dom/README.md)
40* [`TreeScope`](https://cs.chromium.org/?q=symbol:%5Eblink::TreeScope$)
41Represents a tree of elements for a document or shadow root. Gives fast access
42to various things inside the tree of elements. Holds a
43[`ScopedStyleResolver`](https://cs.chromium.org/?q=symbol:%5Eblink::ScopedStyleResolver$)
44for this scope. See
45[dom/README.md](https://chromium.googlesource.com/chromium/src/+/HEAD/third_party/blink/renderer/core/dom/README.md#treescope)
46* [`StyleEngine`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleEngine$)
47* [`StyleResolver`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleResolver$)
48* [`ScopedStyleResolver`](https://cs.chromium.org/?q=symbol:%5Eblink::ScopedStyleResolver$)
49* [`TreeScopeStyleSheetCollection`](https://cs.chromium.org/?q=symbol:%5Eblink::TreeScopeStyleSheetCollection$)
50* [`StyleRule`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleRule$)
51* [`RuleData`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleData$)
52* [`RuleSet`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleSet$)
53
54The following are short-lived objects that are used when computing a single
55element's style.
56
57* [`ElementResolveContext`](https://cs.chromium.org/?q=symbol:%5Eblink::ElementResolveContext$)
58* [`StyleResolverState`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleResolverState$)
59* [`MatchRequest`](https://cs.chromium.org/?q=symbol:%5Eblink::MatchRequest$)
60* [`ElementRuleCollector`](https://cs.chromium.org/?q=symbol:%5Eblink::ElementRuleCollector$)
61* [`SelectorCheckingContext`](https://cs.chromium.org/?q=symbol:%5Eblink::SelectorChecker::SelectorCheckingContext$)
62* [`SelectorChecker`](https://cs.chromium.org/?q=symbol:%5Eblink::SelectorChecker$)
63
64# Compiling and indexing
65
66When changes occur in the style sheet, either in an existing `TreeScope` or with
67the introduction of a new `TreeScope`, the
68[`ScopedStyleResolver`](https://cs.chromium.org/?q=symbol:%5Eblink::ScopedStyleResolver$)
69for that scope must be updated. This is done by calling
70[`AppendActiveStyleSheets`](https://cs.chromium.org/?q=symbol:%5Eblink::ScopedStyleResolver::AppendActiveStyleSheets$)
71and passing in a collection of style sheets. These style sheets are appended to
72the list of active style sheets in the `TreeScope` and also partitioned and
73indexed by
74[`FindBestRuleSetAndAdd`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleSet::FindBestRuleSetAndAdd$). Within
75each [`RuleSet`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleSet$) are several maps of maps of `RuleData`
76objects. `FindBestRuleSetAndAdd` looks at the right-most compound selector of each
77selector and chooses which of these maps to hold this `RuleData`. E.g. the
78selector `p.cname`'s right-most simple selector matches against `class="cname"`,
79so this is added to the `ClassRules` map with a key of `"cname"`.
80
81At 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.
82
83
84# Calculating styles for each element - WIP
85
86This guide starts with the simplest operation and works backwards.
87
88
89## [`SelectorChecker::MatchSelector`](https://cs.chromium.org/?q=symbol:%5Eblink::SelectorChecker::MatchSelector$) - Checking if a rule applies to an element
90
91[`Match`](https://cs.chromium.org/?q=symbol:%5Eblink::SelectorChecker::Match$+file:.h$)
92is the public entrypoint and
93[`MatchSelector`](https://cs.chromium.org/?q=symbol:%5Eblink::SelectorChecker::MatchSelector$+file:.h$)
94is the recursive core of checking if a rule applies to an element. Read their docs and also
95[`SelectorCheckingContext`](https://cs.chromium.org/?q=symbol:%5Eblink::SelectorChecker::SelectorCheckingContext$)
96and
97[`CheckOne`](https://cs.chromium.org/?q=symbol:%5Eblink::SelectorChecker::CheckOne$+file:.h$).
98
99The whole process is started by
100[`ElementRuleCollector::CollectMatchingRulesForList`](https://cs.chromium.org/?q=symbol:%5Eblink::ElementRuleCollector::CollectMatchingRulesForList)
101which creates the initial `SelectorCheckingContext`, pointing to the element we
102are considering and to first simple selector of the CSSSelector array. Read
103[`CSSSelector`](https://cs.chromium.org/?q=symbol:%5Eblink::CSSSelector$)'s
104class description to understand how complex selectors are represented by arrays
105of `CSSSelector`s.
106
107
108## [`StyleForLayoutObject`](https://cs.chromium.org/?q=symbol:%5Eblink::Element::StyleForLayoutObject$) - Calculating the computed style for an element
109
110If there are no custom style callbacks or animations
111[`StyleForLayoutObject`](https://cs.chromium.org/?q=symbol:%5Eblink::Element::StyleForLayoutObject$)
112leads to
113[`StyleResolver::StyleForElement`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleResolver::StyleForElement$)
114which is where the bulk of the work occurs. First by finding all of the rules
115which match the element and then using that and other info to compute the final
116style.
117
118
119### Finding all rules which match the element
120
121Blink considers all of the relevant style sheets for each element by
122partitioning and indexing the rules in each stylesheet inside the
123[`RuleSet`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleSet$) object, Blink
124is able to avoid considering many irrelevant rules for the current
125element. E.g. if the element has `class="cname"` then Blink looks in the
126RuleSet's `ClassRules` map under the key "cname" and considers all of the
127`RuleData` objects found there. This allows it to avoid considering any rules
128with selectors that end in `".othercname"` which would have been under
129`"othercname"` in the `ClassRules` map.
130
131In this way, Blink works through various lists of `RuleData` for the element
132calling
133[`CollectMatchingRulesForList`](https://cs.chromium.org/?q=symbol:%5Eblink::ElementRuleCollector::CollectMatchingRulesForList$)
134on each list, how that works is described
135[below](#CollectMatchingRulesForList).
136
137Inside this method, context is set up that is used while calculating the style.
138
139*   [`ElementResolveContext`](https://cs.chromium.org/?q=symbol:%5Eblink::ElementResolveContext$)
140*   [`StyleResolverState`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleResolverState$)
141*   [`ElementRuleCollector`](https://cs.chromium.org/?q=symbol:%5Eblink::ElementRuleCollector$)
142
143With all of this context set up, it calls
144[`MatchAllRules`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleResolver::MatchAllRules$)
145which matches the rules from the
146
147*   User Agent
148*   User
149*   Author (document's style) via
150    [`MatchAuthorRules`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleResolver::MatchAuthorRules$)
151
152[`MatchAuthorRules`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleResolver::MatchAuthorRules$)
153splits applies the following steps (read the method docs):
154
155- [`MatchHostRules`](https://cs.chromium.org/?q=symbol:%5Eblink::MatchHostRules$)
156- [`MatchSlottedRules`](https://cs.chromium.org/?q=symbol:%5Eblink::MatchSlottedRules$)
157- [`MatchElementScopeRules`](https://cs.chromium.org/?q=symbol:%5Eblink::MatchElementScopeRules$)
158- [`MatchPseudoPartRules`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleResolver::MatchPseudoPartRules$)
159
160
161#### <a name="CollectMatchingRulesForList"></a>[`CollectMatchingRulesForList`](https://cs.chromium.org/?q=symbol:%5Eblink::ElementRuleCollector::CollectMatchingRulesForList$) - testing some rules against an element
162
163This is at the core of all the code paths. It takes
164
165*   list of [`RuleData`](https://cs.chromium.org/?q=symbol:%5Eblink::RuleData$)
166*   [`MatchRequest`](https://cs.chromium.org/?q=symbol:%5Eblink::MatchRequest$)
167
168This creates a `SelectorChecker` and `SelectorCheckerContext` for the element
169and uses it to check try match, one by one, against each `RuleData` object in
170the input list. If `checker.Match(context, result)` returns true then this rule
171applies to this element and it is added to the collection with
172[`DidMatchRule`](https://cs.chromium.org/?q=symbol:%5Eblink::ElementRuleCollector::DidMatchRule$).
173
174
175### Computing style from matched rules
176
177TODO
178
179
180## Descending the DOM trees
181
182[`Document::UpdateStyleAndLayoutTree`](https://cs.chromium.org/?q=symbol:%5Eblink::Document::UpdateStyleAndLayoutTree$)
183is the starting point for computing or recomputing the styles of elements in the
184document. This calls
185[`UpdateActiveStyle`](https://cs.chromium.org/?q=symbol:%5Eblink::Document::UpdateActiveStyle$)
186which calls
187[`UpdateActiveStyle`](https://cs.chromium.org/?q=symbol:%5Eblink::Document::UpdateActiveStyle$)
188and leads into the compiling and index [above](#compiling-and-indexing). Then it
189calls
190[`UpdateStyleInvalidationIfNeeded()`](https://cs.chromium.org/?q=symbol:%5Eblink::Document::UpdateStyleInvalidationIfNeeded$)
191(see [here](README.md#style-invalidation)) and then
192[`UpdateStyle`](https://cs.chromium.org/?q=symbol:%5Eblink::Document::UpdateStyle$)
193which is what starts the traversal of the Element tree.
194
195The tree is traversed in [shadow-including tree
196oreder](https://www.w3.org/TR/shadow-dom/#concept-shadow-including-tree-order). There
197are 2 recursive paths that can be taken. The simpler one is in the case where
198the
199[change](https://chromium.googlesource.com/chromium/src/+/lkcr/third_party/blink/renderer/core/style/stylerecalc.md)
200being applied is
201[`ComputedStyleConstants::kReattach`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleRecalcChange::kReattach$). It
202recurses through
203[`ContainerNode::RecalcDescendantStylesForReattach`](https://cs.chromium.org/?q=symbol:%5Eblink::ContainerNode::RecalcDescendantStylesForReattach$)
204and involves methods with names like
205[`RecalcFooStyleForReattach`](https://cs.chromium.org/search/?q=symbol:%5Eblink::.*::Recalc.*Styles?ForReattach$+file:dom/). The
206more complex recursion is similar. It recurses through
207[`ContainerNode::RecalcDescendantStyles`](https://cs.chromium.org/?q=symbol:%5Eblink::ContainerNode::RecalcDescendantStyles$)
208and involves methods with names like
209[`RecalcFooStyle`](https://cs.chromium.org/search/?q=symbol:%5Eblink::.*::Recalc.*Styles?$+file:dom/)
210but it can enter the reattach code also. In both cases, the actual style
211calculation is performed by
212[`Element::StyleForLayoutObject`](https://cs.chromium.org/?q=symbol:%5Eblink::Element::StyleForLayoutObject$).
213
214
215# Omissions
216
217*   Caching, fast reject,
218    [`NthIndexCache`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/dom/nth_index_cache.h?l=36&gsn=NthIndexCache)
219*   Ordering, e.g. the comment at the start of
220    [`MatchScopedRules`](https://cs.chromium.org/?q=symbol:%5Eblink::StyleResolver::MatchScopedRules$)
221*   How the collected set of rules that match an element are combined with
222    inline style and parent style to get computed-style.
223*   How the [various types of
224    failure](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/css/selector_checker.h?l=153)
225    are used to optimize application of `MatchSelector`
226*   Other entry points that lead into rule collection.
227*   Animation.
228*   [`CalculateBaseComputedStyle`](https://cs.chromium.org/?q=symbol:%5Eblink::CalculateBaseComputedStyle$)
229