1use crate::dom::{now, request_timeout_callback, window, TimeoutCallbackHandle};
2use std::rc::Rc;
3use wasm_bindgen::closure::Closure;
4use wasm_bindgen::{JsCast, JsValue};
5
6#[derive(Clone)]
8pub struct IdleCallbackHandleReal {
9 handle: u32,
10 _closure: Rc<Closure<dyn FnMut(JsValue)>>,
11}
12
13impl Drop for IdleCallbackHandleReal {
15 fn drop(&mut self) {
16 window().cancel_idle_callback(self.handle);
17 }
18}
19
20fn request_idle_callback_real<F>(mut f: F) -> Result<IdleCallbackHandleReal, JsValue>
22where
23 F: FnMut(web_sys::IdleDeadline) + 'static,
24{
25 let closure = Closure::once(move |v: JsValue| {
26 let deadline = v
27 .dyn_into::<web_sys::IdleDeadline>()
28 .expect("must have an idle deadline");
29 f(deadline);
30 });
31
32 let handle = window().request_idle_callback(closure.as_ref().unchecked_ref())?;
33 Ok(IdleCallbackHandleReal {
34 handle,
35 _closure: Rc::new(closure),
36 })
37}
38
39#[derive(Clone)]
42pub enum IdleDeadline {
43 Real(web_sys::IdleDeadline),
45 Polyfill {
47 start: f64,
49 },
50}
51
52#[derive(Clone)]
54pub enum IdleCallbackHandle {
55 Real(IdleCallbackHandleReal),
57 Polyfill(TimeoutCallbackHandle),
59}
60
61impl IdleDeadline {
62 fn polyfill() -> Self {
63 Self::Polyfill { start: now() }
64 }
65
66 pub fn time_remaining(&self) -> f64 {
68 match self {
69 Self::Real(deadline) => deadline.time_remaining(),
70 Self::Polyfill { start } => 0.0_f64.max(50. - now() - start),
71 }
72 }
73
74 pub fn did_timeout(&self) -> bool {
76 match self {
77 Self::Real(deadline) => deadline.did_timeout(),
78 Self::Polyfill { .. } => self.time_remaining() > 0.,
79 }
80 }
81}
82
83pub fn request_idle_callback<F>(mut f: F) -> Result<IdleCallbackHandle, JsValue>
85where
86 F: FnMut(IdleDeadline) + 'static,
87{
88 let is_ric_available = window().get("requestIdleCallback").is_some();
89 if is_ric_available {
90 let handle = request_idle_callback_real(move |dl| {
91 let deadline = IdleDeadline::Real(dl);
92 f(deadline)
93 })?;
94 Ok(IdleCallbackHandle::Real(handle))
95 } else {
96 let handle = request_idle_callback_shim(move || f(IdleDeadline::polyfill()))?;
97 Ok(IdleCallbackHandle::Polyfill(handle))
98 }
99}
100
101fn request_idle_callback_shim<F>(f: F) -> Result<TimeoutCallbackHandle, JsValue>
102where
103 F: FnMut() + 'static,
104{
105 request_timeout_callback(f, 1)
106}