diff options
Diffstat (limited to 'servo/components/style/sharing/checks.rs')
-rw-r--r-- | servo/components/style/sharing/checks.rs | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/servo/components/style/sharing/checks.rs b/servo/components/style/sharing/checks.rs new file mode 100644 index 0000000000..a691ac5c76 --- /dev/null +++ b/servo/components/style/sharing/checks.rs @@ -0,0 +1,182 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! 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. + +use crate::bloom::StyleBloom; +use crate::context::SharedStyleContext; +use crate::dom::TElement; +use crate::sharing::{StyleSharingCandidate, StyleSharingTarget}; +use selectors::NthIndexCache; + +/// Determines whether a target and a candidate have compatible parents for +/// sharing. +pub fn parents_allow_sharing<E>( + target: &mut StyleSharingTarget<E>, + candidate: &mut StyleSharingCandidate<E>, +) -> bool +where + E: TElement, +{ + // If the identity of the parent style isn't equal, we can't share. We check + // this first, because the result is cached. + if target.parent_style_identity() != candidate.parent_style_identity() { + return false; + } + + // Siblings can always share. + let parent = target.inheritance_parent().unwrap(); + let candidate_parent = candidate.element.inheritance_parent().unwrap(); + if parent == candidate_parent { + return true; + } + + // Cousins are a bit more complicated. + // + // The fact that the candidate is here means that its element does not anchor + // the relative selector. However, it may have considered relative selector(s) + // to compute its style, i.e. there's a rule `<..> :has(<..>) <..> candidate`. + // In this case, evaluating style sharing requires evaluating the relative + // selector for the target anyway. + if candidate.considered_relative_selector { + return false; + } + + // If a parent element was already styled and we traversed past it without + // restyling it, that may be because our clever invalidation logic was able + // to prove that the styles of that element would remain unchanged despite + // changes to the id or class attributes. However, style sharing relies in + // the strong guarantee that all the classes and ids up the respective parent + // chains are identical. As such, if we skipped styling for one (or both) of + // the parents on this traversal, we can't share styles across cousins. + // + // This is a somewhat conservative check. We could tighten it by having the + // invalidation logic explicitly flag elements for which it ellided styling. + let parent_data = parent.borrow_data().unwrap(); + let candidate_parent_data = candidate_parent.borrow_data().unwrap(); + if !parent_data.safe_for_cousin_sharing() || !candidate_parent_data.safe_for_cousin_sharing() { + return false; + } + + true +} + +/// Whether two elements have the same same style attribute (by pointer identity). +pub fn have_same_style_attribute<E>( + target: &mut StyleSharingTarget<E>, + candidate: &mut StyleSharingCandidate<E>, +) -> bool +where + E: TElement, +{ + match (target.style_attribute(), candidate.style_attribute()) { + (None, None) => true, + (Some(_), None) | (None, Some(_)) => false, + (Some(a), Some(b)) => &*a as *const _ == &*b as *const _, + } +} + +/// Whether two elements have the same same presentational attributes. +pub fn have_same_presentational_hints<E>( + target: &mut StyleSharingTarget<E>, + candidate: &mut StyleSharingCandidate<E>, +) -> bool +where + E: TElement, +{ + target.pres_hints() == candidate.pres_hints() +} + +/// Whether a given element has the same class attribute as a given candidate. +/// +/// We don't try to share style across elements with different class attributes. +pub fn have_same_class<E>( + target: &mut StyleSharingTarget<E>, + candidate: &mut StyleSharingCandidate<E>, +) -> bool +where + E: TElement, +{ + target.class_list() == candidate.class_list() +} + +/// Whether a given element has the same part attribute as a given candidate. +/// +/// We don't try to share style across elements with different part attributes. +pub fn have_same_parts<E>( + target: &mut StyleSharingTarget<E>, + candidate: &mut StyleSharingCandidate<E>, +) -> bool +where + E: TElement, +{ + target.part_list() == candidate.part_list() +} + +/// Whether a given element and a candidate match the same set of "revalidation" +/// selectors. +/// +/// Revalidation selectors are those that depend on the DOM structure, like +/// :first-child, etc, or on attributes that we don't check off-hand (pretty +/// much every attribute selector except `id` and `class`. +#[inline] +pub fn revalidate<E>( + target: &mut StyleSharingTarget<E>, + candidate: &mut StyleSharingCandidate<E>, + shared_context: &SharedStyleContext, + bloom: &StyleBloom<E>, + nth_index_cache: &mut NthIndexCache, +) -> bool +where + E: TElement, +{ + let stylist = &shared_context.stylist; + + let for_element = target.revalidation_match_results(stylist, bloom, nth_index_cache); + + let for_candidate = candidate.revalidation_match_results(stylist, bloom, nth_index_cache); + + // This assert "ensures", to some extent, that the two candidates have + // matched the same rulehash buckets, and as such, that the bits we're + // comparing represent the same set of selectors. + debug_assert_eq!(for_element.len(), for_candidate.len()); + + for_element == for_candidate +} + +/// Checks whether we might have rules for either of the two ids. +#[inline] +pub fn may_match_different_id_rules<E>( + shared_context: &SharedStyleContext, + element: E, + candidate: E, +) -> bool +where + E: TElement, +{ + let element_id = element.id(); + let candidate_id = candidate.id(); + + if element_id == candidate_id { + return false; + } + + let stylist = &shared_context.stylist; + + let may_have_rules_for_element = match element_id { + Some(id) => stylist.may_have_rules_for_id(id, element), + None => false, + }; + + if may_have_rules_for_element { + return true; + } + + match candidate_id { + Some(id) => stylist.may_have_rules_for_id(id, candidate), + None => false, + } +} |