1use std::cell::Cell;
6use std::collections::VecDeque;
7use std::rc::Rc;
8use std::sync::Arc;
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::thread::{self, JoinHandle};
11
12use crossbeam_channel::{Receiver, Sender, unbounded};
13use devtools_traits::DevtoolScriptControlMsg;
14use dom_struct::dom_struct;
15use fonts::FontContext;
16use js::context::JSContext;
17use js::jsapi::{Heap, JSObject};
18use js::jsval::UndefinedValue;
19use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
20use net_traits::blob_url_store::UrlWithBlobClaim;
21use net_traits::image_cache::ImageCache;
22use net_traits::policy_container::PolicyContainer;
23use net_traits::request::{
24 CredentialsMode, Destination, InsecureRequestsPolicy, Origin, ParserMetadata,
25 PreloadedResources, Referrer, RequestBuilder, RequestClient, RequestMode,
26};
27use script_bindings::cell::DomRefCell;
28use script_bindings::interfaces::HasOrigin;
29use servo_base::generic_channel::{self, GenericReceiver, GenericSender, RoutedReceiver};
30use servo_base::id::{BrowsingContextId, PipelineId, ScriptEventLoopId, WebViewId};
31use servo_constellation_traits::{
32 ScriptToConstellationMessage, WorkerAnimationFrameTick, WorkerGlobalScopeInit,
33 WorkerScriptLoadOrigin,
34};
35use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
36use style::thread_state::{self, ThreadState};
37
38use crate::conversions::Convert;
39use crate::dom::abstractworker::{MessageData, SimpleWorkerErrorHandler, WorkerScriptMsg};
40use crate::dom::abstractworkerglobalscope::{WorkerEventLoopMethods, run_worker_event_loop};
41use crate::dom::bindings::callback::ExceptionHandling;
42use crate::dom::bindings::codegen::Bindings::AnimationFrameProviderBinding::FrameRequestCallback;
43use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
44use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
45use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
46use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
47use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerOptions, WorkerType};
48use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible};
49use crate::dom::bindings::inheritance::Castable;
50use crate::dom::bindings::num::Finite;
51use crate::dom::bindings::refcounted::Trusted;
52use crate::dom::bindings::reflector::DomGlobal;
53use crate::dom::bindings::root::DomRoot;
54use crate::dom::bindings::str::DOMString;
55use crate::dom::bindings::structuredclone;
56use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox};
57use crate::dom::csp::Violation;
58use crate::dom::errorevent::ErrorEvent;
59use crate::dom::event::{Event, EventBubbles, EventCancelable};
60use crate::dom::eventtarget::EventTarget;
61use crate::dom::globalscope::GlobalScope;
62use crate::dom::html::htmlscriptelement::Script;
63use crate::dom::messageevent::MessageEvent;
64use crate::dom::types::DebuggerGlobalScope;
65#[cfg(feature = "webgpu")]
66use crate::dom::webgpu::identityhub::IdentityHub;
67use crate::dom::worker::{TrustedWorkerAddress, Worker};
68use crate::dom::workerglobalscope::{ScriptFetchContext, WorkerGlobalScope};
69use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
70use crate::modules::script_module::fetch_a_module_script_graph;
71use crate::realms::enter_auto_realm;
72use crate::script_runtime::ScriptThreadEventCategory::WorkerEvent;
73use crate::script_runtime::{IntroductionType, Runtime, ThreadSafeJSContext};
74use crate::tasks::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
75use crate::tasks::task_source::TaskSourceName;
76
77pub(crate) struct AutoWorkerReset<'a> {
82 workerscope: &'a DedicatedWorkerGlobalScope,
83 old_worker: Option<TrustedWorkerAddress>,
84}
85
86impl<'a> AutoWorkerReset<'a> {
87 pub(crate) fn new(
88 workerscope: &'a DedicatedWorkerGlobalScope,
89 worker: TrustedWorkerAddress,
90 ) -> AutoWorkerReset<'a> {
91 let old_worker = workerscope.replace_worker(Some(worker));
92 AutoWorkerReset {
93 workerscope,
94 old_worker,
95 }
96 }
97}
98
99impl Drop for AutoWorkerReset<'_> {
100 fn drop(&mut self) {
101 self.workerscope
102 .replace_worker(std::mem::take(&mut self.old_worker));
103 }
104}
105
106pub(crate) enum DedicatedWorkerControlMsg {
108 Exit,
110 AnimationFrameProviderUnsupported,
111}
112
113pub(crate) enum DedicatedWorkerScriptMsg {
114 CommonWorker(TrustedWorkerAddress, WorkerScriptMsg),
116 WakeUp,
118}
119
120pub(crate) enum MixedMessage {
121 Worker(DedicatedWorkerScriptMsg),
122 Devtools(DevtoolScriptControlMsg),
123 Control(DedicatedWorkerControlMsg),
124 AnimationFrameTick(WorkerAnimationFrameTick),
125 Timer,
126}
127
128impl QueuedTaskConversion for DedicatedWorkerScriptMsg {
129 fn task_source_name(&self) -> Option<&TaskSourceName> {
130 let common_worker_msg = match self {
131 DedicatedWorkerScriptMsg::CommonWorker(_, common_worker_msg) => common_worker_msg,
132 _ => return None,
133 };
134 let script_msg = match common_worker_msg {
135 WorkerScriptMsg::Common(script_msg) => script_msg,
136 _ => return None,
137 };
138 match script_msg {
139 CommonScriptMsg::Task(_category, _boxed, _pipeline_id, source_name) => {
140 Some(source_name)
141 },
142 _ => None,
143 }
144 }
145
146 fn pipeline_id(&self) -> Option<PipelineId> {
147 None
150 }
151
152 fn into_queued_task(self) -> Option<QueuedTask> {
153 let (worker, common_worker_msg) = match self {
154 DedicatedWorkerScriptMsg::CommonWorker(worker, common_worker_msg) => {
155 (worker, common_worker_msg)
156 },
157 _ => return None,
158 };
159 let script_msg = match common_worker_msg {
160 WorkerScriptMsg::Common(script_msg) => script_msg,
161 _ => return None,
162 };
163 let (event_category, task, pipeline_id, task_source) = match script_msg {
164 CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => {
165 (category, boxed, pipeline_id, task_source)
166 },
167 _ => return None,
168 };
169 Some(QueuedTask {
170 worker: Some(worker),
171 event_category,
172 task,
173 pipeline_id,
174 task_source,
175 })
176 }
177
178 fn from_queued_task(queued_task: QueuedTask) -> Self {
179 let script_msg = CommonScriptMsg::Task(
180 queued_task.event_category,
181 queued_task.task,
182 queued_task.pipeline_id,
183 queued_task.task_source,
184 );
185 DedicatedWorkerScriptMsg::CommonWorker(
186 queued_task.worker.unwrap(),
187 WorkerScriptMsg::Common(script_msg),
188 )
189 }
190
191 fn inactive_msg() -> Self {
192 panic!("Workers should never receive messages marked as inactive");
194 }
195
196 fn wake_up_msg() -> Self {
197 DedicatedWorkerScriptMsg::WakeUp
198 }
199
200 fn is_wake_up(&self) -> bool {
201 matches!(self, DedicatedWorkerScriptMsg::WakeUp)
202 }
203}
204
205unsafe_no_jsmanaged_fields!(TaskQueue<DedicatedWorkerScriptMsg>);
206
207#[dom_struct]
209pub(crate) struct DedicatedWorkerGlobalScope {
210 workerglobalscope: WorkerGlobalScope,
211 #[no_trace]
213 webview_id: WebViewId,
214 #[ignore_malloc_size_of = "Defined in std"]
215 task_queue: TaskQueue<DedicatedWorkerScriptMsg>,
216 own_sender: Sender<DedicatedWorkerScriptMsg>,
217 worker: DomRefCell<Option<TrustedWorkerAddress>>,
218 parent_event_loop_sender: ScriptEventLoopSender,
220 #[ignore_malloc_size_of = "ImageCache"]
221 #[no_trace]
222 image_cache: Arc<dyn ImageCache>,
223 #[no_trace]
224 browsing_context: Option<BrowsingContextId>,
225 #[conditional_malloc_size_of]
226 animation_frame_provider_supported: Arc<AtomicBool>,
227 animation_frame_provider_registered: Cell<bool>,
228 animation_frame_ident: Cell<u32>,
230 #[ignore_malloc_size_of = "closures are hard"]
232 animation_frame_list: DomRefCell<VecDeque<(u32, Rc<FrameRequestCallback>)>>,
233 #[ignore_malloc_size_of = "closures are hard"]
235 current_animation_frame_list: DomRefCell<VecDeque<(u32, Rc<FrameRequestCallback>)>>,
236 running_animation_callbacks: Cell<bool>,
238 animation_frame_callbacks_active: Cell<bool>,
240 #[no_trace]
242 #[ignore_malloc_size_of = "channels are hard"]
243 animation_frame_tick_sender: Option<GenericSender<WorkerAnimationFrameTick>>,
244 #[no_trace]
246 #[ignore_malloc_size_of = "channels are hard"]
247 animation_frame_tick_receiver: Option<RoutedReceiver<WorkerAnimationFrameTick>>,
248 #[no_trace]
250 control_receiver: Receiver<DedicatedWorkerControlMsg>,
251 #[no_trace]
252 queued_worker_tasks: DomRefCell<Vec<MessageData>>,
253}
254
255impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope {
256 type WorkerMsg = DedicatedWorkerScriptMsg;
257 type ControlMsg = DedicatedWorkerControlMsg;
258 type Event = MixedMessage;
259
260 fn task_queue(&self) -> &TaskQueue<DedicatedWorkerScriptMsg> {
261 &self.task_queue
262 }
263
264 fn handle_event(&self, event: MixedMessage, cx: &mut JSContext) -> bool {
265 self.handle_mixed_message(event, cx)
266 }
267
268 fn handle_worker_post_event(
269 &self,
270 worker: &TrustedWorkerAddress,
271 ) -> Option<AutoWorkerReset<'_>> {
272 let ar = AutoWorkerReset::new(self, worker.clone());
273 Some(ar)
274 }
275
276 fn from_control_msg(msg: DedicatedWorkerControlMsg) -> MixedMessage {
277 MixedMessage::Control(msg)
278 }
279
280 fn from_worker_msg(msg: DedicatedWorkerScriptMsg) -> MixedMessage {
281 MixedMessage::Worker(msg)
282 }
283
284 fn from_devtools_msg(msg: DevtoolScriptControlMsg) -> MixedMessage {
285 MixedMessage::Devtools(msg)
286 }
287
288 fn from_timer_msg() -> MixedMessage {
289 MixedMessage::Timer
290 }
291
292 fn from_animation_frame_tick_msg(msg: WorkerAnimationFrameTick) -> Option<MixedMessage> {
293 Some(MixedMessage::AnimationFrameTick(msg))
294 }
295
296 fn animation_frame_tick_receiver(&self) -> Option<&RoutedReceiver<WorkerAnimationFrameTick>> {
297 self.animation_frame_tick_receiver.as_ref()
298 }
299
300 fn control_receiver(&self) -> &Receiver<DedicatedWorkerControlMsg> {
301 &self.control_receiver
302 }
303}
304
305impl DedicatedWorkerGlobalScope {
306 #[allow(clippy::too_many_arguments)]
307 fn new_inherited(
308 init: WorkerGlobalScopeInit,
309 webview_id: WebViewId,
310 worker_name: DOMString,
311 worker_type: WorkerType,
312 worker_url: ServoUrl,
313 from_devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
314 runtime: Runtime,
315 parent_event_loop_sender: ScriptEventLoopSender,
316 own_sender: Sender<DedicatedWorkerScriptMsg>,
317 receiver: Receiver<DedicatedWorkerScriptMsg>,
318 closing: Arc<AtomicBool>,
319 animation_frame_provider_supported: Arc<AtomicBool>,
320 image_cache: Arc<dyn ImageCache>,
321 browsing_context: Option<BrowsingContextId>,
322 animation_frame_tick_sender: Option<GenericSender<WorkerAnimationFrameTick>>,
323 animation_frame_tick_receiver: Option<RoutedReceiver<WorkerAnimationFrameTick>>,
324 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
325 control_receiver: Receiver<DedicatedWorkerControlMsg>,
326 insecure_requests_policy: InsecureRequestsPolicy,
327 font_context: Arc<FontContext>,
328 ) -> DedicatedWorkerGlobalScope {
329 DedicatedWorkerGlobalScope {
330 workerglobalscope: WorkerGlobalScope::new_inherited(
331 init,
332 worker_name,
333 worker_type,
334 worker_url,
335 runtime,
336 from_devtools_receiver,
337 closing,
338 #[cfg(feature = "webgpu")]
339 gpu_id_hub,
340 insecure_requests_policy,
341 font_context,
342 None,
343 ),
344 webview_id,
345 task_queue: TaskQueue::new(receiver, own_sender.clone()),
346 own_sender,
347 parent_event_loop_sender,
348 worker: DomRefCell::new(None),
349 image_cache,
350 browsing_context,
351 animation_frame_provider_supported,
352 animation_frame_provider_registered: Cell::new(false),
353 animation_frame_ident: Cell::new(0),
354 animation_frame_list: DomRefCell::new(VecDeque::new()),
355 current_animation_frame_list: DomRefCell::new(VecDeque::new()),
356 running_animation_callbacks: Cell::new(false),
357 animation_frame_callbacks_active: Cell::new(false),
358 animation_frame_tick_sender,
359 animation_frame_tick_receiver,
360 control_receiver,
361 queued_worker_tasks: Default::default(),
362 }
363 }
364
365 #[expect(clippy::too_many_arguments)]
366 pub(crate) fn new(
367 init: WorkerGlobalScopeInit,
368 webview_id: WebViewId,
369 worker_name: DOMString,
370 worker_type: WorkerType,
371 worker_url: ServoUrl,
372 from_devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
373 runtime: Runtime,
374 parent_event_loop_sender: ScriptEventLoopSender,
375 own_sender: Sender<DedicatedWorkerScriptMsg>,
376 receiver: Receiver<DedicatedWorkerScriptMsg>,
377 closing: Arc<AtomicBool>,
378 animation_frame_provider_supported: Arc<AtomicBool>,
379 image_cache: Arc<dyn ImageCache>,
380 browsing_context: Option<BrowsingContextId>,
381 animation_frame_tick_sender: Option<GenericSender<WorkerAnimationFrameTick>>,
382 animation_frame_tick_receiver: Option<RoutedReceiver<WorkerAnimationFrameTick>>,
383 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
384 control_receiver: Receiver<DedicatedWorkerControlMsg>,
385 insecure_requests_policy: InsecureRequestsPolicy,
386 font_context: Arc<FontContext>,
387 debugger_global: &DebuggerGlobalScope,
388 cx: &mut js::context::JSContext,
389 ) -> DomRoot<DedicatedWorkerGlobalScope> {
390 let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited(
391 init,
392 webview_id,
393 worker_name,
394 worker_type,
395 worker_url,
396 from_devtools_receiver,
397 runtime,
398 parent_event_loop_sender,
399 own_sender,
400 receiver,
401 closing,
402 animation_frame_provider_supported,
403 image_cache,
404 browsing_context,
405 animation_frame_tick_sender,
406 animation_frame_tick_receiver,
407 #[cfg(feature = "webgpu")]
408 gpu_id_hub,
409 control_receiver,
410 insecure_requests_policy,
411 font_context,
412 ));
413 let scope = DedicatedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(
414 cx,
415 &scope.origin(),
416 scope,
417 );
418 scope
419 .upcast::<WorkerGlobalScope>()
420 .init_debugger_global(debugger_global, cx);
421
422 scope
423 }
424
425 #[expect(unsafe_code)]
427 #[allow(clippy::too_many_arguments)]
428 pub(crate) fn run_worker_scope(
429 mut init: WorkerGlobalScopeInit,
430 webview_id: WebViewId,
431 worker_url: UrlWithBlobClaim,
432 from_devtools_receiver: GenericReceiver<DevtoolScriptControlMsg>,
433 worker: TrustedWorkerAddress,
434 parent_event_loop_sender: ScriptEventLoopSender,
435 own_sender: Sender<DedicatedWorkerScriptMsg>,
436 receiver: Receiver<DedicatedWorkerScriptMsg>,
437 worker_load_origin: WorkerScriptLoadOrigin,
438 worker_options: &WorkerOptions,
439 closing: Arc<AtomicBool>,
440 animation_frame_provider_supported: Arc<AtomicBool>,
441 image_cache: Arc<dyn ImageCache>,
442 browsing_context: Option<BrowsingContextId>,
443 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
444 control_receiver: Receiver<DedicatedWorkerControlMsg>,
445 context_sender: Sender<ThreadSafeJSContext>,
446 insecure_requests_policy: InsecureRequestsPolicy,
447 policy_container: PolicyContainer,
448 font_context: Arc<FontContext>,
449 ) -> JoinHandle<()> {
450 let event_loop_id = ScriptEventLoopId::installed()
451 .expect("Should always be in a ScriptThread or in a dedicated worker");
452 let current_global = GlobalScope::current().expect("No current global object");
453 let origin = current_global.origin().immutable().clone();
454 let referrer = current_global.get_referrer();
455 let parent = current_global.runtime_handle();
456 let current_global_ancestor_trustworthy = current_global.has_trustworthy_ancestor_origin();
457 let is_secure_context = current_global.is_secure_context();
458 let is_nested_browsing_context = current_global.is_nested_browsing_context();
459
460 let worker_type = worker_options.type_;
461 let worker_name = worker_options.name.to_string();
462 let credentials = worker_options.credentials.convert();
463
464 thread::Builder::new()
465 .name(format!("WW:{}", worker_url.debug_compact()))
466 .spawn(move || {
467 thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
468 ScriptEventLoopId::install(event_loop_id);
469
470 let WorkerScriptLoadOrigin {
471 referrer_url,
472 pipeline_id,
473 ..
474 } = worker_load_origin;
475
476 let referrer = referrer_url.map(Referrer::ReferrerUrl).unwrap_or(referrer);
477
478 let request_client = RequestClient {
479 preloaded_resources: PreloadedResources::default(),
480 policy_container,
481 origin: Origin::Origin(origin.clone()),
482 is_nested_browsing_context,
483 insecure_requests_policy,
484 has_trustworthy_ancestor_origin: current_global_ancestor_trustworthy,
485 };
486
487 let event_loop_sender = ScriptEventLoopSender::DedicatedWorker {
488 sender: own_sender.clone(),
489 main_thread_worker: worker.clone(),
490 };
491
492 let runtime = unsafe {
493 Runtime::new_with_parent(Some(parent), Some(event_loop_sender.clone()))
494 };
495 let mut cx = unsafe { runtime.cx() };
499 let cx = &mut cx;
500 let debugger_global = DebuggerGlobalScope::new(
501 pipeline_id,
502 init.to_devtools_sender.clone(),
503 init.from_devtools_sender
504 .clone()
505 .expect("Guaranteed by Worker::Constructor"),
506 init.mem_profiler_chan.clone(),
507 init.time_profiler_chan.clone(),
508 init.script_to_constellation_chan.clone(),
509 init.script_to_embedder_chan.clone(),
510 init.resource_threads.clone(),
511 init.storage_threads.clone(),
512 #[cfg(feature = "webgpu")]
513 gpu_id_hub.clone(),
514 cx,
515 );
516 debugger_global.execute(cx);
517
518 let context_for_interrupt = runtime.thread_safe_js_context();
519 let _ = context_sender.send(context_for_interrupt);
520
521 let devtools_mpsc_port = from_devtools_receiver.route_preserving_errors();
522 let animation_frame_channel = init
523 .animation_frame_provider_supported
524 .then(|| generic_channel::channel().expect("Failed to create generic channel"));
525 let (animation_frame_tick_sender, animation_frame_tick_receiver) =
526 match animation_frame_channel {
527 Some((sender, receiver)) => {
528 (Some(sender), Some(receiver.route_preserving_errors()))
529 },
530 None => (None, None),
531 };
532
533 if worker_url.scheme() == "data" {
541 if is_secure_context {
543 init.origin = ImmutableOrigin::new_opaque_data_url_worker();
544 } else {
545 init.origin = ImmutableOrigin::new_opaque();
546 }
547 }
548
549 let worker_id = init.worker_id;
550 let devtools_enabled = init.to_devtools_sender.is_some();
551 let global = DedicatedWorkerGlobalScope::new(
552 init,
553 webview_id,
554 worker_name.into(),
555 worker_type,
556 worker_url.url(),
557 devtools_mpsc_port,
558 runtime,
559 parent_event_loop_sender,
560 own_sender,
561 receiver,
562 closing,
563 animation_frame_provider_supported,
564 image_cache,
565 browsing_context,
566 animation_frame_tick_sender,
567 animation_frame_tick_receiver,
568 #[cfg(feature = "webgpu")]
569 gpu_id_hub,
570 control_receiver,
571 insecure_requests_policy,
572 font_context,
573 &debugger_global,
574 cx,
575 );
576
577 if devtools_enabled {
578 debugger_global.fire_add_debuggee(
579 cx,
580 global.upcast(),
581 pipeline_id,
582 Some(worker_id),
583 );
584 }
585 let scope = global.upcast::<WorkerGlobalScope>();
586 let global_scope = global.upcast::<GlobalScope>();
587
588 {
590 let _ar = AutoWorkerReset::new(&global, worker.clone());
591 match worker_type {
592 WorkerType::Classic => {
593 fetch_a_classic_worker_script(
594 scope,
595 worker_url,
596 request_client,
597 Destination::Worker,
598 Some(webview_id),
599 referrer,
600 );
601 },
602 WorkerType::Module => {
603 let worker_scope = DomRoot::from_ref(scope);
604 fetch_a_module_script_graph(
605 cx,
606 global_scope,
607 worker_url,
608 request_client,
609 Destination::Worker,
610 referrer,
611 credentials,
612 Some(IntroductionType::WORKER),
613 move |cx, module_tree| {
614 worker_scope.on_complete(cx, module_tree.map(Script::Module));
615 },
616 );
617 },
618 }
619
620 let reporter_name = format!("dedicated-worker-reporter-{}", worker_id);
621 scope
622 .upcast::<GlobalScope>()
623 .mem_profiler_chan()
624 .run_with_memory_reporting(
625 || {
626 while !scope.is_closing() {
632 run_worker_event_loop(&*global, Some(&worker), cx);
633 }
634 },
635 reporter_name,
636 event_loop_sender,
637 CommonScriptMsg::CollectReports,
638 );
639 }
640
641 global.clear_animation_frame_callbacks_and_unregister();
643 scope.clear_js_runtime();
644 })
645 .expect("Thread spawning failed")
646 }
647
648 pub(crate) fn webview_id(&self) -> WebViewId {
649 self.webview_id
650 }
651
652 pub(crate) fn animation_frame_provider_supported(&self) -> bool {
653 self.animation_frame_provider_supported
654 .load(Ordering::SeqCst)
655 }
656
657 pub(crate) fn animation_frame_provider_supported_flag(&self) -> Arc<AtomicBool> {
658 self.animation_frame_provider_supported.clone()
659 }
660
661 fn register_animation_frame_provider(&self) {
662 if !self.animation_frame_provider_supported() {
663 return;
664 }
665
666 let Some(sender) = self.animation_frame_tick_sender.clone() else {
667 return;
668 };
669
670 if self.animation_frame_provider_registered.replace(true) {
671 return;
672 }
673
674 let worker_id = self.upcast::<WorkerGlobalScope>().worker_id();
675 log::debug!(
676 "Registering dedicated worker animation frame provider: worker={worker_id:?} ---->"
677 );
678 let _ = self
679 .upcast::<GlobalScope>()
680 .script_to_constellation_chan()
681 .send(
682 ScriptToConstellationMessage::RegisterWorkerAnimationFrameProvider(
683 worker_id, sender,
684 ),
685 );
686 }
687
688 fn unregister_animation_frame_provider(&self) {
689 if !self.animation_frame_provider_registered.replace(false) {
690 return;
691 }
692
693 self.animation_frame_callbacks_active.set(false);
694 let worker_id = self.upcast::<WorkerGlobalScope>().worker_id();
695 log::debug!(
696 "Unregistering dedicated worker animation frame provider: worker={worker_id:?} ---->"
697 );
698 let _ = self
699 .upcast::<GlobalScope>()
700 .script_to_constellation_chan()
701 .send(ScriptToConstellationMessage::UnregisterWorkerAnimationFrameProvider(worker_id));
702 }
703
704 fn send_animation_frame_callbacks_state(&self, active: bool) {
705 if !self.animation_frame_provider_supported() {
706 return;
707 }
708
709 let worker_id = self.upcast::<WorkerGlobalScope>().worker_id();
710 log::debug!(
711 "Sending dedicated worker animation frame state: worker={worker_id:?}, active={active} ---->",
712 );
713 let _ = self
714 .upcast::<GlobalScope>()
715 .script_to_constellation_chan()
716 .send(
717 ScriptToConstellationMessage::ChangeWorkerAnimationFrameProviderState(
718 worker_id, active,
719 ),
720 );
721 }
722
723 fn set_animation_frame_callbacks_active(&self, active: bool) {
724 if self.animation_frame_callbacks_active.replace(active) == active {
725 return;
726 }
727
728 self.send_animation_frame_callbacks_state(active);
729 }
730
731 fn remove_animation_frame_callback_from(
732 list: &DomRefCell<VecDeque<(u32, Rc<FrameRequestCallback>)>>,
733 ident: u32,
734 ) {
735 let mut list = list.borrow_mut();
736 if let Some(position) = list.iter().position(|(handle, _)| *handle == ident) {
737 list.remove(position);
738 }
739 }
740
741 fn has_animation_frame_callbacks(&self) -> bool {
742 !self.animation_frame_list.borrow().is_empty() ||
743 !self.current_animation_frame_list.borrow().is_empty()
744 }
745
746 pub(crate) fn request_animation_frame(
748 &self,
749 callback: Rc<FrameRequestCallback>,
750 ) -> Fallible<u32> {
751 if !self.animation_frame_provider_supported() {
753 return Err(Error::NotSupported(Some(
754 "Animation frame callbacks are not supported for this worker".to_owned(),
755 )));
756 }
757
758 self.register_animation_frame_provider();
759
760 let ident = self.animation_frame_ident.get() + 1;
764 self.animation_frame_ident.set(ident);
765
766 self.animation_frame_list
769 .borrow_mut()
770 .push_back((ident, callback));
771 log::debug!("Queued dedicated worker animation frame callback: handle={ident} ---->");
772 self.set_animation_frame_callbacks_active(true);
773
774 Ok(ident)
776 }
777
778 pub(crate) fn cancel_animation_frame(&self, ident: u32) -> ErrorResult {
780 if !self.animation_frame_provider_supported() {
782 return Err(Error::NotSupported(Some(
783 "Animation frame callbacks are not supported for this worker".to_owned(),
784 )));
785 }
786
787 Self::remove_animation_frame_callback_from(&self.animation_frame_list, ident);
790
791 Self::remove_animation_frame_callback_from(&self.current_animation_frame_list, ident);
792 log::debug!("Cancelled dedicated worker animation frame callback: handle={ident} ---->");
793
794 if !self.running_animation_callbacks.get() && !self.has_animation_frame_callbacks() {
795 self.set_animation_frame_callbacks_active(false);
796 }
797
798 Ok(())
799 }
800
801 pub(crate) fn run_the_animation_frame_callbacks(&self, cx: &mut JSContext) {
803 if !self.animation_frame_provider_supported() ||
804 self.upcast::<WorkerGlobalScope>().is_closing()
805 {
806 return;
807 }
808
809 let callback_count = self.animation_frame_list.borrow().len();
812 log::debug!("Running dedicated worker animation frame callbacks: count={callback_count}");
813 {
814 let mut pending = self.animation_frame_list.borrow_mut();
815 let mut current = self.current_animation_frame_list.borrow_mut();
816 for _ in 0..callback_count {
817 if let Some(callback) = pending.pop_front() {
818 current.push_back(callback);
819 }
820 }
821 }
822
823 self.running_animation_callbacks.set(true);
824 let timing = self.upcast::<GlobalScope>().performance(cx).Now();
825
826 for _ in 0..callback_count {
828 let callback = self
831 .current_animation_frame_list
832 .borrow_mut()
833 .pop_front()
834 .map(|(_, callback)| callback);
835
836 if let Some(callback) = callback {
837 let _ = callback.Call__(cx, Finite::wrap(*timing), ExceptionHandling::Report);
839 }
840 }
841
842 self.current_animation_frame_list.borrow_mut().clear();
843 self.running_animation_callbacks.set(false);
844
845 if !self.has_animation_frame_callbacks() {
846 self.set_animation_frame_callbacks_active(false);
847 } else {
848 self.send_animation_frame_callbacks_state(true);
851 }
852 }
853
854 pub(crate) fn clear_animation_frame_callbacks_and_unregister(&self) {
855 let worker_id = self.upcast::<WorkerGlobalScope>().worker_id();
857 log::debug!(
858 "Clearing dedicated worker animation frame callbacks: worker={worker_id:?} ---->"
859 );
860 self.animation_frame_list.borrow_mut().clear();
861 self.current_animation_frame_list.borrow_mut().clear();
862 self.running_animation_callbacks.set(false);
863 self.animation_frame_callbacks_active.set(false);
864 self.unregister_animation_frame_provider();
865 }
866
867 fn replace_worker(
872 &self,
873 new_worker: Option<TrustedWorkerAddress>,
874 ) -> Option<TrustedWorkerAddress> {
875 let old_worker = std::mem::replace(&mut *self.worker.borrow_mut(), new_worker);
876
877 self.upcast::<GlobalScope>()
883 .task_manager()
884 .set_sender(self.event_loop_sender());
885
886 old_worker
887 }
888
889 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
890 self.image_cache.clone()
891 }
892
893 pub(crate) fn event_loop_sender(&self) -> Option<ScriptEventLoopSender> {
894 Some(ScriptEventLoopSender::DedicatedWorker {
895 sender: self.own_sender.clone(),
896 main_thread_worker: self.worker.borrow().clone()?,
897 })
898 }
899
900 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
901 let (sender, receiver) = unbounded();
902 let main_thread_worker = self.worker.borrow().as_ref().unwrap().clone();
903 (
904 ScriptEventLoopSender::DedicatedWorker {
905 sender,
906 main_thread_worker,
907 },
908 ScriptEventLoopReceiver::DedicatedWorker(receiver),
909 )
910 }
911
912 pub(crate) fn fire_queued_messages(&self, cx: &mut JSContext) {
913 let queue: Vec<_> = self.queued_worker_tasks.borrow_mut().drain(..).collect();
914 for msg in queue {
915 if self.upcast::<WorkerGlobalScope>().is_closing() {
916 return;
917 }
918 self.dispatch_message_event(cx, msg);
919 }
920 }
921
922 fn dispatch_message_event(&self, cx: &mut JSContext, msg: MessageData) {
923 let scope = self.upcast::<WorkerGlobalScope>();
924 let target = self.upcast();
925 let mut realm = enter_auto_realm(cx, self);
926 let cx = &mut realm;
927 rooted!(&in(cx) let mut message = UndefinedValue());
928 if let Ok(ports) =
929 structuredclone::read(cx, scope.upcast(), *msg.data, message.handle_mut())
930 {
931 MessageEvent::dispatch_jsval(
932 cx,
933 target,
934 scope.upcast(),
935 message.handle(),
936 Some(&msg.origin.ascii_serialization()),
937 None,
938 ports,
939 );
940 } else {
941 MessageEvent::dispatch_error(cx, target, scope.upcast());
942 }
943 }
944
945 fn handle_script_event(&self, msg: WorkerScriptMsg, cx: &mut JSContext) {
946 match msg {
947 WorkerScriptMsg::DOMMessage(message_data) => {
948 if self.upcast::<WorkerGlobalScope>().is_execution_ready() {
949 self.dispatch_message_event(cx, message_data);
950 } else {
951 self.queued_worker_tasks.borrow_mut().push(message_data);
952 }
953 },
954 WorkerScriptMsg::Common(msg) => {
955 self.upcast::<WorkerGlobalScope>().process_event(msg, cx);
956 },
957 }
958 }
959
960 fn handle_mixed_message(&self, msg: MixedMessage, cx: &mut JSContext) -> bool {
962 if self.upcast::<WorkerGlobalScope>().is_closing() {
963 return false;
964 }
965 match msg {
967 MixedMessage::Devtools(msg) => self
968 .upcast::<WorkerGlobalScope>()
969 .handle_devtools_message(msg, cx),
970 MixedMessage::Worker(DedicatedWorkerScriptMsg::CommonWorker(linked_worker, msg)) => {
971 let _ar = AutoWorkerReset::new(self, linked_worker);
972 self.handle_script_event(msg, cx);
973 },
974 MixedMessage::Worker(DedicatedWorkerScriptMsg::WakeUp) => {},
975 MixedMessage::Control(DedicatedWorkerControlMsg::Exit) => {
976 return false;
977 },
978 MixedMessage::Control(DedicatedWorkerControlMsg::AnimationFrameProviderUnsupported) => {
979 self.clear_animation_frame_callbacks_and_unregister();
980 self.upcast::<GlobalScope>()
981 .disable_owned_worker_animation_frame_providers();
982 },
983 MixedMessage::AnimationFrameTick(_) => {
984 self.run_the_animation_frame_callbacks(cx);
987 },
988 MixedMessage::Timer => {},
989 }
990 true
991 }
992
993 pub(crate) fn forward_error_to_worker_object(&self, error_info: ErrorInfo) {
995 let worker = self.worker.borrow().as_ref().unwrap().clone();
997 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
998 let task = Box::new(task!(forward_error_to_worker_object: move |cx| {
999 let worker = worker.root();
1000 let global = worker.global();
1001
1002 let event = ErrorEvent::new(
1005 cx,
1006 &global,
1007 atom!("error"),
1008 EventBubbles::DoesNotBubble,
1009 EventCancelable::Cancelable,
1010 error_info.message.as_str().into(),
1011 error_info.filename.as_str().into(),
1012 error_info.lineno,
1013 error_info.column,
1014 HandleValue::null(),
1015 );
1016
1017 if event.upcast::<Event>().fire(cx, worker.upcast::<EventTarget>()) {
1019 global.report_an_error(cx, error_info, HandleValue::null());
1020 }
1021 }));
1022 self.parent_event_loop_sender
1023 .send(CommonScriptMsg::Task(
1024 WorkerEvent,
1025 task,
1026 Some(pipeline_id),
1027 TaskSourceName::DOMManipulation,
1028 ))
1029 .unwrap();
1030 }
1031
1032 fn post_message_impl(
1034 &self,
1035 cx: &mut JSContext,
1036 message: HandleValue,
1037 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1038 ) -> ErrorResult {
1039 let data = structuredclone::write(cx, message, Some(transfer))?;
1040 let worker = self.worker.borrow().as_ref().unwrap().clone();
1041 let global_scope = self.upcast::<GlobalScope>();
1042 let pipeline_id = global_scope.pipeline_id();
1043 let task = Box::new(task!(post_worker_message: move |cx| {
1044 Worker::handle_message(worker, data, cx);
1045 }));
1046 self.parent_event_loop_sender
1047 .send(CommonScriptMsg::Task(
1048 WorkerEvent,
1049 task,
1050 Some(pipeline_id),
1051 TaskSourceName::DOMManipulation,
1052 ))
1053 .expect("Sending to parent failed");
1054 Ok(())
1055 }
1056
1057 pub(crate) fn browsing_context(&self) -> Option<BrowsingContextId> {
1058 self.browsing_context
1059 }
1060
1061 pub(crate) fn report_csp_violations(&self, violations: Vec<Violation>) {
1062 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
1063 self.parent_event_loop_sender
1064 .send(CommonScriptMsg::ReportCspViolations(
1065 pipeline_id,
1066 violations,
1067 ))
1068 .unwrap_or_else(|error| {
1069 log::warn!("Failed to send CSP violations to parent event loop: {error}");
1070 });
1071 }
1072
1073 pub(crate) fn forward_simple_error_at_worker(&self) {
1074 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
1075 let worker = self.worker.borrow().clone().expect("worker must be set");
1076 self.parent_event_loop_sender
1077 .send(CommonScriptMsg::Task(
1078 WorkerEvent,
1079 Box::new(SimpleWorkerErrorHandler::new(worker)),
1080 Some(pipeline_id),
1081 TaskSourceName::DOMManipulation,
1082 ))
1083 .expect("Sending to parent failed");
1084 }
1085}
1086
1087pub(crate) fn fetch_a_classic_worker_script(
1089 workerscope: &WorkerGlobalScope,
1090 url_with_blob_lock: UrlWithBlobClaim,
1091 fetch_client: RequestClient,
1092 destination: Destination,
1093 webview_id: Option<WebViewId>,
1094 referrer: Referrer,
1095) {
1096 let policy_container = fetch_client.policy_container.clone();
1097
1098 let request = RequestBuilder::new(webview_id, url_with_blob_lock.clone(), referrer)
1100 .client(fetch_client)
1102 .pipeline_id(Some(workerscope.pipeline_id()))
1103 .destination(destination)
1105 .mode(RequestMode::SameOrigin)
1108 .credentials_mode(CredentialsMode::CredentialsSameOrigin)
1110 .parser_metadata(ParserMetadata::NotParserInserted)
1112 .use_url_credentials(true);
1114
1115 let context = ScriptFetchContext::new(
1116 Trusted::new(workerscope),
1117 url_with_blob_lock.url(),
1118 policy_container,
1119 );
1120 let global = workerscope.upcast::<GlobalScope>();
1121 let task_source = global.task_manager().networking_task_source().to_sendable();
1122 global.fetch(request, context, task_source);
1123}
1124
1125impl DedicatedWorkerGlobalScopeMethods<crate::DomTypeHolder> for DedicatedWorkerGlobalScope {
1126 fn Name(&self) -> DOMString {
1128 self.workerglobalscope.worker_name()
1129 }
1130
1131 fn PostMessage(
1133 &self,
1134 cx: &mut JSContext,
1135 message: HandleValue,
1136 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1137 ) -> ErrorResult {
1138 self.post_message_impl(cx, message, transfer)
1139 }
1140
1141 fn PostMessage_(
1143 &self,
1144 cx: &mut JSContext,
1145 message: HandleValue,
1146 options: RootedTraceableBox<StructuredSerializeOptions>,
1147 ) -> ErrorResult {
1148 let mut rooted = CustomAutoRooter::new(
1149 options
1150 .transfer
1151 .iter()
1152 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
1153 .collect(),
1154 );
1155 #[expect(unsafe_code)]
1156 let guard = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
1157 self.post_message_impl(cx, message, guard)
1158 }
1159
1160 fn Close(&self) {
1162 self.upcast::<WorkerGlobalScope>().close();
1164 }
1165
1166 fn RequestAnimationFrame(&self, callback: Rc<FrameRequestCallback>) -> Fallible<u32> {
1168 self.request_animation_frame(callback)
1169 }
1170
1171 fn CancelAnimationFrame(&self, ident: u32) -> ErrorResult {
1173 self.cancel_animation_frame(ident)
1174 }
1175
1176 event_handler!(message, GetOnmessage, SetOnmessage);
1178
1179 event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
1181}
1182
1183impl HasOrigin for DedicatedWorkerGlobalScope {
1184 fn origin(&self) -> MutableOrigin {
1185 self.upcast::<WorkerGlobalScope>().origin()
1186 }
1187}