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