diff options
author | Martin Robinson <mrobinson@igalia.com> | 2020-04-23 17:06:24 +0200 |
---|---|---|
committer | Martin Robinson <mrobinson@igalia.com> | 2020-04-24 14:20:37 +0200 |
commit | d386d6d1f1e1dba33b49e0b44d9216128f29da1c (patch) | |
tree | fe253bdd7d28cc06a606af1e6f9cc39332848652 /components/layout/animation.rs | |
parent | 0540c4a284952145773e3c86d0f57f69a83283f1 (diff) | |
download | servo-d386d6d1f1e1dba33b49e0b44d9216128f29da1c.tar.gz servo-d386d6d1f1e1dba33b49e0b44d9216128f29da1c.zip |
Add support for transitionrun events
These events are triggered as soon as a transition is added to the list
of running transitions. This will allow better test coverage while
reworking the transitions and animations processing model.
Diffstat (limited to 'components/layout/animation.rs')
-rw-r--r-- | components/layout/animation.rs | 133 |
1 files changed, 78 insertions, 55 deletions
diff --git a/components/layout/animation.rs b/components/layout/animation.rs index 185b6d9c9d1..57db0abda42 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -15,54 +15,62 @@ use script_traits::UntrustedNodeAddress; use script_traits::{ AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg, TransitionEventType, }; -use style::animation::{update_style_for_animation, Animation, ElementAnimationState}; +use style::animation::{ + update_style_for_animation, Animation, ElementAnimationState, PropertyAnimation, +}; use style::dom::TElement; use style::font_metrics::ServoMetricsProvider; use style::selector_parser::RestyleDamage; use style::timer::Timer; +/// Collect newly transitioning nodes, which is used by the script process during +/// forced, synchronous reflows to root DOM nodes for the duration of their transitions. +pub fn collect_newly_transitioning_nodes( + animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>, + mut out: Option<&mut Vec<UntrustedNodeAddress>>, +) { + // This extends the output vector with an iterator that contains a copy of the node + // address for every new animation. This is a bit goofy, but the script thread + // currently stores a rooted node for every property that is transitioning. + if let Some(ref mut out) = out { + out.extend(animation_states.iter().flat_map(|(node, state)| { + let num_transitions = state + .new_animations + .iter() + .filter(|animation| animation.is_transition()) + .count(); + std::iter::repeat(node.to_untrusted_node_address()).take(num_transitions) + })); + } +} + /// Processes any new animations that were discovered after style recalculation. Also /// finish any animations that have completed, inserting them into `finished_animations`. -pub fn update_animation_states<E>( +pub fn update_animation_states( constellation_chan: &IpcSender<ConstellationMsg>, script_chan: &IpcSender<ConstellationControlMsg>, animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationState>, invalid_nodes: FxHashSet<OpaqueNode>, - mut newly_transitioning_nodes: Option<&mut Vec<UntrustedNodeAddress>>, pipeline_id: PipelineId, timer: &Timer, -) where - E: TElement, -{ +) { let had_running_animations = animation_states .values() .any(|state| !state.running_animations.is_empty()); + // Cancel all animations on any invalid nodes. These entries will later + // be removed from the list of states, because their states will become + // empty. for node in &invalid_nodes { if let Some(mut state) = animation_states.remove(node) { state.cancel_all_animations(); - send_events_for_cancelled_animations(script_chan, &mut state, pipeline_id); } } let now = timer.seconds(); let mut have_running_animations = false; for (node, animation_state) in animation_states.iter_mut() { - // TODO(mrobinson): This should really be triggering transitionrun messages - // on the script thread. - if let Some(ref mut newly_transitioning_nodes) = newly_transitioning_nodes { - let number_of_new_transitions = animation_state - .new_animations - .iter() - .filter(|animation| animation.is_transition()) - .count(); - for _ in 0..number_of_new_transitions { - newly_transitioning_nodes.push(node.to_untrusted_node_address()); - } - } - - update_animation_state::<E>(script_chan, animation_state, pipeline_id, now); - + update_animation_state(script_chan, animation_state, pipeline_id, now, *node); have_running_animations = have_running_animations || !animation_state.running_animations.is_empty(); } @@ -85,16 +93,37 @@ pub fn update_animation_states<E>( .unwrap(); } -pub fn update_animation_state<E>( - script_chan: &IpcSender<ConstellationControlMsg>, +pub fn update_animation_state( + script_channel: &IpcSender<ConstellationControlMsg>, animation_state: &mut ElementAnimationState, pipeline_id: PipelineId, now: f64, -) where - E: TElement, -{ - send_events_for_cancelled_animations(script_chan, animation_state, pipeline_id); + node: OpaqueNode, +) { + let send_transition_event = |property_animation: &PropertyAnimation, event_type| { + script_channel + .send(ConstellationControlMsg::TransitionEvent { + pipeline_id, + event_type, + node: node.to_untrusted_node_address(), + property_name: property_animation.property_name().into(), + elapsed_time: property_animation.duration, + }) + .unwrap() + }; + handle_cancelled_animations(animation_state, send_transition_event); + handle_running_animations(animation_state, now, send_transition_event); + handle_new_animations(animation_state, send_transition_event); +} + +/// Walk through the list of running animations and remove all of the ones that +/// have ended. +pub fn handle_running_animations( + animation_state: &mut ElementAnimationState, + now: f64, + mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType), +) { let mut running_animations = std::mem::replace(&mut animation_state.running_animations, Vec::new()); for mut running_animation in running_animations.drain(..) { @@ -115,46 +144,25 @@ pub fn update_animation_state<E>( animation_state.running_animations.push(running_animation); } else { debug!("Finishing transition: {:?}", running_animation); - if let Animation::Transition(node, _, ref property_animation) = running_animation { - script_chan - .send(ConstellationControlMsg::TransitionEvent { - pipeline_id, - event_type: TransitionEventType::TransitionEnd, - node: node.to_untrusted_node_address(), - property_name: property_animation.property_name().into(), - elapsed_time: property_animation.duration, - }) - .unwrap(); + if let Animation::Transition(_, _, ref property_animation) = running_animation { + send_transition_event(property_animation, TransitionEventType::TransitionEnd); } animation_state.finished_animations.push(running_animation); } } - - animation_state - .running_animations - .append(&mut animation_state.new_animations); } /// Send events for cancelled animations. Currently this only handles cancelled /// transitions, but eventually this should handle cancelled CSS animations as /// well. -pub fn send_events_for_cancelled_animations( - script_channel: &IpcSender<ConstellationControlMsg>, +pub fn handle_cancelled_animations( animation_state: &mut ElementAnimationState, - pipeline_id: PipelineId, + mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType), ) { for animation in animation_state.cancelled_animations.drain(..) { match animation { - Animation::Transition(node, _, ref property_animation) => { - script_channel - .send(ConstellationControlMsg::TransitionEvent { - pipeline_id, - event_type: TransitionEventType::TransitionCancel, - node: node.to_untrusted_node_address(), - property_name: property_animation.property_name().into(), - elapsed_time: property_animation.duration, - }) - .unwrap(); + Animation::Transition(_, _, ref property_animation) => { + send_transition_event(property_animation, TransitionEventType::TransitionCancel) }, Animation::Keyframes(..) => { warn!("Got unexpected animation in finished transitions list.") @@ -163,6 +171,21 @@ pub fn send_events_for_cancelled_animations( } } +pub fn handle_new_animations( + animation_state: &mut ElementAnimationState, + mut send_transition_event: impl FnMut(&PropertyAnimation, TransitionEventType), +) { + for animation in animation_state.new_animations.drain(..) { + match animation { + Animation::Transition(_, _, ref property_animation) => { + send_transition_event(property_animation, TransitionEventType::TransitionRun) + }, + Animation::Keyframes(..) => {}, + } + animation_state.running_animations.push(animation); + } +} + /// Recalculates style for a set of animations. This does *not* run with the DOM /// lock held. Returns a set of nodes associated with animations that are no longer /// valid. |