1use std::cell::Cell;
8
9use base::id::PipelineId;
10use constellation_traits::ScriptToConstellationMessage;
11use cssparser::ToCss;
12use embedder_traits::{AnimationState as AnimationsPresentState, UntrustedNodeAddress};
13use fxhash::{FxHashMap, FxHashSet};
14use libc::c_void;
15use serde::{Deserialize, Serialize};
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) {
151 let pipeline_id = window.pipeline_id();
152 let mut sets = self.sets.sets.write();
153 self.root_newly_animating_dom_nodes(&sets);
154
155 for (key, set) in sets.iter_mut() {
156 self.handle_canceled_animations(key, set, now, pipeline_id);
157 self.handle_new_animations(key, set, now, pipeline_id);
158 }
159
160 sets.retain(|_, state| !state.is_empty());
164 let have_running_animations = sets.values().any(|state| state.needs_animation_ticks());
165
166 self.update_running_animations_presence(window, have_running_animations);
167 }
168
169 fn update_running_animations_presence(&self, window: &Window, new_value: bool) {
170 let had_running_animations = self.has_running_animations.get();
171 if new_value == had_running_animations {
172 return;
173 }
174
175 self.has_running_animations.set(new_value);
176 self.handle_animation_presence_or_pending_events_change(window);
177 }
178
179 fn handle_animation_presence_or_pending_events_change(&self, window: &Window) {
180 let has_running_animations = self.has_running_animations.get();
181 let has_pending_events = !self.pending_events.borrow().is_empty();
182
183 let state = match has_running_animations || has_pending_events {
186 true => AnimationsPresentState::AnimationsPresent,
187 false => AnimationsPresentState::NoAnimationsPresent,
188 };
189 window.send_to_constellation(ScriptToConstellationMessage::ChangeRunningAnimationsState(
190 state,
191 ));
192 }
193
194 pub(crate) fn running_animation_count(&self) -> usize {
195 self.sets
196 .sets
197 .read()
198 .values()
199 .map(|state| state.running_animation_and_transition_count())
200 .sum()
201 }
202
203 fn start_pending_animations(
206 &self,
207 key: &AnimationSetKey,
208 set: &mut ElementAnimationSet,
209 now: f64,
210 pipeline_id: PipelineId,
211 ) {
212 for animation in set.animations.iter_mut() {
213 if animation.state == AnimationState::Pending && animation.started_at <= now {
214 animation.state = AnimationState::Running;
215 self.add_animation_event(
216 key,
217 animation,
218 TransitionOrAnimationEventType::AnimationStart,
219 now,
220 pipeline_id,
221 );
222 }
223 }
224
225 for transition in set.transitions.iter_mut() {
226 if transition.state == AnimationState::Pending && transition.start_time <= now {
227 transition.state = AnimationState::Running;
228 self.add_transition_event(
229 key,
230 transition,
231 TransitionOrAnimationEventType::TransitionStart,
232 now,
233 pipeline_id,
234 );
235 }
236 }
237 }
238
239 fn finish_running_animations(
242 &self,
243 key: &AnimationSetKey,
244 set: &mut ElementAnimationSet,
245 now: f64,
246 pipeline_id: PipelineId,
247 ) {
248 for animation in set.animations.iter_mut() {
249 if animation.state == AnimationState::Running && animation.has_ended(now) {
250 animation.state = AnimationState::Finished;
251 self.add_animation_event(
252 key,
253 animation,
254 TransitionOrAnimationEventType::AnimationEnd,
255 now,
256 pipeline_id,
257 );
258 }
259 }
260
261 for transition in set.transitions.iter_mut() {
262 if transition.state == AnimationState::Running && transition.has_ended(now) {
263 transition.state = AnimationState::Finished;
264 self.add_transition_event(
265 key,
266 transition,
267 TransitionOrAnimationEventType::TransitionEnd,
268 now,
269 pipeline_id,
270 );
271 }
272 }
273 }
274
275 fn handle_canceled_animations(
279 &self,
280 key: &AnimationSetKey,
281 set: &mut ElementAnimationSet,
282 now: f64,
283 pipeline_id: PipelineId,
284 ) {
285 for transition in &set.transitions {
286 if transition.state == AnimationState::Canceled {
287 self.add_transition_event(
288 key,
289 transition,
290 TransitionOrAnimationEventType::TransitionCancel,
291 now,
292 pipeline_id,
293 );
294 }
295 }
296
297 for animation in &set.animations {
298 if animation.state == AnimationState::Canceled {
299 self.add_animation_event(
300 key,
301 animation,
302 TransitionOrAnimationEventType::AnimationCancel,
303 now,
304 pipeline_id,
305 );
306 }
307 }
308
309 set.clear_canceled_animations();
310 }
311
312 fn handle_new_animations(
313 &self,
314 key: &AnimationSetKey,
315 set: &mut ElementAnimationSet,
316 now: f64,
317 pipeline_id: PipelineId,
318 ) {
319 for animation in set.animations.iter_mut() {
320 animation.is_new = false;
321 }
322
323 for transition in set.transitions.iter_mut() {
324 if transition.is_new {
325 self.add_transition_event(
326 key,
327 transition,
328 TransitionOrAnimationEventType::TransitionRun,
329 now,
330 pipeline_id,
331 );
332 transition.is_new = false;
333 }
334 }
335 }
336
337 #[allow(unsafe_code)]
340 fn root_newly_animating_dom_nodes(
341 &self,
342 sets: &FxHashMap<AnimationSetKey, ElementAnimationSet>,
343 ) {
344 let mut rooted_nodes = self.rooted_nodes.borrow_mut();
345 for (key, set) in sets.iter() {
346 let opaque_node = key.node;
347 if rooted_nodes.contains_key(&NoTrace(opaque_node)) {
348 continue;
349 }
350
351 if set.animations.iter().any(|animation| animation.is_new) ||
352 set.transitions.iter().any(|transition| transition.is_new)
353 {
354 let address = UntrustedNodeAddress(opaque_node.0 as *const c_void);
355 unsafe {
356 rooted_nodes.insert(
357 NoTrace(opaque_node),
358 Dom::from_ref(&*from_untrusted_node_address(address)),
359 )
360 };
361 }
362 }
363 }
364
365 fn unroot_unused_nodes(&self, sets: &FxHashMap<AnimationSetKey, ElementAnimationSet>) {
367 let pending_events = self.pending_events.borrow();
368 let nodes: FxHashSet<OpaqueNode> = sets.keys().map(|key| key.node).collect();
369 self.rooted_nodes.borrow_mut().retain(|node, _| {
370 nodes.contains(&node.0) || pending_events.iter().any(|event| event.node == node.0)
371 });
372 }
373
374 fn add_transition_event(
375 &self,
376 key: &AnimationSetKey,
377 transition: &Transition,
378 event_type: TransitionOrAnimationEventType,
379 now: f64,
380 pipeline_id: PipelineId,
381 ) {
382 let elapsed_time = match event_type {
385 TransitionOrAnimationEventType::TransitionRun |
386 TransitionOrAnimationEventType::TransitionStart => transition
387 .property_animation
388 .duration
389 .min((-transition.delay).max(0.)),
390 TransitionOrAnimationEventType::TransitionEnd => transition.property_animation.duration,
391 TransitionOrAnimationEventType::TransitionCancel => {
392 (now - transition.start_time).max(0.)
393 },
394 _ => unreachable!(),
395 }
396 .abs();
397
398 self.pending_events
399 .borrow_mut()
400 .push(TransitionOrAnimationEvent {
401 pipeline_id,
402 event_type,
403 node: key.node,
404 pseudo_element: key.pseudo_element,
405 property_or_animation_name: transition
406 .property_animation
407 .property_id()
408 .name()
409 .into(),
410 elapsed_time,
411 });
412 }
413
414 fn add_animation_event(
415 &self,
416 key: &AnimationSetKey,
417 animation: &Animation,
418 event_type: TransitionOrAnimationEventType,
419 now: f64,
420 pipeline_id: PipelineId,
421 ) {
422 let iteration_index = match animation.iteration_state {
423 KeyframesIterationState::Finite(current, _) |
424 KeyframesIterationState::Infinite(current) => current,
425 };
426
427 let active_duration = match animation.iteration_state {
428 KeyframesIterationState::Finite(_, max) => max * animation.duration,
429 KeyframesIterationState::Infinite(_) => f64::MAX,
430 };
431
432 let elapsed_time = match event_type {
435 TransitionOrAnimationEventType::AnimationStart => {
436 (-animation.delay).max(0.).min(active_duration)
437 },
438 TransitionOrAnimationEventType::AnimationIteration => {
439 iteration_index * animation.duration
440 },
441 TransitionOrAnimationEventType::AnimationEnd => {
442 (iteration_index * animation.duration) + animation.current_iteration_duration()
443 },
444 TransitionOrAnimationEventType::AnimationCancel => {
445 (iteration_index * animation.duration) + (now - animation.started_at).max(0.)
446 },
447 _ => unreachable!(),
448 }
449 .abs();
450
451 self.pending_events
452 .borrow_mut()
453 .push(TransitionOrAnimationEvent {
454 pipeline_id,
455 event_type,
456 node: key.node,
457 pseudo_element: key.pseudo_element,
458 property_or_animation_name: animation.name.to_string(),
459 elapsed_time,
460 });
461 }
462
463 pub(crate) fn send_pending_events(&self, window: &Window, can_gc: CanGc) {
466 let events = std::mem::take(&mut *self.pending_events.borrow_mut());
472 if events.is_empty() {
473 return;
474 }
475
476 for event in events.into_iter() {
489 let node = match self.rooted_nodes.borrow().get(&NoTrace(event.node)) {
492 Some(node) => DomRoot::from_ref(&**node),
493 None => {
494 warn!("Tried to send an event for an unrooted node");
495 continue;
496 },
497 };
498
499 let event_atom = match event.event_type {
500 TransitionOrAnimationEventType::AnimationEnd => atom!("animationend"),
501 TransitionOrAnimationEventType::AnimationStart => atom!("animationstart"),
502 TransitionOrAnimationEventType::AnimationCancel => atom!("animationcancel"),
503 TransitionOrAnimationEventType::AnimationIteration => atom!("animationiteration"),
504 TransitionOrAnimationEventType::TransitionCancel => atom!("transitioncancel"),
505 TransitionOrAnimationEventType::TransitionEnd => atom!("transitionend"),
506 TransitionOrAnimationEventType::TransitionRun => atom!("transitionrun"),
507 TransitionOrAnimationEventType::TransitionStart => atom!("transitionstart"),
508 };
509 let parent = EventInit {
510 bubbles: true,
511 cancelable: false,
512 composed: false,
513 };
514
515 let property_or_animation_name =
516 DOMString::from(event.property_or_animation_name.clone());
517 let pseudo_element = event
518 .pseudo_element
519 .map_or_else(DOMString::new, |pseudo_element| {
520 DOMString::from(pseudo_element.to_css_string())
521 });
522 let elapsed_time = Finite::new(event.elapsed_time as f32).unwrap();
523 let window = node.owner_window();
524
525 if event.event_type.is_transition_event() {
526 let event_init = TransitionEventInit {
527 parent,
528 propertyName: property_or_animation_name,
529 elapsedTime: elapsed_time,
530 pseudoElement: pseudo_element,
531 };
532 TransitionEvent::new(&window, event_atom, &event_init, can_gc)
533 .upcast::<Event>()
534 .fire(node.upcast(), can_gc);
535 } else {
536 let event_init = AnimationEventInit {
537 parent,
538 animationName: property_or_animation_name,
539 elapsedTime: elapsed_time,
540 pseudoElement: pseudo_element,
541 };
542 AnimationEvent::new(&window, event_atom, &event_init, can_gc)
543 .upcast::<Event>()
544 .fire(node.upcast(), can_gc);
545 }
546 }
547
548 if self.pending_events.borrow().is_empty() {
549 self.handle_animation_presence_or_pending_events_change(window);
550 }
551 }
552}
553
554#[derive(Clone, Debug, Deserialize, JSTraceable, MallocSizeOf, Serialize)]
557pub(crate) enum TransitionOrAnimationEventType {
558 TransitionRun,
561 TransitionStart,
563 TransitionEnd,
567 TransitionCancel,
569 AnimationStart,
572 AnimationIteration,
575 AnimationEnd,
577 AnimationCancel,
580}
581
582impl TransitionOrAnimationEventType {
583 pub(crate) fn is_transition_event(&self) -> bool {
585 match *self {
586 Self::TransitionRun |
587 Self::TransitionEnd |
588 Self::TransitionCancel |
589 Self::TransitionStart => true,
590 Self::AnimationEnd |
591 Self::AnimationIteration |
592 Self::AnimationStart |
593 Self::AnimationCancel => false,
594 }
595 }
596}
597
598#[derive(Deserialize, JSTraceable, MallocSizeOf, Serialize)]
599pub(crate) struct TransitionOrAnimationEvent {
601 #[no_trace]
603 pub(crate) pipeline_id: PipelineId,
604 pub(crate) event_type: TransitionOrAnimationEventType,
606 #[no_trace]
608 pub(crate) node: OpaqueNode,
609 #[no_trace]
611 pub(crate) pseudo_element: Option<PseudoElement>,
612 pub(crate) property_or_animation_name: String,
615 pub(crate) elapsed_time: f64,
617}