use crate::dom::{now, request_timeout_callback, window, TimeoutCallbackHandle};
use wasm_bindgen::closure::Closure;
use wasm_bindgen::{JsCast, JsValue};
pub struct IdleCallbackHandleReal {
handle: u32,
_closure: Closure<dyn FnMut(JsValue)>,
}
impl Drop for IdleCallbackHandleReal {
fn drop(&mut self) {
window().cancel_idle_callback(self.handle);
}
}
fn request_idle_callback_real<F>(mut f: F) -> Result<IdleCallbackHandleReal, JsValue>
where
F: FnMut(web_sys::IdleDeadline) + 'static,
{
let closure = Closure::once(move |v: JsValue| {
let deadline = v
.dyn_into::<web_sys::IdleDeadline>()
.expect("must have an idle deadline");
f(deadline);
});
let handle = window().request_idle_callback(closure.as_ref().unchecked_ref())?;
Ok(IdleCallbackHandleReal {
handle,
_closure: closure,
})
}
pub enum IdleDeadline {
Real(web_sys::IdleDeadline),
Polyfill {
start: f64,
},
}
pub enum IdleCallbackHandle {
Real(IdleCallbackHandleReal),
Polyfill(TimeoutCallbackHandle),
}
impl IdleDeadline {
fn polyfill() -> Self {
Self::Polyfill { start: now() }
}
pub fn time_remaining(&self) -> f64 {
match self {
Self::Real(deadline) => deadline.time_remaining(),
Self::Polyfill { start } => 0.0_f64.max(50. - now() - start),
}
}
pub fn did_timeout(&self) -> bool {
match self {
Self::Real(deadline) => deadline.did_timeout(),
Self::Polyfill { .. } => self.time_remaining() > 0.,
}
}
}
pub fn request_idle_callback<F>(mut f: F) -> Result<IdleCallbackHandle, JsValue>
where
F: FnMut(IdleDeadline) + 'static,
{
let is_ric_available = window().get("requestIdleCallback").is_some();
if is_ric_available {
let handle = request_idle_callback_real(move |dl| {
let deadline = IdleDeadline::Real(dl);
f(deadline)
})?;
Ok(IdleCallbackHandle::Real(handle))
} else {
let handle = request_idle_callback_shim(move || f(IdleDeadline::polyfill()))?;
Ok(IdleCallbackHandle::Polyfill(handle))
}
}
fn request_idle_callback_shim<F>(f: F) -> Result<TimeoutCallbackHandle, JsValue>
where
F: FnMut() + 'static,
{
request_timeout_callback(f, 1)
}