summaryrefslogtreecommitdiffstats
path: root/servo/components/style/sharing/checks.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/sharing/checks.rs')
-rw-r--r--servo/components/style/sharing/checks.rs182
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,
+ }
+}