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