1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
/* 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::matching::SelectorCaches;
/// 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;
}
// 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>,
selector_caches: &mut SelectorCaches,
) -> bool
where
E: TElement,
{
let stylist = &shared_context.stylist;
let for_element = target.revalidation_match_results(stylist, bloom, selector_caches);
let for_candidate = candidate.revalidation_match_results(stylist, bloom, selector_caches);
for_element == for_candidate
}
/// Whether a given element and a candidate share a set of scope activations
/// for revalidation.
#[inline]
pub fn revalidate_scope<E>(
target: &mut StyleSharingTarget<E>,
candidate: &mut StyleSharingCandidate<E>,
shared_context: &SharedStyleContext,
selector_caches: &mut SelectorCaches,
) -> bool
where
E: TElement,
{
let stylist = &shared_context.stylist;
let for_element = target.scope_revalidation_results(stylist, selector_caches);
let for_candidate = candidate.scope_revalidation_results(stylist, selector_caches);
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,
}
}