aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/matching.rs169
1 files changed, 85 insertions, 84 deletions
diff --git a/components/style/matching.rs b/components/style/matching.rs
index 24334512cf0..18d0623c0e3 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -486,6 +486,14 @@ pub enum StyleSharingResult<ConcreteRestyleDamage: TRestyleDamage> {
StyleWasShared(usize, ConcreteRestyleDamage, RestyleResult),
}
+// Callers need to pass several boolean flags to cascade_node_pseudo_element.
+// We encapsulate them in this struct to avoid mixing them up.
+struct CascadeBooleans {
+ shareable: bool,
+ cacheable: bool,
+ animate: bool,
+}
+
trait PrivateMatchMethods: TNode {
/// Actually cascades style for a node or a pseudo-element of a node.
///
@@ -494,27 +502,24 @@ trait PrivateMatchMethods: TNode {
fn cascade_node_pseudo_element<'a, Ctx>(&self,
context: &Ctx,
parent_style: Option<&Arc<ComputedValues>>,
+ old_style: Option<&Arc<ComputedValues>>,
applicable_declarations: &[ApplicableDeclarationBlock],
- mut old_style: Option<&mut Arc<ComputedValues>>,
applicable_declarations_cache:
&mut ApplicableDeclarationsCache,
- shareable: bool,
- animate_properties: bool)
+ booleans: CascadeBooleans)
-> Arc<ComputedValues>
where Ctx: StyleContext<'a>
{
+ let mut cacheable = booleans.cacheable;
+ let shared_context = context.shared_context();
+
// Don’t cache applicable declarations for elements with a style attribute.
// Since the style attribute contributes to that set, no other element would have the same set
// and the cache would not be effective anyway.
// This also works around the test failures at
// https://github.com/servo/servo/pull/13459#issuecomment-250717584
let has_style_attribute = self.as_element().map_or(false, |e| e.style_attribute().is_some());
- let mut cacheable = !has_style_attribute;
- let shared_context = context.shared_context();
- if animate_properties {
- cacheable = !self.update_animations_for_cascade(shared_context,
- &mut old_style) && cacheable;
- }
+ cacheable = cacheable && !has_style_attribute;
let mut cascade_info = CascadeInfo::new();
let (this_style, is_cacheable) = match parent_style {
@@ -527,7 +532,7 @@ trait PrivateMatchMethods: TNode {
cascade(shared_context.viewport_size,
applicable_declarations,
- shareable,
+ booleans.shareable,
Some(&***parent_style),
cached_computed_values,
Some(&mut cascade_info),
@@ -536,7 +541,7 @@ trait PrivateMatchMethods: TNode {
None => {
cascade(shared_context.viewport_size,
applicable_declarations,
- shareable,
+ booleans.shareable,
None,
None,
Some(&mut cascade_info),
@@ -549,7 +554,7 @@ trait PrivateMatchMethods: TNode {
let mut this_style = Arc::new(this_style);
- if animate_properties {
+ if booleans.animate {
let new_animations_sender = &context.local_context().new_animations_sender;
let this_opaque = self.opaque();
// Trigger any present animations if necessary.
@@ -585,13 +590,7 @@ trait PrivateMatchMethods: TNode {
fn update_animations_for_cascade(&self,
context: &SharedStyleContext,
- style: &mut Option<&mut Arc<ComputedValues>>)
- -> bool {
- let style = match *style {
- None => return false,
- Some(ref mut style) => style,
- };
-
+ style: &mut Arc<ComputedValues>) -> bool {
// Finish any expired transitions.
let this_opaque = self.opaque();
let had_animations_to_expire =
@@ -909,18 +908,33 @@ pub trait MatchMethods : TNode {
let (damage, restyle_result) = {
let mut data_ref = self.mutate_data().unwrap();
let mut data = &mut *data_ref;
- let final_style =
- self.cascade_node_pseudo_element(context, parent_style,
+
+ // Compute the parameters for the cascade.
+ let mut old_style = data.style.clone();
+ let cacheable = match old_style {
+ None => true,
+ Some(ref mut old) => {
+ // Update animations before the cascade. This may modify
+ // the value of old_style.
+ !self.update_animations_for_cascade(context.shared_context(), old)
+ },
+ };
+ let shareable = applicable_declarations.normal_shareable;
+
+
+ let new_style =
+ self.cascade_node_pseudo_element(context, parent_style, old_style.as_ref(),
&applicable_declarations.normal,
- data.style.as_mut(),
&mut applicable_declarations_cache,
- applicable_declarations.normal_shareable,
- /* should_animate = */ true);
+ CascadeBooleans {
+ shareable: shareable,
+ cacheable: cacheable,
+ animate: true,
+ });
let (damage, restyle_result) =
- self.compute_damage_and_cascade_pseudos(final_style,
- data,
- context,
+ self.compute_damage_and_cascade_pseudos(new_style, old_style.as_ref(),
+ data, context,
applicable_declarations,
&mut applicable_declarations_cache);
@@ -942,6 +956,7 @@ pub trait MatchMethods : TNode {
fn compute_damage_and_cascade_pseudos<'a, Ctx>(&self,
final_style: Arc<ComputedValues>,
+ old_style: Option<&Arc<ComputedValues>>,
data: &mut PersistentStyleData,
context: &Ctx,
applicable_declarations: &ApplicableDeclarations,
@@ -955,7 +970,7 @@ pub trait MatchMethods : TNode {
// restyle hint.
let this_display = final_style.get_box().clone_display();
if this_display == display::T::none {
- let old_display = data.style.as_ref().map(|old_style| {
+ let old_display = old_style.map(|old_style| {
old_style.get_box().clone_display()
});
@@ -978,7 +993,7 @@ pub trait MatchMethods : TNode {
// Otherwise, we just compute the damage normally, and sum up the damage
// related to pseudo-elements.
let mut damage =
- self.compute_restyle_damage(data.style.as_ref(), &final_style, None);
+ self.compute_restyle_damage(old_style, &final_style, None);
data.style = Some(final_style);
@@ -989,72 +1004,58 @@ pub trait MatchMethods : TNode {
let rebuild_and_reflow =
Self::ConcreteRestyleDamage::rebuild_and_reflow();
+ let no_damage = Self::ConcreteRestyleDamage::empty();
<Self::ConcreteElement as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
- use std::collections::hash_map::Entry;
-
let applicable_declarations_for_this_pseudo =
applicable_declarations.per_pseudo.get(&pseudo).unwrap();
let has_declarations =
!applicable_declarations_for_this_pseudo.is_empty();
- // If there are declarations matching, we're going to need to
- // recompute the style anyway, so do it now to simplify the logic
- // below.
- let pseudo_style_if_declarations = if has_declarations {
- // NB: Transitions and animations should only work for
- // pseudo-elements ::before and ::after
- let should_animate_properties =
- <Self::ConcreteElement as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
-
- Some(self.cascade_node_pseudo_element(context,
- new_style,
- &*applicable_declarations_for_this_pseudo,
- data_per_pseudo.get_mut(&pseudo),
- &mut applicable_declarations_cache,
- /* shareable = */ false,
- should_animate_properties))
- } else {
- None
- };
-
- // Let's see what we had before.
- match data_per_pseudo.entry(pseudo.clone()) {
- Entry::Vacant(vacant_entry) => {
- // If we had a vacant entry, and no rules that match, we're
- // fine so far.
- if !has_declarations {
- return;
- }
+ // The old entry will be replaced. Remove it from the map but keep
+ // it for analysis.
+ let mut old_pseudo_style = data_per_pseudo.remove(&pseudo);
+
+ if has_declarations {
+ // We have declarations, so we need to cascade. Compute parameters.
+ let animate = <Self::ConcreteElement as MatchAttr>::Impl
+ ::pseudo_is_before_or_after(&pseudo);
+ let cacheable = if animate && old_pseudo_style.is_some() {
+ // Update animations before the cascade. This may modify
+ // the value of old_pseudo_style.
+ !self.update_animations_for_cascade(context.shared_context(),
+ old_pseudo_style.as_mut().unwrap())
+ } else {
+ true
+ };
- // Otherwise, we need to insert the new computed styles, and
- // generate a rebuild_and_reflow damage.
- damage = damage | Self::ConcreteRestyleDamage::rebuild_and_reflow();
- vacant_entry.insert(pseudo_style_if_declarations.unwrap());
+ let new_pseudo_style =
+ self.cascade_node_pseudo_element(context, new_style, old_pseudo_style.as_ref(),
+ &*applicable_declarations_for_this_pseudo,
+ &mut applicable_declarations_cache,
+ CascadeBooleans {
+ shareable: false,
+ cacheable: cacheable,
+ animate: animate,
+ });
+
+ // Compute restyle damage unless we've already maxed it out.
+ if damage != rebuild_and_reflow {
+ damage = damage | match old_pseudo_style {
+ None => rebuild_and_reflow,
+ Some(ref old) => self.compute_restyle_damage(Some(old), &new_pseudo_style,
+ Some(&pseudo)),
+ };
}
- Entry::Occupied(mut occupied_entry) => {
- // If there was an existing style, and no declarations, we
- // need to remove us from the map, and ensure we're
- // reconstructing.
- if !has_declarations {
- damage = damage | Self::ConcreteRestyleDamage::rebuild_and_reflow();
- occupied_entry.remove();
- return;
- }
- // If there's a new style, we need to diff it and add the
- // damage, except if the damage was already
- // rebuild_and_reflow, in which case we can avoid it.
- if damage != rebuild_and_reflow {
- damage = damage |
- self.compute_restyle_damage(Some(occupied_entry.get()),
- pseudo_style_if_declarations.as_ref().unwrap(),
- Some(&pseudo));
- }
-
- // And now, of course, use the new style.
- occupied_entry.insert(pseudo_style_if_declarations.unwrap());
+ // Insert the new entry into the map.
+ let existing = data_per_pseudo.insert(pseudo, new_pseudo_style);
+ debug_assert!(existing.is_none());
+ } else {
+ damage = damage | match old_pseudo_style {
+ Some(_) => rebuild_and_reflow,
+ None => no_damage,
}
}
});