Skip to main content

style/servo/
animation.rs

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.
9
10use crate::context::{CascadeInputs, SharedStyleContext};
11use crate::derives::*;
12use crate::dom::{OpaqueNode, TDocument, TElement, TNode};
13use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
14use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
15use crate::properties::longhands::animation_fill_mode::computed_value::single_value::T as AnimationFillMode;
16use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
17use crate::properties::AnimationDeclarations;
18use crate::properties::{
19    ComputedValues, Importance, LonghandId, PropertyDeclarationBlock, PropertyDeclarationId,
20    PropertyDeclarationIdSet,
21};
22use crate::rule_tree::{CascadeLevel, CascadeOrigin, RuleCascadeFlags};
23use crate::selector_parser::PseudoElement;
24use crate::shared_lock::{Locked, SharedRwLock};
25use crate::style_resolver::StyleResolverForElement;
26use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
27use crate::stylesheets::layer_rule::LayerOrder;
28use crate::values::animated::{Animate, Procedure};
29use crate::values::computed::TimingFunction;
30use crate::values::generics::easing::BeforeFlag;
31use crate::values::specified::TransitionBehavior;
32use crate::Atom;
33use parking_lot::RwLock;
34use rustc_hash::FxHashMap;
35use servo_arc::Arc;
36use std::fmt;
37
38/// Represents an animation for a given property.
39#[derive(Clone, Debug, MallocSizeOf)]
40pub struct PropertyAnimation {
41    /// The value we are animating from.
42    from: AnimationValue,
43
44    /// The value we are animating to.
45    to: AnimationValue,
46
47    /// The timing function of this `PropertyAnimation`.
48    timing_function: TimingFunction,
49
50    /// The duration of this `PropertyAnimation` in seconds.
51    pub duration: f64,
52}
53
54impl PropertyAnimation {
55    /// Returns the given property longhand id.
56    pub fn property_id(&self) -> PropertyDeclarationId<'_> {
57        debug_assert_eq!(self.from.id(), self.to.id());
58        self.from.id()
59    }
60
61    /// The output of the timing function given the progress ration of this animation.
62    fn timing_function_output(&self, progress: f64) -> f64 {
63        let epsilon = 1. / (200. * self.duration);
64        // FIXME: Need to set the before flag correctly.
65        // In order to get the before flag, we have to know the current animation phase
66        // and whether the iteration is reversed. For now, we skip this calculation
67        // by treating as if the flag is unset at all times.
68        // https://drafts.csswg.org/css-easing/#step-timing-function-algo
69        self.timing_function
70            .calculate_output(progress, BeforeFlag::Unset, epsilon)
71    }
72
73    /// Update the given animation at a given point of progress.
74    fn calculate_value(&self, progress: f64) -> AnimationValue {
75        let progress = self.timing_function_output(progress);
76        let procedure = Procedure::Interpolate { progress };
77        self.from.animate(&self.to, procedure).unwrap_or_else(|()| {
78            // Fall back to discrete interpolation
79            if progress < 0.5 {
80                self.from.clone()
81            } else {
82                self.to.clone()
83            }
84        })
85    }
86}
87
88/// This structure represents the state of an animation.
89#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
90pub enum AnimationState {
91    /// The animation has been created, but is not running yet. This state
92    /// is also used when an animation is still in the first delay phase.
93    Pending,
94    /// This animation is currently running.
95    Running,
96    /// This animation is paused. The inner field is the percentage of progress
97    /// when it was paused, from 0 to 1.
98    Paused(f64),
99    /// This animation has finished.
100    Finished,
101    /// This animation has been canceled.
102    Canceled,
103}
104
105impl AnimationState {
106    /// Whether or not this state requires its owning animation to be ticked.
107    fn needs_to_be_ticked(&self) -> bool {
108        *self == AnimationState::Running || *self == AnimationState::Pending
109    }
110}
111
112enum IgnoreTransitions {
113    Canceled,
114    CanceledAndFinished,
115}
116
117/// This structure represents a keyframes animation current iteration state.
118///
119/// If the iteration count is infinite, there's no other state, otherwise we
120/// have to keep track the current iteration and the max iteration count.
121#[derive(Clone, Debug, MallocSizeOf)]
122pub enum KeyframesIterationState {
123    /// Infinite iterations with the current iteration count.
124    Infinite(f64),
125    /// Current and max iterations.
126    Finite(f64, f64),
127}
128
129/// A temporary data structure used when calculating ComputedKeyframes for an
130/// animation. This data structure is used to collapse information for steps
131/// which may be spread across multiple keyframe declarations into a single
132/// instance per `start_percentage`.
133struct IntermediateComputedKeyframe {
134    declarations: PropertyDeclarationBlock,
135    timing_function: Option<TimingFunction>,
136    start_percentage: f32,
137}
138
139impl IntermediateComputedKeyframe {
140    fn new(start_percentage: f32) -> Self {
141        IntermediateComputedKeyframe {
142            declarations: PropertyDeclarationBlock::new(),
143            timing_function: None,
144            start_percentage,
145        }
146    }
147
148    /// Walk through all keyframe declarations and combine all declarations with the
149    /// same `start_percentage` into individual `IntermediateComputedKeyframe`s.
150    fn generate_for_keyframes(
151        animation: &KeyframesAnimation,
152        context: &SharedStyleContext,
153        base_style: &ComputedValues,
154    ) -> Vec<Self> {
155        if animation.steps.is_empty() {
156            return vec![];
157        }
158
159        let mut intermediate_steps: Vec<Self> = Vec::with_capacity(animation.steps.len());
160        let mut current_step = IntermediateComputedKeyframe::new(0.);
161        for step in animation.steps.iter() {
162            let start_percentage = step.start_percentage.0;
163            if start_percentage != current_step.start_percentage {
164                let new_step = IntermediateComputedKeyframe::new(start_percentage);
165                intermediate_steps.push(std::mem::replace(&mut current_step, new_step));
166            }
167
168            current_step.update_from_step(step, context, base_style);
169        }
170        intermediate_steps.push(current_step);
171
172        // We should always have a first and a last step, even if these are just
173        // generated by KeyframesStepValue::ComputedValues.
174        debug_assert!(intermediate_steps.first().unwrap().start_percentage == 0.);
175        debug_assert!(intermediate_steps.last().unwrap().start_percentage == 1.);
176
177        intermediate_steps
178    }
179
180    fn update_from_step(
181        &mut self,
182        step: &KeyframesStep,
183        context: &SharedStyleContext,
184        base_style: &ComputedValues,
185    ) {
186        // Each keyframe declaration may optionally specify a timing function, falling
187        // back to the one defined global for the animation.
188        let guard = &context.guards.author;
189        if let Some(timing_function) = step.get_animation_timing_function(&guard) {
190            self.timing_function = Some(timing_function.to_computed_value_without_context());
191        }
192
193        let block = match step.value {
194            KeyframesStepValue::ComputedValues => return,
195            KeyframesStepValue::Declarations { ref block } => block,
196        };
197
198        // Filter out !important, non-animatable properties, and the
199        // 'display' property (which is only animatable from SMIL).
200        let guard = block.read_with(&guard);
201        for declaration in guard.normal_declaration_iter() {
202            if let PropertyDeclarationId::Longhand(id) = declaration.id() {
203                if id == LonghandId::Display {
204                    continue;
205                }
206
207                if !id.is_animatable() {
208                    continue;
209                }
210            }
211
212            self.declarations.push(
213                declaration.to_physical(base_style.writing_mode),
214                Importance::Normal,
215            );
216        }
217    }
218
219    fn resolve_style<E>(
220        self,
221        element: E,
222        context: &SharedStyleContext,
223        base_style: &Arc<ComputedValues>,
224        resolver: &mut StyleResolverForElement<E>,
225    ) -> Arc<ComputedValues>
226    where
227        E: TElement,
228    {
229        if !self.declarations.any_normal() {
230            return base_style.clone();
231        }
232
233        let document = element.as_node().owner_doc();
234        let locked_block = Arc::new(document.shared_lock().wrap(self.declarations));
235        let mut important_rules_changed = false;
236        let rule_node = base_style.rules().clone();
237        let new_node = context.stylist.rule_tree().update_rule_at_level(
238            CascadeLevel::new(CascadeOrigin::Animations),
239            LayerOrder::root(),
240            Some(locked_block.borrow_arc()),
241            &rule_node,
242            &context.guards,
243            &mut important_rules_changed,
244        );
245
246        if new_node.is_none() {
247            return base_style.clone();
248        }
249
250        let inputs = CascadeInputs {
251            rules: new_node,
252            visited_rules: base_style.visited_rules().cloned(),
253            flags: base_style.flags.for_cascade_inputs(),
254            included_cascade_flags: RuleCascadeFlags::empty(),
255        };
256        resolver
257            .cascade_style_and_visited_with_default_parents(inputs)
258            .0
259    }
260}
261
262/// A single computed keyframe for a CSS Animation.
263#[derive(Clone, MallocSizeOf)]
264struct ComputedKeyframe {
265    /// The timing function to use for transitions between this step
266    /// and the next one.
267    timing_function: TimingFunction,
268
269    /// The starting percentage (a number between 0 and 1) which represents
270    /// at what point in an animation iteration this step is.
271    start_percentage: f32,
272
273    /// The animation values to transition to and from when processing this
274    /// keyframe animation step.
275    values: Box<[AnimationValue]>,
276}
277
278impl ComputedKeyframe {
279    fn generate_for_keyframes<E>(
280        element: E,
281        animation: &KeyframesAnimation,
282        context: &SharedStyleContext,
283        base_style: &Arc<ComputedValues>,
284        default_timing_function: TimingFunction,
285        resolver: &mut StyleResolverForElement<E>,
286    ) -> Box<[Self]>
287    where
288        E: TElement,
289    {
290        let mut animating_properties = PropertyDeclarationIdSet::default();
291        for property in animation.properties_changed.iter() {
292            debug_assert!(property.is_animatable());
293            animating_properties.insert(property.to_physical(base_style.writing_mode));
294        }
295
296        let animation_values_from_style: Vec<AnimationValue> = animating_properties
297            .iter()
298            .map(|property| {
299                AnimationValue::from_computed_values(property, &**base_style)
300                    .expect("Unexpected non-animatable property.")
301            })
302            .collect();
303
304        let intermediate_steps =
305            IntermediateComputedKeyframe::generate_for_keyframes(animation, context, base_style);
306
307        let mut computed_steps: Vec<Self> = Vec::with_capacity(intermediate_steps.len());
308        for (step_index, step) in intermediate_steps.into_iter().enumerate() {
309            let start_percentage = step.start_percentage;
310            let properties_changed_in_step = step.declarations.property_ids().clone();
311            let step_timing_function = step.timing_function.clone();
312            let step_style = step.resolve_style(element, context, base_style, resolver);
313            let timing_function =
314                step_timing_function.unwrap_or_else(|| default_timing_function.clone());
315
316            let values = {
317                // If a value is not set in a property declaration we use the value from
318                // the style for the first and last keyframe. For intermediate ones, we
319                // use the value from the previous keyframe.
320                //
321                // TODO(mrobinson): According to the spec, we should use an interpolated
322                // value for properties missing from keyframe declarations.
323                let default_values = if start_percentage == 0. || start_percentage == 1.0 {
324                    animation_values_from_style.as_slice()
325                } else {
326                    debug_assert!(step_index != 0);
327                    &computed_steps[step_index - 1].values
328                };
329
330                // For each property that is animating, pull the value from the resolved
331                // style for this step if it's in one of the declarations. Otherwise, we
332                // use the default value from the set we calculated above.
333                animating_properties
334                    .iter()
335                    .zip(default_values.iter())
336                    .map(|(property_declaration, default_value)| {
337                        if properties_changed_in_step.contains(property_declaration) {
338                            AnimationValue::from_computed_values(property_declaration, &step_style)
339                                .unwrap_or_else(|| default_value.clone())
340                        } else {
341                            default_value.clone()
342                        }
343                    })
344                    .collect()
345            };
346
347            computed_steps.push(ComputedKeyframe {
348                timing_function,
349                start_percentage,
350                values,
351            });
352        }
353        computed_steps.into_boxed_slice()
354    }
355}
356
357/// A CSS Animation
358#[derive(Clone, MallocSizeOf)]
359pub struct Animation {
360    /// The name of this animation as defined by the style.
361    pub name: Atom,
362
363    /// The properties that change in this animation.
364    properties_changed: PropertyDeclarationIdSet,
365
366    /// The computed style for each keyframe of this animation.
367    computed_steps: Box<[ComputedKeyframe]>,
368
369    /// The time this animation started at, which is the current value of the animation
370    /// timeline when this animation was created plus any animation delay.
371    pub started_at: f64,
372
373    /// The duration of this animation.
374    pub duration: f64,
375
376    /// The delay of the animation.
377    pub delay: f64,
378
379    /// The `animation-fill-mode` property of this animation.
380    pub fill_mode: AnimationFillMode,
381
382    /// The current iteration state for the animation.
383    pub iteration_state: KeyframesIterationState,
384
385    /// Whether this animation is paused.
386    pub state: AnimationState,
387
388    /// The declared animation direction of this animation.
389    pub direction: AnimationDirection,
390
391    /// The current animation direction. This can only be `normal` or `reverse`.
392    pub current_direction: AnimationDirection,
393
394    /// The original cascade style, needed to compute the generated keyframes of
395    /// the animation.
396    #[ignore_malloc_size_of = "ComputedValues"]
397    pub cascade_style: Arc<ComputedValues>,
398
399    /// Whether or not this animation is new and or has already been tracked
400    /// by the script thread.
401    pub is_new: bool,
402}
403
404impl Animation {
405    /// Whether or not this animation is cancelled by changes from a new style.
406    fn is_cancelled_in_new_style(&self, new_style: &Arc<ComputedValues>) -> bool {
407        let new_ui = new_style.get_ui();
408        let index = new_ui
409            .animation_name_iter()
410            .position(|animation_name| Some(&self.name) == animation_name.as_atom());
411        let index = match index {
412            Some(index) => index,
413            None => return true,
414        };
415
416        new_ui.animation_duration_mod(index).seconds() == 0.
417    }
418
419    /// Given the current time, advances this animation to the next iteration,
420    /// updates times, and then toggles the direction if appropriate. Otherwise
421    /// does nothing. Returns true if this animation has iterated.
422    pub fn iterate_if_necessary(&mut self, time: f64) -> bool {
423        if !self.iteration_over(time) {
424            return false;
425        }
426
427        // Only iterate animations that are currently running.
428        if self.state != AnimationState::Running {
429            return false;
430        }
431
432        if self.on_last_iteration() {
433            return false;
434        }
435
436        self.iterate();
437        true
438    }
439
440    fn iterate(&mut self) {
441        debug_assert!(!self.on_last_iteration());
442
443        if let KeyframesIterationState::Finite(ref mut current, max) = self.iteration_state {
444            *current = (*current + 1.).min(max);
445        }
446
447        if let AnimationState::Paused(ref mut progress) = self.state {
448            debug_assert!(*progress > 1.);
449            *progress -= 1.;
450        }
451
452        // Update the next iteration direction if applicable.
453        self.started_at += self.duration;
454        match self.direction {
455            AnimationDirection::Alternate | AnimationDirection::AlternateReverse => {
456                self.current_direction = match self.current_direction {
457                    AnimationDirection::Normal => AnimationDirection::Reverse,
458                    AnimationDirection::Reverse => AnimationDirection::Normal,
459                    _ => unreachable!(),
460                };
461            },
462            _ => {},
463        }
464    }
465
466    /// A number (> 0 and <= 1) which represents the fraction of a full iteration
467    /// that the current iteration of the animation lasts. This will be less than 1
468    /// if the current iteration is the fractional remainder of a non-integral
469    /// iteration count.
470    pub fn current_iteration_end_progress(&self) -> f64 {
471        match self.iteration_state {
472            KeyframesIterationState::Finite(current, max) => (max - current).min(1.),
473            KeyframesIterationState::Infinite(_) => 1.,
474        }
475    }
476
477    /// The duration of the current iteration of this animation which may be less
478    /// than the animation duration if it has a non-integral iteration count.
479    pub fn current_iteration_duration(&self) -> f64 {
480        self.current_iteration_end_progress() * self.duration
481    }
482
483    /// Whether or not the current iteration is over. Note that this method assumes that
484    /// the animation is still running.
485    fn iteration_over(&self, time: f64) -> bool {
486        time > (self.started_at + self.current_iteration_duration())
487    }
488
489    /// Assuming this animation is running, whether or not it is on the last iteration.
490    fn on_last_iteration(&self) -> bool {
491        match self.iteration_state {
492            KeyframesIterationState::Finite(current, max) => current >= (max - 1.),
493            KeyframesIterationState::Infinite(_) => false,
494        }
495    }
496
497    /// Whether or not this animation has finished at the provided time. This does
498    /// not take into account canceling i.e. when an animation or transition is
499    /// canceled due to changes in the style.
500    pub fn has_ended(&self, time: f64) -> bool {
501        if !self.on_last_iteration() {
502            return false;
503        }
504
505        let progress = match self.state {
506            AnimationState::Finished => return true,
507            AnimationState::Paused(progress) => progress,
508            AnimationState::Running => (time - self.started_at) / self.duration,
509            AnimationState::Pending | AnimationState::Canceled => return false,
510        };
511
512        progress >= self.current_iteration_end_progress()
513    }
514
515    /// Updates the appropiate state from other animation.
516    ///
517    /// This happens when an animation is re-submitted to layout, presumably
518    /// because of an state change.
519    ///
520    /// There are some bits of state we can't just replace, over all taking in
521    /// account times, so here's that logic.
522    pub fn update_from_other(&mut self, other: &Self, now: f64) {
523        use self::AnimationState::*;
524
525        debug!(
526            "KeyframesAnimationState::update_from_other({:?}, {:?})",
527            self, other
528        );
529
530        // NB: We shall not touch the started_at field, since we don't want to
531        // restart the animation.
532        let old_started_at = self.started_at;
533        let old_duration = self.duration;
534        let old_direction = self.current_direction;
535        let old_state = self.state.clone();
536        let old_iteration_state = self.iteration_state.clone();
537
538        *self = other.clone();
539
540        self.started_at = old_started_at;
541        self.current_direction = old_direction;
542
543        // Don't update the iteration count, just the iteration limit.
544        // TODO: see how changing the limit affects rendering in other browsers.
545        // We might need to keep the iteration count even when it's infinite.
546        match (&mut self.iteration_state, old_iteration_state) {
547            (
548                &mut KeyframesIterationState::Finite(ref mut iters, _),
549                KeyframesIterationState::Finite(old_iters, _),
550            ) => *iters = old_iters,
551            _ => {},
552        }
553
554        // Don't pause or restart animations that should remain finished.
555        // We call mem::replace because `has_ended(...)` looks at `Animation::state`.
556        let new_state = std::mem::replace(&mut self.state, Running);
557        if old_state == Finished && self.has_ended(now) {
558            self.state = Finished;
559        } else {
560            self.state = new_state;
561        }
562
563        // If we're unpausing the animation, fake the start time so we seem to
564        // restore it.
565        //
566        // If the animation keeps paused, keep the old value.
567        //
568        // If we're pausing the animation, compute the progress value.
569        match (&mut self.state, &old_state) {
570            (&mut Pending, &Paused(progress)) => {
571                self.started_at = now - (self.duration * progress);
572            },
573            (&mut Paused(ref mut new), &Paused(old)) => *new = old,
574            (&mut Paused(ref mut progress), &Running) => {
575                *progress = (now - old_started_at) / old_duration
576            },
577            _ => {},
578        }
579
580        // Try to detect when we should skip straight to the running phase to
581        // avoid sending multiple animationstart events.
582        if self.state == Pending && self.started_at <= now && old_state != Pending {
583            self.state = Running;
584        }
585    }
586
587    /// Fill in an `AnimationValueMap` with values calculated from this animation at
588    /// the given time value.
589    fn get_property_declaration_at_time(&self, now: f64, map: &mut AnimationValueMap) {
590        if self.computed_steps.is_empty() {
591            // Nothing to do.
592            return;
593        }
594
595        let total_progress = match self.state {
596            AnimationState::Running | AnimationState::Pending | AnimationState::Finished => {
597                (now - self.started_at) / self.duration
598            },
599            AnimationState::Paused(progress) => progress,
600            AnimationState::Canceled => return,
601        };
602
603        if total_progress < 0.
604            && self.fill_mode != AnimationFillMode::Backwards
605            && self.fill_mode != AnimationFillMode::Both
606        {
607            return;
608        }
609        if self.has_ended(now)
610            && self.fill_mode != AnimationFillMode::Forwards
611            && self.fill_mode != AnimationFillMode::Both
612        {
613            return;
614        }
615        let total_progress = total_progress
616            .min(self.current_iteration_end_progress())
617            .max(0.0);
618
619        // Get the indices of the previous (from) keyframe and the next (to) keyframe.
620        let next_keyframe_index;
621        let prev_keyframe_index;
622        let num_steps = self.computed_steps.len();
623        match self.current_direction {
624            AnimationDirection::Normal => {
625                next_keyframe_index = self
626                    .computed_steps
627                    .iter()
628                    .position(|step| total_progress as f32 <= step.start_percentage);
629                prev_keyframe_index = next_keyframe_index
630                    .and_then(|pos| if pos != 0 { Some(pos - 1) } else { None })
631                    .unwrap_or(0);
632            },
633            AnimationDirection::Reverse => {
634                next_keyframe_index = self
635                    .computed_steps
636                    .iter()
637                    .rev()
638                    .position(|step| total_progress as f32 <= 1. - step.start_percentage)
639                    .map(|pos| num_steps - pos - 1);
640                prev_keyframe_index = next_keyframe_index
641                    .and_then(|pos| {
642                        if pos != num_steps - 1 {
643                            Some(pos + 1)
644                        } else {
645                            None
646                        }
647                    })
648                    .unwrap_or(num_steps - 1)
649            },
650            _ => unreachable!(),
651        }
652
653        debug!(
654            "Animation::get_property_declaration_at_time: keyframe from {:?} to {:?}",
655            prev_keyframe_index, next_keyframe_index
656        );
657
658        let prev_keyframe = &self.computed_steps[prev_keyframe_index];
659        let next_keyframe = match next_keyframe_index {
660            Some(index) => &self.computed_steps[index],
661            None => return,
662        };
663
664        // If we only need to take into account one keyframe, then exit early
665        // in order to avoid doing more work.
666        let mut add_declarations_to_map = |keyframe: &ComputedKeyframe| {
667            for value in keyframe.values.iter() {
668                map.insert(value.id().to_owned(), value.clone());
669            }
670        };
671        if total_progress <= 0.0 {
672            add_declarations_to_map(&prev_keyframe);
673            return;
674        }
675        if total_progress >= 1.0 {
676            add_declarations_to_map(&next_keyframe);
677            return;
678        }
679
680        let percentage_between_keyframes =
681            (next_keyframe.start_percentage - prev_keyframe.start_percentage).abs() as f64;
682        let duration_between_keyframes = percentage_between_keyframes * self.duration;
683        let direction_aware_prev_keyframe_start_percentage = match self.current_direction {
684            AnimationDirection::Normal => prev_keyframe.start_percentage as f64,
685            AnimationDirection::Reverse => 1. - prev_keyframe.start_percentage as f64,
686            _ => unreachable!(),
687        };
688        let progress_between_keyframes = (total_progress
689            - direction_aware_prev_keyframe_start_percentage)
690            / percentage_between_keyframes;
691
692        for (from, to) in prev_keyframe.values.iter().zip(next_keyframe.values.iter()) {
693            let animation = PropertyAnimation {
694                from: from.clone(),
695                to: to.clone(),
696                timing_function: prev_keyframe.timing_function.clone(),
697                duration: duration_between_keyframes as f64,
698            };
699
700            let value = animation.calculate_value(progress_between_keyframes);
701            map.insert(value.id().to_owned(), value);
702        }
703    }
704}
705
706impl fmt::Debug for Animation {
707    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
708        f.debug_struct("Animation")
709            .field("name", &self.name)
710            .field("started_at", &self.started_at)
711            .field("duration", &self.duration)
712            .field("delay", &self.delay)
713            .field("iteration_state", &self.iteration_state)
714            .field("state", &self.state)
715            .field("direction", &self.direction)
716            .field("current_direction", &self.current_direction)
717            .field("cascade_style", &())
718            .finish()
719    }
720}
721
722/// A CSS Transition
723#[derive(Clone, Debug, MallocSizeOf)]
724pub struct Transition {
725    /// The start time of this transition, which is the current value of the animation
726    /// timeline when this transition was created plus any animation delay.
727    pub start_time: f64,
728
729    /// The delay used for this transition.
730    pub delay: f64,
731
732    /// The internal style `PropertyAnimation` for this transition.
733    pub property_animation: PropertyAnimation,
734
735    /// The state of this transition.
736    pub state: AnimationState,
737
738    /// Whether or not this transition is new and or has already been tracked
739    /// by the script thread.
740    pub is_new: bool,
741
742    /// If this `Transition` has been replaced by a new one this field is
743    /// used to help produce better reversed transitions.
744    pub reversing_adjusted_start_value: AnimationValue,
745
746    /// If this `Transition` has been replaced by a new one this field is
747    /// used to help produce better reversed transitions.
748    pub reversing_shortening_factor: f64,
749}
750
751impl Transition {
752    fn new(
753        start_time: f64,
754        delay: f64,
755        duration: f64,
756        from: AnimationValue,
757        to: AnimationValue,
758        timing_function: &TimingFunction,
759    ) -> Self {
760        let property_animation = PropertyAnimation {
761            from: from.clone(),
762            to,
763            timing_function: timing_function.clone(),
764            duration,
765        };
766        Self {
767            start_time,
768            delay,
769            property_animation,
770            state: AnimationState::Pending,
771            is_new: true,
772            reversing_adjusted_start_value: from,
773            reversing_shortening_factor: 1.0,
774        }
775    }
776
777    fn update_for_possibly_reversed_transition(
778        &mut self,
779        replaced_transition: &Transition,
780        delay: f64,
781        now: f64,
782    ) {
783        // If we reach here, we need to calculate a reversed transition according to
784        // https://drafts.csswg.org/css-transitions/#starting
785        //
786        //  "...if the reversing-adjusted start value of the running transition
787        //  is the same as the value of the property in the after-change style (see
788        //  the section on reversing of transitions for why these case exists),
789        //  implementations must cancel the running transition and start
790        //  a new transition..."
791        if replaced_transition.reversing_adjusted_start_value != self.property_animation.to {
792            return;
793        }
794
795        // "* reversing-adjusted start value is the end value of the running transition"
796        let replaced_animation = &replaced_transition.property_animation;
797        self.reversing_adjusted_start_value = replaced_animation.to.clone();
798
799        // "* reversing shortening factor is the absolute value, clamped to the
800        //    range [0, 1], of the sum of:
801        //    1. the output of the timing function of the old transition at the
802        //      time of the style change event, times the reversing shortening
803        //      factor of the old transition
804        //    2.  1 minus the reversing shortening factor of the old transition."
805        let transition_progress = ((now - replaced_transition.start_time)
806            / (replaced_transition.property_animation.duration))
807            .min(1.0)
808            .max(0.0);
809        let timing_function_output = replaced_animation.timing_function_output(transition_progress);
810        let old_reversing_shortening_factor = replaced_transition.reversing_shortening_factor;
811        self.reversing_shortening_factor = ((timing_function_output
812            * old_reversing_shortening_factor)
813            + (1.0 - old_reversing_shortening_factor))
814            .abs()
815            .min(1.0)
816            .max(0.0);
817
818        // "* start time is the time of the style change event plus:
819        //    1. if the matching transition delay is nonnegative, the matching
820        //       transition delay, or.
821        //    2. if the matching transition delay is negative, the product of the new
822        //       transition’s reversing shortening factor and the matching transition delay,"
823        self.start_time = if delay >= 0. {
824            now + delay
825        } else {
826            now + (self.reversing_shortening_factor * delay)
827        };
828
829        // "* end time is the start time plus the product of the matching transition
830        //    duration and the new transition’s reversing shortening factor,"
831        self.property_animation.duration *= self.reversing_shortening_factor;
832
833        // "* start value is the current value of the property in the running transition,
834        //  * end value is the value of the property in the after-change style,"
835        let procedure = Procedure::Interpolate {
836            progress: timing_function_output,
837        };
838        match replaced_animation
839            .from
840            .animate(&replaced_animation.to, procedure)
841        {
842            Ok(new_start) => self.property_animation.from = new_start,
843            Err(..) => {},
844        }
845    }
846
847    /// Whether or not this animation has ended at the provided time. This does
848    /// not take into account canceling i.e. when an animation or transition is
849    /// canceled due to changes in the style.
850    pub fn has_ended(&self, time: f64) -> bool {
851        time >= self.start_time + (self.property_animation.duration)
852    }
853
854    /// Update the given animation at a given point of progress.
855    pub fn calculate_value(&self, time: f64) -> AnimationValue {
856        let progress = (time - self.start_time) / (self.property_animation.duration);
857        self.property_animation
858            .calculate_value(progress.clamp(0.0, 1.0))
859    }
860}
861
862/// Holds the animation state for a particular element.
863#[derive(Debug, Default, MallocSizeOf)]
864pub struct ElementAnimationSet {
865    /// The animations for this element.
866    pub animations: Vec<Animation>,
867
868    /// The transitions for this element.
869    pub transitions: Vec<Transition>,
870
871    /// Whether or not this ElementAnimationSet has had animations or transitions
872    /// which have been added, removed, or had their state changed.
873    pub dirty: bool,
874}
875
876impl ElementAnimationSet {
877    /// Cancel all animations in this `ElementAnimationSet`. This is typically called
878    /// when the element has been removed from the DOM.
879    pub fn cancel_all_animations(&mut self) {
880        self.dirty = !self.animations.is_empty();
881        for animation in self.animations.iter_mut() {
882            animation.state = AnimationState::Canceled;
883        }
884        self.cancel_active_transitions();
885    }
886
887    fn cancel_active_transitions(&mut self) {
888        for transition in self.transitions.iter_mut() {
889            if transition.state != AnimationState::Finished {
890                self.dirty = true;
891                transition.state = AnimationState::Canceled;
892            }
893        }
894    }
895
896    /// Apply all active animations.
897    pub fn apply_active_animations(
898        &self,
899        context: &SharedStyleContext,
900        style: &mut Arc<ComputedValues>,
901    ) {
902        let now = context.current_time_for_animations;
903        let mutable_style = Arc::make_mut(style);
904        if let Some(map) = self.get_value_map_for_active_animations(now) {
905            for value in map.values() {
906                value.set_in_style_for_servo(mutable_style, context);
907            }
908        }
909
910        if let Some(map) = self.get_value_map_for_transitions(now, IgnoreTransitions::Canceled) {
911            for value in map.values() {
912                value.set_in_style_for_servo(mutable_style, context);
913            }
914        }
915    }
916
917    /// Clear all canceled animations and transitions from this `ElementAnimationSet`.
918    pub fn clear_canceled_animations(&mut self) {
919        self.animations
920            .retain(|animation| animation.state != AnimationState::Canceled);
921        self.transitions
922            .retain(|animation| animation.state != AnimationState::Canceled);
923    }
924
925    /// Whether this `ElementAnimationSet` is empty, which means it doesn't
926    /// hold any animations in any state.
927    pub fn is_empty(&self) -> bool {
928        self.animations.is_empty() && self.transitions.is_empty()
929    }
930
931    /// Whether or not this state needs animation ticks for its transitions
932    /// or animations.
933    pub fn needs_animation_ticks(&self) -> bool {
934        self.animations
935            .iter()
936            .any(|animation| animation.state.needs_to_be_ticked())
937            || self
938                .transitions
939                .iter()
940                .any(|transition| transition.state.needs_to_be_ticked())
941    }
942
943    /// The number of running animations and transitions for this `ElementAnimationSet`.
944    pub fn running_animation_and_transition_count(&self) -> usize {
945        self.animations
946            .iter()
947            .filter(|animation| animation.state.needs_to_be_ticked())
948            .count()
949            + self
950                .transitions
951                .iter()
952                .filter(|transition| transition.state.needs_to_be_ticked())
953                .count()
954    }
955
956    /// If this `ElementAnimationSet` has any any active animations.
957    pub fn has_active_animation(&self) -> bool {
958        self.animations
959            .iter()
960            .any(|animation| animation.state != AnimationState::Canceled)
961    }
962
963    /// If this `ElementAnimationSet` has any any active transitions.
964    pub fn has_active_transition(&self) -> bool {
965        self.transitions
966            .iter()
967            .any(|transition| transition.state != AnimationState::Canceled)
968    }
969
970    /// Update our animations given a new style, canceling or starting new animations
971    /// when appropriate.
972    pub fn update_animations_for_new_style<E>(
973        &mut self,
974        element: E,
975        context: &SharedStyleContext,
976        new_style: &Arc<ComputedValues>,
977        resolver: &mut StyleResolverForElement<E>,
978    ) where
979        E: TElement,
980    {
981        for animation in self.animations.iter_mut() {
982            if animation.is_cancelled_in_new_style(new_style) {
983                animation.state = AnimationState::Canceled;
984            }
985        }
986
987        maybe_start_animations(element, &context, &new_style, self, resolver);
988    }
989
990    /// Update our transitions given a new style, canceling or starting new animations
991    /// when appropriate.
992    pub fn update_transitions_for_new_style(
993        &mut self,
994        might_need_transitions_update: bool,
995        context: &SharedStyleContext,
996        old_style: Option<&Arc<ComputedValues>>,
997        after_change_style: &Arc<ComputedValues>,
998    ) {
999        // If this is the first style, we don't trigger any transitions and we assume
1000        // there were no previously triggered transitions.
1001        let mut before_change_style = match old_style {
1002            Some(old_style) => Arc::clone(old_style),
1003            None => return,
1004        };
1005
1006        // If the style of this element is display:none, then cancel all active transitions.
1007        if after_change_style.get_box().clone_display().is_none() {
1008            self.cancel_active_transitions();
1009            return;
1010        }
1011
1012        if !might_need_transitions_update {
1013            return;
1014        }
1015
1016        // We convert old values into `before-change-style` here.
1017        if self.has_active_transition() || self.has_active_animation() {
1018            self.apply_active_animations(context, &mut before_change_style);
1019        }
1020
1021        let transitioning_properties = start_transitions_if_applicable(
1022            context,
1023            &before_change_style,
1024            after_change_style,
1025            self,
1026        );
1027
1028        // Cancel any non-finished transitions that have properties which no
1029        // longer transition.
1030        //
1031        // Step 3 in https://drafts.csswg.org/css-transitions/#starting:
1032        // > If the element has a running transition or completed transition for
1033        // > the property, and there is not a matching transition-property value,
1034        // > then implementations must cancel the running transition or remove the
1035        // > completed transition from the set of completed transitions.
1036        //
1037        // TODO: This is happening here as opposed to in
1038        // `start_transition_if_applicable` as an optimization, but maybe this
1039        // code should be reworked to be more like the specification.
1040        for transition in self.transitions.iter_mut() {
1041            if transition.state == AnimationState::Finished
1042                || transition.state == AnimationState::Canceled
1043            {
1044                continue;
1045            }
1046            if transitioning_properties.contains(transition.property_animation.property_id()) {
1047                continue;
1048            }
1049            transition.state = AnimationState::Canceled;
1050            self.dirty = true;
1051        }
1052    }
1053
1054    fn start_transition_if_applicable(
1055        &mut self,
1056        context: &SharedStyleContext,
1057        property_declaration_id: &PropertyDeclarationId,
1058        index: usize,
1059        old_style: &ComputedValues,
1060        new_style: &Arc<ComputedValues>,
1061    ) {
1062        let style = new_style.get_ui();
1063        let allow_discrete =
1064            style.transition_behavior_mod(index) == TransitionBehavior::AllowDiscrete;
1065
1066        // FIXME(emilio): Handle the case where old_style and new_style's writing mode differ.
1067        let Some(from) = AnimationValue::from_computed_values(*property_declaration_id, old_style)
1068        else {
1069            return;
1070        };
1071        let Some(to) = AnimationValue::from_computed_values(*property_declaration_id, new_style)
1072        else {
1073            return;
1074        };
1075
1076        let timing_function = style.transition_timing_function_mod(index);
1077        let duration = style.transition_duration_mod(index).seconds() as f64;
1078        let delay = style.transition_delay_mod(index).seconds() as f64;
1079        let now = context.current_time_for_animations;
1080        let transitionable = property_declaration_id.is_animatable()
1081            && (allow_discrete || !property_declaration_id.is_discrete_animatable())
1082            && (allow_discrete || from.interpolable_with(&to));
1083
1084        let mut existing_transition = self.transitions.iter_mut().find(|transition| {
1085            transition.property_animation.property_id() == *property_declaration_id
1086        });
1087
1088        // Step 1:
1089        // > If all of the following are true:
1090        // >  - the element does not have a running transition for the property,
1091        // >  - the before-change style is different from the after-change style
1092        // >    for that property, and the values for the property are
1093        // >    transitionable,
1094        // >  - the element does not have a completed transition for the property
1095        // >    or the end value of the completed transition is different from the
1096        // >    after-change style for the property,
1097        // >  - there is a matching transition-property value, and
1098        // >  - the combined duration is greater than 0s,
1099        //
1100        // This function is only run if there is a matching transition-property
1101        // value, so that check is skipped here.
1102        let has_running_transition = existing_transition.as_ref().is_some_and(|transition| {
1103            transition.state != AnimationState::Finished
1104                && transition.state != AnimationState::Canceled
1105        });
1106        let no_completed_transition_or_end_values_differ =
1107            existing_transition.as_ref().is_none_or(|transition| {
1108                transition.state != AnimationState::Finished
1109                    || transition.property_animation.to != to
1110            });
1111        if !has_running_transition
1112            && from != to
1113            && transitionable
1114            && no_completed_transition_or_end_values_differ
1115            && (duration + delay > 0.0)
1116        {
1117            // > then implementations must remove the completed transition (if
1118            // > present) from the set of completed transitions and start a
1119            // > transition whose:
1120            // >
1121            // > - start time is the time of the style change event plus the matching transition delay,
1122            // > - end time is the start time plus the matching transition duration,
1123            // > - start value is the value of the transitioning property in the before-change style,
1124            // > - end value is the value of the transitioning property in the after-change style,
1125            // > - reversing-adjusted start value is the same as the start value, and
1126            // > - reversing shortening factor is 1.
1127            self.transitions.push(Transition::new(
1128                now + delay, /* start_time */
1129                delay,
1130                duration,
1131                from,
1132                to,
1133                &timing_function,
1134            ));
1135            self.dirty = true;
1136            return;
1137        }
1138
1139        // > Step 2: Otherwise, if the element has a completed transition for the
1140        // > property and the end value of the completed transition is different
1141        // > from the after-change style for the property, then implementations
1142        // > must remove the completed transition from the set of completed
1143        // > transitions.
1144        //
1145        // All completed transitions will be cleared from the `AnimationSet` in
1146        // `process_animations_for_style in `matching.rs`.
1147
1148        // > Step 3: If the element has a running transition or completed
1149        // > transition for the property, and there is not a matching
1150        // > transition-property value, then implementations must cancel the
1151        // > running transition or remove the completed transition from the set
1152        // > of completed transitions.
1153        //
1154        // - All completed transitions will be cleared cleared from the `AnimationSet` in
1155        //   `process_animations_for_style in `matching.rs`.
1156        // - Transitions for properties that don't have a matching transition-property
1157        //   value will be canceled in `Self::update_transitions_for_new_style`. In addition,
1158        //   this method is only called for properties that do ahave a matching
1159        //   transition-property value.
1160
1161        let Some(existing_transition) = existing_transition.as_mut() else {
1162            return;
1163        };
1164
1165        // > Step 4: If the element has a running transition for the property,
1166        // > there is a matching transition-property value, and the end value of
1167        // > the running transition is not equal to the value of the property in
1168        // > the after-change style, then:
1169        if has_running_transition && existing_transition.property_animation.to != to {
1170            // > Step 4.1: If the current value of the property in the running transition is
1171            // > equal to the value of the property in the after-change style, or
1172            // > if these two values are not transitionable, then implementations
1173            // > must cancel the running transition.
1174            let current_value = existing_transition.calculate_value(now);
1175            let transitionable_from_current_value =
1176                transitionable && (allow_discrete || current_value.interpolable_with(&to));
1177            if current_value == to || !transitionable_from_current_value {
1178                existing_transition.state = AnimationState::Canceled;
1179                self.dirty = true;
1180                return;
1181            }
1182
1183            // > Step 4.2: Otherwise, if the combined duration is less than or
1184            // > equal to 0s, or if the current value of the property in the
1185            // > running transition is not transitionable with the value of the
1186            // > property in the after-change style, then implementations must
1187            // > cancel the running transition.
1188            if duration + delay <= 0.0 {
1189                existing_transition.state = AnimationState::Canceled;
1190                self.dirty = true;
1191                return;
1192            }
1193
1194            // > Step 4.3: Otherwise, if the reversing-adjusted start value of the
1195            // > running transition is the same as the value of the property in
1196            // > the after-change style (see the section on reversing of
1197            // > transitions for why these case exists), implementations must
1198            // > cancel the running transition and start a new transition whose:
1199            if existing_transition.reversing_adjusted_start_value == to {
1200                existing_transition.state = AnimationState::Canceled;
1201
1202                let mut transition = Transition::new(
1203                    now + delay, /* start_time */
1204                    delay,
1205                    duration,
1206                    from,
1207                    to,
1208                    &timing_function,
1209                );
1210
1211                // This function takes care of applying all of the modifications to the transition
1212                // after "whose:" above.
1213                transition.update_for_possibly_reversed_transition(
1214                    &existing_transition,
1215                    delay,
1216                    now,
1217                );
1218
1219                self.transitions.push(transition);
1220                self.dirty = true;
1221                return;
1222            }
1223
1224            // > Step 4.4: Otherwise, implementations must cancel the running
1225            // > transition and start a new transition whose:
1226            // >  - start time is the time of the style change event plus the matching transition delay,
1227            // >  - end time is the start time plus the matching transition duration,
1228            // >  - start value is the current value of the property in the running transition,
1229            // >  - end value is the value of the property in the after-change style,
1230            // >  - reversing-adjusted start value is the same as the start value, and
1231            // >  - reversing shortening factor is 1.
1232            existing_transition.state = AnimationState::Canceled;
1233            self.transitions.push(Transition::new(
1234                now + delay, /* start_time */
1235                delay,
1236                duration,
1237                current_value,
1238                to,
1239                &timing_function,
1240            ));
1241            self.dirty = true;
1242        }
1243    }
1244
1245    /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s
1246    /// transitions, ignoring those specified by the `ignore_transitions`
1247    /// argument.
1248    fn get_value_map_for_transitions(
1249        &self,
1250        now: f64,
1251        ignore_transitions: IgnoreTransitions,
1252    ) -> Option<AnimationValueMap> {
1253        if !self.has_active_transition() {
1254            return None;
1255        }
1256
1257        let mut map =
1258            AnimationValueMap::with_capacity_and_hasher(self.transitions.len(), Default::default());
1259        for transition in &self.transitions {
1260            match ignore_transitions {
1261                IgnoreTransitions::Canceled => {
1262                    if transition.state == AnimationState::Canceled {
1263                        continue;
1264                    }
1265                },
1266                IgnoreTransitions::CanceledAndFinished => {
1267                    if transition.state == AnimationState::Canceled
1268                        || transition.state == AnimationState::Finished
1269                    {
1270                        continue;
1271                    }
1272                },
1273            }
1274
1275            let value = transition.calculate_value(now);
1276            map.insert(value.id().to_owned(), value);
1277        }
1278
1279        Some(map)
1280    }
1281
1282    /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s
1283    /// active animations at the given time value.
1284    pub fn get_value_map_for_active_animations(&self, now: f64) -> Option<AnimationValueMap> {
1285        if !self.has_active_animation() {
1286            return None;
1287        }
1288
1289        let mut map = Default::default();
1290        for animation in &self.animations {
1291            animation.get_property_declaration_at_time(now, &mut map);
1292        }
1293
1294        Some(map)
1295    }
1296}
1297
1298#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
1299/// A key that is used to identify nodes in the `DocumentAnimationSet`.
1300pub struct AnimationSetKey {
1301    /// The node for this `AnimationSetKey`.
1302    pub node: OpaqueNode,
1303    /// The pseudo element for this `AnimationSetKey`. If `None` this key will
1304    /// refer to the main content for its node.
1305    pub pseudo_element: Option<PseudoElement>,
1306}
1307
1308impl AnimationSetKey {
1309    /// Create a new key given a node and optional pseudo element.
1310    pub fn new(node: OpaqueNode, pseudo_element: Option<PseudoElement>) -> Self {
1311        AnimationSetKey {
1312            node,
1313            pseudo_element,
1314        }
1315    }
1316
1317    /// Create a new key for the main content of this node.
1318    pub fn new_for_non_pseudo(node: OpaqueNode) -> Self {
1319        AnimationSetKey {
1320            node,
1321            pseudo_element: None,
1322        }
1323    }
1324
1325    /// Create a new key for given node and pseudo element.
1326    pub fn new_for_pseudo(node: OpaqueNode, pseudo_element: PseudoElement) -> Self {
1327        AnimationSetKey {
1328            node,
1329            pseudo_element: Some(pseudo_element),
1330        }
1331    }
1332}
1333
1334#[derive(Clone, Debug, Default, MallocSizeOf)]
1335/// A set of animations for a document.
1336pub struct DocumentAnimationSet {
1337    /// The `ElementAnimationSet`s that this set contains.
1338    #[ignore_malloc_size_of = "Arc is hard"]
1339    pub sets: Arc<RwLock<FxHashMap<AnimationSetKey, ElementAnimationSet>>>,
1340}
1341
1342impl DocumentAnimationSet {
1343    /// Return whether or not the provided node has active CSS animations.
1344    pub fn has_active_animations(&self, key: &AnimationSetKey) -> bool {
1345        self.sets
1346            .read()
1347            .get(key)
1348            .map_or(false, |set| set.has_active_animation())
1349    }
1350
1351    /// Return whether or not the provided node has active CSS transitions.
1352    pub fn has_active_transitions(&self, key: &AnimationSetKey) -> bool {
1353        self.sets
1354            .read()
1355            .get(key)
1356            .map_or(false, |set| set.has_active_transition())
1357    }
1358
1359    /// Return a locked PropertyDeclarationBlock with animation values for the given
1360    /// key and time.
1361    pub fn get_animation_declarations(
1362        &self,
1363        key: &AnimationSetKey,
1364        time: f64,
1365        shared_lock: &SharedRwLock,
1366    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
1367        self.sets
1368            .read()
1369            .get(key)
1370            .and_then(|set| set.get_value_map_for_active_animations(time))
1371            .map(|map| {
1372                let block = PropertyDeclarationBlock::from_animation_value_map(&map);
1373                Arc::new(shared_lock.wrap(block))
1374            })
1375    }
1376
1377    /// Return a locked PropertyDeclarationBlock with transition values for the given
1378    /// key and time.
1379    pub fn get_transition_declarations(
1380        &self,
1381        key: &AnimationSetKey,
1382        time: f64,
1383        shared_lock: &SharedRwLock,
1384    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
1385        self.sets
1386            .read()
1387            .get(key)
1388            .and_then(|set| {
1389                set.get_value_map_for_transitions(time, IgnoreTransitions::CanceledAndFinished)
1390            })
1391            .map(|map| {
1392                let block = PropertyDeclarationBlock::from_animation_value_map(&map);
1393                Arc::new(shared_lock.wrap(block))
1394            })
1395    }
1396
1397    /// Get all the animation declarations for the given key, returning an empty
1398    /// `AnimationDeclarations` if there are no animations.
1399    pub fn get_all_declarations(
1400        &self,
1401        key: &AnimationSetKey,
1402        time: f64,
1403        shared_lock: &SharedRwLock,
1404    ) -> AnimationDeclarations {
1405        let sets = self.sets.read();
1406        let set = match sets.get(key) {
1407            Some(set) => set,
1408            None => return Default::default(),
1409        };
1410
1411        let animations = set.get_value_map_for_active_animations(time).map(|map| {
1412            let block = PropertyDeclarationBlock::from_animation_value_map(&map);
1413            Arc::new(shared_lock.wrap(block))
1414        });
1415        let transitions = set
1416            .get_value_map_for_transitions(time, IgnoreTransitions::CanceledAndFinished)
1417            .map(|map| {
1418                let block = PropertyDeclarationBlock::from_animation_value_map(&map);
1419                Arc::new(shared_lock.wrap(block))
1420            });
1421        AnimationDeclarations {
1422            animations,
1423            transitions,
1424        }
1425    }
1426
1427    /// Cancel all animations for set at the given key.
1428    pub fn cancel_all_animations_for_key(&self, key: &AnimationSetKey) {
1429        if let Some(set) = self.sets.write().get_mut(key) {
1430            set.cancel_all_animations();
1431        }
1432    }
1433}
1434
1435/// Kick off any new transitions for this node and return all of the properties that are
1436/// transitioning. This is at the end of calculating style for a single node.
1437pub fn start_transitions_if_applicable(
1438    context: &SharedStyleContext,
1439    old_style: &ComputedValues,
1440    new_style: &Arc<ComputedValues>,
1441    animation_state: &mut ElementAnimationSet,
1442) -> PropertyDeclarationIdSet {
1443    // See <https://www.w3.org/TR/css-transitions-1/#transitions>
1444    // "If a property is specified multiple times in the value of transition-property
1445    // (either on its own, via a shorthand that contains it, or via the all value),
1446    // then the transition that starts uses the duration, delay, and timing function
1447    // at the index corresponding to the last item in the value of transition-property
1448    // that calls for animating that property."
1449    // See Example 3 of <https://www.w3.org/TR/css-transitions-1/#transitions>
1450    //
1451    // Reversing the transition order here means that transitions defined later in the list
1452    // have preference, in accordance with the specification.
1453    //
1454    // TODO: It would be better to be able to do this without having to allocate an array.
1455    // We should restructure the code or make `transition_properties()` return a reversible
1456    // iterator in order to avoid the allocation.
1457    let mut transition_properties = new_style.transition_properties().collect::<Vec<_>>();
1458    transition_properties.reverse();
1459
1460    let mut properties_that_transition = PropertyDeclarationIdSet::default();
1461    for transition in transition_properties {
1462        let physical_property = transition
1463            .property
1464            .as_borrowed()
1465            .to_physical(new_style.writing_mode);
1466        if properties_that_transition.contains(physical_property) {
1467            continue;
1468        }
1469
1470        properties_that_transition.insert(physical_property);
1471        animation_state.start_transition_if_applicable(
1472            context,
1473            &physical_property,
1474            transition.index,
1475            old_style,
1476            new_style,
1477        );
1478    }
1479
1480    properties_that_transition
1481}
1482
1483/// Triggers animations for a given node looking at the animation property
1484/// values.
1485pub fn maybe_start_animations<E>(
1486    element: E,
1487    context: &SharedStyleContext,
1488    new_style: &Arc<ComputedValues>,
1489    animation_state: &mut ElementAnimationSet,
1490    resolver: &mut StyleResolverForElement<E>,
1491) where
1492    E: TElement,
1493{
1494    let style = new_style.get_ui();
1495    for (i, name) in style.animation_name_iter().enumerate() {
1496        let name = match name.as_atom() {
1497            Some(atom) => atom,
1498            None => continue,
1499        };
1500
1501        debug!("maybe_start_animations: name={}", name);
1502        let duration = style.animation_duration_mod(i).seconds() as f64;
1503        if duration == 0. {
1504            continue;
1505        }
1506
1507        let Some(keyframe_animation) = context.stylist.lookup_keyframes(name, element) else {
1508            continue;
1509        };
1510
1511        debug!("maybe_start_animations: animation {} found", name);
1512
1513        // NB: This delay may be negative, meaning that the animation may be created
1514        // in a state where we have advanced one or more iterations or even that the
1515        // animation begins in a finished state.
1516        let delay = style.animation_delay_mod(i).seconds();
1517
1518        let iteration_count = style.animation_iteration_count_mod(i);
1519        let iteration_state = if iteration_count.0.is_infinite() {
1520            KeyframesIterationState::Infinite(0.0)
1521        } else {
1522            KeyframesIterationState::Finite(0.0, iteration_count.0 as f64)
1523        };
1524
1525        let animation_direction = style.animation_direction_mod(i);
1526
1527        let initial_direction = match animation_direction {
1528            AnimationDirection::Normal | AnimationDirection::Alternate => {
1529                AnimationDirection::Normal
1530            },
1531            AnimationDirection::Reverse | AnimationDirection::AlternateReverse => {
1532                AnimationDirection::Reverse
1533            },
1534        };
1535
1536        let now = context.current_time_for_animations;
1537        let started_at = now + delay as f64;
1538        let mut starting_progress = (now - started_at) / duration;
1539        let state = match style.animation_play_state_mod(i) {
1540            AnimationPlayState::Paused => AnimationState::Paused(starting_progress),
1541            AnimationPlayState::Running => AnimationState::Pending,
1542        };
1543
1544        let computed_steps = ComputedKeyframe::generate_for_keyframes(
1545            element,
1546            &keyframe_animation,
1547            context,
1548            new_style,
1549            style.animation_timing_function_mod(i),
1550            resolver,
1551        );
1552
1553        let mut new_animation = Animation {
1554            name: name.clone(),
1555            properties_changed: keyframe_animation.properties_changed.clone(),
1556            computed_steps,
1557            started_at,
1558            duration,
1559            fill_mode: style.animation_fill_mode_mod(i),
1560            delay: delay as f64,
1561            iteration_state,
1562            state,
1563            direction: animation_direction,
1564            current_direction: initial_direction,
1565            cascade_style: new_style.clone(),
1566            is_new: true,
1567        };
1568
1569        // If we started with a negative delay, make sure we iterate the animation if
1570        // the delay moves us past the first iteration.
1571        while starting_progress > 1. && !new_animation.on_last_iteration() {
1572            new_animation.iterate();
1573            starting_progress -= 1.;
1574        }
1575
1576        animation_state.dirty = true;
1577
1578        // If the animation was already present in the list for the node, just update its state.
1579        for existing_animation in animation_state.animations.iter_mut() {
1580            if existing_animation.state == AnimationState::Canceled {
1581                continue;
1582            }
1583
1584            if new_animation.name == existing_animation.name {
1585                existing_animation
1586                    .update_from_other(&new_animation, context.current_time_for_animations);
1587                return;
1588            }
1589        }
1590
1591        animation_state.animations.push(new_animation);
1592    }
1593}