runtime/globals/
timers.rs1use std::time::Duration;
8
9use ion::function::{Clamp, Enforce, Opt, Rest};
10use ion::{Context, Error, Function, Object, Result, function_spec, js_fn};
11use mozjs::jsapi::JSFunctionSpec;
12use mozjs::jsval::JSVal;
13
14use crate::ContextExt as _;
15use crate::event_loop::macrotasks::{Macrotask, TimerMacrotask, UserMacrotask};
16
17const MINIMUM_DELAY: i32 = 1;
18const MINIMUM_DELAY_NESTED: i32 = 4;
19
20fn set_timer(
21 cx: &Context, callback: &Function, duration: Option<Clamp<i32>>, arguments: Box<[JSVal]>, repeat: bool,
22) -> Result<u32> {
23 let event_loop = unsafe { &mut cx.get_private().event_loop };
24 if let Some(queue) = &mut event_loop.macrotasks {
25 let minimum = if queue.nesting > 5 {
26 MINIMUM_DELAY_NESTED
27 } else {
28 MINIMUM_DELAY
29 };
30
31 let duration = duration.map_or(minimum, |t| t.0.max(minimum));
32 let timer = TimerMacrotask::new(
33 callback,
34 arguments,
35 repeat,
36 Duration::from_millis(u64::try_from(duration).unwrap()),
37 );
38 Ok(queue.enqueue(Macrotask::Timer(timer), None))
39 } else {
40 Err(Error::new("Macrotask Queue has not been initialised.", None))
41 }
42}
43
44fn clear_timer(cx: &Context, id: Option<Enforce<u32>>) -> Result<()> {
45 if let Some(id) = id {
46 let event_loop = unsafe { &mut cx.get_private().event_loop };
47 if let Some(queue) = &mut event_loop.macrotasks {
48 queue.remove(id.0);
49 Ok(())
50 } else {
51 Err(Error::new("Macrotask Queue has not been initialised.", None))
52 }
53 } else {
54 Ok(())
55 }
56}
57
58#[js_fn]
59fn set_timeout(
60 cx: &Context, callback: Function, Opt(duration): Opt<Clamp<i32>>, Rest(arguments): Rest<JSVal>,
61) -> Result<u32> {
62 set_timer(cx, &callback, duration, arguments, false)
63}
64
65#[js_fn]
66fn set_interval(
67 cx: &Context, callback: Function, Opt(duration): Opt<Clamp<i32>>, Rest(arguments): Rest<JSVal>,
68) -> Result<u32> {
69 set_timer(cx, &callback, duration, arguments, true)
70}
71
72#[js_fn]
73fn clear_timeout(cx: &Context, Opt(id): Opt<Enforce<u32>>) -> Result<()> {
74 clear_timer(cx, id)
75}
76
77#[js_fn]
78fn clear_interval(cx: &Context, Opt(id): Opt<Enforce<u32>>) -> Result<()> {
79 clear_timer(cx, id)
80}
81
82#[js_fn]
83fn queue_macrotask(cx: &Context, callback: Function) -> Result<()> {
84 let event_loop = unsafe { &mut cx.get_private().event_loop };
85 if let Some(queue) = &mut event_loop.macrotasks {
86 queue.enqueue(Macrotask::User(UserMacrotask::new(&callback)), None);
87 Ok(())
88 } else {
89 Err(Error::new("Macrotask Queue has not been initialised.", None))
90 }
91}
92
93const FUNCTIONS: &[JSFunctionSpec] = &[
94 function_spec!(set_timeout, c"setTimeout", 1),
95 function_spec!(set_interval, c"setInterval", 1),
96 function_spec!(clear_timeout, c"clearTimeout", 0),
97 function_spec!(clear_interval, c"clearInterval", 0),
98 function_spec!(queue_macrotask, c"queueMacrotask", 1),
99 JSFunctionSpec::ZERO,
100];
101
102pub fn define(cx: &Context, global: &Object) -> bool {
103 unsafe { global.define_methods(cx, FUNCTIONS) }
104}