1use std::cell::Cell;
6use std::cmp::{Ord, Ordering};
7use std::collections::VecDeque;
8use std::default::Default;
9use std::rc::Rc;
10use std::time::{Duration, Instant};
11
12use deny_public_fields::DenyPublicFields;
13use js::context::JSContext;
14use js::jsapi::Heap;
15use js::jsval::{JSVal, UndefinedValue};
16use js::rust::wrappers2::JS_GetScriptedCallerPrivate;
17use js::rust::{HandleValue, IntoHandle};
18use net_traits::request::ParserMetadata;
19use rustc_hash::FxHashMap;
20use script_bindings::cell::DomRefCell;
21use script_bindings::reflector::DomObject;
22use serde::{Deserialize, Serialize};
23use servo_base::id::PipelineId;
24use servo_config::pref;
25use servo_url::ServoUrl;
26use timers::{BoxedTimerCallback, TimerEventRequest};
27
28use crate::dom::bindings::callback::ExceptionHandling::Report;
29use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
30use crate::dom::bindings::codegen::UnionTypes::TrustedScriptOrString;
31use crate::dom::bindings::error::Fallible;
32use crate::dom::bindings::inheritance::Castable;
33use crate::dom::bindings::refcounted::Trusted;
34use crate::dom::bindings::reflector::DomGlobal;
35use crate::dom::bindings::root::{AsHandleValue, Dom};
36use crate::dom::bindings::str::DOMString;
37use crate::dom::csp::CspReporting;
38use crate::dom::document::RefreshRedirectDue;
39use crate::dom::eventsource::EventSourceTimeoutCallback;
40use crate::dom::global_scope_script_execution::{ErrorReporting, RethrowErrors};
41use crate::dom::globalscope::GlobalScope;
42#[cfg(feature = "testbinding")]
43use crate::dom::testbinding::TestBindingCallback;
44use crate::dom::trustedtypes::trustedscript::TrustedScript;
45use crate::dom::types::{Window, WorkerGlobalScope};
46use crate::dom::xmlhttprequest::XHRTimeoutCallback;
47use crate::script_module::{ScriptFetchOptions, module_script_from_reference_private};
48use crate::script_runtime::IntroductionType;
49use crate::script_thread::ScriptThread;
50use crate::task_source::SendableTaskSource;
51
52type TimerKey = i32;
53type RunStepsDeadline = Instant;
54type CompletionStep = Box<dyn FnOnce(&mut JSContext, &GlobalScope) + 'static>;
55
56type OrderingIdentifier = DOMString;
59
60#[derive(JSTraceable, MallocSizeOf)]
61struct OrderingEntry {
62 milliseconds: u64,
63 start_seq: u64,
64 handle: OneshotTimerHandle,
65}
66
67type OrderingQueues = FxHashMap<OrderingIdentifier, Vec<OrderingEntry>>;
69
70type RunStepsActiveMap = FxHashMap<TimerKey, RunStepsDeadline>;
72
73#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, MallocSizeOf, Ord, PartialEq, PartialOrd)]
74pub(crate) struct OneshotTimerHandle(i32);
75
76#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
77#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
78pub(crate) struct OneshotTimers {
79 global_scope: Dom<GlobalScope>,
80 js_timers: JsTimers,
81 next_timer_handle: Cell<OneshotTimerHandle>,
82 timers: DomRefCell<VecDeque<OneshotTimer>>,
83 suspended_since: Cell<Option<Instant>>,
84 suspension_offset: Cell<Duration>,
89 #[no_trace]
96 expected_event_id: Cell<TimerEventId>,
97 map_of_active_timers: DomRefCell<RunStepsActiveMap>,
101
102 runsteps_queues: DomRefCell<OrderingQueues>,
106
107 next_runsteps_key: Cell<TimerKey>,
109
110 runsteps_start_seq: Cell<u64>,
113}
114
115#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
116struct OneshotTimer {
117 handle: OneshotTimerHandle,
118 #[no_trace]
119 source: TimerSource,
120 callback: OneshotTimerCallback,
121 scheduled_for: Instant,
122}
123
124#[derive(JSTraceable, MallocSizeOf)]
128pub(crate) enum OneshotTimerCallback {
129 XhrTimeout(XHRTimeoutCallback),
130 EventSourceTimeout(EventSourceTimeoutCallback),
131 JsTimer(JsTimerTask),
132 #[cfg(feature = "testbinding")]
133 TestBindingCallback(TestBindingCallback),
134 RefreshRedirectDue(RefreshRedirectDue),
135 RunStepsAfterTimeout {
137 timer_key: i32,
139 ordering_id: DOMString,
141 milliseconds: u64,
143 #[no_trace]
145 #[ignore_malloc_size_of = "Closure"]
146 completion: CompletionStep,
147 },
148}
149
150impl OneshotTimerCallback {
151 fn invoke<T: DomObject>(self, this: &T, js_timers: &JsTimers, cx: &mut JSContext) {
152 match self {
153 OneshotTimerCallback::XhrTimeout(callback) => callback.invoke(cx),
154 OneshotTimerCallback::EventSourceTimeout(callback) => callback.invoke(),
155 OneshotTimerCallback::JsTimer(task) => task.invoke(this, js_timers, cx),
156 #[cfg(feature = "testbinding")]
157 OneshotTimerCallback::TestBindingCallback(callback) => callback.invoke(),
158 OneshotTimerCallback::RefreshRedirectDue(callback) => callback.invoke(cx),
159 OneshotTimerCallback::RunStepsAfterTimeout { completion, .. } => {
160 completion(cx, &this.global());
163 },
164 }
165 }
166}
167
168impl Ord for OneshotTimer {
169 fn cmp(&self, other: &OneshotTimer) -> Ordering {
170 match self.scheduled_for.cmp(&other.scheduled_for).reverse() {
171 Ordering::Equal => self.handle.cmp(&other.handle).reverse(),
172 res => res,
173 }
174 }
175}
176
177impl PartialOrd for OneshotTimer {
178 fn partial_cmp(&self, other: &OneshotTimer) -> Option<Ordering> {
179 Some(self.cmp(other))
180 }
181}
182
183impl Eq for OneshotTimer {}
184impl PartialEq for OneshotTimer {
185 fn eq(&self, other: &OneshotTimer) -> bool {
186 std::ptr::eq(self, other)
187 }
188}
189
190impl OneshotTimers {
191 pub(crate) fn new(global_scope: &GlobalScope) -> OneshotTimers {
192 OneshotTimers {
193 global_scope: Dom::from_ref(global_scope),
194 js_timers: JsTimers::default(),
195 next_timer_handle: Cell::new(OneshotTimerHandle(1)),
196 timers: DomRefCell::new(VecDeque::new()),
197 suspended_since: Cell::new(None),
198 suspension_offset: Cell::new(Duration::ZERO),
199 expected_event_id: Cell::new(TimerEventId(0)),
200 map_of_active_timers: Default::default(),
201 runsteps_queues: Default::default(),
202 next_runsteps_key: Cell::new(1),
203 runsteps_start_seq: Cell::new(0),
204 }
205 }
206
207 #[inline]
209 pub(crate) fn now_for_runsteps(&self) -> Instant {
210 self.base_time()
212 }
213
214 pub(crate) fn fresh_runsteps_key(&self) -> TimerKey {
217 let k = self.next_runsteps_key.get();
218 self.next_runsteps_key.set(k + 1);
219 k
220 }
221
222 pub(crate) fn runsteps_set_active(&self, timer_key: TimerKey, deadline: RunStepsDeadline) {
225 self.map_of_active_timers
226 .borrow_mut()
227 .insert(timer_key, deadline);
228 }
229
230 fn runsteps_enqueue_sorted(
233 &self,
234 ordering_id: &DOMString,
235 handle: OneshotTimerHandle,
236 milliseconds: u64,
237 ) {
238 let mut map = self.runsteps_queues.borrow_mut();
239 let q = map.entry(ordering_id.clone()).or_default();
240
241 let seq = {
242 let cur = self.runsteps_start_seq.get();
243 self.runsteps_start_seq.set(cur + 1);
244 cur
245 };
246
247 let key = OrderingEntry {
248 milliseconds,
249 start_seq: seq,
250 handle,
251 };
252
253 let idx = q
254 .binary_search_by(|ordering_entry| {
255 match ordering_entry.milliseconds.cmp(&milliseconds) {
256 Ordering::Less => Ordering::Less,
257 Ordering::Greater => Ordering::Greater,
258 Ordering::Equal => ordering_entry.start_seq.cmp(&seq),
259 }
260 })
261 .unwrap_or_else(|i| i);
262
263 q.insert(idx, key);
264 }
265
266 pub(crate) fn schedule_callback(
267 &self,
268 callback: OneshotTimerCallback,
269 duration: Duration,
270 source: TimerSource,
271 ) -> OneshotTimerHandle {
272 let new_handle = self.next_timer_handle.get();
273 self.next_timer_handle
274 .set(OneshotTimerHandle(new_handle.0 + 1));
275
276 let timer = OneshotTimer {
277 handle: new_handle,
278 source,
279 callback,
280 scheduled_for: self.base_time() + duration,
281 };
282
283 if let OneshotTimerCallback::RunStepsAfterTimeout {
286 ordering_id,
287 milliseconds,
288 ..
289 } = &timer.callback
290 {
291 self.runsteps_enqueue_sorted(ordering_id, new_handle, *milliseconds);
292 }
293
294 {
295 let mut timers = self.timers.borrow_mut();
296 let insertion_index = timers.binary_search(&timer).err().unwrap();
297 timers.insert(insertion_index, timer);
298 }
299
300 if self.is_next_timer(new_handle) {
301 self.schedule_timer_call();
302 }
303
304 new_handle
305 }
306
307 pub(crate) fn unschedule_callback(&self, handle: OneshotTimerHandle) {
308 let was_next = self.is_next_timer(handle);
309
310 self.timers.borrow_mut().retain(|t| t.handle != handle);
311
312 if was_next {
313 self.invalidate_expected_event_id();
314 self.schedule_timer_call();
315 }
316 }
317
318 fn is_next_timer(&self, handle: OneshotTimerHandle) -> bool {
319 match self.timers.borrow().back() {
320 None => false,
321 Some(max_timer) => max_timer.handle == handle,
322 }
323 }
324
325 pub(crate) fn fire_timer(&self, id: TimerEventId, global: &GlobalScope, cx: &mut JSContext) {
327 let expected_id = self.expected_event_id.get();
329 if expected_id != id {
330 debug!(
331 "ignoring timer fire event {:?} (expected {:?})",
332 id, expected_id
333 );
334 return;
335 }
336
337 assert!(self.suspended_since.get().is_none());
338
339 let base_time = self.base_time();
340
341 if base_time < self.timers.borrow().back().unwrap().scheduled_for {
343 warn!("Unexpected timing!");
344 return;
345 }
346
347 let mut timers_to_run = Vec::new();
350
351 loop {
352 let mut timers = self.timers.borrow_mut();
353
354 if timers.is_empty() || timers.back().unwrap().scheduled_for > base_time {
355 break;
356 }
357
358 timers_to_run.push(timers.pop_back().unwrap());
359 }
360
361 for timer in timers_to_run {
362 if !global.can_continue_running() {
367 return;
368 }
369 match &timer.callback {
370 OneshotTimerCallback::RunStepsAfterTimeout { ordering_id, .. } => {
372 let head_handle_opt = {
375 let queues_ref = self.runsteps_queues.borrow();
376 queues_ref
377 .get(ordering_id)
378 .and_then(|v| v.first().map(|t| t.handle))
379 };
380 let is_head = head_handle_opt.is_none_or(|head| head == timer.handle);
381
382 if !is_head {
383 let rein = OneshotTimer {
385 handle: timer.handle,
386 source: timer.source,
387 callback: timer.callback,
388 scheduled_for: self.base_time(),
389 };
390 let mut timers = self.timers.borrow_mut();
391 let idx = timers.binary_search(&rein).err().unwrap();
392 timers.insert(idx, rein);
393 continue;
394 }
395
396 let (timer_key, ordering_id_owned, completion) = match timer.callback {
397 OneshotTimerCallback::RunStepsAfterTimeout {
398 timer_key,
399 ordering_id,
400 milliseconds: _,
401 completion,
402 } => (timer_key, ordering_id, completion),
403 _ => unreachable!(),
404 };
405
406 (completion)(cx, global);
411
412 self.map_of_active_timers.borrow_mut().remove(&timer_key);
414
415 {
416 let mut queues_mut = self.runsteps_queues.borrow_mut();
417 if let Some(q) = queues_mut.get_mut(&ordering_id_owned) {
418 if !q.is_empty() {
419 q.remove(0);
420 }
421 if q.is_empty() {
422 queues_mut.remove(&ordering_id_owned);
423 }
424 }
425 }
426 },
427 _ => {
428 let cb = timer.callback;
429 cb.invoke(global, &self.js_timers, cx);
430 },
431 }
432 }
433
434 self.schedule_timer_call();
435 }
436
437 fn base_time(&self) -> Instant {
438 let offset = self.suspension_offset.get();
439 match self.suspended_since.get() {
440 Some(suspend_time) => suspend_time - offset,
441 None => Instant::now() - offset,
442 }
443 }
444
445 pub(crate) fn slow_down(&self) {
446 let min_duration_ms = pref!(js_timers_minimum_duration) as u64;
447 self.js_timers
448 .set_min_duration(Duration::from_millis(min_duration_ms));
449 }
450
451 pub(crate) fn speed_up(&self) {
452 self.js_timers.remove_min_duration();
453 }
454
455 pub(crate) fn suspend(&self) {
456 if self.suspended_since.get().is_some() {
458 return warn!("Suspending an already suspended timer.");
459 }
460
461 debug!("Suspending timers.");
462 self.suspended_since.set(Some(Instant::now()));
463 self.invalidate_expected_event_id();
464 }
465
466 pub(crate) fn resume(&self) {
467 let additional_offset = match self.suspended_since.get() {
469 Some(suspended_since) => Instant::now() - suspended_since,
470 None => return warn!("Resuming an already resumed timer."),
471 };
472
473 debug!("Resuming timers.");
474 self.suspension_offset
475 .set(self.suspension_offset.get() + additional_offset);
476 self.suspended_since.set(None);
477
478 self.schedule_timer_call();
479 }
480
481 fn schedule_timer_call(&self) {
483 if self.suspended_since.get().is_some() {
484 return;
486 }
487
488 let timers = self.timers.borrow();
489 let Some(timer) = timers.back() else {
490 return;
491 };
492
493 let expected_event_id = self.invalidate_expected_event_id();
494 let callback = TimerListener {
497 context: Trusted::new(&*self.global_scope),
498 task_source: self
499 .global_scope
500 .task_manager()
501 .timer_task_source()
502 .to_sendable(),
503 source: timer.source,
504 id: expected_event_id,
505 }
506 .into_callback();
507
508 let event_request = TimerEventRequest {
509 callback,
510 duration: timer.scheduled_for - self.base_time(),
511 };
512
513 self.global_scope.schedule_timer(event_request);
514 }
515
516 fn invalidate_expected_event_id(&self) -> TimerEventId {
517 let TimerEventId(currently_expected) = self.expected_event_id.get();
518 let next_id = TimerEventId(currently_expected + 1);
519 debug!(
520 "invalidating expected timer (was {:?}, now {:?}",
521 currently_expected, next_id
522 );
523 self.expected_event_id.set(next_id);
524 next_id
525 }
526
527 #[allow(clippy::too_many_arguments)]
528 pub(crate) fn set_timeout_or_interval(
529 &self,
530 cx: &mut JSContext,
531 global: &GlobalScope,
532 callback: TimerCallback,
533 arguments: Vec<HandleValue>,
534 timeout: Duration,
535 is_interval: IsInterval,
536 source: TimerSource,
537 ) -> Fallible<i32> {
538 self.js_timers.set_timeout_or_interval(
539 cx,
540 global,
541 callback,
542 arguments,
543 timeout,
544 is_interval,
545 source,
546 )
547 }
548
549 pub(crate) fn clear_timeout_or_interval(&self, global: &GlobalScope, handle: i32) {
550 self.js_timers.clear_timeout_or_interval(global, handle)
551 }
552}
553
554#[derive(Clone, Copy, Eq, Hash, JSTraceable, MallocSizeOf, Ord, PartialEq, PartialOrd)]
555pub(crate) struct JsTimerHandle(i32);
556
557#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
558pub(crate) struct JsTimers {
559 next_timer_handle: Cell<JsTimerHandle>,
560 active_timers: DomRefCell<FxHashMap<JsTimerHandle, JsTimerEntry>>,
562 nesting_level: Cell<u32>,
564 min_duration: Cell<Option<Duration>>,
566}
567
568#[derive(JSTraceable, MallocSizeOf)]
569struct JsTimerEntry {
570 oneshot_handle: OneshotTimerHandle,
571}
572
573#[derive(JSTraceable, MallocSizeOf)]
578pub(crate) struct JsTimerTask {
579 handle: JsTimerHandle,
580 #[no_trace]
581 source: TimerSource,
582 callback: InternalTimerCallback,
583 is_interval: IsInterval,
584 nesting_level: u32,
585 duration: Duration,
586 is_user_interacting: bool,
587}
588
589#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
591pub(crate) enum IsInterval {
592 Interval,
593 NonInterval,
594}
595
596pub(crate) enum TimerCallback {
597 StringTimerCallback(TrustedScriptOrString),
598 FunctionTimerCallback(Rc<Function>),
599}
600
601#[derive(Clone, JSTraceable, MallocSizeOf)]
602#[cfg_attr(crown, expect(crown::unrooted_must_root))]
603enum InternalTimerCallback {
604 StringTimerCallback(DOMString, InitiatingScriptFetchInfo),
605 FunctionTimerCallback(
606 #[conditional_malloc_size_of] Rc<Function>,
607 #[ignore_malloc_size_of = "mozjs"] Rc<Box<[Heap<JSVal>]>>,
608 ),
609}
610
611impl Default for JsTimers {
612 fn default() -> Self {
613 JsTimers {
614 next_timer_handle: Cell::new(JsTimerHandle(1)),
615 active_timers: DomRefCell::new(FxHashMap::default()),
616 nesting_level: Cell::new(0),
617 min_duration: Cell::new(None),
618 }
619 }
620}
621
622impl JsTimers {
623 #[allow(clippy::too_many_arguments)]
625 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
626 pub(crate) fn set_timeout_or_interval(
627 &self,
628 cx: &mut JSContext,
629 global: &GlobalScope,
630 callback: TimerCallback,
631 arguments: Vec<HandleValue>,
632 timeout: Duration,
633 is_interval: IsInterval,
634 source: TimerSource,
635 ) -> Fallible<i32> {
636 let callback = match callback {
637 TimerCallback::StringTimerCallback(trusted_script_or_string) => {
638 let global_name = if global.is::<Window>() {
640 "Window"
641 } else {
642 "WorkerGlobalScope"
643 };
644 let method_name = if is_interval == IsInterval::Interval {
646 "setInterval"
647 } else {
648 "setTimeout"
649 };
650 let sink = format!("{} {}", global_name, method_name);
652 let code_str = TrustedScript::get_trusted_type_compliant_string(
655 cx,
656 global,
657 trusted_script_or_string,
658 &sink,
659 )?;
660
661 let initiating_script_fetch_info = active_script_fetch_info(cx, global);
662
663 if global
666 .get_csp_list()
667 .is_js_evaluation_allowed(global, &code_str.str())
668 {
669 InternalTimerCallback::StringTimerCallback(
671 code_str,
672 initiating_script_fetch_info,
673 )
674 } else {
675 return Ok(0);
676 }
677 },
678 TimerCallback::FunctionTimerCallback(function) => {
679 let mut args = Vec::with_capacity(arguments.len());
682 for _ in 0..arguments.len() {
683 args.push(Heap::default());
684 }
685 for (i, item) in arguments.iter().enumerate() {
686 args.get_mut(i).unwrap().set(item.get());
687 }
688 InternalTimerCallback::FunctionTimerCallback(
691 function,
692 Rc::new(args.into_boxed_slice()),
693 )
694 },
695 };
696
697 let JsTimerHandle(new_handle) = self.next_timer_handle.get();
701 self.next_timer_handle.set(JsTimerHandle(new_handle + 1));
702
703 let mut task = JsTimerTask {
707 handle: JsTimerHandle(new_handle),
708 source,
709 callback,
710 is_interval,
711 is_user_interacting: ScriptThread::is_user_interacting(),
712 nesting_level: 0,
713 duration: Duration::ZERO,
714 };
715
716 task.duration = timeout.max(Duration::ZERO);
718
719 self.initialize_and_schedule(global, task);
720
721 Ok(new_handle)
723 }
724
725 pub(crate) fn clear_timeout_or_interval(&self, global: &GlobalScope, handle: i32) {
726 let mut active_timers = self.active_timers.borrow_mut();
727
728 if let Some(entry) = active_timers.remove(&JsTimerHandle(handle)) {
729 global.unschedule_callback(entry.oneshot_handle);
730 }
731 }
732
733 pub(crate) fn set_min_duration(&self, duration: Duration) {
734 self.min_duration.set(Some(duration));
735 }
736
737 pub(crate) fn remove_min_duration(&self) {
738 self.min_duration.set(None);
739 }
740
741 fn user_agent_pad(&self, current_duration: Duration) -> Duration {
743 match self.min_duration.get() {
744 Some(min_duration) => min_duration.max(current_duration),
745 None => current_duration,
746 }
747 }
748
749 fn initialize_and_schedule(&self, global: &GlobalScope, mut task: JsTimerTask) {
751 let handle = task.handle;
752 let mut active_timers = self.active_timers.borrow_mut();
753
754 let nesting_level = self.nesting_level.get();
758
759 let duration = self.user_agent_pad(clamp_duration(nesting_level, task.duration));
760 task.nesting_level = nesting_level + 1;
763
764 let callback = OneshotTimerCallback::JsTimer(task);
767 let oneshot_handle = global.schedule_callback(callback, duration);
768
769 let entry = active_timers
771 .entry(handle)
772 .or_insert(JsTimerEntry { oneshot_handle });
773 entry.oneshot_handle = oneshot_handle;
774 }
775}
776
777fn clamp_duration(nesting_level: u32, unclamped: Duration) -> Duration {
779 let lower_bound_ms = if nesting_level > 5 { 4 } else { 0 };
781 let lower_bound = Duration::from_millis(lower_bound_ms);
782 lower_bound.max(unclamped)
783}
784
785impl JsTimerTask {
786 fn invoke<T: DomObject>(self, this: &T, timers: &JsTimers, cx: &mut JSContext) {
788 timers.nesting_level.set(self.nesting_level);
793
794 let _guard = ScriptThread::user_interacting_guard();
795 match self.callback {
796 InternalTimerCallback::StringTimerCallback(ref code_str, ref fetch_info) => {
797 let global = this.global();
800
801 let InitiatingScriptFetchInfo {
804 fetch_options,
805 base_url,
806 } = fetch_info.clone();
807
808 let script = global.create_a_classic_script(
811 cx,
812 (*code_str.str()).into(),
813 base_url,
814 fetch_options,
815 ErrorReporting::Unmuted,
816 Some(IntroductionType::DOM_TIMER),
817 1,
818 false,
819 );
820
821 _ = global.run_a_classic_script(cx, script, RethrowErrors::No);
823 },
824 InternalTimerCallback::FunctionTimerCallback(ref function, ref arguments) => {
827 let arguments = self.collect_heap_args(arguments);
828 rooted!(&in(cx) let mut value: JSVal);
829 let _ = function.Call_(cx, this, arguments, value.handle_mut(), Report);
830 },
831 };
832
833 timers.nesting_level.set(0);
835
836 if self.is_interval == IsInterval::Interval &&
842 timers.active_timers.borrow().contains_key(&self.handle)
843 {
844 timers.initialize_and_schedule(&this.global(), self);
845 }
846 }
847
848 fn collect_heap_args<'b>(&self, args: &'b [Heap<JSVal>]) -> Vec<HandleValue<'b>> {
849 args.iter().map(|arg| arg.as_handle_value()).collect()
850 }
851}
852
853#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
855pub enum TimerSource {
856 FromWindow(PipelineId),
858 FromWorker,
860}
861
862#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
864pub struct TimerEventId(pub u32);
865
866#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
870pub struct TimerEvent(pub TimerSource, pub TimerEventId);
871
872#[derive(Clone)]
874struct TimerListener {
875 task_source: SendableTaskSource,
876 context: Trusted<GlobalScope>,
877 source: TimerSource,
878 id: TimerEventId,
879}
880
881impl TimerListener {
882 fn handle(&self, event: TimerEvent) {
886 let context = self.context.clone();
887 self.task_source.queue(task!(timer_event: move |cx| {
889 let global = context.root();
890 let TimerEvent(source, id) = event;
891 match source {
892 TimerSource::FromWorker => {
893 global.downcast::<WorkerGlobalScope>().expect("Window timer delivered to worker");
894 },
895 TimerSource::FromWindow(pipeline) => {
896 assert_eq!(pipeline, global.pipeline_id());
897 global.downcast::<Window>().expect("Worker timer delivered to window");
898 },
899 };
900 global.fire_timer(id, cx);
901 })
902 );
903 }
904
905 fn into_callback(self) -> BoxedTimerCallback {
906 let timer_event = TimerEvent(self.source, self.id);
907 Box::new(move || self.handle(timer_event))
908 }
909}
910
911#[derive(Clone, JSTraceable, MallocSizeOf)]
912struct InitiatingScriptFetchInfo {
913 fetch_options: ScriptFetchOptions,
914 #[no_trace]
915 base_url: ServoUrl,
916}
917
918#[expect(unsafe_code)]
919fn active_script_fetch_info(cx: &mut JSContext, global: &GlobalScope) -> InitiatingScriptFetchInfo {
921 rooted!(&in(cx) let mut value = UndefinedValue());
922 unsafe { JS_GetScriptedCallerPrivate(cx, value.handle_mut()) };
923
924 let reference_private = value.handle().into_handle();
925
926 let initiating_script = unsafe { module_script_from_reference_private(&reference_private) };
928
929 let (fetch_options, base_url) = match initiating_script {
930 Some(script) => (
932 ScriptFetchOptions {
934 cryptographic_nonce: script.options.cryptographic_nonce.clone(),
936 integrity_metadata: String::new(),
938 parser_metadata: ParserMetadata::NotParserInserted,
940 credentials_mode: script.options.credentials_mode,
942 referrer_policy: script.options.referrer_policy,
944 render_blocking: false,
946 },
947 script.base_url.clone(),
949 ),
950 None => (
951 ScriptFetchOptions::default_classic_script(),
953 global.api_base_url(),
955 ),
956 };
957
958 InitiatingScriptFetchInfo {
959 fetch_options,
960 base_url,
961 }
962}