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