diff --git a/components/style/sharing/checks.rs b/components/style/sharing/checks.rs index 7c82b2550d8..6ff0bf68f51 100644 --- a/components/style/sharing/checks.rs +++ b/components/style/sharing/checks.rs @@ -10,26 +10,30 @@ use Atom; use bloom::StyleBloom; use context::{SelectorFlagsMap, SharedStyleContext}; use dom::TElement; -use servo_arc::Arc; use sharing::{StyleSharingCandidate, StyleSharingTarget}; -/// Whether styles may be shared across the children of the given parent elements. -/// This is used to share style across cousins. -/// -/// Both elements need to be styled already. -pub fn can_share_style_across_parents(first: Option, second: Option) -> bool +/// Determines whether a target and a candidate have compatible parents for sharing. +pub fn parents_allow_sharing( + target: &mut StyleSharingTarget, + candidate: &mut StyleSharingCandidate +) -> bool where E: TElement, { - let (first, second) = match (first, second) { - (Some(f), Some(s)) => (f, s), - _ => return false, - }; + // 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; + } - debug_assert_ne!(first, second); - - let first_data = first.borrow_data().unwrap(); - let second_data = second.borrow_data().unwrap(); + // 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. + // // 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 @@ -40,14 +44,14 @@ pub fn can_share_style_across_parents(first: Option, second: Option) -> // // This is a somewhat conservative check. We could tighten it by having the // invalidation logic explicitly flag elements for which it ellided styling. - if first_data.traversed_without_styling() || second_data.traversed_without_styling() { + let parent_data = parent.borrow_data().unwrap(); + let candidate_parent_data = candidate_parent.borrow_data().unwrap(); + if parent_data.traversed_without_styling() || + candidate_parent_data.traversed_without_styling() { return false; } - let same_computed_values = - Arc::ptr_eq(first_data.styles.primary(), second_data.styles.primary()); - - same_computed_values + true } /// Whether two elements have the same same style attribute (by pointer identity). diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index 47f82e1a095..db980a8c79a 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -76,7 +76,7 @@ use matching::MatchMethods; use owning_ref::OwningHandle; use properties::ComputedValues; use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode}; -use servo_arc::Arc; +use servo_arc::{Arc, NonZeroPtrMut}; use smallbitvec::SmallBitVec; use smallvec::SmallVec; use std::marker::PhantomData; @@ -108,6 +108,16 @@ pub enum StyleSharingBehavior { Disallow, } +/// Opaque pointer type to compare ComputedValues identities. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct OpaqueComputedValues(NonZeroPtrMut<()>); +impl OpaqueComputedValues { + fn from(cv: &ComputedValues) -> Self { + let p = NonZeroPtrMut::new(cv as *const ComputedValues as *const () as *mut ()); + OpaqueComputedValues(p) + } +} + /// Some data we want to avoid recomputing all the time while trying to share /// style. #[derive(Debug, Default)] @@ -121,6 +131,9 @@ pub struct ValidationData { /// The list of presentational attributes of the element. pres_hints: Option>, + /// The pointer identity of the parent ComputedValues. + parent_style_identity: Option, + /// The cached result of matching this entry against the revalidation /// selectors. revalidation_match_results: Option, @@ -167,6 +180,18 @@ impl ValidationData { &*self.class_list.as_ref().unwrap() } + /// Get or compute the parent style identity. + pub fn parent_style_identity(&mut self, el: E) -> OpaqueComputedValues + where E: TElement, + { + if self.parent_style_identity.is_none() { + let parent = el.inheritance_parent().unwrap(); + self.parent_style_identity = + Some(OpaqueComputedValues::from(parent.borrow_data().unwrap().styles.primary())); + } + self.parent_style_identity.as_ref().unwrap().clone() + } + /// Computes the revalidation results if needed, and returns it. /// Inline so we know at compile time what bloom_known_valid is. #[inline] @@ -250,6 +275,11 @@ impl StyleSharingCandidate { self.validation_data.pres_hints(self.element) } + /// Get the parent style identity. + fn parent_style_identity(&mut self) -> OpaqueComputedValues { + self.validation_data.parent_style_identity(self.element) + } + /// Compute the bit vector of revalidation selector match results /// for this candidate. fn revalidation_match_results( @@ -304,6 +334,11 @@ impl StyleSharingTarget { self.validation_data.pres_hints(self.element) } + /// Get the parent style identity. + fn parent_style_identity(&mut self) -> OpaqueComputedValues { + self.validation_data.parent_style_identity(self.element) + } + fn revalidation_match_results( &mut self, stylist: &Stylist, @@ -615,10 +650,7 @@ impl StyleSharingCache { // share styles and permit sharing across their children. The latter // check allows us to share style between cousins if the parents // shared style. - let parent = target.inheritance_parent(); - let candidate_parent = candidate.element.inheritance_parent(); - if parent != candidate_parent && - !checks::can_share_style_across_parents(parent, candidate_parent) { + if !checks::parents_allow_sharing(target, candidate) { trace!("Miss: Parent"); return false; }