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;
16use js::rust::HandleValue;
17use rustc_hash::FxHashMap;
18use serde::{Deserialize, Serialize};
19use servo_base::id::PipelineId;
20use servo_config::pref;
21use timers::{BoxedTimerCallback, TimerEventRequest};
22
23use crate::dom::bindings::callback::ExceptionHandling::Report;
24use crate::dom::bindings::cell::DomRefCell;
25use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
26use crate::dom::bindings::codegen::UnionTypes::TrustedScriptOrString;
27use crate::dom::bindings::error::Fallible;
28use crate::dom::bindings::inheritance::Castable;
29use crate::dom::bindings::refcounted::Trusted;
30use crate::dom::bindings::reflector::{DomGlobal, DomObject};
31use crate::dom::bindings::root::{AsHandleValue, Dom};
32use crate::dom::bindings::str::DOMString;
33use crate::dom::csp::CspReporting;
34use crate::dom::document::RefreshRedirectDue;
35use crate::dom::eventsource::EventSourceTimeoutCallback;
36use crate::dom::global_scope_script_execution::{ErrorReporting, RethrowErrors};
37use crate::dom::globalscope::GlobalScope;
38#[cfg(feature = "testbinding")]
39use crate::dom::testbinding::TestBindingCallback;
40use crate::dom::trustedtypes::trustedscript::TrustedScript;
41use crate::dom::types::{Window, WorkerGlobalScope};
42use crate::dom::xmlhttprequest::XHRTimeoutCallback;
43use crate::script_module::ScriptFetchOptions;
44use crate::script_runtime::{CanGc, IntroductionType};
45use crate::script_thread::ScriptThread;
46use crate::task_source::SendableTaskSource;
47
48type TimerKey = i32;
49type RunStepsDeadline = Instant;
50type CompletionStep = Box<dyn FnOnce(&mut JSContext, &GlobalScope) + 'static>;
51
52type OrderingIdentifier = DOMString;
55
56#[derive(JSTraceable, MallocSizeOf)]
57struct OrderingEntry {
58 milliseconds: u64,
59 start_seq: u64,
60 handle: OneshotTimerHandle,
61}
62
63type OrderingQueues = FxHashMap<OrderingIdentifier, Vec<OrderingEntry>>;
65
66type RunStepsActiveMap = FxHashMap<TimerKey, RunStepsDeadline>;
68
69#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, MallocSizeOf, Ord, PartialEq, PartialOrd)]
70pub(crate) struct OneshotTimerHandle(i32);
71
72#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
73#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
74pub(crate) struct OneshotTimers {
75 global_scope: Dom<GlobalScope>,
76 js_timers: JsTimers,
77 next_timer_handle: Cell<OneshotTimerHandle>,
78 timers: DomRefCell<VecDeque<OneshotTimer>>,
79 suspended_since: Cell<Option<Instant>>,
80 suspension_offset: Cell<Duration>,
85 #[no_trace]
92 expected_event_id: Cell<TimerEventId>,
93 map_of_active_timers: DomRefCell<RunStepsActiveMap>,
97
98 runsteps_queues: DomRefCell<OrderingQueues>,
102
103 next_runsteps_key: Cell<TimerKey>,
105
106 runsteps_start_seq: Cell<u64>,
109}
110
111#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
112struct OneshotTimer {
113 handle: OneshotTimerHandle,
114 #[no_trace]
115 source: TimerSource,
116 callback: OneshotTimerCallback,
117 scheduled_for: Instant,
118}
119
120#[derive(JSTraceable, MallocSizeOf)]
124pub(crate) enum OneshotTimerCallback {
125 XhrTimeout(XHRTimeoutCallback),
126 EventSourceTimeout(EventSourceTimeoutCallback),
127 JsTimer(JsTimerTask),
128 #[cfg(feature = "testbinding")]
129 TestBindingCallback(TestBindingCallback),
130 RefreshRedirectDue(RefreshRedirectDue),
131 RunStepsAfterTimeout {
133 timer_key: i32,
135 ordering_id: DOMString,
137 milliseconds: u64,
139 #[no_trace]
141 #[ignore_malloc_size_of = "Closure"]
142 completion: CompletionStep,
143 },
144}
145
146impl OneshotTimerCallback {
147 fn invoke<T: DomObject>(self, this: &T, js_timers: &JsTimers, cx: &mut JSContext) {
148 match self {
149 OneshotTimerCallback::XhrTimeout(callback) => callback.invoke(CanGc::from_cx(cx)),
150 OneshotTimerCallback::EventSourceTimeout(callback) => callback.invoke(),
151 OneshotTimerCallback::JsTimer(task) => task.invoke(this, js_timers, cx),
152 #[cfg(feature = "testbinding")]
153 OneshotTimerCallback::TestBindingCallback(callback) => callback.invoke(),
154 OneshotTimerCallback::RefreshRedirectDue(callback) => callback.invoke(cx),
155 OneshotTimerCallback::RunStepsAfterTimeout { completion, .. } => {
156 completion(cx, &this.global());
159 },
160 }
161 }
162}
163
164impl Ord for OneshotTimer {
165 fn cmp(&self, other: &OneshotTimer) -> Ordering {
166 match self.scheduled_for.cmp(&other.scheduled_for).reverse() {
167 Ordering::Equal => self.handle.cmp(&other.handle).reverse(),
168 res => res,
169 }
170 }
171}
172
173impl PartialOrd for OneshotTimer {
174 fn partial_cmp(&self, other: &OneshotTimer) -> Option<Ordering> {
175 Some(self.cmp(other))
176 }
177}
178
179impl Eq for OneshotTimer {}
180impl PartialEq for OneshotTimer {
181 fn eq(&self, other: &OneshotTimer) -> bool {
182 std::ptr::eq(self, other)
183 }
184}
185
186impl OneshotTimers {
187 pub(crate) fn new(global_scope: &GlobalScope) -> OneshotTimers {
188 OneshotTimers {
189 global_scope: Dom::from_ref(global_scope),
190 js_timers: JsTimers::default(),
191 next_timer_handle: Cell::new(OneshotTimerHandle(1)),
192 timers: DomRefCell::new(VecDeque::new()),
193 suspended_since: Cell::new(None),
194 suspension_offset: Cell::new(Duration::ZERO),
195 expected_event_id: Cell::new(TimerEventId(0)),
196 map_of_active_timers: Default::default(),
197 runsteps_queues: Default::default(),
198 next_runsteps_key: Cell::new(1),
199 runsteps_start_seq: Cell::new(0),
200 }
201 }
202
203 #[inline]
205 pub(crate) fn now_for_runsteps(&self) -> Instant {
206 self.base_time()
208 }
209
210 pub(crate) fn fresh_runsteps_key(&self) -> TimerKey {
213 let k = self.next_runsteps_key.get();
214 self.next_runsteps_key.set(k + 1);
215 k
216 }
217
218 pub(crate) fn runsteps_set_active(&self, timer_key: TimerKey, deadline: RunStepsDeadline) {
221 self.map_of_active_timers
222 .borrow_mut()
223 .insert(timer_key, deadline);
224 }
225
226 fn runsteps_enqueue_sorted(
229 &self,
230 ordering_id: &DOMString,
231 handle: OneshotTimerHandle,
232 milliseconds: u64,
233 ) {
234 let mut map = self.runsteps_queues.borrow_mut();
235 let q = map.entry(ordering_id.clone()).or_default();
236
237 let seq = {
238 let cur = self.runsteps_start_seq.get();
239 self.runsteps_start_seq.set(cur + 1);
240 cur
241 };
242
243 let key = OrderingEntry {
244 milliseconds,
245 start_seq: seq,
246 handle,
247 };
248
249 let idx = q
250 .binary_search_by(|ordering_entry| {
251 match ordering_entry.milliseconds.cmp(&milliseconds) {
252 Ordering::Less => Ordering::Less,
253 Ordering::Greater => Ordering::Greater,
254 Ordering::Equal => ordering_entry.start_seq.cmp(&seq),
255 }
256 })
257 .unwrap_or_else(|i| i);
258
259 q.insert(idx, key);
260 }
261
262 pub(crate) fn schedule_callback(
263 &self,
264 callback: OneshotTimerCallback,
265 duration: Duration,
266 source: TimerSource,
267 ) -> OneshotTimerHandle {
268 let new_handle = self.next_timer_handle.get();
269 self.next_timer_handle
270 .set(OneshotTimerHandle(new_handle.0 + 1));
271
272 let timer = OneshotTimer {
273 handle: new_handle,
274 source,
275 callback,
276 scheduled_for: self.base_time() + duration,
277 };
278
279 if let OneshotTimerCallback::RunStepsAfterTimeout {
282 ordering_id,
283 milliseconds,
284 ..
285 } = &timer.callback
286 {
287 self.runsteps_enqueue_sorted(ordering_id, new_handle, *milliseconds);
288 }
289
290 {
291 let mut timers = self.timers.borrow_mut();
292 let insertion_index = timers.binary_search(&timer).err().unwrap();
293 timers.insert(insertion_index, timer);
294 }
295
296 if self.is_next_timer(new_handle) {
297 self.schedule_timer_call();
298 }
299
300 new_handle
301 }
302
303 pub(crate) fn unschedule_callback(&self, handle: OneshotTimerHandle) {
304 let was_next = self.is_next_timer(handle);
305
306 self.timers.borrow_mut().retain(|t| t.handle != handle);
307
308 if was_next {
309 self.invalidate_expected_event_id();
310 self.schedule_timer_call();
311 }
312 }
313
314 fn is_next_timer(&self, handle: OneshotTimerHandle) -> bool {
315 match self.timers.borrow().back() {
316 None => false,
317 Some(max_timer) => max_timer.handle == handle,
318 }
319 }
320
321 pub(crate) fn fire_timer(&self, id: TimerEventId, global: &GlobalScope, cx: &mut JSContext) {
323 let expected_id = self.expected_event_id.get();
325 if expected_id != id {
326 debug!(
327 "ignoring timer fire event {:?} (expected {:?})",
328 id, expected_id
329 );
330 return;
331 }
332
333 assert!(self.suspended_since.get().is_none());
334
335 let base_time = self.base_time();
336
337 if base_time < self.timers.borrow().back().unwrap().scheduled_for {
339 warn!("Unexpected timing!");
340 return;
341 }
342
343 let mut timers_to_run = Vec::new();
346
347 loop {
348 let mut timers = self.timers.borrow_mut();
349
350 if timers.is_empty() || timers.back().unwrap().scheduled_for > base_time {
351 break;
352 }
353
354 timers_to_run.push(timers.pop_back().unwrap());
355 }
356
357 for timer in timers_to_run {
358 if !global.can_continue_running() {
363 return;
364 }
365 match &timer.callback {
366 OneshotTimerCallback::RunStepsAfterTimeout { ordering_id, .. } => {
368 let head_handle_opt = {
371 let queues_ref = self.runsteps_queues.borrow();
372 queues_ref
373 .get(ordering_id)
374 .and_then(|v| v.first().map(|t| t.handle))
375 };
376 let is_head = head_handle_opt.is_none_or(|head| head == timer.handle);
377
378 if !is_head {
379 let rein = OneshotTimer {
381 handle: timer.handle,
382 source: timer.source,
383 callback: timer.callback,
384 scheduled_for: self.base_time(),
385 };
386 let mut timers = self.timers.borrow_mut();
387 let idx = timers.binary_search(&rein).err().unwrap();
388 timers.insert(idx, rein);
389 continue;
390 }
391
392 let (timer_key, ordering_id_owned, completion) = match timer.callback {
393 OneshotTimerCallback::RunStepsAfterTimeout {
394 timer_key,
395 ordering_id,
396 milliseconds: _,
397 completion,
398 } => (timer_key, ordering_id, completion),
399 _ => unreachable!(),
400 };
401
402 (completion)(cx, global);
407
408 self.map_of_active_timers.borrow_mut().remove(&timer_key);
410
411 {
412 let mut queues_mut = self.runsteps_queues.borrow_mut();
413 if let Some(q) = queues_mut.get_mut(&ordering_id_owned) {
414 if !q.is_empty() {
415 q.remove(0);
416 }
417 if q.is_empty() {
418 queues_mut.remove(&ordering_id_owned);
419 }
420 }
421 }
422 },
423 _ => {
424 let cb = timer.callback;
425 cb.invoke(global, &self.js_timers, cx);
426 },
427 }
428 }
429
430 self.schedule_timer_call();
431 }
432
433 fn base_time(&self) -> Instant {
434 let offset = self.suspension_offset.get();
435 match self.suspended_since.get() {
436 Some(suspend_time) => suspend_time - offset,
437 None => Instant::now() - offset,
438 }
439 }
440
441 pub(crate) fn slow_down(&self) {
442 let min_duration_ms = pref!(js_timers_minimum_duration) as u64;
443 self.js_timers
444 .set_min_duration(Duration::from_millis(min_duration_ms));
445 }
446
447 pub(crate) fn speed_up(&self) {
448 self.js_timers.remove_min_duration();
449 }
450
451 pub(crate) fn suspend(&self) {
452 if self.suspended_since.get().is_some() {
454 return warn!("Suspending an already suspended timer.");
455 }
456
457 debug!("Suspending timers.");
458 self.suspended_since.set(Some(Instant::now()));
459 self.invalidate_expected_event_id();
460 }
461
462 pub(crate) fn resume(&self) {
463 let additional_offset = match self.suspended_since.get() {
465 Some(suspended_since) => Instant::now() - suspended_since,
466 None => return warn!("Resuming an already resumed timer."),
467 };
468
469 debug!("Resuming timers.");
470 self.suspension_offset
471 .set(self.suspension_offset.get() + additional_offset);
472 self.suspended_since.set(None);
473
474 self.schedule_timer_call();
475 }
476
477 fn schedule_timer_call(&self) {
479 if self.suspended_since.get().is_some() {
480 return;
482 }
483
484 let timers = self.timers.borrow();
485 let Some(timer) = timers.back() else {
486 return;
487 };
488
489 let expected_event_id = self.invalidate_expected_event_id();
490 let callback = TimerListener {
493 context: Trusted::new(&*self.global_scope),
494 task_source: self
495 .global_scope
496 .task_manager()
497 .timer_task_source()
498 .to_sendable(),
499 source: timer.source,
500 id: expected_event_id,
501 }
502 .into_callback();
503
504 let event_request = TimerEventRequest {
505 callback,
506 duration: timer.scheduled_for - self.base_time(),
507 };
508
509 self.global_scope.schedule_timer(event_request);
510 }
511
512 fn invalidate_expected_event_id(&self) -> TimerEventId {
513 let TimerEventId(currently_expected) = self.expected_event_id.get();
514 let next_id = TimerEventId(currently_expected + 1);
515 debug!(
516 "invalidating expected timer (was {:?}, now {:?}",
517 currently_expected, next_id
518 );
519 self.expected_event_id.set(next_id);
520 next_id
521 }
522
523 #[allow(clippy::too_many_arguments)]
524 pub(crate) fn set_timeout_or_interval(
525 &self,
526 cx: &mut JSContext,
527 global: &GlobalScope,
528 callback: TimerCallback,
529 arguments: Vec<HandleValue>,
530 timeout: Duration,
531 is_interval: IsInterval,
532 source: TimerSource,
533 ) -> Fallible<i32> {
534 self.js_timers.set_timeout_or_interval(
535 cx,
536 global,
537 callback,
538 arguments,
539 timeout,
540 is_interval,
541 source,
542 )
543 }
544
545 pub(crate) fn clear_timeout_or_interval(&self, global: &GlobalScope, handle: i32) {
546 self.js_timers.clear_timeout_or_interval(global, handle)
547 }
548}
549
550#[derive(Clone, Copy, Eq, Hash, JSTraceable, MallocSizeOf, Ord, PartialEq, PartialOrd)]
551pub(crate) struct JsTimerHandle(i32);
552
553#[derive(DenyPublicFields, JSTraceable, MallocSizeOf)]
554pub(crate) struct JsTimers {
555 next_timer_handle: Cell<JsTimerHandle>,
556 active_timers: DomRefCell<FxHashMap<JsTimerHandle, JsTimerEntry>>,
558 nesting_level: Cell<u32>,
560 min_duration: Cell<Option<Duration>>,
562}
563
564#[derive(JSTraceable, MallocSizeOf)]
565struct JsTimerEntry {
566 oneshot_handle: OneshotTimerHandle,
567}
568
569#[derive(JSTraceable, MallocSizeOf)]
574pub(crate) struct JsTimerTask {
575 handle: JsTimerHandle,
576 #[no_trace]
577 source: TimerSource,
578 callback: InternalTimerCallback,
579 is_interval: IsInterval,
580 nesting_level: u32,
581 duration: Duration,
582 is_user_interacting: bool,
583}
584
585#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
587pub(crate) enum IsInterval {
588 Interval,
589 NonInterval,
590}
591
592pub(crate) enum TimerCallback {
593 StringTimerCallback(TrustedScriptOrString),
594 FunctionTimerCallback(Rc<Function>),
595}
596
597#[derive(Clone, JSTraceable, MallocSizeOf)]
598#[cfg_attr(crown, expect(crown::unrooted_must_root))]
599enum InternalTimerCallback {
600 StringTimerCallback(DOMString),
601 FunctionTimerCallback(
602 #[conditional_malloc_size_of] Rc<Function>,
603 #[ignore_malloc_size_of = "mozjs"] Rc<Box<[Heap<JSVal>]>>,
604 ),
605}
606
607impl Default for JsTimers {
608 fn default() -> Self {
609 JsTimers {
610 next_timer_handle: Cell::new(JsTimerHandle(1)),
611 active_timers: DomRefCell::new(FxHashMap::default()),
612 nesting_level: Cell::new(0),
613 min_duration: Cell::new(None),
614 }
615 }
616}
617
618impl JsTimers {
619 #[allow(clippy::too_many_arguments)]
621 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
622 pub(crate) fn set_timeout_or_interval(
623 &self,
624 cx: &mut JSContext,
625 global: &GlobalScope,
626 callback: TimerCallback,
627 arguments: Vec<HandleValue>,
628 timeout: Duration,
629 is_interval: IsInterval,
630 source: TimerSource,
631 ) -> Fallible<i32> {
632 let callback = match callback {
633 TimerCallback::StringTimerCallback(trusted_script_or_string) => {
634 let global_name = if global.is::<Window>() {
636 "Window"
637 } else {
638 "WorkerGlobalScope"
639 };
640 let method_name = if is_interval == IsInterval::Interval {
642 "setInterval"
643 } else {
644 "setTimeout"
645 };
646 let sink = format!("{} {}", global_name, method_name);
648 let code_str = TrustedScript::get_trusted_type_compliant_string(
651 cx,
652 global,
653 trusted_script_or_string,
654 &sink,
655 )?;
656 if global
659 .get_csp_list()
660 .is_js_evaluation_allowed(global, &code_str.str())
661 {
662 InternalTimerCallback::StringTimerCallback(code_str)
664 } else {
665 return Ok(0);
666 }
667 },
668 TimerCallback::FunctionTimerCallback(function) => {
669 let mut args = Vec::with_capacity(arguments.len());
672 for _ in 0..arguments.len() {
673 args.push(Heap::default());
674 }
675 for (i, item) in arguments.iter().enumerate() {
676 args.get_mut(i).unwrap().set(item.get());
677 }
678 InternalTimerCallback::FunctionTimerCallback(
681 function,
682 Rc::new(args.into_boxed_slice()),
683 )
684 },
685 };
686
687 let JsTimerHandle(new_handle) = self.next_timer_handle.get();
691 self.next_timer_handle.set(JsTimerHandle(new_handle + 1));
692
693 let mut task = JsTimerTask {
697 handle: JsTimerHandle(new_handle),
698 source,
699 callback,
700 is_interval,
701 is_user_interacting: ScriptThread::is_user_interacting(),
702 nesting_level: 0,
703 duration: Duration::ZERO,
704 };
705
706 task.duration = timeout.max(Duration::ZERO);
708
709 self.initialize_and_schedule(global, task);
710
711 Ok(new_handle)
713 }
714
715 pub(crate) fn clear_timeout_or_interval(&self, global: &GlobalScope, handle: i32) {
716 let mut active_timers = self.active_timers.borrow_mut();
717
718 if let Some(entry) = active_timers.remove(&JsTimerHandle(handle)) {
719 global.unschedule_callback(entry.oneshot_handle);
720 }
721 }
722
723 pub(crate) fn set_min_duration(&self, duration: Duration) {
724 self.min_duration.set(Some(duration));
725 }
726
727 pub(crate) fn remove_min_duration(&self) {
728 self.min_duration.set(None);
729 }
730
731 fn user_agent_pad(&self, current_duration: Duration) -> Duration {
733 match self.min_duration.get() {
734 Some(min_duration) => min_duration.max(current_duration),
735 None => current_duration,
736 }
737 }
738
739 fn initialize_and_schedule(&self, global: &GlobalScope, mut task: JsTimerTask) {
741 let handle = task.handle;
742 let mut active_timers = self.active_timers.borrow_mut();
743
744 let nesting_level = self.nesting_level.get();
748
749 let duration = self.user_agent_pad(clamp_duration(nesting_level, task.duration));
750 task.nesting_level = nesting_level + 1;
753
754 let callback = OneshotTimerCallback::JsTimer(task);
757 let oneshot_handle = global.schedule_callback(callback, duration);
758
759 let entry = active_timers
761 .entry(handle)
762 .or_insert(JsTimerEntry { oneshot_handle });
763 entry.oneshot_handle = oneshot_handle;
764 }
765}
766
767fn clamp_duration(nesting_level: u32, unclamped: Duration) -> Duration {
769 let lower_bound_ms = if nesting_level > 5 { 4 } else { 0 };
771 let lower_bound = Duration::from_millis(lower_bound_ms);
772 lower_bound.max(unclamped)
773}
774
775impl JsTimerTask {
776 pub(crate) fn invoke<T: DomObject>(self, this: &T, timers: &JsTimers, cx: &mut JSContext) {
778 timers.nesting_level.set(self.nesting_level);
783
784 let _guard = ScriptThread::user_interacting_guard();
785 match self.callback {
786 InternalTimerCallback::StringTimerCallback(ref code_str) => {
787 let global = this.global();
790 let fetch_options = ScriptFetchOptions::default_classic_script();
794
795 let base_url = global.api_base_url();
797
798 let script = global.create_a_classic_script(
810 cx,
811 (*code_str.str()).into(),
812 base_url,
813 fetch_options,
814 ErrorReporting::Unmuted,
815 Some(IntroductionType::DOM_TIMER),
816 1,
817 false,
818 );
819
820 _ = global.run_a_classic_script(cx, script, RethrowErrors::No);
822 },
823 InternalTimerCallback::FunctionTimerCallback(ref function, ref arguments) => {
826 let arguments = self.collect_heap_args(arguments);
827 rooted!(&in(cx) let mut value: JSVal);
828 let _ = function.Call_(
829 this,
830 arguments,
831 value.handle_mut(),
832 Report,
833 CanGc::from_cx(cx),
834 );
835 },
836 };
837
838 timers.nesting_level.set(0);
840
841 if self.is_interval == IsInterval::Interval &&
847 timers.active_timers.borrow().contains_key(&self.handle)
848 {
849 timers.initialize_and_schedule(&this.global(), self);
850 }
851 }
852
853 fn collect_heap_args<'b>(&self, args: &'b [Heap<JSVal>]) -> Vec<HandleValue<'b>> {
854 args.iter().map(|arg| arg.as_handle_value()).collect()
855 }
856}
857
858#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
860pub enum TimerSource {
861 FromWindow(PipelineId),
863 FromWorker,
865}
866
867#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
869pub struct TimerEventId(pub u32);
870
871#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
875pub struct TimerEvent(pub TimerSource, pub TimerEventId);
876
877#[derive(Clone)]
879struct TimerListener {
880 task_source: SendableTaskSource,
881 context: Trusted<GlobalScope>,
882 source: TimerSource,
883 id: TimerEventId,
884}
885
886impl TimerListener {
887 fn handle(&self, event: TimerEvent) {
891 let context = self.context.clone();
892 self.task_source.queue(task!(timer_event: move |cx| {
894 let global = context.root();
895 let TimerEvent(source, id) = event;
896 match source {
897 TimerSource::FromWorker => {
898 global.downcast::<WorkerGlobalScope>().expect("Window timer delivered to worker");
899 },
900 TimerSource::FromWindow(pipeline) => {
901 assert_eq!(pipeline, global.pipeline_id());
902 global.downcast::<Window>().expect("Worker timer delivered to window");
903 },
904 };
905 global.fire_timer(id, cx);
906 })
907 );
908 }
909
910 fn into_callback(self) -> BoxedTimerCallback {
911 let timer_event = TimerEvent(self.source, self.id);
912 Box::new(move || self.handle(timer_event))
913 }
914}