1use std::cell::Cell;
8
9use cssparser::ToCss;
10use embedder_traits::{AnimationState as AnimationsPresentState, UntrustedNodeAddress};
11use libc::c_void;
12use rustc_hash::{FxHashMap, FxHashSet};
13use script_bindings::cell::DomRefCell;
14use serde::{Deserialize, Serialize};
15use servo_base::id::PipelineId;
16use servo_constellation_traits::ScriptToConstellationMessage;
17use style::animation::{
18 Animation, AnimationSetKey, AnimationState, DocumentAnimationSet, ElementAnimationSet,
19 KeyframesIterationState, Transition,
20};
21use style::dom::OpaqueNode;
22use style::selector_parser::PseudoElement;
23
24use crate::dom::animationevent::AnimationEvent;
25use crate::dom::bindings::codegen::Bindings::AnimationEventBinding::AnimationEventInit;
26use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
27use crate::dom::bindings::codegen::Bindings::TransitionEventBinding::TransitionEventInit;
28use crate::dom::bindings::inheritance::Castable;
29use crate::dom::bindings::num::Finite;
30use crate::dom::bindings::root::{Dom, DomRoot};
31use crate::dom::bindings::str::DOMString;
32use crate::dom::bindings::trace::NoTrace;
33use crate::dom::event::Event;
34use crate::dom::node::{Node, NodeDamage, NodeTraits, from_untrusted_node_address};
35use crate::dom::transitionevent::TransitionEvent;
36use crate::dom::window::Window;
37
38#[derive(Default, JSTraceable, MallocSizeOf)]
40#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
41pub(crate) struct Animations {
42 #[no_trace]
44 pub(crate) sets: DocumentAnimationSet,
45
46 has_running_animations: Cell<bool>,
48
49 rooted_nodes: DomRefCell<FxHashMap<NoTrace<OpaqueNode>, Dom<Node>>>,
51
52 pending_events: DomRefCell<Vec<TransitionOrAnimationEvent>>,
54
55 timeline_value_at_last_dirty: Cell<f64>,
59}
60
61impl Animations {
62 pub(crate) fn new() -> Self {
63 Animations {
64 sets: Default::default(),
65 has_running_animations: Cell::new(false),
66 rooted_nodes: Default::default(),
67 pending_events: Default::default(),
68 timeline_value_at_last_dirty: Cell::new(0.0),
69 }
70 }
71
72 pub(crate) fn clear(&self) {
73 self.sets.sets.write().clear();
74 self.rooted_nodes.borrow_mut().clear();
75 self.pending_events.borrow_mut().clear();
76 }
77
78 pub(crate) fn mark_animating_nodes_as_dirty(&self, current_timeline_value: f64) -> bool {
82 if current_timeline_value <= self.timeline_value_at_last_dirty.get() {
83 return false;
84 }
85 self.timeline_value_at_last_dirty
86 .set(current_timeline_value);
87
88 let sets = self.sets.sets.read();
89 let rooted_nodes = self.rooted_nodes.borrow();
90 for node in sets
91 .keys()
92 .filter_map(|key| rooted_nodes.get(&NoTrace(key.node)))
93 {
94 node.dirty(NodeDamage::Style);
95 }
96
97 true
98 }
99
100 pub(crate) fn update_for_new_timeline_value(&self, window: &Window, now: f64) {
101 let pipeline_id = window.pipeline_id();
102 let mut sets = self.sets.sets.write();
103
104 for (key, set) in sets.iter_mut() {
105 self.start_pending_animations(key, set, now, pipeline_id);
106
107 for animation in set.animations.iter_mut() {
109 if animation.iterate_if_necessary(now) {
110 self.add_animation_event(
111 key,
112 animation,
113 TransitionOrAnimationEventType::AnimationIteration,
114 now,
115 pipeline_id,
116 );
117 }
118 }
119
120 self.finish_running_animations(key, set, now, pipeline_id);
121 }
122
123 self.unroot_unused_nodes(&sets);
124 }
125
126 pub(crate) fn cancel_animations_for_node(&self, node: &Node) {
128 let mut animations = self.sets.sets.write();
129 let mut cancel_animations_for = |key| {
130 if let Some(set) = animations.get_mut(&key) {
131 set.cancel_all_animations();
132 }
133 };
134
135 let opaque_node = node.to_opaque();
136 cancel_animations_for(AnimationSetKey::new_for_non_pseudo(opaque_node));
137 cancel_animations_for(AnimationSetKey::new_for_pseudo(
138 opaque_node,
139 PseudoElement::Before,
140 ));
141 cancel_animations_for(AnimationSetKey::new_for_pseudo(
142 opaque_node,
143 PseudoElement::After,
144 ));
145 }
146
147 pub(crate) fn do_post_reflow_update(&self, window: &Window, now: f64) {
152 let mut sets = self.sets.sets.write();
153 {
154 let rooted_nodes = self.rooted_nodes.borrow();
155 for (key, set) in sets.iter_mut() {
156 if rooted_nodes.get(&NoTrace(key.node)).is_some_and(|node| {
157 !node.is_being_rendered_or_delegates_rendering(key.pseudo_element)
158 }) {
159 set.cancel_all_animations();
160 }
161 }
162 }
163
164 let pipeline_id = window.pipeline_id();
165 self.root_newly_animating_dom_nodes(&sets);
166
167 for (key, set) in sets.iter_mut() {
168 self.handle_canceled_animations(key, set, now, pipeline_id);
169 self.handle_new_animations(key, set, now, pipeline_id);
170 }
171
172 sets.retain(|_, state| !state.is_empty());
176 let have_running_animations = sets.values().any(|state| state.needs_animation_ticks());
177
178 self.update_running_animations_presence(window, have_running_animations);
179 }
180
181 fn update_running_animations_presence(&self, window: &Window, new_value: bool) {
182 let had_running_animations = self.has_running_animations.get();
183 if new_value == had_running_animations {
184 return;
185 }
186
187 self.has_running_animations.set(new_value);
188 self.handle_animation_presence_or_pending_events_change(window);
189 }
190
191 fn handle_animation_presence_or_pending_events_change(&self, window: &Window) {
192 let has_running_animations = self.has_running_animations.get();
193 let has_pending_events = !self.pending_events.borrow().is_empty();
194
195 let state = match has_running_animations || has_pending_events {
198 true => AnimationsPresentState::AnimationsPresent,
199 false => AnimationsPresentState::NoAnimationsPresent,
200 };
201 window.send_to_constellation(ScriptToConstellationMessage::ChangeRunningAnimationsState(
202 state,
203 ));
204 }
205
206 pub(crate) fn running_animation_count(&self) -> usize {
207 self.sets
208 .sets
209 .read()
210 .values()
211 .map(|state| state.running_animation_and_transition_count())
212 .sum()
213 }
214
215 fn start_pending_animations(
218 &self,
219 key: &AnimationSetKey,
220 set: &mut ElementAnimationSet,
221 now: f64,
222 pipeline_id: PipelineId,
223 ) {
224 for animation in set.animations.iter_mut() {
225 if animation.state == AnimationState::Pending && animation.started_at <= now {
226 animation.state = AnimationState::Running;
227 self.add_animation_event(
228 key,
229 animation,
230 TransitionOrAnimationEventType::AnimationStart,
231 now,
232 pipeline_id,
233 );
234 }
235 }
236
237 for transition in set.transitions.iter_mut() {
238 if transition.state == AnimationState::Pending && transition.start_time <= now {
239 transition.state = AnimationState::Running;
240 self.add_transition_event(
241 key,
242 transition,
243 TransitionOrAnimationEventType::TransitionStart,
244 now,
245 pipeline_id,
246 );
247 }
248 }
249 }
250
251 fn finish_running_animations(
254 &self,
255 key: &AnimationSetKey,
256 set: &mut ElementAnimationSet,
257 now: f64,
258 pipeline_id: PipelineId,
259 ) {
260 for animation in set.animations.iter_mut() {
261 if animation.state == AnimationState::Running && animation.has_ended(now) {
262 animation.state = AnimationState::Finished;
263 self.add_animation_event(
264 key,
265 animation,
266 TransitionOrAnimationEventType::AnimationEnd,
267 now,
268 pipeline_id,
269 );
270 }
271 }
272
273 for transition in set.transitions.iter_mut() {
274 if transition.state == AnimationState::Running && transition.has_ended(now) {
275 transition.state = AnimationState::Finished;
276 self.add_transition_event(
277 key,
278 transition,
279 TransitionOrAnimationEventType::TransitionEnd,
280 now,
281 pipeline_id,
282 );
283 }
284 }
285 }
286
287 fn handle_canceled_animations(
291 &self,
292 key: &AnimationSetKey,
293 set: &mut ElementAnimationSet,
294 now: f64,
295 pipeline_id: PipelineId,
296 ) {
297 for transition in &set.transitions {
298 if transition.state == AnimationState::Canceled {
299 self.add_transition_event(
300 key,
301 transition,
302 TransitionOrAnimationEventType::TransitionCancel,
303 now,
304 pipeline_id,
305 );
306 }
307 }
308
309 for animation in &set.animations {
310 if animation.state == AnimationState::Canceled {
311 self.add_animation_event(
312 key,
313 animation,
314 TransitionOrAnimationEventType::AnimationCancel,
315 now,
316 pipeline_id,
317 );
318 }
319 }
320
321 set.clear_canceled_animations();
322 }
323
324 fn handle_new_animations(
325 &self,
326 key: &AnimationSetKey,
327 set: &mut ElementAnimationSet,
328 now: f64,
329 pipeline_id: PipelineId,
330 ) {
331 for animation in set.animations.iter_mut() {
332 animation.is_new = false;
333 }
334
335 for transition in set.transitions.iter_mut() {
336 if transition.is_new {
337 self.add_transition_event(
338 key,
339 transition,
340 TransitionOrAnimationEventType::TransitionRun,
341 now,
342 pipeline_id,
343 );
344 transition.is_new = false;
345 }
346 }
347 }
348
349 #[expect(unsafe_code)]
352 fn root_newly_animating_dom_nodes(
353 &self,
354 sets: &FxHashMap<AnimationSetKey, ElementAnimationSet>,
355 ) {
356 let mut rooted_nodes = self.rooted_nodes.borrow_mut();
357 for (key, set) in sets.iter() {
358 let opaque_node = key.node;
359 if rooted_nodes.contains_key(&NoTrace(opaque_node)) {
360 continue;
361 }
362
363 if set.animations.iter().any(|animation| animation.is_new) ||
364 set.transitions.iter().any(|transition| transition.is_new)
365 {
366 let address = UntrustedNodeAddress(opaque_node.0 as *const c_void);
367 unsafe {
368 rooted_nodes.insert(
369 NoTrace(opaque_node),
370 Dom::from_ref(&*from_untrusted_node_address(address)),
371 )
372 };
373 }
374 }
375 }
376
377 fn unroot_unused_nodes(&self, sets: &FxHashMap<AnimationSetKey, ElementAnimationSet>) {
379 let pending_events = self.pending_events.borrow();
380 let nodes: FxHashSet<OpaqueNode> = sets.keys().map(|key| key.node).collect();
381 self.rooted_nodes.borrow_mut().retain(|node, _| {
382 nodes.contains(&node.0) || pending_events.iter().any(|event| event.node == node.0)
383 });
384 }
385
386 fn add_transition_event(
387 &self,
388 key: &AnimationSetKey,
389 transition: &Transition,
390 event_type: TransitionOrAnimationEventType,
391 now: f64,
392 pipeline_id: PipelineId,
393 ) {
394 let elapsed_time = match event_type {
397 TransitionOrAnimationEventType::TransitionRun |
398 TransitionOrAnimationEventType::TransitionStart => transition
399 .property_animation
400 .duration
401 .min((-transition.delay).max(0.)),
402 TransitionOrAnimationEventType::TransitionEnd => transition.property_animation.duration,
403 TransitionOrAnimationEventType::TransitionCancel => {
404 (now - transition.start_time).max(0.)
405 },
406 _ => unreachable!(),
407 }
408 .abs();
409
410 self.pending_events
411 .borrow_mut()
412 .push(TransitionOrAnimationEvent {
413 pipeline_id,
414 event_type,
415 node: key.node,
416 pseudo_element: key.pseudo_element,
417 property_or_animation_name: transition
418 .property_animation
419 .property_id()
420 .name()
421 .into(),
422 elapsed_time,
423 });
424 }
425
426 fn add_animation_event(
427 &self,
428 key: &AnimationSetKey,
429 animation: &Animation,
430 event_type: TransitionOrAnimationEventType,
431 now: f64,
432 pipeline_id: PipelineId,
433 ) {
434 let iteration_index = match animation.iteration_state {
435 KeyframesIterationState::Finite(current, _) |
436 KeyframesIterationState::Infinite(current) => current,
437 };
438
439 let active_duration = match animation.iteration_state {
440 KeyframesIterationState::Finite(_, max) => max * animation.duration,
441 KeyframesIterationState::Infinite(_) => f64::MAX,
442 };
443
444 let elapsed_time = match event_type {
447 TransitionOrAnimationEventType::AnimationStart => {
448 (-animation.delay).max(0.).min(active_duration)
449 },
450 TransitionOrAnimationEventType::AnimationIteration => {
451 iteration_index * animation.duration
452 },
453 TransitionOrAnimationEventType::AnimationEnd => {
454 (iteration_index * animation.duration) + animation.current_iteration_duration()
455 },
456 TransitionOrAnimationEventType::AnimationCancel => {
457 (iteration_index * animation.duration) + (now - animation.started_at).max(0.)
458 },
459 _ => unreachable!(),
460 }
461 .abs();
462
463 self.pending_events
464 .borrow_mut()
465 .push(TransitionOrAnimationEvent {
466 pipeline_id,
467 event_type,
468 node: key.node,
469 pseudo_element: key.pseudo_element,
470 property_or_animation_name: animation.name.to_string(),
471 elapsed_time,
472 });
473 }
474
475 pub(crate) fn send_pending_events(&self, window: &Window, cx: &mut js::context::JSContext) {
478 let events = std::mem::take(&mut *self.pending_events.borrow_mut());
484 if events.is_empty() {
485 return;
486 }
487
488 for event in events.into_iter() {
501 let node = match self.rooted_nodes.borrow().get(&NoTrace(event.node)) {
504 Some(node) => DomRoot::from_ref(&**node),
505 None => {
506 warn!("Tried to send an event for an unrooted node");
507 continue;
508 },
509 };
510
511 let event_atom = match event.event_type {
512 TransitionOrAnimationEventType::AnimationEnd => atom!("animationend"),
513 TransitionOrAnimationEventType::AnimationStart => atom!("animationstart"),
514 TransitionOrAnimationEventType::AnimationCancel => atom!("animationcancel"),
515 TransitionOrAnimationEventType::AnimationIteration => atom!("animationiteration"),
516 TransitionOrAnimationEventType::TransitionCancel => atom!("transitioncancel"),
517 TransitionOrAnimationEventType::TransitionEnd => atom!("transitionend"),
518 TransitionOrAnimationEventType::TransitionRun => atom!("transitionrun"),
519 TransitionOrAnimationEventType::TransitionStart => atom!("transitionstart"),
520 };
521 let parent = EventInit {
522 bubbles: true,
523 cancelable: false,
524 composed: false,
525 };
526
527 let property_or_animation_name =
528 DOMString::from(event.property_or_animation_name.clone());
529 let pseudo_element = event
530 .pseudo_element
531 .map_or_else(DOMString::new, |pseudo_element| {
532 DOMString::from(pseudo_element.to_css_string())
533 });
534 let elapsed_time = Finite::new(event.elapsed_time as f32).unwrap();
535 let window = node.owner_window();
536
537 if event.event_type.is_transition_event() {
538 let event_init = TransitionEventInit {
539 parent,
540 propertyName: property_or_animation_name,
541 elapsedTime: elapsed_time,
542 pseudoElement: pseudo_element,
543 };
544 TransitionEvent::new(cx, &window, event_atom, &event_init)
545 .upcast::<Event>()
546 .fire(cx, node.upcast());
547 } else {
548 let event_init = AnimationEventInit {
549 parent,
550 animationName: property_or_animation_name,
551 elapsedTime: elapsed_time,
552 pseudoElement: pseudo_element,
553 };
554 AnimationEvent::new(cx, &window, event_atom, &event_init)
555 .upcast::<Event>()
556 .fire(cx, node.upcast());
557 }
558 }
559
560 if self.pending_events.borrow().is_empty() {
561 self.handle_animation_presence_or_pending_events_change(window);
562 }
563 }
564}
565
566#[derive(Clone, Debug, Deserialize, JSTraceable, MallocSizeOf, Serialize)]
569pub(crate) enum TransitionOrAnimationEventType {
570 TransitionRun,
573 TransitionStart,
575 TransitionEnd,
579 TransitionCancel,
581 AnimationStart,
584 AnimationIteration,
587 AnimationEnd,
589 AnimationCancel,
592}
593
594impl TransitionOrAnimationEventType {
595 pub(crate) fn is_transition_event(&self) -> bool {
597 match *self {
598 Self::TransitionRun |
599 Self::TransitionEnd |
600 Self::TransitionCancel |
601 Self::TransitionStart => true,
602 Self::AnimationEnd |
603 Self::AnimationIteration |
604 Self::AnimationStart |
605 Self::AnimationCancel => false,
606 }
607 }
608}
609
610#[derive(Deserialize, JSTraceable, MallocSizeOf, Serialize)]
611pub(crate) struct TransitionOrAnimationEvent {
613 #[no_trace]
615 pub(crate) pipeline_id: PipelineId,
616 pub(crate) event_type: TransitionOrAnimationEventType,
618 #[no_trace]
620 pub(crate) node: OpaqueNode,
621 #[no_trace]
623 pub(crate) pseudo_element: Option<PseudoElement>,
624 pub(crate) property_or_animation_name: String,
627 pub(crate) elapsed_time: f64,
629}