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