1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 //! CSS transitions and animations.
6 
7 // NOTE(emilio): This code isn't really executed in Gecko, but we don't want to
8 // compile it out so that people remember it exists, thus the cfg'd Sender
9 // import.
10 
11 use crate::bezier::Bezier;
12 use crate::context::SharedStyleContext;
13 use crate::dom::{OpaqueNode, TElement};
14 use crate::font_metrics::FontMetricsProvider;
15 use crate::properties::animated_properties::AnimatedProperty;
16 use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
17 use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
18 use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
19 #[cfg(feature = "servo")]
20 use crate::properties::LonghandIdSet;
21 use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
22 use crate::stylesheets::Origin;
23 use crate::timer::Timer;
24 use crate::values::computed::Time;
25 use crate::values::computed::TimingFunction;
26 use crate::values::generics::box_::AnimationIterationCount;
27 use crate::values::generics::easing::{StepPosition, TimingFunction as GenericTimingFunction};
28 use crate::Atom;
29 #[cfg(feature = "servo")]
30 use crossbeam_channel::Sender;
31 use servo_arc::Arc;
32 use std::fmt;
33 #[cfg(feature = "gecko")]
34 use std::sync::mpsc::Sender;
35 
36 /// This structure represents a keyframes animation current iteration state.
37 ///
38 /// If the iteration count is infinite, there's no other state, otherwise we
39 /// have to keep track the current iteration and the max iteration count.
40 #[derive(Clone, Debug)]
41 pub enum KeyframesIterationState {
42     /// Infinite iterations, so no need to track a state.
43     Infinite,
44     /// Current and max iterations.
45     Finite(f32, f32),
46 }
47 
48 /// This structure represents wether an animation is actually running.
49 ///
50 /// An animation can be running, or paused at a given time.
51 #[derive(Clone, Debug)]
52 pub enum KeyframesRunningState {
53     /// This animation is paused. The inner field is the percentage of progress
54     /// when it was paused, from 0 to 1.
55     Paused(f64),
56     /// This animation is actually running.
57     Running,
58 }
59 
60 /// This structure represents the current keyframe animation state, i.e., the
61 /// duration, the current and maximum iteration count, and the state (either
62 /// playing or paused).
63 // TODO: unify the use of f32/f64 in this file.
64 #[derive(Clone)]
65 pub struct KeyframesAnimationState {
66     /// The time this animation started at.
67     pub started_at: f64,
68     /// The duration of this animation.
69     pub duration: f64,
70     /// The delay of the animation.
71     pub delay: f64,
72     /// The current iteration state for the animation.
73     pub iteration_state: KeyframesIterationState,
74     /// Werther this animation is paused.
75     pub running_state: KeyframesRunningState,
76     /// The declared animation direction of this animation.
77     pub direction: AnimationDirection,
78     /// The current animation direction. This can only be `normal` or `reverse`.
79     pub current_direction: AnimationDirection,
80     /// Werther this keyframe animation is outdated due to a restyle.
81     pub expired: bool,
82     /// The original cascade style, needed to compute the generated keyframes of
83     /// the animation.
84     pub cascade_style: Arc<ComputedValues>,
85 }
86 
87 impl KeyframesAnimationState {
88     /// Performs a tick in the animation state, i.e., increments the counter of
89     /// the current iteration count, updates times and then toggles the
90     /// direction if appropriate.
91     ///
92     /// Returns true if the animation should keep running.
tick(&mut self) -> bool93     pub fn tick(&mut self) -> bool {
94         debug!("KeyframesAnimationState::tick");
95         debug_assert!(!self.expired);
96 
97         self.started_at += self.duration + self.delay;
98         match self.running_state {
99             // If it's paused, don't update direction or iteration count.
100             KeyframesRunningState::Paused(_) => return true,
101             KeyframesRunningState::Running => {},
102         }
103 
104         if let KeyframesIterationState::Finite(ref mut current, ref max) = self.iteration_state {
105             *current += 1.0;
106             // NB: This prevent us from updating the direction, which might be
107             // needed for the correct handling of animation-fill-mode.
108             if *current >= *max {
109                 return false;
110             }
111         }
112 
113         // Update the next iteration direction if applicable.
114         match self.direction {
115             AnimationDirection::Alternate | AnimationDirection::AlternateReverse => {
116                 self.current_direction = match self.current_direction {
117                     AnimationDirection::Normal => AnimationDirection::Reverse,
118                     AnimationDirection::Reverse => AnimationDirection::Normal,
119                     _ => unreachable!(),
120                 };
121             },
122             _ => {},
123         }
124 
125         true
126     }
127 
128     /// Updates the appropiate state from other animation.
129     ///
130     /// This happens when an animation is re-submitted to layout, presumably
131     /// because of an state change.
132     ///
133     /// There are some bits of state we can't just replace, over all taking in
134     /// account times, so here's that logic.
update_from_other(&mut self, other: &Self, timer: &Timer)135     pub fn update_from_other(&mut self, other: &Self, timer: &Timer) {
136         use self::KeyframesRunningState::*;
137 
138         debug!(
139             "KeyframesAnimationState::update_from_other({:?}, {:?})",
140             self, other
141         );
142 
143         // NB: We shall not touch the started_at field, since we don't want to
144         // restart the animation.
145         let old_started_at = self.started_at;
146         let old_duration = self.duration;
147         let old_direction = self.current_direction;
148         let old_running_state = self.running_state.clone();
149         let old_iteration_state = self.iteration_state.clone();
150         *self = other.clone();
151 
152         let mut new_started_at = old_started_at;
153 
154         // If we're unpausing the animation, fake the start time so we seem to
155         // restore it.
156         //
157         // If the animation keeps paused, keep the old value.
158         //
159         // If we're pausing the animation, compute the progress value.
160         match (&mut self.running_state, old_running_state) {
161             (&mut Running, Paused(progress)) => {
162                 new_started_at = timer.seconds() - (self.duration * progress)
163             },
164             (&mut Paused(ref mut new), Paused(old)) => *new = old,
165             (&mut Paused(ref mut progress), Running) => {
166                 *progress = (timer.seconds() - old_started_at) / old_duration
167             },
168             _ => {},
169         }
170 
171         // Don't update the iteration count, just the iteration limit.
172         // TODO: see how changing the limit affects rendering in other browsers.
173         // We might need to keep the iteration count even when it's infinite.
174         match (&mut self.iteration_state, old_iteration_state) {
175             (
176                 &mut KeyframesIterationState::Finite(ref mut iters, _),
177                 KeyframesIterationState::Finite(old_iters, _),
178             ) => *iters = old_iters,
179             _ => {},
180         }
181 
182         self.current_direction = old_direction;
183         self.started_at = new_started_at;
184     }
185 }
186 
187 impl fmt::Debug for KeyframesAnimationState {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result188     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189         f.debug_struct("KeyframesAnimationState")
190             .field("started_at", &self.started_at)
191             .field("duration", &self.duration)
192             .field("delay", &self.delay)
193             .field("iteration_state", &self.iteration_state)
194             .field("running_state", &self.running_state)
195             .field("direction", &self.direction)
196             .field("current_direction", &self.current_direction)
197             .field("expired", &self.expired)
198             .field("cascade_style", &())
199             .finish()
200     }
201 }
202 
203 /// State relating to an animation.
204 #[derive(Clone, Debug)]
205 pub enum Animation {
206     /// A transition is just a single frame triggered at a time, with a reflow.
207     ///
208     /// the f64 field is the start time as returned by `time::precise_time_s()`.
209     Transition(OpaqueNode, f64, PropertyAnimation),
210 
211     /// A keyframes animation is identified by a name, and can have a
212     /// node-dependent state (i.e. iteration count, etc.).
213     ///
214     /// TODO(emilio): The animation object could be refcounted.
215     Keyframes(
216         OpaqueNode,
217         KeyframesAnimation,
218         Atom,
219         KeyframesAnimationState,
220     ),
221 }
222 
223 impl Animation {
224     /// Whether this animation is expired.
225     #[inline]
is_expired(&self) -> bool226     pub fn is_expired(&self) -> bool {
227         match *self {
228             Animation::Transition(..) => false,
229             Animation::Keyframes(_, _, _, ref state) => state.expired,
230         }
231     }
232 
233     /// The opaque node that owns the animation.
234     #[inline]
node(&self) -> &OpaqueNode235     pub fn node(&self) -> &OpaqueNode {
236         match *self {
237             Animation::Transition(ref node, _, _) => node,
238             Animation::Keyframes(ref node, _, _, _) => node,
239         }
240     }
241 
242     /// Whether this animation is a transition.
243     #[inline]
is_transition(&self) -> bool244     pub fn is_transition(&self) -> bool {
245         match *self {
246             Animation::Transition(..) => true,
247             Animation::Keyframes(..) => false,
248         }
249     }
250 
251     /// Whether this animation has the same end value as another one.
252     #[inline]
is_transition_with_same_end_value(&self, other_animation: &PropertyAnimation) -> bool253     pub fn is_transition_with_same_end_value(&self, other_animation: &PropertyAnimation) -> bool {
254         match *self {
255             Animation::Transition(_, _, ref animation) => {
256                 animation.has_the_same_end_value_as(other_animation)
257             },
258             Animation::Keyframes(..) => false,
259         }
260     }
261 }
262 
263 /// Represents an animation for a given property.
264 #[derive(Clone, Debug)]
265 pub struct PropertyAnimation {
266     /// An `AnimatedProperty` that this `PropertyAnimation` corresponds to.
267     property: AnimatedProperty,
268 
269     /// The timing function of this `PropertyAnimation`.
270     timing_function: TimingFunction,
271 
272     /// The duration of this `PropertyAnimation` in seconds.
273     pub duration: f64,
274 }
275 
276 impl PropertyAnimation {
277     /// Returns the given property longhand id.
property_id(&self) -> LonghandId278     pub fn property_id(&self) -> LonghandId {
279         self.property.id()
280     }
281 
282     /// Returns the given property name.
property_name(&self) -> &'static str283     pub fn property_name(&self) -> &'static str {
284         self.property.name()
285     }
286 
from_longhand( longhand: LonghandId, timing_function: TimingFunction, duration: Time, old_style: &ComputedValues, new_style: &ComputedValues, ) -> Option<PropertyAnimation>287     fn from_longhand(
288         longhand: LonghandId,
289         timing_function: TimingFunction,
290         duration: Time,
291         old_style: &ComputedValues,
292         new_style: &ComputedValues,
293     ) -> Option<PropertyAnimation> {
294         let animated_property = AnimatedProperty::from_longhand(longhand, old_style, new_style)?;
295 
296         let property_animation = PropertyAnimation {
297             property: animated_property,
298             timing_function,
299             duration: duration.seconds() as f64,
300         };
301 
302         if property_animation.does_animate() {
303             Some(property_animation)
304         } else {
305             None
306         }
307     }
308 
309     /// Update the given animation at a given point of progress.
update(&self, style: &mut ComputedValues, time: f64)310     pub fn update(&self, style: &mut ComputedValues, time: f64) {
311         let epsilon = 1. / (200. * self.duration);
312         let progress = match self.timing_function {
313             GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => {
314                 Bezier::new(x1, y1, x2, y2).solve(time, epsilon)
315             },
316             GenericTimingFunction::Steps(steps, pos) => {
317                 let mut current_step = (time * (steps as f64)).floor() as i32;
318 
319                 if pos == StepPosition::Start ||
320                     pos == StepPosition::JumpStart ||
321                     pos == StepPosition::JumpBoth
322                 {
323                     current_step = current_step + 1;
324                 }
325 
326                 // FIXME: We should update current_step according to the "before flag".
327                 // In order to get the before flag, we have to know the current animation phase
328                 // and whether the iteration is reversed. For now, we skip this calculation.
329                 // (i.e. Treat before_flag is unset,)
330                 // https://drafts.csswg.org/css-easing/#step-timing-function-algo
331 
332                 if time >= 0.0 && current_step < 0 {
333                     current_step = 0;
334                 }
335 
336                 let jumps = match pos {
337                     StepPosition::JumpBoth => steps + 1,
338                     StepPosition::JumpNone => steps - 1,
339                     StepPosition::JumpStart |
340                     StepPosition::JumpEnd |
341                     StepPosition::Start |
342                     StepPosition::End => steps,
343                 };
344 
345                 if time <= 1.0 && current_step > jumps {
346                     current_step = jumps;
347                 }
348 
349                 (current_step as f64) / (jumps as f64)
350             },
351             GenericTimingFunction::Keyword(keyword) => {
352                 let (x1, x2, y1, y2) = keyword.to_bezier();
353                 Bezier::new(x1, x2, y1, y2).solve(time, epsilon)
354             },
355         };
356 
357         self.property.update(style, progress);
358     }
359 
360     #[inline]
does_animate(&self) -> bool361     fn does_animate(&self) -> bool {
362         self.property.does_animate() && self.duration != 0.0
363     }
364 
365     /// Whether this animation has the same end value as another one.
366     #[inline]
has_the_same_end_value_as(&self, other: &Self) -> bool367     pub fn has_the_same_end_value_as(&self, other: &Self) -> bool {
368         self.property.has_the_same_end_value_as(&other.property)
369     }
370 }
371 
372 /// Start any new transitions for this node and ensure that any existing transitions
373 /// that are cancelled are marked as cancelled in the SharedStyleContext. This is
374 /// at the end of calculating style for a single node.
375 #[cfg(feature = "servo")]
update_transitions( context: &SharedStyleContext, new_animations_sender: &Sender<Animation>, opaque_node: OpaqueNode, old_style: &ComputedValues, new_style: &mut Arc<ComputedValues>, expired_transitions: &[PropertyAnimation], )376 pub fn update_transitions(
377     context: &SharedStyleContext,
378     new_animations_sender: &Sender<Animation>,
379     opaque_node: OpaqueNode,
380     old_style: &ComputedValues,
381     new_style: &mut Arc<ComputedValues>,
382     expired_transitions: &[PropertyAnimation],
383 ) {
384     let mut all_running_animations = context.running_animations.write();
385     let previously_running_animations = all_running_animations
386         .remove(&opaque_node)
387         .unwrap_or_else(Vec::new);
388 
389     let properties_that_transition = start_transitions_if_applicable(
390         context,
391         new_animations_sender,
392         opaque_node,
393         old_style,
394         new_style,
395         expired_transitions,
396         &previously_running_animations,
397     );
398 
399     let mut all_cancelled_animations = context.cancelled_animations.write();
400     let mut cancelled_animations = all_cancelled_animations
401         .remove(&opaque_node)
402         .unwrap_or_else(Vec::new);
403     let mut running_animations = vec![];
404 
405     // For every animation that was running before this style change, we cancel it
406     // if the property no longer transitions.
407     for running_animation in previously_running_animations.into_iter() {
408         if let Animation::Transition(_, _, ref property_animation) = running_animation {
409             if !properties_that_transition.contains(property_animation.property_id()) {
410                 cancelled_animations.push(running_animation);
411                 continue;
412             }
413         }
414         running_animations.push(running_animation);
415     }
416 
417     if !cancelled_animations.is_empty() {
418         all_cancelled_animations.insert(opaque_node, cancelled_animations);
419     }
420     if !running_animations.is_empty() {
421         all_running_animations.insert(opaque_node, running_animations);
422     }
423 }
424 
425 /// Kick off any new transitions for this node and return all of the properties that are
426 /// transitioning. This is at the end of calculating style for a single node.
427 #[cfg(feature = "servo")]
start_transitions_if_applicable( context: &SharedStyleContext, new_animations_sender: &Sender<Animation>, opaque_node: OpaqueNode, old_style: &ComputedValues, new_style: &mut Arc<ComputedValues>, expired_transitions: &[PropertyAnimation], running_animations: &[Animation], ) -> LonghandIdSet428 pub fn start_transitions_if_applicable(
429     context: &SharedStyleContext,
430     new_animations_sender: &Sender<Animation>,
431     opaque_node: OpaqueNode,
432     old_style: &ComputedValues,
433     new_style: &mut Arc<ComputedValues>,
434     expired_transitions: &[PropertyAnimation],
435     running_animations: &[Animation],
436 ) -> LonghandIdSet {
437     use crate::properties::animated_properties::TransitionPropertyIteration;
438 
439     // If the style of this element is display:none, then we don't start any transitions
440     // and we cancel any currently running transitions by returning an empty LonghandIdSet.
441     if new_style.get_box().clone_display().is_none() {
442         return LonghandIdSet::new();
443     }
444 
445     let mut properties_that_transition = LonghandIdSet::new();
446     let transitions: Vec<TransitionPropertyIteration> = new_style.transition_properties().collect();
447     for transition in &transitions {
448         if properties_that_transition.contains(transition.longhand_id) {
449             continue;
450         } else {
451             properties_that_transition.insert(transition.longhand_id);
452         }
453 
454         let property_animation = match PropertyAnimation::from_longhand(
455             transition.longhand_id,
456             new_style
457                 .get_box()
458                 .transition_timing_function_mod(transition.index),
459             new_style
460                 .get_box()
461                 .transition_duration_mod(transition.index),
462             old_style,
463             Arc::make_mut(new_style),
464         ) {
465             Some(property_animation) => property_animation,
466             None => continue,
467         };
468 
469         // Set the property to the initial value.
470         //
471         // NB: get_mut is guaranteed to succeed since we called make_mut()
472         // above.
473         property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0);
474 
475         // Per [1], don't trigger a new transition if the end state for that
476         // transition is the same as that of a transition that's expired.
477         // [1]: https://drafts.csswg.org/css-transitions/#starting
478         debug!("checking {:?} for matching end value", expired_transitions);
479         if expired_transitions
480             .iter()
481             .any(|animation| animation.has_the_same_end_value_as(&property_animation))
482         {
483             debug!(
484                 "Not initiating transition for {}, expired transition \
485                  found with the same end value",
486                 property_animation.property_name()
487             );
488             continue;
489         }
490 
491         if running_animations
492             .iter()
493             .any(|animation| animation.is_transition_with_same_end_value(&property_animation))
494         {
495             debug!(
496                 "Not initiating transition for {}, running transition \
497                  found with the same end value",
498                 property_animation.property_name()
499             );
500             continue;
501         }
502 
503         // Kick off the animation.
504         debug!("Kicking off transition of {:?}", property_animation);
505         let box_style = new_style.get_box();
506         let now = context.timer.seconds();
507         let start_time = now + (box_style.transition_delay_mod(transition.index).seconds() as f64);
508         new_animations_sender
509             .send(Animation::Transition(
510                 opaque_node,
511                 start_time,
512                 property_animation,
513             ))
514             .unwrap();
515     }
516 
517     properties_that_transition
518 }
519 
compute_style_for_animation_step<E>( context: &SharedStyleContext, step: &KeyframesStep, previous_style: &ComputedValues, style_from_cascade: &Arc<ComputedValues>, font_metrics_provider: &dyn FontMetricsProvider, ) -> Arc<ComputedValues> where E: TElement,520 fn compute_style_for_animation_step<E>(
521     context: &SharedStyleContext,
522     step: &KeyframesStep,
523     previous_style: &ComputedValues,
524     style_from_cascade: &Arc<ComputedValues>,
525     font_metrics_provider: &dyn FontMetricsProvider,
526 ) -> Arc<ComputedValues>
527 where
528     E: TElement,
529 {
530     match step.value {
531         KeyframesStepValue::ComputedValues => style_from_cascade.clone(),
532         KeyframesStepValue::Declarations {
533             block: ref declarations,
534         } => {
535             let guard = declarations.read_with(context.guards.author);
536 
537             // This currently ignores visited styles, which seems acceptable,
538             // as existing browsers don't appear to animate visited styles.
539             let computed = properties::apply_declarations::<E, _>(
540                 context.stylist.device(),
541                 /* pseudo = */ None,
542                 previous_style.rules(),
543                 &context.guards,
544                 // It's possible to have !important properties in keyframes
545                 // so we have to filter them out.
546                 // See the spec issue https://github.com/w3c/csswg-drafts/issues/1824
547                 // Also we filter our non-animatable properties.
548                 guard
549                     .normal_declaration_iter()
550                     .filter(|declaration| declaration.is_animatable())
551                     .map(|decl| (decl, Origin::Author)),
552                 Some(previous_style),
553                 Some(previous_style),
554                 Some(previous_style),
555                 font_metrics_provider,
556                 CascadeMode::Unvisited {
557                     visited_rules: None,
558                 },
559                 context.quirks_mode(),
560                 /* rule_cache = */ None,
561                 &mut Default::default(),
562                 /* element = */ None,
563             );
564             computed
565         },
566     }
567 }
568 
569 /// Triggers animations for a given node looking at the animation property
570 /// values.
maybe_start_animations<E>( element: E, context: &SharedStyleContext, new_animations_sender: &Sender<Animation>, node: OpaqueNode, new_style: &Arc<ComputedValues>, ) -> bool where E: TElement,571 pub fn maybe_start_animations<E>(
572     element: E,
573     context: &SharedStyleContext,
574     new_animations_sender: &Sender<Animation>,
575     node: OpaqueNode,
576     new_style: &Arc<ComputedValues>,
577 ) -> bool
578 where
579     E: TElement,
580 {
581     let mut had_animations = false;
582 
583     let box_style = new_style.get_box();
584     for (i, name) in box_style.animation_name_iter().enumerate() {
585         let name = match name.as_atom() {
586             Some(atom) => atom,
587             None => continue,
588         };
589 
590         debug!("maybe_start_animations: name={}", name);
591         let total_duration = box_style.animation_duration_mod(i).seconds();
592         if total_duration == 0. {
593             continue;
594         }
595 
596         let anim = match context.stylist.get_animation(name, element) {
597             Some(animation) => animation,
598             None => continue,
599         };
600 
601         debug!("maybe_start_animations: animation {} found", name);
602 
603         // If this animation doesn't have any keyframe, we can just continue
604         // without submitting it to the compositor, since both the first and
605         // the second keyframes would be synthetised from the computed
606         // values.
607         if anim.steps.is_empty() {
608             continue;
609         }
610 
611         let delay = box_style.animation_delay_mod(i).seconds();
612         let now = context.timer.seconds();
613         let animation_start = now + delay as f64;
614         let duration = box_style.animation_duration_mod(i).seconds();
615         let iteration_state = match box_style.animation_iteration_count_mod(i) {
616             AnimationIterationCount::Infinite => KeyframesIterationState::Infinite,
617             AnimationIterationCount::Number(n) => KeyframesIterationState::Finite(0.0, n),
618         };
619 
620         let animation_direction = box_style.animation_direction_mod(i);
621 
622         let initial_direction = match animation_direction {
623             AnimationDirection::Normal | AnimationDirection::Alternate => {
624                 AnimationDirection::Normal
625             },
626             AnimationDirection::Reverse | AnimationDirection::AlternateReverse => {
627                 AnimationDirection::Reverse
628             },
629         };
630 
631         let running_state = match box_style.animation_play_state_mod(i) {
632             AnimationPlayState::Paused => KeyframesRunningState::Paused(0.),
633             AnimationPlayState::Running => KeyframesRunningState::Running,
634         };
635 
636         new_animations_sender
637             .send(Animation::Keyframes(
638                 node,
639                 anim.clone(),
640                 name.clone(),
641                 KeyframesAnimationState {
642                     started_at: animation_start,
643                     duration: duration as f64,
644                     delay: delay as f64,
645                     iteration_state,
646                     running_state,
647                     direction: animation_direction,
648                     current_direction: initial_direction,
649                     expired: false,
650                     cascade_style: new_style.clone(),
651                 },
652             ))
653             .unwrap();
654         had_animations = true;
655     }
656 
657     had_animations
658 }
659 
660 /// Returns the kind of animation update that happened.
661 pub enum AnimationUpdate {
662     /// The style was successfully updated, the animation is still running.
663     Regular,
664     /// A style change canceled this animation.
665     AnimationCanceled,
666 }
667 
668 /// Updates a single animation and associated style based on the current time.
669 ///
670 /// FIXME(emilio): This doesn't handle any kind of dynamic change to the
671 /// animation or transition properties in any reasonable way.
672 ///
673 /// This should probably be split in two, one from updating animations and
674 /// transitions in response to a style change (that is,
675 /// consider_starting_transitions + maybe_start_animations, but handling
676 /// canceled animations, duration changes, etc, there instead of here), and this
677 /// function should be only about the style update in response of a transition.
update_style_for_animation<E>( context: &SharedStyleContext, animation: &Animation, style: &mut Arc<ComputedValues>, font_metrics_provider: &dyn FontMetricsProvider, ) -> AnimationUpdate where E: TElement,678 pub fn update_style_for_animation<E>(
679     context: &SharedStyleContext,
680     animation: &Animation,
681     style: &mut Arc<ComputedValues>,
682     font_metrics_provider: &dyn FontMetricsProvider,
683 ) -> AnimationUpdate
684 where
685     E: TElement,
686 {
687     debug!("update_style_for_animation: {:?}", animation);
688     debug_assert!(!animation.is_expired());
689 
690     match *animation {
691         Animation::Transition(_, start_time, ref property_animation) => {
692             let now = context.timer.seconds();
693             let progress = (now - start_time) / (property_animation.duration);
694             let progress = progress.min(1.0);
695             if progress >= 0.0 {
696                 property_animation.update(Arc::make_mut(style), progress);
697             }
698             AnimationUpdate::Regular
699         },
700         Animation::Keyframes(_, ref animation, ref name, ref state) => {
701             let duration = state.duration;
702             let started_at = state.started_at;
703 
704             let now = match state.running_state {
705                 KeyframesRunningState::Running => context.timer.seconds(),
706                 KeyframesRunningState::Paused(progress) => started_at + duration * progress,
707             };
708 
709             debug_assert!(!animation.steps.is_empty());
710 
711             let maybe_index = style
712                 .get_box()
713                 .animation_name_iter()
714                 .position(|animation_name| Some(name) == animation_name.as_atom());
715 
716             let index = match maybe_index {
717                 Some(index) => index,
718                 None => return AnimationUpdate::AnimationCanceled,
719             };
720 
721             let total_duration = style.get_box().animation_duration_mod(index).seconds() as f64;
722             if total_duration == 0. {
723                 return AnimationUpdate::AnimationCanceled;
724             }
725 
726             let mut total_progress = (now - started_at) / total_duration;
727             if total_progress < 0. {
728                 warn!("Negative progress found for animation {:?}", name);
729                 return AnimationUpdate::Regular;
730             }
731             if total_progress > 1. {
732                 total_progress = 1.;
733             }
734 
735             // Get the target and the last keyframe position.
736             let last_keyframe_position;
737             let target_keyframe_position;
738             match state.current_direction {
739                 AnimationDirection::Normal => {
740                     target_keyframe_position = animation
741                         .steps
742                         .iter()
743                         .position(|step| total_progress as f32 <= step.start_percentage.0);
744 
745                     last_keyframe_position = target_keyframe_position
746                         .and_then(|pos| if pos != 0 { Some(pos - 1) } else { None })
747                         .unwrap_or(0);
748                 },
749                 AnimationDirection::Reverse => {
750                     target_keyframe_position = animation
751                         .steps
752                         .iter()
753                         .rev()
754                         .position(|step| total_progress as f32 <= 1. - step.start_percentage.0)
755                         .map(|pos| animation.steps.len() - pos - 1);
756 
757                     last_keyframe_position = target_keyframe_position
758                         .and_then(|pos| {
759                             if pos != animation.steps.len() - 1 {
760                                 Some(pos + 1)
761                             } else {
762                                 None
763                             }
764                         })
765                         .unwrap_or(animation.steps.len() - 1);
766                 },
767                 _ => unreachable!(),
768             }
769 
770             debug!(
771                 "update_style_for_animation: keyframe from {:?} to {:?}",
772                 last_keyframe_position, target_keyframe_position
773             );
774 
775             let target_keyframe = match target_keyframe_position {
776                 Some(target) => &animation.steps[target],
777                 None => return AnimationUpdate::Regular,
778             };
779 
780             let last_keyframe = &animation.steps[last_keyframe_position];
781 
782             let relative_timespan =
783                 (target_keyframe.start_percentage.0 - last_keyframe.start_percentage.0).abs();
784             let relative_duration = relative_timespan as f64 * duration;
785             let last_keyframe_ended_at = match state.current_direction {
786                 AnimationDirection::Normal => {
787                     state.started_at + (total_duration * last_keyframe.start_percentage.0 as f64)
788                 },
789                 AnimationDirection::Reverse => {
790                     state.started_at +
791                         (total_duration * (1. - last_keyframe.start_percentage.0 as f64))
792                 },
793                 _ => unreachable!(),
794             };
795             let relative_progress = (now - last_keyframe_ended_at) / relative_duration;
796 
797             // TODO: How could we optimise it? Is it such a big deal?
798             let from_style = compute_style_for_animation_step::<E>(
799                 context,
800                 last_keyframe,
801                 &**style,
802                 &state.cascade_style,
803                 font_metrics_provider,
804             );
805 
806             // NB: The spec says that the timing function can be overwritten
807             // from the keyframe style.
808             let mut timing_function = style.get_box().animation_timing_function_mod(index);
809             if last_keyframe.declared_timing_function {
810                 // NB: animation_timing_function can never be empty, always has
811                 // at least the default value (`ease`).
812                 timing_function = from_style.get_box().animation_timing_function_at(0);
813             }
814 
815             let target_style = compute_style_for_animation_step::<E>(
816                 context,
817                 target_keyframe,
818                 &from_style,
819                 &state.cascade_style,
820                 font_metrics_provider,
821             );
822 
823             let mut new_style = (*style).clone();
824 
825             for property in animation.properties_changed.iter() {
826                 debug!(
827                     "update_style_for_animation: scanning prop {:?} for animation \"{}\"",
828                     property, name
829                 );
830                 let animation = PropertyAnimation::from_longhand(
831                     property,
832                     timing_function,
833                     Time::from_seconds(relative_duration as f32),
834                     &from_style,
835                     &target_style,
836                 );
837 
838                 match animation {
839                     Some(property_animation) => {
840                         debug!(
841                             "update_style_for_animation: got property animation for prop {:?}",
842                             property
843                         );
844                         debug!("update_style_for_animation: {:?}", property_animation);
845                         property_animation.update(Arc::make_mut(&mut new_style), relative_progress);
846                     },
847                     None => {
848                         debug!(
849                             "update_style_for_animation: property animation {:?} not animating",
850                             property
851                         );
852                     },
853                 }
854             }
855 
856             debug!(
857                 "update_style_for_animation: got style change in animation \"{}\"",
858                 name
859             );
860             *style = new_style;
861             AnimationUpdate::Regular
862         },
863     }
864 }
865