Code related to the style sharing cache, an optimization that allows similar nodes to share style without having to run selector matching twice.
The basic setup is as follows. We have an LRU cache of style sharing candidates. When we try to style a target element, we first check whether we can quickly determine that styles match something in this cache, and if so we just use the cached style information. This check is done with a StyleBloom filter set up for the target element, which may not be a correct state for the cached candidate element if they’re cousins instead of siblings.
The complicated part is determining that styles match. This is subject to the following constraints:
- The target and candidate must be inheriting the same styles.
- The target and candidate must have exactly the same rules matching them.
- The target and candidate must have exactly the same non-selector-based style information (inline styles, presentation hints).
- The target and candidate must have exactly the same rules matching their pseudo-elements, because an element’s style data points to the style data for its pseudo-elements.
These constraints are satisfied in the following ways:
We check that the parents of the target and the candidate have the same computed style. This addresses constraint 1.
We check that the target and candidate have the same inline style and presentation hint declarations. This addresses constraint 3.
We ensure that a target matches a candidate only if they have the same matching result for all selectors that target either elements or the originating elements of pseudo-elements. This addresses constraint 4 (because it prevents a target that has pseudo-element styles from matching a candidate that has different pseudo-element styles) as well as constraint 2.
The actual checks that ensure that elements match the same rules are conceptually split up into two pieces. First, we do various checks on elements that make sure that the set of possible rules in all selector maps in the stylist (for normal styling and for pseudo-elements) that might match the two elements is the same. For example, we enforce that the target and candidate must have the same localname and namespace. Second, we have a selector map of “revalidation selectors” that the stylist maintains that we actually match against the target and candidate and then check whether the two sets of results were the same. Due to the up-front selector map checks, we know that the target and candidate will be matched against the same exact set of revalidation selectors, so the match result arrays can be compared directly.
It’s very important that a selector be added to the set of revalidation selectors any time there are two elements that could pass all the up-front checks but match differently against some ComplexSelector in the selector. If that happens, then they can have descendants that might themselves pass the up-front checks but would have different matching results for the selector in question. In this case, “descendants” includes pseudo-elements, so there is a single selector map of revalidation selectors that includes both selectors targeting elements and selectors targeting pseudo-element originating elements. We ensure that the pseudo-element parts of all these selectors are effectively stripped off, so that matching them all against elements makes sense.
Different checks done during the style sharing process in order to determine quickly whether it’s worth to share style, and whether two different elements can indeed share the same style.
Opaque pointer type to compare ComputedValues identities.
An LRU cache of the last few nodes seen, so that we can aggressively try to reuse their styles.
Information regarding a style sharing candidate, that is, an entry in the style sharing cache.
An element we want to test against the style sharing cache.
Some data we want to avoid recomputing all the time while trying to share style.
The amount of nodes that the style sharing candidate cache should hold at most.
Style sharing caches are are large allocations, so we store them in thread-local storage such that they can be reused across style traversals. Ideally, we’d just stack-allocate these buffers with uninitialized memory, but right now rustc can’t avoid memmoving the entire cache during setup, which gets very expensive. See issues like  and .