1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
8
9use std::cell::Cell;
10use std::sync::Arc;
11use std::time::Duration;
12
13use cssparser::ToCss;
14use embedder_traits::{AnimationState as AnimationsPresentState, UntrustedNodeAddress};
15use js::context::NoGC;
16use layout_api::AnimatingImages;
17use libc::c_void;
18use paint_api::ImageUpdate;
19use parking_lot::RwLock;
20use rustc_hash::FxHashMap;
21use script_bindings::cell::DomRefCell;
22use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
23use script_bindings::refcounted::Trusted;
24use serde::{Deserialize, Serialize};
25use servo_base::id::PipelineId;
26use servo_constellation_traits::ScriptToConstellationMessage;
27use style::animation::{
28 Animation, AnimationSetKey, AnimationState, DocumentAnimationSet, ElementAnimationSet,
29 KeyframesIterationState, Transition,
30};
31use style::dom::OpaqueNode;
32use style::selector_parser::PseudoElement;
33use timers::TimerId;
34
35use crate::dom::NodeFlags;
36use crate::dom::animationevent::AnimationEvent;
37use crate::dom::bindings::codegen::Bindings::AnimationEventBinding::AnimationEventInit;
38use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
39use crate::dom::bindings::codegen::Bindings::TransitionEventBinding::TransitionEventInit;
40use crate::dom::bindings::inheritance::Castable;
41use crate::dom::bindings::num::Finite;
42use crate::dom::bindings::root::{Dom, DomRoot};
43use crate::dom::bindings::str::DOMString;
44use crate::dom::bindings::trace::NoTrace;
45use crate::dom::event::Event;
46use crate::dom::node::{Node, NodeDamage, NodeTraits, from_untrusted_node_address};
47use crate::dom::transitionevent::TransitionEvent;
48use crate::dom::window::Window;
49use crate::event_loop::script_thread::with_script_thread;
50
51#[derive(Default, JSTraceable, MallocSizeOf)]
53#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
54pub(crate) struct AnimationManager {
55 #[no_trace]
57 sets: DocumentAnimationSet,
58
59 #[no_trace]
62 #[conditional_malloc_size_of]
63 animating_images: Arc<RwLock<AnimatingImages>>,
64
65 has_running_animations: Cell<bool>,
67
68 rooted_animation_nodes: DomRefCell<FxHashMap<NoTrace<OpaqueNode>, Dom<Node>>>,
70
71 rooted_image_nodes: DomRefCell<FxHashMap<NoTrace<OpaqueNode>, Dom<Node>>>,
74
75 pending_events: DomRefCell<Vec<TransitionOrAnimationEvent>>,
77
78 timeline_value_at_last_dirty: Cell<f64>,
82
83 #[no_trace]
85 image_callback_timer_id: Cell<Option<TimerId>>,
86}
87
88impl AnimationManager {
89 pub(crate) fn new() -> Self {
90 AnimationManager {
91 sets: Default::default(),
92 animating_images: Default::default(),
93 has_running_animations: Cell::new(false),
94 rooted_animation_nodes: Default::default(),
95 rooted_image_nodes: Default::default(),
96 pending_events: Default::default(),
97 timeline_value_at_last_dirty: Cell::new(0.0),
98 image_callback_timer_id: Default::default(),
99 }
100 }
101
102 pub(crate) fn animating_images(&self) -> Arc<RwLock<AnimatingImages>> {
103 self.animating_images.clone()
104 }
105
106 pub(crate) fn sets(&self) -> DocumentAnimationSet {
107 self.sets.clone()
108 }
109
110 pub(crate) fn clear(&self) {
111 self.sets.sets.write().clear();
112 self.animating_images.write().node_to_state_map.clear();
113 self.rooted_animation_nodes.borrow_mut().clear();
114 self.rooted_image_nodes.borrow_mut().clear();
115 self.pending_events.borrow_mut().clear();
116 }
117
118 pub(crate) fn mark_animating_nodes_as_dirty(
122 &self,
123 no_gc: &NoGC,
124 current_timeline_value: f64,
125 ) -> bool {
126 if current_timeline_value <= self.timeline_value_at_last_dirty.get() {
127 return false;
128 }
129 self.timeline_value_at_last_dirty
130 .set(current_timeline_value);
131
132 let sets = self.sets.sets.read();
133 let rooted_nodes = self.rooted_animation_nodes.borrow();
134 for node in sets
135 .keys()
136 .filter_map(|key| rooted_nodes.get(&NoTrace(key.node)))
137 {
138 node.dirty(no_gc, NodeDamage::Style);
139 }
140
141 true
142 }
143
144 pub(crate) fn update_for_new_timeline_value(&self, window: &Window, now: f64) {
145 let pipeline_id = window.pipeline_id();
146 let mut sets = self.sets.sets.write();
147
148 for (key, set) in sets.iter_mut() {
149 self.start_pending_animations(key, set, now, pipeline_id);
150
151 if now > self.timeline_value_at_last_dirty.get() {
153 for animation in set.animations.iter_mut() {
154 if animation.iterate_if_necessary(now) {
155 self.add_animation_event(
156 key,
157 animation,
158 TransitionOrAnimationEventType::AnimationIteration,
159 now,
160 pipeline_id,
161 );
162 }
163 }
164 }
165
166 self.finish_running_animations(key, set, now, pipeline_id);
167 }
168
169 self.unroot_unused_nodes(&sets);
170 }
171
172 pub(crate) fn cancel_animations_for_node(&self, node: &Node) {
174 if !node.get_flag(NodeFlags::HAS_EVER_ANIMATED) {
175 return;
176 }
177
178 let opaque_node = node.to_opaque();
179 let animation_keys = [
180 AnimationSetKey::new_for_non_pseudo(opaque_node),
181 AnimationSetKey::new_for_pseudo(opaque_node, PseudoElement::Before),
182 AnimationSetKey::new_for_pseudo(opaque_node, PseudoElement::After),
183 ];
184
185 let mut animations = self.sets.sets.write();
186 for key in animation_keys {
187 if let Some(set) = animations.get_mut(&key) {
188 set.cancel_all_animations();
189 }
190 }
191
192 self.animating_images().write().remove(opaque_node);
193 self.rooted_image_nodes
194 .borrow_mut()
195 .remove(&NoTrace(opaque_node));
196 }
197
198 pub(crate) fn do_post_reflow_update(&self, window: &Window, now: f64) {
205 let mut sets = self.sets.sets.write();
206 {
207 let rooted_nodes = self.rooted_animation_nodes.borrow();
208 for (key, set) in sets.iter_mut() {
209 if rooted_nodes.get(&NoTrace(key.node)).is_some_and(|node| {
210 !node.is_being_rendered_or_delegates_rendering(key.pseudo_element)
211 }) {
212 set.cancel_all_animations();
213 }
214 }
215 }
216
217 let pipeline_id = window.pipeline_id();
218 self.root_newly_animating_dom_nodes(&sets);
219
220 for (key, set) in sets.iter_mut() {
221 self.handle_canceled_animations(key, set, now, pipeline_id);
222 self.handle_new_animations(key, set, now, pipeline_id);
223 }
224
225 sets.retain(|_, state| !state.is_empty());
229 let have_running_animations = sets.values().any(|state| state.needs_animation_ticks());
230
231 self.update_running_animations_presence(window, have_running_animations);
232
233 self.rooted_image_nodes
235 .borrow_mut()
236 .retain(|opaque_node, node| {
237 if node.is_being_rendered(None) {
238 return true;
239 }
240 self.animating_images.write().remove(opaque_node.0);
241 false
242 });
243
244 if self.animating_images().write().clear_dirty() {
245 self.root_nodes_with_newly_animating_images();
246 self.maybe_schedule_image_animation_update(window, now);
247 }
248 }
249
250 pub(crate) fn update_active_image_animation_frames(&self, window: &Window, now: f64) {
251 if self.animating_images.read().is_empty() {
252 return;
253 }
254
255 let updates = self
256 .animating_images
257 .write()
258 .node_to_state_map
259 .values_mut()
260 .filter_map(|state| {
261 if !state.update_frame_for_animation_timeline_value(now) {
262 return None;
263 }
264
265 let image = &state.image;
266 let frame = image
267 .frame_data(state.active_frame)
268 .expect("No frame found")
269 .clone();
270 if let Some(mut descriptor) =
271 image.webrender_image_descriptor_and_offset_for_frame()
272 {
273 descriptor.offset = frame.byte_range.start as i32;
274 Some(ImageUpdate::UpdateImageForAnimation(
275 image.id.unwrap(),
276 descriptor,
277 ))
278 } else {
279 error!("Doing normal image update which will be slow!");
280 None
281 }
282 })
283 .collect();
284 window
285 .paint_api()
286 .update_images(window.webview_id().into(), updates);
287
288 self.maybe_schedule_image_animation_update(window, now);
289 }
290
291 fn root_nodes_with_newly_animating_images(&self) {
292 let mut rooted_nodes = self.rooted_image_nodes.borrow_mut();
293 for opaque_node in self.animating_images.read().node_to_state_map.keys() {
294 root_node_and_mark_as_having_animated(&mut rooted_nodes, *opaque_node);
295 }
296 }
297
298 fn maybe_schedule_image_animation_update(&self, window: &Window, now: f64) {
299 with_script_thread(|script_thread| {
300 if let Some(current_timer_id) = self.image_callback_timer_id.take() {
301 self.image_callback_timer_id.set(None);
302 script_thread.cancel_timer(current_timer_id);
303 }
304
305 if let Some(duration) = self.duration_to_next_image_animation_frame(now) {
306 let trusted_window = Trusted::new(window);
307 let timer_id = script_thread.schedule_timer(timers::TimerEventRequest {
308 callback: Box::new(move || {
309 let window = trusted_window.root();
310 window.Document().set_has_pending_animated_image_update();
311 }),
312 duration,
313 });
314 self.image_callback_timer_id.set(Some(timer_id));
315 }
316 })
317 }
318
319 fn duration_to_next_image_animation_frame(&self, now: f64) -> Option<Duration> {
320 self.animating_images
321 .read()
322 .node_to_state_map
323 .values()
324 .filter_map(|state| state.duration_to_next_frame(now))
325 .min()
326 }
327
328 fn update_running_animations_presence(&self, window: &Window, new_value: bool) {
329 let had_running_animations = self.has_running_animations.get();
330 if new_value == had_running_animations {
331 return;
332 }
333
334 self.has_running_animations.set(new_value);
335 self.handle_animation_presence_or_pending_events_change(window);
336 }
337
338 fn handle_animation_presence_or_pending_events_change(&self, window: &Window) {
339 let has_running_animations = self.has_running_animations.get();
340 let has_pending_events = !self.pending_events.borrow().is_empty();
341
342 let state = match has_running_animations || has_pending_events {
345 true => AnimationsPresentState::AnimationsPresent,
346 false => AnimationsPresentState::NoAnimationsPresent,
347 };
348 window.send_to_constellation(ScriptToConstellationMessage::ChangeRunningAnimationsState(
349 state,
350 ));
351 }
352
353 pub(crate) fn running_animation_count(&self) -> usize {
354 self.sets
355 .sets
356 .read()
357 .values()
358 .map(|state| state.running_animation_and_transition_count())
359 .sum()
360 }
361
362 fn start_pending_animations(
365 &self,
366 key: &AnimationSetKey,
367 set: &mut ElementAnimationSet,
368 now: f64,
369 pipeline_id: PipelineId,
370 ) {
371 for animation in set.animations.iter_mut() {
372 if animation.state == AnimationState::Pending && animation.started_at <= now {
373 animation.state = AnimationState::Running;
374 self.add_animation_event(
375 key,
376 animation,
377 TransitionOrAnimationEventType::AnimationStart,
378 now,
379 pipeline_id,
380 );
381 }
382 }
383
384 for transition in set.transitions.iter_mut() {
385 if transition.state == AnimationState::Pending && transition.start_time <= now {
386 transition.state = AnimationState::Running;
387 self.add_transition_event(
388 key,
389 transition,
390 TransitionOrAnimationEventType::TransitionStart,
391 now,
392 pipeline_id,
393 );
394 }
395 }
396 }
397
398 fn finish_running_animations(
401 &self,
402 key: &AnimationSetKey,
403 set: &mut ElementAnimationSet,
404 now: f64,
405 pipeline_id: PipelineId,
406 ) {
407 for animation in set.animations.iter_mut() {
408 if animation.state == AnimationState::Running && animation.has_ended(now) {
409 animation.state = AnimationState::Finished;
410 self.add_animation_event(
411 key,
412 animation,
413 TransitionOrAnimationEventType::AnimationEnd,
414 now,
415 pipeline_id,
416 );
417 }
418 }
419
420 for transition in set.transitions.iter_mut() {
421 if transition.state == AnimationState::Running && transition.has_ended(now) {
422 transition.state = AnimationState::Finished;
423 self.add_transition_event(
424 key,
425 transition,
426 TransitionOrAnimationEventType::TransitionEnd,
427 now,
428 pipeline_id,
429 );
430 }
431 }
432 }
433
434 fn handle_canceled_animations(
438 &self,
439 key: &AnimationSetKey,
440 set: &mut ElementAnimationSet,
441 now: f64,
442 pipeline_id: PipelineId,
443 ) {
444 for transition in &set.transitions {
445 if transition.state == AnimationState::Canceled {
446 self.add_transition_event(
447 key,
448 transition,
449 TransitionOrAnimationEventType::TransitionCancel,
450 now,
451 pipeline_id,
452 );
453 }
454 }
455
456 for animation in &set.animations {
457 if animation.state == AnimationState::Canceled {
458 self.add_animation_event(
459 key,
460 animation,
461 TransitionOrAnimationEventType::AnimationCancel,
462 now,
463 pipeline_id,
464 );
465 }
466 }
467
468 set.clear_canceled_animations();
469 }
470
471 fn handle_new_animations(
472 &self,
473 key: &AnimationSetKey,
474 set: &mut ElementAnimationSet,
475 now: f64,
476 pipeline_id: PipelineId,
477 ) {
478 for animation in set.animations.iter_mut() {
479 animation.is_new = false;
480 }
481
482 for transition in set.transitions.iter_mut() {
483 if transition.is_new {
484 self.add_transition_event(
485 key,
486 transition,
487 TransitionOrAnimationEventType::TransitionRun,
488 now,
489 pipeline_id,
490 );
491 transition.is_new = false;
492 }
493 }
494 }
495
496 fn root_newly_animating_dom_nodes(
499 &self,
500 sets: &style::FxHashMap<AnimationSetKey, ElementAnimationSet>,
501 ) {
502 let mut rooted_nodes = self.rooted_animation_nodes.borrow_mut();
503 for (key, set) in sets.iter() {
504 let opaque_node = key.node;
505 if rooted_nodes.contains_key(&NoTrace(opaque_node)) {
506 continue;
507 }
508
509 if set.animations.iter().any(|animation| animation.is_new) ||
510 set.transitions.iter().any(|transition| transition.is_new)
511 {
512 root_node_and_mark_as_having_animated(&mut rooted_nodes, opaque_node);
513 }
514 }
515 }
516
517 fn unroot_unused_nodes(&self, sets: &style::FxHashMap<AnimationSetKey, ElementAnimationSet>) {
519 let pending_events = self.pending_events.borrow();
520 let nodes: style::FxHashSet<OpaqueNode> = sets.keys().map(|key| key.node).collect();
521 self.rooted_animation_nodes.borrow_mut().retain(|node, _| {
522 nodes.contains(&node.0) || pending_events.iter().any(|event| event.node == node.0)
523 });
524 }
525
526 fn add_transition_event(
527 &self,
528 key: &AnimationSetKey,
529 transition: &Transition,
530 event_type: TransitionOrAnimationEventType,
531 now: f64,
532 pipeline_id: PipelineId,
533 ) {
534 let elapsed_time = match event_type {
537 TransitionOrAnimationEventType::TransitionRun |
538 TransitionOrAnimationEventType::TransitionStart => transition
539 .property_animation
540 .duration
541 .min((-transition.delay).max(0.)),
542 TransitionOrAnimationEventType::TransitionEnd => transition.property_animation.duration,
543 TransitionOrAnimationEventType::TransitionCancel => {
544 (now - transition.start_time).max(0.)
545 },
546 _ => unreachable!(),
547 }
548 .abs();
549
550 self.pending_events
551 .borrow_mut()
552 .push(TransitionOrAnimationEvent {
553 pipeline_id,
554 event_type,
555 node: key.node,
556 pseudo_element: key.pseudo_element,
557 property_or_animation_name: transition
558 .property_animation
559 .property_id()
560 .name()
561 .into(),
562 elapsed_time,
563 });
564 }
565
566 fn add_animation_event(
567 &self,
568 key: &AnimationSetKey,
569 animation: &Animation,
570 event_type: TransitionOrAnimationEventType,
571 now: f64,
572 pipeline_id: PipelineId,
573 ) {
574 let iteration_index = match animation.iteration_state {
575 KeyframesIterationState::Finite(current, _) |
576 KeyframesIterationState::Infinite(current) => current,
577 };
578
579 let active_duration = match animation.iteration_state {
580 KeyframesIterationState::Finite(_, max) => max * animation.duration,
581 KeyframesIterationState::Infinite(_) => f64::MAX,
582 };
583
584 let elapsed_time = match event_type {
587 TransitionOrAnimationEventType::AnimationStart => {
588 (-animation.delay).max(0.).min(active_duration)
589 },
590 TransitionOrAnimationEventType::AnimationIteration => {
591 iteration_index * animation.duration
592 },
593 TransitionOrAnimationEventType::AnimationEnd => {
594 (iteration_index * animation.duration) + animation.current_iteration_duration()
595 },
596 TransitionOrAnimationEventType::AnimationCancel => {
597 (iteration_index * animation.duration) + (now - animation.started_at).max(0.)
598 },
599 _ => unreachable!(),
600 }
601 .abs();
602
603 self.pending_events
604 .borrow_mut()
605 .push(TransitionOrAnimationEvent {
606 pipeline_id,
607 event_type,
608 node: key.node,
609 pseudo_element: key.pseudo_element,
610 property_or_animation_name: animation.name.to_string(),
611 elapsed_time,
612 });
613 }
614
615 pub(crate) fn send_pending_events(&self, window: &Window, cx: &mut js::context::JSContext) {
618 let events = std::mem::take(&mut *self.pending_events.safe_borrow_mut(cx.no_gc()));
624 if events.is_empty() {
625 return;
626 }
627
628 for event in events.into_iter() {
641 let node = match self
644 .rooted_animation_nodes
645 .borrow()
646 .get(&NoTrace(event.node))
647 {
648 Some(node) => DomRoot::from_ref(&**node),
649 None => {
650 warn!("Tried to send an event for an unrooted node");
651 continue;
652 },
653 };
654
655 let event_atom = match event.event_type {
656 TransitionOrAnimationEventType::AnimationEnd => atom!("animationend"),
657 TransitionOrAnimationEventType::AnimationStart => atom!("animationstart"),
658 TransitionOrAnimationEventType::AnimationCancel => atom!("animationcancel"),
659 TransitionOrAnimationEventType::AnimationIteration => atom!("animationiteration"),
660 TransitionOrAnimationEventType::TransitionCancel => atom!("transitioncancel"),
661 TransitionOrAnimationEventType::TransitionEnd => atom!("transitionend"),
662 TransitionOrAnimationEventType::TransitionRun => atom!("transitionrun"),
663 TransitionOrAnimationEventType::TransitionStart => atom!("transitionstart"),
664 };
665 let parent = EventInit {
666 bubbles: true,
667 cancelable: false,
668 composed: false,
669 };
670
671 let property_or_animation_name =
672 DOMString::from(event.property_or_animation_name.clone());
673 let pseudo_element = event
674 .pseudo_element
675 .map_or_else(DOMString::new, |pseudo_element| {
676 DOMString::from(pseudo_element.to_css_string())
677 });
678 let elapsed_time = Finite::new(event.elapsed_time as f32).unwrap();
679 let window = node.owner_window();
680
681 if event.event_type.is_transition_event() {
682 let event_init = TransitionEventInit {
683 parent,
684 propertyName: property_or_animation_name,
685 elapsedTime: elapsed_time,
686 pseudoElement: pseudo_element,
687 };
688 TransitionEvent::new(cx, &window, event_atom, &event_init)
689 .upcast::<Event>()
690 .fire(cx, node.upcast());
691 } else {
692 let event_init = AnimationEventInit {
693 parent,
694 animationName: property_or_animation_name,
695 elapsedTime: elapsed_time,
696 pseudoElement: pseudo_element,
697 };
698 AnimationEvent::new(cx, &window, event_atom, &event_init)
699 .upcast::<Event>()
700 .fire(cx, node.upcast());
701 }
702 }
703
704 if self.pending_events.borrow().is_empty() {
705 self.handle_animation_presence_or_pending_events_change(window);
706 }
707 }
708}
709
710#[derive(Clone, Debug, Deserialize, JSTraceable, MallocSizeOf, Serialize)]
713pub(crate) enum TransitionOrAnimationEventType {
714 TransitionRun,
717 TransitionStart,
719 TransitionEnd,
723 TransitionCancel,
725 AnimationStart,
728 AnimationIteration,
731 AnimationEnd,
733 AnimationCancel,
736}
737
738impl TransitionOrAnimationEventType {
739 pub(crate) fn is_transition_event(&self) -> bool {
741 match *self {
742 Self::TransitionRun |
743 Self::TransitionEnd |
744 Self::TransitionCancel |
745 Self::TransitionStart => true,
746 Self::AnimationEnd |
747 Self::AnimationIteration |
748 Self::AnimationStart |
749 Self::AnimationCancel => false,
750 }
751 }
752}
753
754#[derive(Deserialize, JSTraceable, MallocSizeOf, Serialize)]
755pub(crate) struct TransitionOrAnimationEvent {
757 #[no_trace]
759 pub(crate) pipeline_id: PipelineId,
760 pub(crate) event_type: TransitionOrAnimationEventType,
762 #[no_trace]
764 pub(crate) node: OpaqueNode,
765 #[no_trace]
767 pub(crate) pseudo_element: Option<PseudoElement>,
768 pub(crate) property_or_animation_name: String,
771 pub(crate) elapsed_time: f64,
773}
774
775fn root_node_and_mark_as_having_animated(
778 rooted_nodes: &mut FxHashMap<NoTrace<OpaqueNode>, Dom<Node>>,
779 opaque_node: OpaqueNode,
780) {
781 #[expect(unsafe_code)]
782 rooted_nodes.entry(NoTrace(opaque_node)).or_insert_with(|| {
783 let address = UntrustedNodeAddress(opaque_node.0 as *const c_void);
786 let node = unsafe { DomRoot::from_ref(&*from_untrusted_node_address(address)) };
787 node.set_flag(NodeFlags::HAS_EVER_ANIMATED, true);
788 node.as_traced()
789 });
790}