style: Expire keyframes animations when no longer referenced by the style.

It's a long way to make this sound in general...

Fixes #20731
This commit is contained in:
Emilio Cobos Álvarez 2018-05-05 19:15:59 +02:00
parent 1ba57cbaad
commit a949e9e1e8
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
2 changed files with 46 additions and 25 deletions

View file

@ -630,13 +630,31 @@ pub fn update_style_for_animation_frame(
true true
} }
/// Returns the kind of animation update that happened.
pub enum AnimationUpdate {
/// The style was successfully updated, the animation is still running.
Regular,
/// A style change canceled this animation.
AnimationCanceled,
}
/// Updates a single animation and associated style based on the current time. /// Updates a single animation and associated style based on the current time.
///
/// FIXME(emilio): This doesn't handle any kind of dynamic change to the
/// animation or transition properties in any reasonable way.
///
/// This should probably be split in two, one from updating animations and
/// transitions in response to a style change (that is,
/// consider_starting_transitions + maybe_start_animations, but handling
/// canceled animations, duration changes, etc, there instead of here), and this
/// function should be only about the style update in response of a transition.
pub fn update_style_for_animation<E>( pub fn update_style_for_animation<E>(
context: &SharedStyleContext, context: &SharedStyleContext,
animation: &Animation, animation: &Animation,
style: &mut Arc<ComputedValues>, style: &mut Arc<ComputedValues>,
font_metrics_provider: &FontMetricsProvider, font_metrics_provider: &FontMetricsProvider,
) where ) -> AnimationUpdate
where
E: TElement, E: TElement,
{ {
debug!("update_style_for_animation: entering"); debug!("update_style_for_animation: entering");
@ -652,6 +670,12 @@ pub fn update_style_for_animation<E>(
if updated_style { if updated_style {
*style = new_style *style = new_style
} }
// FIXME(emilio): Should check before updating the style that the
// transition_property still transitions this, or bail out if not.
//
// Or doing it in process_animations, only if transition_property
// changed somehow (even better).
AnimationUpdate::Regular
}, },
Animation::Keyframes(_, ref animation, ref name, ref state) => { Animation::Keyframes(_, ref animation, ref name, ref state) => {
debug!( debug!(
@ -675,28 +699,18 @@ pub fn update_style_for_animation<E>(
let index = match maybe_index { let index = match maybe_index {
Some(index) => index, Some(index) => index,
None => { None => return AnimationUpdate::AnimationCanceled,
warn!(
"update_style_for_animation: Animation {:?} not found in style",
name
);
return;
},
}; };
let total_duration = style.get_box().animation_duration_mod(index).seconds() as f64; let total_duration = style.get_box().animation_duration_mod(index).seconds() as f64;
if total_duration == 0. { if total_duration == 0. {
debug!( return AnimationUpdate::AnimationCanceled;
"update_style_for_animation: zero duration for animation {:?}",
name
);
return;
} }
let mut total_progress = (now - started_at) / total_duration; let mut total_progress = (now - started_at) / total_duration;
if total_progress < 0. { if total_progress < 0. {
warn!("Negative progress found for animation {:?}", name); warn!("Negative progress found for animation {:?}", name);
return; return AnimationUpdate::Regular;
} }
if total_progress > 1. { if total_progress > 1. {
total_progress = 1.; total_progress = 1.;
@ -748,11 +762,7 @@ pub fn update_style_for_animation<E>(
let target_keyframe = match target_keyframe_position { let target_keyframe = match target_keyframe_position {
Some(target) => &animation.steps[target], Some(target) => &animation.steps[target],
None => { None => return AnimationUpdate::Regular,
warn!("update_style_for_animation: No current keyframe found for animation \"{}\" at progress {}",
name, total_progress);
return;
},
}; };
let last_keyframe = &animation.steps[last_keyframe_position]; let last_keyframe = &animation.steps[last_keyframe_position];
@ -836,6 +846,7 @@ pub fn update_style_for_animation<E>(
name name
); );
*style = new_style; *style = new_style;
AnimationUpdate::Regular
}, },
} }
} }

View file

@ -583,7 +583,7 @@ trait PrivateMatchMethods: TElement {
possibly_expired_animations: &mut Vec<::animation::PropertyAnimation>, possibly_expired_animations: &mut Vec<::animation::PropertyAnimation>,
font_metrics: &::font_metrics::FontMetricsProvider, font_metrics: &::font_metrics::FontMetricsProvider,
) { ) {
use animation::{self, Animation}; use animation::{self, Animation, AnimationUpdate};
use dom::TNode; use dom::TNode;
// Finish any expired transitions. // Finish any expired transitions.
@ -601,7 +601,7 @@ trait PrivateMatchMethods: TElement {
} }
let mut all_running_animations = context.running_animations.write(); let mut all_running_animations = context.running_animations.write();
for running_animation in all_running_animations.get_mut(&this_opaque).unwrap() { for mut running_animation in all_running_animations.get_mut(&this_opaque).unwrap() {
// This shouldn't happen frequently, but under some circumstances // This shouldn't happen frequently, but under some circumstances
// mainly huge load or debug builds, the constellation might be // mainly huge load or debug builds, the constellation might be
// delayed in sending the `TickAllAnimations` message to layout. // delayed in sending the `TickAllAnimations` message to layout.
@ -616,16 +616,26 @@ trait PrivateMatchMethods: TElement {
continue; continue;
} }
animation::update_style_for_animation::<Self>( let update = animation::update_style_for_animation::<Self>(
context, context,
running_animation, &mut running_animation,
style, style,
font_metrics, font_metrics,
); );
if let Animation::Transition(_, _, ref frame) = *running_animation { match *running_animation {
Animation::Transition(_, _, ref frame) => {
possibly_expired_animations.push(frame.property_animation.clone()) possibly_expired_animations.push(frame.property_animation.clone())
} }
Animation::Keyframes(_, _, _, ref mut state) => {
match update {
AnimationUpdate::Regular => {},
AnimationUpdate::AnimationCanceled => {
state.expired = true;
}
}
}
}
} }
} }
} }