style/sharing/
checks.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Different checks done during the style sharing process in order to determine
6//! quickly whether it's worth to share style, and whether two different
7//! elements can indeed share the same style.
8
9use crate::bloom::StyleBloom;
10use crate::context::SharedStyleContext;
11use crate::dom::TElement;
12use crate::sharing::{StyleSharingCandidate, StyleSharingTarget};
13use selectors::matching::SelectorCaches;
14
15/// Determines whether a target and a candidate have compatible parents for
16/// sharing.
17pub fn parents_allow_sharing<E>(
18    target: &mut StyleSharingTarget<E>,
19    candidate: &mut StyleSharingCandidate<E>,
20) -> bool
21where
22    E: TElement,
23{
24    // If the identity of the parent style isn't equal, we can't share. We check
25    // this first, because the result is cached.
26    if target.parent_style_identity() != candidate.parent_style_identity() {
27        return false;
28    }
29
30    // Siblings can always share.
31    let parent = target.inheritance_parent().unwrap();
32    let candidate_parent = candidate.element.inheritance_parent().unwrap();
33    if parent == candidate_parent {
34        return true;
35    }
36
37    // If a parent element was already styled and we traversed past it without
38    // restyling it, that may be because our clever invalidation logic was able
39    // to prove that the styles of that element would remain unchanged despite
40    // changes to the id or class attributes. However, style sharing relies in
41    // the strong guarantee that all the classes and ids up the respective parent
42    // chains are identical. As such, if we skipped styling for one (or both) of
43    // the parents on this traversal, we can't share styles across cousins.
44    //
45    // This is a somewhat conservative check. We could tighten it by having the
46    // invalidation logic explicitly flag elements for which it ellided styling.
47    let parent_data = parent.borrow_data().unwrap();
48    let candidate_parent_data = candidate_parent.borrow_data().unwrap();
49    if !parent_data.safe_for_cousin_sharing() || !candidate_parent_data.safe_for_cousin_sharing() {
50        return false;
51    }
52
53    true
54}
55
56/// Whether two elements have the same same style attribute (by pointer identity).
57pub fn have_same_style_attribute<E>(
58    target: &mut StyleSharingTarget<E>,
59    candidate: &mut StyleSharingCandidate<E>,
60) -> bool
61where
62    E: TElement,
63{
64    match (target.style_attribute(), candidate.style_attribute()) {
65        (None, None) => true,
66        (Some(_), None) | (None, Some(_)) => false,
67        (Some(a), Some(b)) => &*a as *const _ == &*b as *const _,
68    }
69}
70
71/// Whether two elements have the same same presentational attributes.
72pub fn have_same_presentational_hints<E>(
73    target: &mut StyleSharingTarget<E>,
74    candidate: &mut StyleSharingCandidate<E>,
75) -> bool
76where
77    E: TElement,
78{
79    target.pres_hints() == candidate.pres_hints()
80}
81
82/// Whether a given element has the same class attribute as a given candidate.
83///
84/// We don't try to share style across elements with different class attributes.
85pub fn have_same_class<E>(
86    target: &mut StyleSharingTarget<E>,
87    candidate: &mut StyleSharingCandidate<E>,
88) -> bool
89where
90    E: TElement,
91{
92    target.class_list() == candidate.class_list()
93}
94
95/// Whether a given element has the same part attribute as a given candidate.
96///
97/// We don't try to share style across elements with different part attributes.
98pub fn have_same_parts<E>(
99    target: &mut StyleSharingTarget<E>,
100    candidate: &mut StyleSharingCandidate<E>,
101) -> bool
102where
103    E: TElement,
104{
105    target.part_list() == candidate.part_list()
106}
107
108/// Whether a given element and a candidate match the same set of "revalidation"
109/// selectors.
110///
111/// Revalidation selectors are those that depend on the DOM structure, like
112/// :first-child, etc, or on attributes that we don't check off-hand (pretty
113/// much every attribute selector except `id` and `class`.
114#[inline]
115pub fn revalidate<E>(
116    target: &mut StyleSharingTarget<E>,
117    candidate: &mut StyleSharingCandidate<E>,
118    shared_context: &SharedStyleContext,
119    bloom: &StyleBloom<E>,
120    selector_caches: &mut SelectorCaches,
121) -> bool
122where
123    E: TElement,
124{
125    let stylist = &shared_context.stylist;
126
127    let for_element = target.revalidation_match_results(stylist, bloom, selector_caches);
128
129    let for_candidate = candidate.revalidation_match_results(stylist, bloom, selector_caches);
130
131    for_element == for_candidate
132}
133
134/// Whether a given element and a candidate share a set of scope activations
135/// for revalidation.
136#[inline]
137pub fn revalidate_scope<E>(
138    target: &mut StyleSharingTarget<E>,
139    candidate: &mut StyleSharingCandidate<E>,
140    shared_context: &SharedStyleContext,
141    selector_caches: &mut SelectorCaches,
142) -> bool
143where
144    E: TElement,
145{
146    let stylist = &shared_context.stylist;
147    let for_element = target.scope_revalidation_results(stylist, selector_caches);
148    let for_candidate = candidate.scope_revalidation_results(stylist, selector_caches);
149
150    for_element == for_candidate
151}
152
153/// Checks whether we might have rules for either of the two ids.
154#[inline]
155pub fn may_match_different_id_rules<E>(
156    shared_context: &SharedStyleContext,
157    element: E,
158    candidate: E,
159) -> bool
160where
161    E: TElement,
162{
163    let element_id = element.id();
164    let candidate_id = candidate.id();
165
166    if element_id == candidate_id {
167        return false;
168    }
169
170    let stylist = &shared_context.stylist;
171
172    let may_have_rules_for_element = match element_id {
173        Some(id) => stylist.may_have_rules_for_id(id, element),
174        None => false,
175    };
176
177    if may_have_rules_for_element {
178        return true;
179    }
180
181    match candidate_id {
182        Some(id) => stylist.may_have_rules_for_id(id, candidate),
183        None => false,
184    }
185}