From 364235ac0c60cfd1b1e109456843c1cf2065e71a Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Wed, 20 May 2020 15:44:17 +0200 Subject: [PATCH] Include animations and transitions in the cascade Instead of applying animations and transitions to styled elements, include them in the cascade. This allows them to interact properly with things like font-size and !important rules. --- components/layout_thread/dom_wrapper.rs | 64 ++++++- components/layout_thread_2020/dom_wrapper.rs | 64 ++++++- components/style/animation.rs | 164 ++++++++++++------ components/style/dom.rs | 26 +-- components/style/gecko/wrapper.rs | 24 +-- components/style/matching.rs | 79 +++++++-- .../style/properties/properties.mako.rs | 11 +- components/style/sharing/mod.rs | 7 +- components/style/style_resolver.rs | 2 +- components/style/traversal.rs | 2 + .../animation-base-response-003.html.ini | 4 - .../animation/opacity-interpolation.html.ini | 63 ------- .../non-rendered-element-001.html.ini | 4 - .../transition-base-response-003.html.ini | 4 - .../animation-base-response-003.html.ini | 4 - .../non-rendered-element-001.html.ini | 4 - .../transition-base-response-003.html.ini | 4 - 17 files changed, 314 insertions(+), 216 deletions(-) delete mode 100644 tests/wpt/metadata-layout-2020/css/css-animations/animation-base-response-003.html.ini delete mode 100644 tests/wpt/metadata-layout-2020/css/css-transitions/non-rendered-element-001.html.ini delete mode 100644 tests/wpt/metadata-layout-2020/css/css-transitions/transition-base-response-003.html.ini delete mode 100644 tests/wpt/metadata/css/css-animations/animation-base-response-003.html.ini delete mode 100644 tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini delete mode 100644 tests/wpt/metadata/css/css-transitions/transition-base-response-003.html.ini diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index edf51d848b9..b477a653188 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -464,6 +464,54 @@ impl<'le> TElement for ServoLayoutElement<'le> { } } + fn may_have_animations(&self) -> bool { + true + } + + fn animation_rule( + &self, + context: &SharedStyleContext, + ) -> Option>> { + let node = self.as_node(); + let document = node.owner_doc(); + context + .animation_states + .read() + .get(&node.opaque()) + .and_then(|set| { + set.get_value_map_for_active_animations(context.current_time_for_animations) + }) + .map(|map| { + Arc::new( + document + .style_shared_lock() + .wrap(PropertyDeclarationBlock::from_animation_value_map(&map)), + ) + }) + } + + fn transition_rule( + &self, + context: &SharedStyleContext, + ) -> Option>> { + let node = self.as_node(); + let document = node.owner_doc(); + context + .animation_states + .read() + .get(&node.opaque()) + .and_then(|set| { + set.get_value_map_for_active_transitions(context.current_time_for_animations) + }) + .map(|map| { + Arc::new( + document + .style_shared_lock() + .wrap(PropertyDeclarationBlock::from_animation_value_map(&map)), + ) + }) + } + fn state(&self) -> ElementState { self.element.get_state_for_layout() } @@ -581,11 +629,8 @@ impl<'le> TElement for ServoLayoutElement<'le> { self.element.has_selector_flags(flags) } - fn has_animations(&self) -> bool { - // We use this function not only for Gecko but also for Servo to know if this element has - // animations, so we maybe try to get the important rules of this element. This is used for - // off-main thread animations, but we don't support it on Servo, so return false directly. - false + fn has_animations(&self, context: &SharedStyleContext) -> bool { + return self.has_css_animations(context) || self.has_css_transitions(context); } fn has_css_animations(&self, context: &SharedStyleContext) -> bool { @@ -597,8 +642,13 @@ impl<'le> TElement for ServoLayoutElement<'le> { .unwrap_or(false) } - fn has_css_transitions(&self) -> bool { - unreachable!("this should be only called on gecko"); + fn has_css_transitions(&self, context: &SharedStyleContext) -> bool { + context + .animation_states + .read() + .get(&self.as_node().opaque()) + .map(|set| set.has_active_transition()) + .unwrap_or(false) } #[inline] diff --git a/components/layout_thread_2020/dom_wrapper.rs b/components/layout_thread_2020/dom_wrapper.rs index 926866b9e14..037b927539e 100644 --- a/components/layout_thread_2020/dom_wrapper.rs +++ b/components/layout_thread_2020/dom_wrapper.rs @@ -472,6 +472,54 @@ impl<'le> TElement for ServoLayoutElement<'le> { } } + fn may_have_animations(&self) -> bool { + true + } + + fn animation_rule( + &self, + context: &SharedStyleContext, + ) -> Option>> { + let node = self.as_node(); + let document = node.owner_doc(); + context + .animation_states + .read() + .get(&node.opaque()) + .and_then(|set| { + set.get_value_map_for_active_animations(context.current_time_for_animations) + }) + .map(|map| { + Arc::new( + document + .style_shared_lock() + .wrap(PropertyDeclarationBlock::from_animation_value_map(&map)), + ) + }) + } + + fn transition_rule( + &self, + context: &SharedStyleContext, + ) -> Option>> { + let node = self.as_node(); + let document = node.owner_doc(); + context + .animation_states + .read() + .get(&node.opaque()) + .and_then(|set| { + set.get_value_map_for_active_transitions(context.current_time_for_animations) + }) + .map(|map| { + Arc::new( + document + .style_shared_lock() + .wrap(PropertyDeclarationBlock::from_animation_value_map(&map)), + ) + }) + } + fn state(&self) -> ElementState { self.element.get_state_for_layout() } @@ -589,11 +637,8 @@ impl<'le> TElement for ServoLayoutElement<'le> { self.element.has_selector_flags(flags) } - fn has_animations(&self) -> bool { - // We use this function not only for Gecko but also for Servo to know if this element has - // animations, so we maybe try to get the important rules of this element. This is used for - // off-main thread animations, but we don't support it on Servo, so return false directly. - false + fn has_animations(&self, context: &SharedStyleContext) -> bool { + return self.has_css_animations(context) || self.has_css_transitions(context); } fn has_css_animations(&self, context: &SharedStyleContext) -> bool { @@ -605,8 +650,13 @@ impl<'le> TElement for ServoLayoutElement<'le> { .unwrap_or(false) } - fn has_css_transitions(&self) -> bool { - unreachable!("this should be only called on gecko"); + fn has_css_transitions(&self, context: &SharedStyleContext) -> bool { + context + .animation_states + .read() + .get(&self.as_node().opaque()) + .map(|set| set.has_active_transition()) + .unwrap_or(false) } #[inline] diff --git a/components/style/animation.rs b/components/style/animation.rs index d7601a8ed73..b107aaaf262 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -10,7 +10,7 @@ use crate::bezier::Bezier; use crate::context::{CascadeInputs, SharedStyleContext}; use crate::dom::{OpaqueNode, TDocument, TElement, TNode}; -use crate::properties::animated_properties::AnimationValue; +use crate::properties::animated_properties::{AnimationValue, AnimationValueMap}; use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection; use crate::properties::longhands::animation_fill_mode::computed_value::single_value::T as AnimationFillMode; use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState; @@ -127,13 +127,11 @@ impl PropertyAnimation { } /// Update the given animation at a given point of progress. - fn update(&self, style: &mut ComputedValues, progress: f64) { + fn calculate_value(&self, progress: f64) -> Result { let procedure = Procedure::Interpolate { progress: self.timing_function_output(progress), }; - if let Ok(new_value) = self.from.animate(&self.to, procedure) { - new_value.set_in_style_for_servo(style); - } + self.from.animate(&self.to, procedure) } } @@ -598,16 +596,14 @@ impl Animation { } } - /// Update the given style to reflect the values specified by this `Animation` - /// at the time provided by the given `SharedStyleContext`. - fn update_style(&self, context: &SharedStyleContext, style: &mut Arc) { + /// Fill in an `AnimationValueMap` with values calculated from this animation at + /// the given time value. + fn get_property_declaration_at_time(&self, now: f64, map: &mut AnimationValueMap) { let duration = self.duration; let started_at = self.started_at; let now = match self.state { - AnimationState::Running | AnimationState::Pending | AnimationState::Finished => { - context.current_time_for_animations - }, + AnimationState::Running | AnimationState::Pending | AnimationState::Finished => now, AnimationState::Paused(progress) => started_at + duration * progress, AnimationState::Canceled => return, }; @@ -666,7 +662,7 @@ impl Animation { } debug!( - "Animation::update_style: keyframe from {:?} to {:?}", + "Animation::get_property_declaration_at_time: keyframe from {:?} to {:?}", prev_keyframe_index, next_keyframe_index ); @@ -676,20 +672,19 @@ impl Animation { None => return, }; - let update_with_single_keyframe_style = |style, keyframe: &ComputedKeyframe| { - let mutable_style = Arc::make_mut(style); + let mut add_declarations_to_map = |keyframe: &ComputedKeyframe| { for value in keyframe.values.iter() { - value.set_in_style_for_servo(mutable_style); + map.insert(value.id(), value.clone()); } }; if total_progress <= 0.0 { - update_with_single_keyframe_style(style, &prev_keyframe); + add_declarations_to_map(&prev_keyframe); return; } if total_progress >= 1.0 { - update_with_single_keyframe_style(style, &next_keyframe); + add_declarations_to_map(&next_keyframe); return; } @@ -707,18 +702,18 @@ impl Animation { }; let relative_progress = (now - last_keyframe_ended_at) / relative_duration; - let mut new_style = (**style).clone(); for (from, to) in prev_keyframe.values.iter().zip(next_keyframe.values.iter()) { - PropertyAnimation { + let animation = PropertyAnimation { from: from.clone(), to: to.clone(), timing_function: prev_keyframe.timing_function, duration: relative_duration as f64, - } - .update(&mut new_style, relative_progress); - } + }; - *Arc::make_mut(style) = new_style; + if let Ok(value) = animation.calculate_value(relative_progress) { + map.insert(value.id(), value); + } + } } } @@ -799,7 +794,10 @@ impl Transition { // time of the style change event, times the reversing shortening // factor of the old transition // 2. 1 minus the reversing shortening factor of the old transition." - let transition_progress = replaced_transition.progress(now); + let transition_progress = ((now - replaced_transition.start_time) / + (replaced_transition.property_animation.duration)) + .min(1.0) + .max(0.0); let timing_function_output = replaced_animation.timing_function_output(transition_progress); let old_reversing_shortening_factor = replaced_transition.reversing_shortening_factor; self.reversing_shortening_factor = ((timing_function_output * @@ -845,25 +843,16 @@ impl Transition { time >= self.start_time + (self.property_animation.duration) } - /// Whether this animation has the same end value as another one. - #[inline] - fn progress(&self, now: f64) -> f64 { - let progress = (now - self.start_time) / (self.property_animation.duration); - progress.min(1.0) - } - - /// Update a style to the value specified by this `Transition` given a `SharedStyleContext`. - fn update_style(&self, context: &SharedStyleContext, style: &mut Arc) { - // Never apply canceled transitions to a style. - if self.state == AnimationState::Canceled { - return; + /// Update the given animation at a given point of progress. + pub fn calculate_value(&self, time: f64) -> Option { + let progress = (time - self.start_time) / (self.property_animation.duration); + if progress < 0.0 { + return None; } - let progress = self.progress(context.current_time_for_animations); - if progress >= 0.0 { - self.property_animation - .update(Arc::make_mut(style), progress); - } + self.property_animation + .calculate_value(progress.min(1.0)) + .ok() } } @@ -875,17 +864,30 @@ pub struct ElementAnimationSet { /// The transitions for this element. pub transitions: Vec, + + /// Whether or not this ElementAnimationSet has had animations or transitions + /// which have been added, removed, or had their state changed. + pub dirty: bool, } impl ElementAnimationSet { /// Cancel all animations in this `ElementAnimationSet`. This is typically called /// when the element has been removed from the DOM. pub fn cancel_all_animations(&mut self) { + self.dirty = !self.animations.is_empty(); for animation in self.animations.iter_mut() { animation.state = AnimationState::Canceled; } + self.cancel_active_transitions(); + } + + fn cancel_active_transitions(&mut self) { + self.dirty = !self.transitions.is_empty(); + for transition in self.transitions.iter_mut() { - transition.state = AnimationState::Canceled; + if transition.state != AnimationState::Finished { + transition.state = AnimationState::Canceled; + } } } @@ -894,12 +896,18 @@ impl ElementAnimationSet { context: &SharedStyleContext, style: &mut Arc, ) { - for animation in &self.animations { - animation.update_style(context, style); + let now = context.current_time_for_animations; + let mutable_style = Arc::make_mut(style); + if let Some(map) = self.get_value_map_for_active_animations(now) { + for value in map.values() { + value.set_in_style_for_servo(mutable_style); + } } - for transition in &self.transitions { - transition.update_style(context, style); + if let Some(map) = self.get_value_map_for_active_transitions(now) { + for value in map.values() { + value.set_in_style_for_servo(mutable_style); + } } } @@ -978,6 +986,7 @@ impl ElementAnimationSet { /// when appropriate. pub fn update_transitions_for_new_style( &mut self, + might_need_transitions_update: bool, context: &SharedStyleContext, opaque_node: OpaqueNode, old_style: Option<&Arc>, @@ -990,12 +999,18 @@ impl ElementAnimationSet { None => return, }; + // If the style of this element is display:none, then cancel all active transitions. + if after_change_style.get_box().clone_display().is_none() { + self.cancel_active_transitions(); + return; + } + + if !might_need_transitions_update { + return; + } + // We convert old values into `before-change-style` here. - // See https://drafts.csswg.org/css-transitions/#starting. We need to clone the - // style because this might still be a reference to the original `old_style` and - // we want to preserve that so that we can later properly calculate restyle damage. if self.has_active_transition() || self.has_active_animation() { - before_change_style = before_change_style.clone(); self.apply_active_animations(context, &mut before_change_style); } @@ -1016,6 +1031,7 @@ impl ElementAnimationSet { continue; } transition.state = AnimationState::Canceled; + self.dirty = true; } } @@ -1086,6 +1102,45 @@ impl ElementAnimationSet { } self.transitions.push(new_transition); + self.dirty = true; + } + + /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s + /// active transitions at the given time value. + pub fn get_value_map_for_active_transitions(&self, now: f64) -> Option { + if !self.has_active_transition() { + return None; + } + + let mut map = + AnimationValueMap::with_capacity_and_hasher(self.transitions.len(), Default::default()); + for transition in &self.transitions { + if transition.state == AnimationState::Canceled { + continue; + } + let value = match transition.calculate_value(now) { + Some(value) => value, + None => continue, + }; + map.insert(value.id(), value); + } + + Some(map) + } + + /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s + /// active animations at the given time value. + pub fn get_value_map_for_active_animations(&self, now: f64) -> Option { + if !self.has_active_animation() { + return None; + } + + let mut map = Default::default(); + for animation in &self.animations { + animation.get_property_declaration_at_time(now, &mut map); + } + + Some(map) } } @@ -1098,13 +1153,6 @@ pub fn start_transitions_if_applicable( new_style: &Arc, animation_state: &mut ElementAnimationSet, ) -> LonghandIdSet { - // If the style of this element is display:none, then we don't start any transitions - // and we cancel any currently running transitions by returning an empty LonghandIdSet. - let box_style = new_style.get_box(); - if box_style.clone_display().is_none() { - return LonghandIdSet::new(); - } - let mut properties_that_transition = LonghandIdSet::new(); for transition in new_style.transition_properties() { let physical_property = transition.longhand_id.to_physical(new_style.writing_mode); @@ -1214,6 +1262,8 @@ pub fn maybe_start_animations( is_new: true, }; + animation_state.dirty = true; + // If the animation was already present in the list for the node, just update its state. for existing_animation in animation_state.animations.iter_mut() { if existing_animation.state == AnimationState::Canceled { diff --git a/components/style/dom.rs b/components/style/dom.rs index 4e8d8ce9ca8..9b61cfbd3af 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -479,23 +479,25 @@ pub trait TElement: /// Get the combined animation and transition rules. /// /// FIXME(emilio): Is this really useful? - fn animation_rules(&self) -> AnimationRules { + fn animation_rules(&self, context: &SharedStyleContext) -> AnimationRules { if !self.may_have_animations() { return AnimationRules(None, None); } - AnimationRules(self.animation_rule(), self.transition_rule()) + AnimationRules(self.animation_rule(context), self.transition_rule(context)) } /// Get this element's animation rule. - fn animation_rule(&self) -> Option>> { - None - } + fn animation_rule( + &self, + _: &SharedStyleContext, + ) -> Option>>; /// Get this element's transition rule. - fn transition_rule(&self) -> Option>> { - None - } + fn transition_rule( + &self, + context: &SharedStyleContext, + ) -> Option>>; /// Get this element's state, for non-tree-structural pseudos. fn state(&self) -> ElementState; @@ -729,9 +731,7 @@ pub trait TElement: /// In Gecko, element has a flag that represents the element may have /// any type of animations or not to bail out animation stuff early. /// Whereas Servo doesn't have such flag. - fn may_have_animations(&self) -> bool { - false - } + fn may_have_animations(&self) -> bool; /// Creates a task to update various animation state on a given (pseudo-)element. #[cfg(feature = "gecko")] @@ -748,14 +748,14 @@ pub trait TElement: /// Returns true if the element has relevant animations. Relevant /// animations are those animations that are affecting the element's style /// or are scheduled to do so in the future. - fn has_animations(&self) -> bool; + fn has_animations(&self, context: &SharedStyleContext) -> bool; /// Returns true if the element has a CSS animation. fn has_css_animations(&self, context: &SharedStyleContext) -> bool; /// Returns true if the element has a CSS transition (including running transitions and /// completed transitions). - fn has_css_transitions(&self) -> bool; + fn has_css_transitions(&self, context: &SharedStyleContext) -> bool; /// Returns true if the element has animation restyle hints. fn has_animation_restyle_hints(&self) -> bool { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index fd119d93c6c..4d680c3ff02 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -1524,32 +1524,10 @@ impl<'le> TElement for GeckoElement<'le> { self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) } } - fn has_css_transitions(&self) -> bool { + fn has_css_transitions(&self, _: &SharedStyleContext) -> bool { self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) } } - fn might_need_transitions_update( - &self, - old_style: Option<&ComputedValues>, - new_style: &ComputedValues, - ) -> bool { - let old_style = match old_style { - Some(v) => v, - None => return false, - }; - - let new_box_style = new_style.get_box(); - if !self.has_css_transitions() && !new_box_style.specifies_transitions() { - return false; - } - - if new_box_style.clone_display().is_none() || old_style.clone_display().is_none() { - return false; - } - - return true; - } - // Detect if there are any changes that require us to update transitions. // This is used as a more thoroughgoing check than the, cheaper // might_need_transitions_update check. diff --git a/components/style/matching.rs b/components/style/matching.rs index 3e0c00aaa88..fda77cc2685 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -8,7 +8,7 @@ #![deny(missing_docs)] use crate::computed_value_flags::ComputedValueFlags; -use crate::context::{ElementCascadeInputs, QuirksMode, SelectorFlagsMap}; +use crate::context::{CascadeInputs, ElementCascadeInputs, QuirksMode, SelectorFlagsMap}; use crate::context::{SharedStyleContext, StyleContext}; use crate::data::ElementData; use crate::dom::TElement; @@ -176,7 +176,9 @@ trait PrivateMatchMethods: TElement { if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) { replace_rule_node( CascadeLevel::Transitions, - self.transition_rule().as_ref().map(|a| a.borrow_arc()), + self.transition_rule(&context.shared) + .as_ref() + .map(|a| a.borrow_arc()), primary_rules, ); } @@ -184,7 +186,9 @@ trait PrivateMatchMethods: TElement { if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) { replace_rule_node( CascadeLevel::Animations, - self.animation_rule().as_ref().map(|a| a.borrow_arc()), + self.animation_rule(&context.shared) + .as_ref() + .map(|a| a.borrow_arc()), primary_rules, ); } @@ -194,14 +198,11 @@ trait PrivateMatchMethods: TElement { } /// If there is no transition rule in the ComputedValues, it returns None. - #[cfg(feature = "gecko")] fn after_change_style( &self, context: &mut StyleContext, primary_style: &Arc, ) -> Option> { - use crate::context::CascadeInputs; - let rule_node = primary_style.rules(); let without_transition_rules = context .shared @@ -314,6 +315,29 @@ trait PrivateMatchMethods: TElement { false } + fn might_need_transitions_update( + &self, + context: &StyleContext, + old_style: Option<&ComputedValues>, + new_style: &ComputedValues, + ) -> bool { + let old_style = match old_style { + Some(v) => v, + None => return false, + }; + + let new_box_style = new_style.get_box(); + if !self.has_css_transitions(context.shared) && !new_box_style.specifies_transitions() { + return false; + } + + if new_box_style.clone_display().is_none() || old_style.clone_display().is_none() { + return false; + } + + return true; + } + /// Create a SequentialTask for resolving descendants in a SMIL display /// property animation if the display property changed from none. #[cfg(feature = "gecko")] @@ -374,10 +398,12 @@ trait PrivateMatchMethods: TElement { tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS); } - let before_change_style = if self - .might_need_transitions_update(old_values.as_ref().map(|s| &**s), new_values) - { - let after_change_style = if self.has_css_transitions() { + let before_change_style = if self.might_need_transitions_update( + context, + old_values.as_ref().map(|s| &**s), + new_values, + ) { + let after_change_style = if self.has_css_transitions(context.shared) { self.after_change_style(context, new_values) } else { None @@ -442,6 +468,16 @@ trait PrivateMatchMethods: TElement { // map because this call will do a RwLock::read(). let needs_animations_update = self.needs_animations_update(context, old_values.as_ref().map(|s| &**s), new_values); + let might_need_transitions_update = self.might_need_transitions_update( + context, + old_values.as_ref().map(|s| &**s), + new_values, + ); + + let mut after_change_style = None; + if might_need_transitions_update { + after_change_style = self.after_change_style(context, new_values); + } let this_opaque = self.as_node().opaque(); let shared_context = context.shared; @@ -471,28 +507,43 @@ trait PrivateMatchMethods: TElement { } animation_set.update_transitions_for_new_style( + might_need_transitions_update, &shared_context, this_opaque, old_values.as_ref(), - new_values, + after_change_style.as_ref().unwrap_or(new_values), ); - animation_set.apply_active_animations(shared_context, new_values); - // We clear away any finished transitions, but retain animations, because they - // might still be used for proper calculation of `animation-fill-mode`. + // might still be used for proper calculation of `animation-fill-mode`. This + // should change the computed values in the style, so we don't need to mark + // this set as dirty. animation_set .transitions .retain(|transition| transition.state != AnimationState::Finished); // If the ElementAnimationSet is empty, and don't store it in order to // save memory and to avoid extra processing later. + let changed_animations = animation_set.dirty; if !animation_set.is_empty() { + animation_set.dirty = false; shared_context .animation_states .write() .insert(this_opaque, animation_set); } + + // If we have modified animation or transitions, we recascade style for this node. + if changed_animations { + let mut resolver = StyleResolverForElement::new( + *self, + context, + RuleInclusion::All, + PseudoElementResolution::IfApplicable, + ); + let new_primary = resolver.resolve_style_with_default_parents(); + *new_values = new_primary.primary.style.0; + } } /// Computes and applies non-redundant damage. diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index a098534793f..619b5b2fc54 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2828,11 +2828,12 @@ pub mod style_structs { /// Returns whether there are any transitions specified. #[cfg(feature = "servo")] pub fn specifies_transitions(&self) -> bool { - // TODO(mrobinson): This should check the combined duration and not just - // the duration. - self.transition_duration_iter() - .take(self.transition_property_count()) - .any(|t| t.seconds() > 0.) + (0..self.transition_property_count()).any(|index| { + let combined_duration = + self.transition_duration_mod(index).seconds().max(0.) + + self.transition_delay_mod(index).seconds(); + combined_duration > 0. + }) } /// Returns true if animation properties are equal between styles, but without diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index 7f85104cb7c..fab21901bd9 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -587,6 +587,7 @@ impl StyleSharingCache { style: &PrimaryStyle, validation_data_holder: Option<&mut StyleSharingTarget>, dom_depth: usize, + shared_context: &SharedStyleContext, ) { let parent = match element.traversal_parent() { Some(element) => element, @@ -619,7 +620,7 @@ impl StyleSharingCache { // * Our computed style can still be affected by animations after we no // longer match any animation rules, since removing animations involves // a sequential task and an additional traversal. - if element.has_animations() { + if element.has_animations(shared_context) { debug!("Failing to insert to the cache: running animations"); return; } @@ -700,6 +701,7 @@ impl StyleSharingCache { bloom_filter, nth_index_cache, selector_flags_map, + shared_context, ) }) } @@ -711,6 +713,7 @@ impl StyleSharingCache { bloom: &StyleBloom, nth_index_cache: &mut NthIndexCache, selector_flags_map: &mut SelectorFlagsMap, + shared_context: &SharedStyleContext, ) -> Option { debug_assert!(!target.is_in_native_anonymous_subtree()); @@ -770,7 +773,7 @@ impl StyleSharingCache { return None; } - if target.element.has_animations() { + if target.element.has_animations(shared_context) { trace!("Miss: Has Animations"); return None; } diff --git a/components/style/style_resolver.rs b/components/style/style_resolver.rs index f3f2c6b41b7..bb4a1c8f77b 100644 --- a/components/style/style_resolver.rs +++ b/components/style/style_resolver.rs @@ -433,7 +433,7 @@ where implemented_pseudo.as_ref(), self.element.style_attribute(), self.element.smil_override(), - self.element.animation_rules(), + self.element.animation_rules(self.context.shared), self.rule_inclusion, &mut applicable_declarations, &mut matching_context, diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 48e42656e86..a60faaa446e 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -613,6 +613,7 @@ where &new_styles.primary, Some(&mut target), traversal_data.current_dom_depth, + &context.shared, ); new_styles @@ -669,6 +670,7 @@ where &new_styles.primary, None, traversal_data.current_dom_depth, + &context.shared, ); } diff --git a/tests/wpt/metadata-layout-2020/css/css-animations/animation-base-response-003.html.ini b/tests/wpt/metadata-layout-2020/css/css-animations/animation-base-response-003.html.ini deleted file mode 100644 index 261190d6a21..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-animations/animation-base-response-003.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[animation-base-response-003.html] - [Identical elements are all responsive to font-size animation] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/css/css-color/animation/opacity-interpolation.html.ini b/tests/wpt/metadata-layout-2020/css/css-color/animation/opacity-interpolation.html.ini index 4b117661081..864bb88190e 100644 --- a/tests/wpt/metadata-layout-2020/css/css-color/animation/opacity-interpolation.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-color/animation/opacity-interpolation.html.ini @@ -89,66 +89,3 @@ [Web Animations: property from [initial\] to [0.2\] at (0.3) should be [0.76\]] expected: FAIL - [CSS Transitions with transition: all: property from [inherit\] to [0.2\] at (1.5) should be [0\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [initial\] to [0.2\] at (1.5) should be [0\]] - expected: FAIL - - [CSS Transitions: property from [inherit\] to [0.2\] at (1.5) should be [0\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [unset\] to [0.2\] at (1.5) should be [0\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [0\] to [1\] at (-0.3) should be [0\]] - expected: FAIL - - [CSS Animations: property from [inherit\] to [0.2\] at (1.5) should be [0\]] - expected: FAIL - - [CSS Transitions: property from [initial\] to [0.2\] at (1.5) should be [0\]] - expected: FAIL - - [CSS Animations: property from [unset\] to [0.2\] at (-0.3) should be [1\]] - expected: FAIL - - [CSS Transitions: property from [unset\] to [0.2\] at (1.5) should be [0\]] - expected: FAIL - - [CSS Animations: property from [0\] to [1\] at (1.5) should be [1\]] - expected: FAIL - - [CSS Animations: property from [initial\] to [0.2\] at (1.5) should be [0\]] - expected: FAIL - - [CSS Animations: property from [0\] to [1\] at (-0.3) should be [0\]] - expected: FAIL - - [CSS Transitions: property from [initial\] to [0.2\] at (-0.3) should be [1\]] - expected: FAIL - - [CSS Animations: property from [unset\] to [0.2\] at (1.5) should be [0\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [initial\] to [0.2\] at (-0.3) should be [1\]] - expected: FAIL - - [CSS Transitions: property from [0\] to [1\] at (-0.3) should be [0\]] - expected: FAIL - - [CSS Transitions: property from [unset\] to [0.2\] at (-0.3) should be [1\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [unset\] to [0.2\] at (-0.3) should be [1\]] - expected: FAIL - - [CSS Animations: property from [initial\] to [0.2\] at (-0.3) should be [1\]] - expected: FAIL - - [CSS Transitions: property from [0\] to [1\] at (1.5) should be [1\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [0\] to [1\] at (1.5) should be [1\]] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/css/css-transitions/non-rendered-element-001.html.ini b/tests/wpt/metadata-layout-2020/css/css-transitions/non-rendered-element-001.html.ini deleted file mode 100644 index 545eae021e5..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transitions/non-rendered-element-001.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[non-rendered-element-001.html] - [Transitions do not run for an element newly rendered] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/css/css-transitions/transition-base-response-003.html.ini b/tests/wpt/metadata-layout-2020/css/css-transitions/transition-base-response-003.html.ini deleted file mode 100644 index 28a4e5febe6..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transitions/transition-base-response-003.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[transition-base-response-003.html] - [Identical elements are all responsive to font-size transition] - expected: FAIL - diff --git a/tests/wpt/metadata/css/css-animations/animation-base-response-003.html.ini b/tests/wpt/metadata/css/css-animations/animation-base-response-003.html.ini deleted file mode 100644 index 261190d6a21..00000000000 --- a/tests/wpt/metadata/css/css-animations/animation-base-response-003.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[animation-base-response-003.html] - [Identical elements are all responsive to font-size animation] - expected: FAIL - diff --git a/tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini b/tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini deleted file mode 100644 index 545eae021e5..00000000000 --- a/tests/wpt/metadata/css/css-transitions/non-rendered-element-001.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[non-rendered-element-001.html] - [Transitions do not run for an element newly rendered] - expected: FAIL - diff --git a/tests/wpt/metadata/css/css-transitions/transition-base-response-003.html.ini b/tests/wpt/metadata/css/css-transitions/transition-base-response-003.html.ini deleted file mode 100644 index 28a4e5febe6..00000000000 --- a/tests/wpt/metadata/css/css-transitions/transition-base-response-003.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[transition-base-response-003.html] - [Identical elements are all responsive to font-size transition] - expected: FAIL -