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 style attribute.
57///
58/// First checks pointer identity (fast path), then falls back to value comparison.
59pub fn have_same_style_attribute<E>(
60    target: &mut StyleSharingTarget<E>,
61    candidate: &mut StyleSharingCandidate<E>,
62    shared_context: &SharedStyleContext,
63) -> bool
64where
65    E: TElement,
66{
67    match (target.style_attribute(), candidate.style_attribute()) {
68        (None, None) => true,
69        (Some(_), None) | (None, Some(_)) => false,
70        (Some(a), Some(b)) => {
71            if std::ptr::eq(&*a, &*b) {
72                return true;
73            }
74            let guard = shared_context.guards.author;
75            *a.read_with(guard) == *b.read_with(guard)
76        },
77    }
78}
79
80/// Whether two elements have the same same presentational attributes.
81pub fn have_same_presentational_hints<E>(
82    target: &mut StyleSharingTarget<E>,
83    candidate: &mut StyleSharingCandidate<E>,
84) -> bool
85where
86    E: TElement,
87{
88    target.pres_hints() == candidate.pres_hints()
89}
90
91/// Whether a given element has the same class attribute as a given candidate.
92///
93/// We don't try to share style across elements with different class attributes.
94pub fn have_same_class<E>(
95    target: &mut StyleSharingTarget<E>,
96    candidate: &mut StyleSharingCandidate<E>,
97) -> bool
98where
99    E: TElement,
100{
101    target.class_list() == candidate.class_list()
102}
103
104/// Whether a given element has the same part attribute as a given candidate.
105///
106/// We don't try to share style across elements with different part attributes.
107pub fn have_same_parts<E>(
108    target: &mut StyleSharingTarget<E>,
109    candidate: &mut StyleSharingCandidate<E>,
110) -> bool
111where
112    E: TElement,
113{
114    target.part_list() == candidate.part_list()
115}
116
117/// Whether a given element and a candidate match the same set of "revalidation"
118/// selectors.
119///
120/// Revalidation selectors are those that depend on the DOM structure, like
121/// :first-child, etc, or on attributes that we don't check off-hand (pretty
122/// much every attribute selector except `id` and `class`.
123#[inline]
124pub fn revalidate<E>(
125    target: &mut StyleSharingTarget<E>,
126    candidate: &mut StyleSharingCandidate<E>,
127    shared_context: &SharedStyleContext,
128    bloom: &StyleBloom<E>,
129    selector_caches: &mut SelectorCaches,
130) -> bool
131where
132    E: TElement,
133{
134    let stylist = &shared_context.stylist;
135
136    let for_element = target.revalidation_match_results(stylist, bloom, selector_caches);
137
138    let for_candidate = candidate.revalidation_match_results(stylist, bloom, selector_caches);
139
140    for_element == for_candidate
141}
142
143/// Whether a given element and a candidate share a set of scope activations
144/// for revalidation.
145#[inline]
146pub fn revalidate_scope<E>(
147    target: &mut StyleSharingTarget<E>,
148    candidate: &mut StyleSharingCandidate<E>,
149    shared_context: &SharedStyleContext,
150    selector_caches: &mut SelectorCaches,
151) -> bool
152where
153    E: TElement,
154{
155    let stylist = &shared_context.stylist;
156    let for_element = target.scope_revalidation_results(stylist, selector_caches);
157    let for_candidate = candidate.scope_revalidation_results(stylist, selector_caches);
158
159    for_element == for_candidate
160}
161
162/// Checks whether we might have rules for either of the two ids.
163#[inline]
164pub fn may_match_different_id_rules<E>(
165    shared_context: &SharedStyleContext,
166    element: E,
167    candidate: E,
168) -> bool
169where
170    E: TElement,
171{
172    let element_id = element.id();
173    let candidate_id = candidate.id();
174
175    if element_id == candidate_id {
176        return false;
177    }
178
179    let stylist = &shared_context.stylist;
180
181    let may_have_rules_for_element = match element_id {
182        Some(id) => stylist.may_have_rules_for_id(id, element),
183        None => false,
184    };
185
186    if may_have_rules_for_element {
187        return true;
188    }
189
190    match candidate_id {
191        Some(id) => stylist.may_have_rules_for_id(id, candidate),
192        None => false,
193    }
194}