diff options
-rw-r--r-- | components/style/data.rs | 11 | ||||
-rw-r--r-- | components/style/matching.rs | 4 | ||||
-rw-r--r-- | components/style/sharing/mod.rs | 71 | ||||
-rw-r--r-- | components/style/style_resolver.rs | 138 | ||||
-rw-r--r-- | components/style/traversal.rs | 8 | ||||
-rw-r--r-- | ports/geckolib/glue.rs | 6 |
6 files changed, 121 insertions, 117 deletions
diff --git a/components/style/data.rs b/components/style/data.rs index 575d18e55b0..8d9a982b897 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -284,13 +284,18 @@ impl ElementData { /// Returns this element's primary style as a resolved style to use for sharing. pub fn share_primary_style(&self) -> PrimaryStyle { - let primary_is_reused = self.flags.contains(PRIMARY_STYLE_REUSED_VIA_RULE_NODE); - PrimaryStyle(ResolvedStyle::new(self.styles.primary().clone(), primary_is_reused)) + let reused_via_rule_node = + self.flags.contains(PRIMARY_STYLE_REUSED_VIA_RULE_NODE); + + PrimaryStyle { + style: ResolvedStyle(self.styles.primary().clone()), + reused_via_rule_node, + } } /// Sets a new set of styles, returning the old ones. pub fn set_styles(&mut self, new_styles: ResolvedElementStyles) -> ElementStyles { - if new_styles.primary.0.reused_via_rule_node { + if new_styles.primary.reused_via_rule_node { self.flags.insert(PRIMARY_STYLE_REUSED_VIA_RULE_NODE); } else { self.flags.remove(PRIMARY_STYLE_REUSED_VIA_RULE_NODE); diff --git a/components/style/matching.rs b/components/style/matching.rs index 62777e70d96..826b5d3bfce 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -158,7 +158,7 @@ trait PrivateMatchMethods: TElement { StyleResolverForElement::new(*self, context, RuleInclusion::All, PseudoElementResolution::IfApplicable) .cascade_style_and_visited_with_default_parents(inputs); - Some(style.into()) + Some(style.0) } #[cfg(feature = "gecko")] @@ -542,7 +542,7 @@ pub trait MatchMethods : TElement { self.process_animations( context, &mut data.styles.primary, - &mut new_styles.primary.0.style, + &mut new_styles.primary.style.0, data.hint, important_rules_changed, ); diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index a9c6a06181d..83ad650c5e2 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -82,7 +82,7 @@ use smallvec::SmallVec; use std::marker::PhantomData; use std::mem; use std::ops::Deref; -use style_resolver::PrimaryStyle; +use style_resolver::{PrimaryStyle, ResolvedElementStyles}; use stylist::Stylist; mod checks; @@ -380,7 +380,7 @@ impl<E: TElement> StyleSharingTarget<E> { pub fn share_style_if_possible( &mut self, context: &mut StyleContext<E>, - ) -> Option<E> { + ) -> Option<ResolvedElementStyles> { let cache = &mut context.thread_local.sharing_cache; let shared_context = &context.shared; let selector_flags_map = &mut context.thread_local.selector_flags; @@ -443,25 +443,25 @@ impl<E: TElement> SharingCache<E> { self.entries.insert(StyleSharingCandidate { element, validation_data }); } - fn lookup<F>(&mut self, mut is_match: F) -> Option<E> + fn lookup<F, R>(&mut self, mut test_one: F) -> Option<R> where - F: FnMut(&mut StyleSharingCandidate<E>) -> bool + F: FnMut(&mut StyleSharingCandidate<E>) -> Option<R> { - let mut index = None; + let mut result = None; for (i, candidate) in self.entries.iter_mut() { - if is_match(candidate) { - index = Some(i); + if let Some(r) = test_one(candidate) { + result = Some((i, r)); break; } }; - match index { + match result { None => None, - Some(i) => { + Some((i, r)) => { self.entries.touch(i); let front = self.entries.front_mut().unwrap(); - debug_assert!(is_match(front)); - Some(front.element) + debug_assert!(test_one(front).is_some()); + Some(r) } } } @@ -620,7 +620,7 @@ impl<E: TElement> StyleSharingCache<E> { selector_flags_map: &mut SelectorFlagsMap<E>, bloom_filter: &StyleBloom<E>, target: &mut StyleSharingTarget<E>, - ) -> Option<E> { + ) -> Option<ResolvedElementStyles> { if shared_context.options.disable_style_sharing_cache { debug!("{:?} Cannot share style: style sharing cache disabled", target.element); @@ -655,42 +655,42 @@ impl<E: TElement> StyleSharingCache<E> { shared: &SharedStyleContext, bloom: &StyleBloom<E>, selector_flags_map: &mut SelectorFlagsMap<E> - ) -> bool { + ) -> Option<ResolvedElementStyles> { // Check that we have the same parent, or at least that the parents // share styles and permit sharing across their children. The latter // check allows us to share style between cousins if the parents // shared style. if !checks::parents_allow_sharing(target, candidate) { trace!("Miss: Parent"); - return false; + return None; } if target.is_native_anonymous() { debug_assert!(!candidate.element.is_native_anonymous(), "Why inserting NAC into the cache?"); trace!("Miss: Native Anonymous Content"); - return false; + return None; } if *target.get_local_name() != *candidate.element.get_local_name() { trace!("Miss: Local Name"); - return false; + return None; } if *target.get_namespace() != *candidate.element.get_namespace() { trace!("Miss: Namespace"); - return false; + return None; } if target.is_link() != candidate.element.is_link() { trace!("Miss: Link"); - return false; + return None; } if target.matches_user_and_author_rules() != candidate.element.matches_user_and_author_rules() { trace!("Miss: User and Author Rules"); - return false; + return None; } // We do not ignore visited state here, because Gecko @@ -698,7 +698,7 @@ impl<E: TElement> StyleSharingCache<E> { // so these contexts cannot be shared if target.element.get_state() != candidate.get_state() { trace!("Miss: User and Author State"); - return false; + return None; } let element_id = target.element.get_id(); @@ -708,38 +708,38 @@ impl<E: TElement> StyleSharingCache<E> { if checks::may_have_rules_for_ids(shared, element_id.as_ref(), candidate_id.as_ref()) { trace!("Miss: ID Attr"); - return false; + return None; } } if !checks::have_same_style_attribute(target, candidate) { trace!("Miss: Style Attr"); - return false; + return None; } if !checks::have_same_class(target, candidate) { trace!("Miss: Class"); - return false; + return None; } if !checks::have_same_presentational_hints(target, candidate) { trace!("Miss: Pres Hints"); - return false; + return None; } if !checks::revalidate(target, candidate, shared, bloom, selector_flags_map) { trace!("Miss: Revalidation"); - return false; + return None; } debug_assert!(target.has_current_styles_for_traversal( &candidate.element.borrow_data().unwrap(), - shared.traversal_flags) - ); - debug!("Sharing allowed between {:?} and {:?}", target.element, candidate.element); + shared.traversal_flags, + )); - true + debug!("Sharing allowed between {:?} and {:?}", target.element, candidate.element); + Some(candidate.element.borrow_data().unwrap().share_styles()) } /// Attempts to find an element in the cache with the given primary rule node and parent. @@ -749,18 +749,19 @@ impl<E: TElement> StyleSharingCache<E> { rules: &StrongRuleNode, visited_rules: Option<&StrongRuleNode>, target: E, - ) -> Option<E> { + ) -> Option<PrimaryStyle> { self.cache_mut().lookup(|candidate| { + debug_assert_ne!(candidate.element, target); if !candidate.parent_style_identity().eq(inherited) { - return false; + return None; } let data = candidate.element.borrow_data().unwrap(); let style = data.styles.primary(); if style.rules.as_ref() != Some(&rules) { - return false; + return None; } if style.visited_rules() != visited_rules { - return false; + return None; } // Rule nodes and styles are computed independent of the element's @@ -775,10 +776,10 @@ impl<E: TElement> StyleSharingCache<E> { // requirements of visited, assuming we get a cache hit on only one // of unvisited vs. visited. if target.is_visited_link() != candidate.element.is_visited_link() { - return false; + return None; } - true + Some(data.share_primary_style()) }) } } diff --git a/components/style/style_resolver.rs b/components/style/style_resolver.rs index 1a993e15662..41b7ed86237 100644 --- a/components/style/style_resolver.rs +++ b/components/style/style_resolver.rs @@ -50,16 +50,17 @@ struct MatchingResults { } /// A style returned from the resolver machinery. -pub struct ResolvedStyle { +pub struct ResolvedStyle(pub Arc<ComputedValues>); + +/// The primary style of an element or an element-backed pseudo-element. +pub struct PrimaryStyle { /// The style itself. - pub style: Arc<ComputedValues>, - /// Whether the style was reused from another element via the rule node. + pub style: ResolvedStyle, + /// Whether the style was reused from another element via the rule node (see + /// `StyleSharingCache::lookup_by_rules`). pub reused_via_rule_node: bool, } -/// The primary style of an element or an element-backed pseudo-element. -pub struct PrimaryStyle(pub ResolvedStyle); - /// A set of style returned from the resolver machinery. pub struct ResolvedElementStyles { /// Primary style. @@ -68,30 +69,17 @@ pub struct ResolvedElementStyles { pub pseudos: EagerPseudoStyles, } -impl ResolvedStyle { - /// Creates a new ResolvedStyle. - pub fn new(style: Arc<ComputedValues>, reused_via_rule_node: bool) -> Self { - ResolvedStyle { style, reused_via_rule_node } - } -} - impl PrimaryStyle { /// Convenience accessor for the style. pub fn style(&self) -> &ComputedValues { - &*self.0.style - } -} - -impl From<ResolvedStyle> for Arc<ComputedValues> { - fn from(r: ResolvedStyle) -> Arc<ComputedValues> { - r.style + &*self.style.0 } } impl From<ResolvedElementStyles> for ElementStyles { fn from(r: ResolvedElementStyles) -> ElementStyles { ElementStyles { - primary: Some(r.primary.0.into()), + primary: Some(r.primary.style.0), pseudos: r.pseudos, } } @@ -182,17 +170,54 @@ where None }; - PrimaryStyle( - self.cascade_style_and_visited( - CascadeInputs { - rules: Some(primary_results.rule_node), - visited_rules, - }, + self.cascade_primary_style( + CascadeInputs { + rules: Some(primary_results.rule_node), + visited_rules, + }, + parent_style, + layout_parent_style, + ) + } + + fn cascade_primary_style( + &mut self, + inputs: CascadeInputs, + parent_style: Option<&ComputedValues>, + layout_parent_style: Option<&ComputedValues>, + ) -> PrimaryStyle { + // Before doing the cascade, check the sharing cache and see if we can + // reuse the style via rule node identity. + let may_reuse = + !self.element.is_native_anonymous() && + parent_style.is_some() && + inputs.rules.is_some(); + + if may_reuse { + let cached = self.context.thread_local.sharing_cache.lookup_by_rules( + parent_style.unwrap(), + inputs.rules.as_ref().unwrap(), + inputs.visited_rules.as_ref(), + self.element, + ); + if let Some(mut primary_style) = cached { + self.context.thread_local.statistics.styles_reused += 1; + primary_style.reused_via_rule_node |= true; + return primary_style; + } + } + + // No style to reuse. Cascade the style, starting with visited style + // if necessary. + PrimaryStyle { + style: self.cascade_style_and_visited( + inputs, parent_style, layout_parent_style, /* pseudo = */ None, - ) - ) + ), + reused_via_rule_node: false, + } } /// Resolve the style of a given element, and all its eager pseudo-elements. @@ -222,10 +247,10 @@ where if let Some(style) = pseudo_style { if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) && - eager_pseudo_is_definitely_not_generated(&pseudo, &style) { + eager_pseudo_is_definitely_not_generated(&pseudo, &style.0) { return; } - pseudo_styles.set(&pseudo, style); + pseudo_styles.set(&pseudo, style.0); } }) } @@ -266,27 +291,6 @@ where layout_parent_style: Option<&ComputedValues>, pseudo: Option<&PseudoElement>, ) -> ResolvedStyle { - // Before doing the cascade, check the sharing cache and see if we can reuse - // the style via rule node identity. - let may_reuse = pseudo.is_none() && !self.element.is_native_anonymous() && - parent_style.is_some() && inputs.rules.is_some(); - if may_reuse { - let cached = self.context.thread_local.sharing_cache.lookup_by_rules( - parent_style.unwrap(), - inputs.rules.as_ref().unwrap(), - inputs.visited_rules.as_ref(), - self.element, - ); - if let Some(el) = cached { - self.context.thread_local.statistics.styles_reused += 1; - let mut style = el.borrow_data().unwrap().share_primary_style().0; - style.reused_via_rule_node = true; - return style; - } - } - - // No style to reuse. Cascade the style, starting with visited style - // if necessary. let mut style_if_visited = None; if parent_style.map_or(false, |s| s.get_visited_style().is_some()) || inputs.visited_rules.is_some() { @@ -300,17 +304,16 @@ where )); } - ResolvedStyle { - style: self.cascade_style( + ResolvedStyle( + self.cascade_style( inputs.rules.as_ref(), style_if_visited, parent_style, layout_parent_style, CascadeVisitedMode::Unvisited, pseudo, - ), - reused_via_rule_node: false, - } + ) + ) } /// Cascade the element and pseudo-element styles with the default parents. @@ -319,13 +322,10 @@ where inputs: ElementCascadeInputs, ) -> ResolvedElementStyles { with_default_parent_styles(self.element, move |parent_style, layout_parent_style| { - let primary_style = PrimaryStyle( - self.cascade_style_and_visited( - inputs.primary, - parent_style, - layout_parent_style, - /* pseudo = */ None, - ) + let primary_style = self.cascade_primary_style( + inputs.primary, + parent_style, + layout_parent_style, ); let mut pseudo_styles = EagerPseudoStyles::default(); @@ -344,17 +344,17 @@ where let style = self.cascade_style_and_visited( inputs, - Some(&*primary_style.style()), + Some(primary_style.style()), layout_parent_style_for_pseudo, Some(&pseudo), ); if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) && - eager_pseudo_is_definitely_not_generated(&pseudo, &style.style) { + eager_pseudo_is_definitely_not_generated(&pseudo, &style.0) { continue; } - pseudo_styles.set(&pseudo, style.style); + pseudo_styles.set(&pseudo, style.0); } } } @@ -371,7 +371,7 @@ where pseudo: &PseudoElement, originating_element_style: &PrimaryStyle, layout_parent_style: Option<&ComputedValues>, - ) -> Option<Arc<ComputedValues>> { + ) -> Option<ResolvedStyle> { let rules = self.match_pseudo( originating_element_style.style(), pseudo, @@ -399,7 +399,7 @@ where Some(originating_element_style.style()), layout_parent_style, Some(pseudo), - ).style) + )) } fn match_primary( diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 762e28644aa..1982589e985 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -421,7 +421,7 @@ where let is_display_contents = primary_style.style().is_display_contents(); - style = Some(primary_style.0.into()); + style = Some(primary_style.style.0); if !is_display_contents { layout_parent_style = style.clone(); } @@ -659,9 +659,9 @@ where // Now that our bloom filter is set up, try the style sharing // cache. match target.share_style_if_possible(context) { - Some(shareable_element) => { + Some(shared_styles) => { context.thread_local.statistics.styles_shared += 1; - shareable_element.borrow_data().unwrap().share_styles() + shared_styles } None => { context.thread_local.statistics.elements_matched += 1; @@ -738,7 +738,7 @@ where // number of eventual styles, but would potentially miss out on various // opportunities for skipping selector matching, which could hurt // performance. - if !new_styles.primary.0.reused_via_rule_node { + if !new_styles.primary.reused_via_rule_node { context.thread_local.sharing_cache.insert_if_possible( &element, &new_styles.primary, diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 049f7496df6..514ea445ae9 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -731,11 +731,9 @@ pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawSe }; // Actually `PseudoElementResolution` doesn't matter. - let style: Arc<ComputedValues> = - StyleResolverForElement::new(element, &mut context, RuleInclusion::All, PseudoElementResolution::IfApplicable) + StyleResolverForElement::new(element, &mut context, RuleInclusion::All, PseudoElementResolution::IfApplicable) .cascade_style_and_visited_with_default_parents(inputs) - .into(); - style.into() + .0.into() } #[no_mangle] |