diff --git a/components/layout/animation.rs b/components/layout/animation.rs index b8e5bed96d4..42a71206522 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -4,6 +4,7 @@ //! CSS transitions and animations. +use context::SharedLayoutContext; use flow::{self, Flow}; use gfx::display_list::OpaqueNode; use ipc_channel::ipc::IpcSender; @@ -13,7 +14,7 @@ use script_traits::{AnimationState, LayoutMsg as ConstellationMsg}; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::sync::mpsc::Receiver; -use style::animation::{Animation, update_style_for_animation}; +use style::animation::{Animation, KeyframesAnimationState, KeyframesIterationState, update_style_for_animation}; use time; /// Processes any new animations that were discovered after style recalculation. @@ -39,15 +40,33 @@ pub fn update_animation_state(constellation_chan: &IpcSender, let mut keys_to_remove = vec![]; for (key, running_animations) in running_animations.iter_mut() { let mut animations_still_running = vec![]; - for running_animation in running_animations.drain(..) { - if now < running_animation.end_time { + for mut running_animation in running_animations.drain(..) { + let still_running = match running_animation { + Animation::Transition(_, started_at, ref frame) => { + now < started_at + frame.duration + } + Animation::Keyframes(_, _, ref mut state) => { + // This animation is still running, or we need to keep + // iterating. + now < state.started_at + state.duration || + match state.iteration_state { + KeyframesIterationState::Finite(ref mut current, ref max) => { + *current += 1; + *current < *max + } + KeyframesIterationState::Infinite => { + state.started_at += state.duration; + true + } + } + } + }; + + if still_running { animations_still_running.push(running_animation); continue - } else if running_animation.state.pending_iterations > 0 { - // if the animation should run again, just tick it... - let duration = running_animation.end_time - running_animation.start_time; - running_animation.start_time += duration; } + expired_animations.entry(*key) .or_insert_with(Vec::new) .push(running_animation); @@ -59,40 +78,41 @@ pub fn update_animation_state(constellation_chan: &IpcSender, *running_animations = animations_still_running } } + for key in keys_to_remove { running_animations.remove(&key).unwrap(); } // Add new running animations. for new_running_animation in new_running_animations { - match running_animations.entry(new_running_animation.node) { - Entry::Vacant(entry) => { - entry.insert(vec![new_running_animation]); - } - Entry::Occupied(mut entry) => entry.get_mut().push(new_running_animation), - } + running_animations.entry(*new_running_animation.node()) + .or_insert_with(Vec::new) + .push(new_running_animation); } - let animation_state; - if running_animations.is_empty() { - animation_state = AnimationState::NoAnimationsPresent; + let animation_state = if running_animations.is_empty() { + AnimationState::NoAnimationsPresent } else { - animation_state = AnimationState::AnimationsPresent; - } + AnimationState::AnimationsPresent + }; constellation_chan.send(ConstellationMsg::ChangeRunningAnimationsState(pipeline_id, animation_state)) .unwrap(); } /// Recalculates style for a set of animations. This does *not* run with the DOM lock held. -pub fn recalc_style_for_animations(flow: &mut Flow, +pub fn recalc_style_for_animations(context: &SharedLayoutContext, + flow: &mut Flow, animations: &mut HashMap>) { let mut damage = RestyleDamage::empty(); flow.mutate_fragments(&mut |fragment| { - if let Some(ref animations) = animations.get_mut(&fragment.node) { - for mut animation in *animations { + if let Some(ref mut animations) = animations.get_mut(&fragment.node) { + for ref mut animation in animations.iter_mut() { if !animation.is_paused() { - update_style_for_animation(animation, &mut fragment.style, Some(&mut damage)); + update_style_for_animation(&context.style_context, + animation, + &mut fragment.style, + Some(&mut damage)); animation.increment_keyframe_if_applicable(); } } @@ -102,6 +122,6 @@ pub fn recalc_style_for_animations(flow: &mut Flow, let base = flow::mut_base(flow); base.restyle_damage.insert(damage); for kid in base.children.iter_mut() { - recalc_style_for_animations(kid, animations) + recalc_style_for_animations(context, kid, animations) } } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 67e21ab744b..b37d35e905a 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -1307,7 +1307,8 @@ impl LayoutThread { self.profiler_metadata(), self.time_profiler_chan.clone(), || { - animation::recalc_style_for_animations(flow_ref::deref_mut(&mut root_flow), + animation::recalc_style_for_animations(&layout_context, + flow_ref::deref_mut(&mut root_flow), &mut animations) }); } diff --git a/components/style/animation.rs b/components/style/animation.rs index 83c7fffc1e2..a0375b380ad 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -61,7 +61,30 @@ pub enum Animation { Keyframes(OpaqueNode, Atom, KeyframesAnimationState), } -/// A keyframes animation previously sent to layout. +impl Animation { + pub fn node(&self) -> &OpaqueNode { + match *self { + Animation::Transition(ref node, _, _) => node, + Animation::Keyframes(ref node, _, _) => node, + } + } + + pub fn is_paused(&self) -> bool { + match *self { + Animation::Transition(..) => false, + Animation::Keyframes(_, _, ref state) => state.paused, + } + } + + pub fn increment_keyframe_if_applicable(&mut self) { + if let Animation::Keyframes(_, _, ref mut state) = *self { + if let KeyframesIterationState::Finite(ref mut iterations, _) = state.iteration_state { + *iterations += 1; + } + } + } +} + /// A single animation frame of a single property. #[derive(Debug, Clone)] @@ -255,7 +278,8 @@ pub fn maybe_start_animations(context: &SharedStyleContex continue } - if let Some(ref animation) = context.stylist.animations().get(&name) { + if context.stylist.animations().get(&name).is_some() { + debug!("maybe_start_animations: animation {} found", name); let delay = box_style.animation_delay.0.get_mod(i).seconds(); let animation_start = time::precise_time_s() + delay as f64; let duration = box_style.animation_duration.0.get_mod(i).seconds(); @@ -307,9 +331,11 @@ pub fn update_style_for_animation(context: &SharedStyleContext) where Impl: SelectorImplExt, Damage: TRestyleDamage { + debug!("update_style_for_animation: entering"); let now = time::precise_time_s(); match *animation { Animation::Transition(_, start_time, ref frame) => { + debug!("update_style_for_animation: transition found"); let mut new_style = (*style).clone(); let updated_style = update_style_for_animation_frame(&mut new_style, now, start_time, @@ -323,6 +349,7 @@ where Impl: SelectorImplExt, } } Animation::Keyframes(_, ref name, ref state) => { + debug!("update_style_for_animation: animation found {:?}", name); debug_assert!(!state.paused); let duration = state.duration; let started_at = state.started_at; @@ -369,8 +396,8 @@ where Impl: SelectorImplExt, for i in 1..animation.steps.len() { if total_progress as f32 <= animation.steps[i].start_percentage.0 { // We might have found our current keyframe. - target_keyframe = Some(&animation.steps[i]); last_keyframe = target_keyframe; + target_keyframe = Some(&animation.steps[i]); } } @@ -414,6 +441,7 @@ where Impl: SelectorImplExt, let mut style_changed = false; for transition_property in &animation.properties_changed { + debug!("update_style_for_animation: scanning prop {:?} for animation {}", transition_property, name); if let Some(property_animation) = PropertyAnimation::from_transition_property(*transition_property, timing_function, Time(relative_duration as f32), @@ -426,6 +454,7 @@ where Impl: SelectorImplExt, } if style_changed { + debug!("update_style_for_animation: got style change in animation {:?}", name); if let Some(damage) = damage { *damage = *damage | Damage::compute(Some(style), &new_style); }