script/
image_animation.rs1use std::cell::Cell;
6use std::sync::Arc;
7use std::time::Duration;
8
9use compositing_traits::{ImageUpdate, SerializableImageData};
10use embedder_traits::UntrustedNodeAddress;
11use fxhash::FxHashMap;
12use ipc_channel::ipc::IpcSharedMemory;
13use layout_api::ImageAnimationState;
14use libc::c_void;
15use malloc_size_of::MallocSizeOf;
16use parking_lot::RwLock;
17use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
18use script_bindings::root::Dom;
19use style::dom::OpaqueNode;
20use timers::{TimerEventRequest, TimerId};
21use webrender_api::units::DeviceIntSize;
22use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat};
23
24use crate::dom::bindings::cell::DomRefCell;
25use crate::dom::bindings::refcounted::Trusted;
26use crate::dom::bindings::trace::NoTrace;
27use crate::dom::node::{Node, from_untrusted_node_address};
28use crate::dom::window::Window;
29use crate::script_thread::with_script_thread;
30
31#[derive(Clone, Default, JSTraceable)]
32#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
33pub struct ImageAnimationManager {
34 #[no_trace]
35 node_to_image_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
36
37 rooted_nodes: DomRefCell<FxHashMap<NoTrace<OpaqueNode>, Dom<Node>>>,
41
42 #[no_trace]
44 callback_timer_id: Cell<Option<TimerId>>,
45}
46
47impl MallocSizeOf for ImageAnimationManager {
48 fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
49 (*self.node_to_image_map.read()).size_of(ops) + self.rooted_nodes.size_of(ops)
50 }
51}
52
53impl ImageAnimationManager {
54 pub(crate) fn node_to_image_map(
55 &self,
56 ) -> Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>> {
57 self.node_to_image_map.clone()
58 }
59
60 fn duration_to_next_frame(&self, now: f64) -> Option<Duration> {
61 self.node_to_image_map
62 .read()
63 .values()
64 .map(|state| state.duration_to_next_frame(now))
65 .min()
66 }
67
68 pub(crate) fn update_active_frames(&self, window: &Window, now: f64) {
69 if self.node_to_image_map.read().is_empty() {
70 return;
71 }
72
73 let rooted_nodes = self.rooted_nodes.borrow();
74 let updates = self
75 .node_to_image_map
76 .write()
77 .iter_mut()
78 .filter_map(|(node, state)| {
79 if !state.update_frame_for_animation_timeline_value(now) {
80 return None;
81 }
82
83 let image = &state.image;
84 let frame = image
85 .frame(state.active_frame)
86 .expect("active_frame should within range of frames");
87
88 if let Some(node) = rooted_nodes.get(&NoTrace(*node)) {
89 node.dirty(crate::dom::node::NodeDamage::Other);
90 }
91 Some(ImageUpdate::UpdateImage(
92 image.id.unwrap(),
93 ImageDescriptor {
94 format: ImageFormat::BGRA8,
95 size: DeviceIntSize::new(
96 image.metadata.width as i32,
97 image.metadata.height as i32,
98 ),
99 stride: None,
100 offset: 0,
101 flags: ImageDescriptorFlags::ALLOW_MIPMAPS,
102 },
103 SerializableImageData::Raw(IpcSharedMemory::from_bytes(frame.bytes)),
104 None,
105 ))
106 })
107 .collect();
108 window.compositor_api().update_images(updates);
109
110 self.maybe_schedule_animated_image_update_callback(window, now);
111 }
112
113 #[allow(unsafe_code)]
117 pub(crate) fn update_rooted_dom_nodes(&self, window: &Window, now: f64) {
118 let mut rooted_nodes = self.rooted_nodes.borrow_mut();
119 let node_to_image_map = self.node_to_image_map.read();
120
121 let mut added_node = false;
122 for opaque_node in node_to_image_map.keys() {
123 let opaque_node = *opaque_node;
124 if rooted_nodes.contains_key(&NoTrace(opaque_node)) {
125 continue;
126 }
127
128 added_node = true;
129 let address = UntrustedNodeAddress(opaque_node.0 as *const c_void);
130 unsafe {
131 rooted_nodes.insert(
132 NoTrace(opaque_node),
133 Dom::from_ref(&*from_untrusted_node_address(address)),
134 )
135 };
136 }
137
138 let length_before = rooted_nodes.len();
139 rooted_nodes.retain(|node, _| node_to_image_map.contains_key(&node.0));
140
141 if added_node || length_before != rooted_nodes.len() {
142 self.maybe_schedule_animated_image_update_callback(window, now);
143 }
144 }
145
146 fn maybe_schedule_animated_image_update_callback(&self, window: &Window, now: f64) {
147 with_script_thread(|script_thread| {
148 if let Some(current_timer_id) = self.callback_timer_id.take() {
149 self.callback_timer_id.set(None);
150 script_thread.cancel_timer(current_timer_id);
151 }
152
153 if let Some(duration) = self.duration_to_next_frame(now) {
154 let trusted_window = Trusted::new(window);
155 let timer_id = script_thread.schedule_timer(TimerEventRequest {
156 callback: Box::new(move || {
157 let window = trusted_window.root();
158 window.Document().set_has_pending_animated_image_update();
159 }),
160 duration,
161 });
162 self.callback_timer_id.set(Some(timer_id));
163 }
164 })
165 }
166}