@@ -5,7 +5,7 @@ use boa_engine::interop::JsRest;
55use boa_engine:: job:: { CancellationToken , IntervalJob , NativeJobFn } ;
66use boa_engine:: job:: { NativeJob , TimeoutJob } ;
77use boa_engine:: object:: builtins:: JsFunction ;
8- use boa_engine :: value :: { IntegerOrInfinity , Nullable } ;
8+
99use boa_engine:: { Context , IntoJsFunctionCopied , JsResult , JsValue , js_error, js_string} ;
1010use std:: collections:: HashMap ;
1111use std:: num:: NonZeroU32 ;
@@ -54,8 +54,9 @@ impl IntervalInnerState {
5454 }
5555
5656 /// Delete an interval ID from the active map.
57- fn clear_interval ( & mut self , id : NonZeroU32 ) -> Option < CancellationToken > {
57+ fn clear_interval ( & mut self , id : u32 ) -> Option < CancellationToken > {
5858 self . active_map . retain ( |_, v| !v. revoked ( ) ) ;
59+ let id = NonZeroU32 :: new ( id) ?;
5960 self . active_map . remove ( & id)
6061 }
6162}
@@ -78,13 +79,10 @@ pub fn set_timeout(
7879 return Ok ( 0 ) ;
7980 } ;
8081
81- // Spec says if delay is not a number, it should be equal to 0.
82- let delay = delay_in_msec
83- . unwrap_or_default ( )
84- . to_integer_or_infinity ( context)
85- . unwrap_or ( IntegerOrInfinity :: Integer ( 0 ) ) ;
86- // The spec converts the delay to a 32-bit signed integer.
87- let delay = u64:: from ( delay. clamp_finite ( 0 , u32:: MAX ) ) ;
82+ // The spec converts the delay to a WebIDL `long`, which maps to `i32`.
83+ // Negative values are clamped to 0.
84+ let delay_i32 = delay_in_msec. unwrap_or_default ( ) . to_i32 ( context) ?;
85+ let delay = u64:: from ( u32:: try_from ( delay_i32) . unwrap_or ( 0 ) ) ;
8886
8987 let state = IntervalInnerState :: from_context ( context) ;
9088 let id = state. next_id ( ) ?;
@@ -133,12 +131,10 @@ pub fn set_interval(
133131 return Ok ( 0 ) ;
134132 } ;
135133
136- // Spec says if delay is not a number, it should be equal to 0.
137- let delay = delay_in_msec
138- . unwrap_or_default ( )
139- . to_integer_or_infinity ( context)
140- . unwrap_or ( IntegerOrInfinity :: Integer ( 0 ) ) ;
141- let delay = u64:: from ( delay. clamp_finite ( 0 , u32:: MAX ) ) ;
134+ // The spec converts the delay to a WebIDL `long`, which maps to `i32`.
135+ // Negative values are clamped to 0.
136+ let delay_i32 = delay_in_msec. unwrap_or_default ( ) . to_i32 ( context) ?;
137+ let delay = u64:: from ( u32:: try_from ( delay_i32) . unwrap_or ( 0 ) ) ;
142138
143139 let state = IntervalInnerState :: from_context ( context) ;
144140 let id = state. next_id ( ) ?;
@@ -169,18 +165,20 @@ pub fn set_interval(
169165/// See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout).
170166///
171167/// Please note that this is the same exact method as `clearInterval`, as both can be
172- /// used interchangeably.
173- pub fn clear_timeout ( id : Nullable < Option < u32 > > , context : & mut Context ) {
174- let Some ( id) = id. flatten ( ) else {
175- return ;
176- } ;
177- let Some ( id) = NonZeroU32 :: new ( id) else {
178- return ;
179- } ;
180- let handler_map = IntervalInnerState :: from_context ( context) ;
181- if let Some ( token) = handler_map. clear_interval ( id) {
182- token. cancel ( context) ;
168+ /// used interchangeably. Invalid, zero, or negative IDs are silently ignored,
169+ /// matching browser behavior.
170+ ///
171+ /// # Errors
172+ /// Returns an error if the `id` argument cannot be converted to an `i32`.
173+ pub fn clear_timeout ( id : Option < JsValue > , context : & mut Context ) -> JsResult < JsValue > {
174+ let id = id. unwrap_or_default ( ) . to_i32 ( context) ?;
175+ if id > 0 {
176+ let handler_map = IntervalInnerState :: from_context ( context) ;
177+ if let Some ( token) = handler_map. clear_interval ( id. cast_unsigned ( ) ) {
178+ token. cancel ( context) ;
179+ }
183180 }
181+ Ok ( JsValue :: undefined ( ) )
184182}
185183
186184/// Register the interval module into the given context.
0 commit comments