1use std::sync::Arc;
6use std::sync::atomic::AtomicBool;
7use std::thread::{self, JoinHandle};
8
9use crossbeam_channel::{Receiver, Sender, unbounded};
10use devtools_traits::DevtoolScriptControlMsg;
11use dom_struct::dom_struct;
12use fonts::FontContext;
13use js::context::JSContext;
14use js::jsapi::{Heap, JSContext as RawJSContext, JSObject};
15use js::jsval::UndefinedValue;
16use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
17use net_traits::blob_url_store::UrlWithBlobClaim;
18use net_traits::image_cache::ImageCache;
19use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
20use net_traits::request::{
21 CredentialsMode, Destination, InsecureRequestsPolicy, Origin, ParserMetadata,
22 PreloadedResources, Referrer, RequestBuilder, RequestClient, RequestMode,
23};
24use servo_base::generic_channel::{GenericReceiver, RoutedReceiver};
25use servo_base::id::{BrowsingContextId, PipelineId, ScriptEventLoopId, WebViewId};
26use servo_constellation_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
27use servo_url::{ImmutableOrigin, ServoUrl};
28use style::thread_state::{self, ThreadState};
29
30use crate::conversions::Convert;
31use crate::dom::abstractworker::{MessageData, SimpleWorkerErrorHandler, WorkerScriptMsg};
32use crate::dom::abstractworkerglobalscope::{WorkerEventLoopMethods, run_worker_event_loop};
33use crate::dom::bindings::cell::DomRefCell;
34use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
35use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
36use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
37use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerOptions, WorkerType};
38use crate::dom::bindings::error::{ErrorInfo, ErrorResult};
39use crate::dom::bindings::inheritance::Castable;
40use crate::dom::bindings::refcounted::Trusted;
41use crate::dom::bindings::reflector::DomGlobal;
42use crate::dom::bindings::root::{Dom, DomRoot};
43use crate::dom::bindings::str::DOMString;
44use crate::dom::bindings::structuredclone;
45use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox};
46use crate::dom::csp::Violation;
47use crate::dom::errorevent::ErrorEvent;
48use crate::dom::event::{Event, EventBubbles, EventCancelable};
49use crate::dom::eventtarget::EventTarget;
50use crate::dom::globalscope::GlobalScope;
51use crate::dom::messageevent::MessageEvent;
52use crate::dom::types::DebuggerGlobalScope;
53#[cfg(feature = "webgpu")]
54use crate::dom::webgpu::identityhub::IdentityHub;
55use crate::dom::worker::{TrustedWorkerAddress, Worker};
56use crate::dom::workerglobalscope::{ScriptFetchContext, WorkerGlobalScope};
57use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
58use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
59use crate::script_module::{ModuleFetchClient, ModuleOwner, fetch_a_module_worker_script_graph};
60use crate::script_runtime::ScriptThreadEventCategory::WorkerEvent;
61use crate::script_runtime::{CanGc, JSContext as SafeJSContext, Runtime, ThreadSafeJSContext};
62use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
63use crate::task_source::TaskSourceName;
64
65pub(crate) struct AutoWorkerReset<'a> {
70 workerscope: &'a DedicatedWorkerGlobalScope,
71 old_worker: Option<TrustedWorkerAddress>,
72}
73
74impl<'a> AutoWorkerReset<'a> {
75 pub(crate) fn new(
76 workerscope: &'a DedicatedWorkerGlobalScope,
77 worker: TrustedWorkerAddress,
78 ) -> AutoWorkerReset<'a> {
79 let old_worker = workerscope.replace_worker(Some(worker));
80 AutoWorkerReset {
81 workerscope,
82 old_worker,
83 }
84 }
85}
86
87impl Drop for AutoWorkerReset<'_> {
88 fn drop(&mut self) {
89 self.workerscope
90 .replace_worker(std::mem::take(&mut self.old_worker));
91 }
92}
93
94pub(crate) enum DedicatedWorkerControlMsg {
96 Exit,
98}
99
100pub(crate) enum DedicatedWorkerScriptMsg {
101 CommonWorker(TrustedWorkerAddress, WorkerScriptMsg),
103 WakeUp,
105}
106
107pub(crate) enum MixedMessage {
108 Worker(DedicatedWorkerScriptMsg),
109 Devtools(DevtoolScriptControlMsg),
110 Control(DedicatedWorkerControlMsg),
111 Timer,
112}
113
114impl QueuedTaskConversion for DedicatedWorkerScriptMsg {
115 fn task_source_name(&self) -> Option<&TaskSourceName> {
116 let common_worker_msg = match self {
117 DedicatedWorkerScriptMsg::CommonWorker(_, common_worker_msg) => common_worker_msg,
118 _ => return None,
119 };
120 let script_msg = match common_worker_msg {
121 WorkerScriptMsg::Common(script_msg) => script_msg,
122 _ => return None,
123 };
124 match script_msg {
125 CommonScriptMsg::Task(_category, _boxed, _pipeline_id, source_name) => {
126 Some(source_name)
127 },
128 _ => None,
129 }
130 }
131
132 fn pipeline_id(&self) -> Option<PipelineId> {
133 None
136 }
137
138 fn into_queued_task(self) -> Option<QueuedTask> {
139 let (worker, common_worker_msg) = match self {
140 DedicatedWorkerScriptMsg::CommonWorker(worker, common_worker_msg) => {
141 (worker, common_worker_msg)
142 },
143 _ => return None,
144 };
145 let script_msg = match common_worker_msg {
146 WorkerScriptMsg::Common(script_msg) => script_msg,
147 _ => return None,
148 };
149 let (event_category, task, pipeline_id, task_source) = match script_msg {
150 CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => {
151 (category, boxed, pipeline_id, task_source)
152 },
153 _ => return None,
154 };
155 Some(QueuedTask {
156 worker: Some(worker),
157 event_category,
158 task,
159 pipeline_id,
160 task_source,
161 })
162 }
163
164 fn from_queued_task(queued_task: QueuedTask) -> Self {
165 let script_msg = CommonScriptMsg::Task(
166 queued_task.event_category,
167 queued_task.task,
168 queued_task.pipeline_id,
169 queued_task.task_source,
170 );
171 DedicatedWorkerScriptMsg::CommonWorker(
172 queued_task.worker.unwrap(),
173 WorkerScriptMsg::Common(script_msg),
174 )
175 }
176
177 fn inactive_msg() -> Self {
178 panic!("Workers should never receive messages marked as inactive");
180 }
181
182 fn wake_up_msg() -> Self {
183 DedicatedWorkerScriptMsg::WakeUp
184 }
185
186 fn is_wake_up(&self) -> bool {
187 matches!(self, DedicatedWorkerScriptMsg::WakeUp)
188 }
189}
190
191unsafe_no_jsmanaged_fields!(TaskQueue<DedicatedWorkerScriptMsg>);
192
193#[dom_struct]
195pub(crate) struct DedicatedWorkerGlobalScope {
196 workerglobalscope: WorkerGlobalScope,
197 #[no_trace]
199 webview_id: WebViewId,
200 #[ignore_malloc_size_of = "Defined in std"]
201 task_queue: TaskQueue<DedicatedWorkerScriptMsg>,
202 own_sender: Sender<DedicatedWorkerScriptMsg>,
203 worker: DomRefCell<Option<TrustedWorkerAddress>>,
204 parent_event_loop_sender: ScriptEventLoopSender,
206 #[ignore_malloc_size_of = "ImageCache"]
207 #[no_trace]
208 image_cache: Arc<dyn ImageCache>,
209 #[no_trace]
210 browsing_context: Option<BrowsingContextId>,
211 #[no_trace]
214 control_receiver: Receiver<DedicatedWorkerControlMsg>,
215 #[no_trace]
216 queued_worker_tasks: DomRefCell<Vec<MessageData>>,
217 debugger_global: Dom<DebuggerGlobalScope>,
218}
219
220impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope {
221 type WorkerMsg = DedicatedWorkerScriptMsg;
222 type ControlMsg = DedicatedWorkerControlMsg;
223 type Event = MixedMessage;
224
225 fn task_queue(&self) -> &TaskQueue<DedicatedWorkerScriptMsg> {
226 &self.task_queue
227 }
228
229 fn handle_event(&self, event: MixedMessage, cx: &mut JSContext) -> bool {
230 self.handle_mixed_message(event, cx)
231 }
232
233 fn handle_worker_post_event(
234 &self,
235 worker: &TrustedWorkerAddress,
236 ) -> Option<AutoWorkerReset<'_>> {
237 let ar = AutoWorkerReset::new(self, worker.clone());
238 Some(ar)
239 }
240
241 fn from_control_msg(msg: DedicatedWorkerControlMsg) -> MixedMessage {
242 MixedMessage::Control(msg)
243 }
244
245 fn from_worker_msg(msg: DedicatedWorkerScriptMsg) -> MixedMessage {
246 MixedMessage::Worker(msg)
247 }
248
249 fn from_devtools_msg(msg: DevtoolScriptControlMsg) -> MixedMessage {
250 MixedMessage::Devtools(msg)
251 }
252
253 fn from_timer_msg() -> MixedMessage {
254 MixedMessage::Timer
255 }
256
257 fn control_receiver(&self) -> &Receiver<DedicatedWorkerControlMsg> {
258 &self.control_receiver
259 }
260}
261
262impl DedicatedWorkerGlobalScope {
263 #[allow(clippy::too_many_arguments)]
264 fn new_inherited(
265 init: WorkerGlobalScopeInit,
266 webview_id: WebViewId,
267 worker_name: DOMString,
268 worker_type: WorkerType,
269 worker_url: ServoUrl,
270 from_devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
271 runtime: Runtime,
272 parent_event_loop_sender: ScriptEventLoopSender,
273 own_sender: Sender<DedicatedWorkerScriptMsg>,
274 receiver: Receiver<DedicatedWorkerScriptMsg>,
275 closing: Arc<AtomicBool>,
276 image_cache: Arc<dyn ImageCache>,
277 browsing_context: Option<BrowsingContextId>,
278 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
279 control_receiver: Receiver<DedicatedWorkerControlMsg>,
280 insecure_requests_policy: InsecureRequestsPolicy,
281 font_context: Option<Arc<FontContext>>,
282 debugger_global: &DebuggerGlobalScope,
283 ) -> DedicatedWorkerGlobalScope {
284 DedicatedWorkerGlobalScope {
285 workerglobalscope: WorkerGlobalScope::new_inherited(
286 init,
287 worker_name,
288 worker_type,
289 worker_url,
290 runtime,
291 from_devtools_receiver,
292 closing,
293 #[cfg(feature = "webgpu")]
294 gpu_id_hub,
295 insecure_requests_policy,
296 font_context,
297 ),
298 webview_id,
299 task_queue: TaskQueue::new(receiver, own_sender.clone()),
300 own_sender,
301 parent_event_loop_sender,
302 worker: DomRefCell::new(None),
303 image_cache,
304 browsing_context,
305 control_receiver,
306 queued_worker_tasks: Default::default(),
307 debugger_global: Dom::from_ref(debugger_global),
308 }
309 }
310
311 #[expect(clippy::too_many_arguments)]
312 pub(crate) fn new(
313 init: WorkerGlobalScopeInit,
314 webview_id: WebViewId,
315 worker_name: DOMString,
316 worker_type: WorkerType,
317 worker_url: ServoUrl,
318 from_devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
319 runtime: Runtime,
320 parent_event_loop_sender: ScriptEventLoopSender,
321 own_sender: Sender<DedicatedWorkerScriptMsg>,
322 receiver: Receiver<DedicatedWorkerScriptMsg>,
323 closing: Arc<AtomicBool>,
324 image_cache: Arc<dyn ImageCache>,
325 browsing_context: Option<BrowsingContextId>,
326 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
327 control_receiver: Receiver<DedicatedWorkerControlMsg>,
328 insecure_requests_policy: InsecureRequestsPolicy,
329 font_context: Option<Arc<FontContext>>,
330 debugger_global: &DebuggerGlobalScope,
331 cx: &mut js::context::JSContext,
332 ) -> DomRoot<DedicatedWorkerGlobalScope> {
333 let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited(
334 init,
335 webview_id,
336 worker_name,
337 worker_type,
338 worker_url,
339 from_devtools_receiver,
340 runtime,
341 parent_event_loop_sender,
342 own_sender,
343 receiver,
344 closing,
345 image_cache,
346 browsing_context,
347 #[cfg(feature = "webgpu")]
348 gpu_id_hub,
349 control_receiver,
350 insecure_requests_policy,
351 font_context,
352 debugger_global,
353 ));
354 DedicatedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(cx, scope)
355 }
356
357 #[expect(unsafe_code)]
359 #[allow(clippy::too_many_arguments)]
360 pub(crate) fn run_worker_scope(
361 mut init: WorkerGlobalScopeInit,
362 webview_id: WebViewId,
363 worker_url: UrlWithBlobClaim,
364 from_devtools_receiver: GenericReceiver<DevtoolScriptControlMsg>,
365 worker: TrustedWorkerAddress,
366 parent_event_loop_sender: ScriptEventLoopSender,
367 own_sender: Sender<DedicatedWorkerScriptMsg>,
368 receiver: Receiver<DedicatedWorkerScriptMsg>,
369 worker_load_origin: WorkerScriptLoadOrigin,
370 worker_options: &WorkerOptions,
371 closing: Arc<AtomicBool>,
372 image_cache: Arc<dyn ImageCache>,
373 browsing_context: Option<BrowsingContextId>,
374 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
375 control_receiver: Receiver<DedicatedWorkerControlMsg>,
376 context_sender: Sender<ThreadSafeJSContext>,
377 insecure_requests_policy: InsecureRequestsPolicy,
378 policy_container: PolicyContainer,
379 font_context: Option<Arc<FontContext>>,
380 ) -> JoinHandle<()> {
381 let event_loop_id = ScriptEventLoopId::installed()
382 .expect("Should always be in a ScriptThread or in a dedicated worker");
383 let current_global = GlobalScope::current().expect("No current global object");
384 let origin = current_global.origin().immutable().clone();
385 let referrer = current_global.get_referrer();
386 let parent = current_global.runtime_handle();
387 let current_global_https_state = current_global.get_https_state();
388 let current_global_ancestor_trustworthy = current_global.has_trustworthy_ancestor_origin();
389 let is_secure_context = current_global.is_secure_context();
390 let is_nested_browsing_context = current_global.is_nested_browsing_context();
391
392 let worker_type = worker_options.type_;
393 let worker_name = worker_options.name.to_string();
394 let credentials = worker_options.credentials.convert();
395
396 thread::Builder::new()
397 .name(format!("WW:{}", worker_url.debug_compact()))
398 .spawn(move || {
399 thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
400 ScriptEventLoopId::install(event_loop_id);
401
402 let WorkerScriptLoadOrigin {
403 referrer_url,
404 pipeline_id,
405 ..
406 } = worker_load_origin;
407
408 let referrer = referrer_url.map(Referrer::ReferrerUrl).unwrap_or(referrer);
409
410 let request_client = RequestClient {
411 preloaded_resources: PreloadedResources::default(),
412 policy_container: RequestPolicyContainer::PolicyContainer(
413 policy_container.clone(),
414 ),
415 origin: Origin::Origin(origin.clone()),
416 is_nested_browsing_context,
417 insecure_requests_policy,
418 };
419
420 let event_loop_sender = ScriptEventLoopSender::DedicatedWorker {
421 sender: own_sender.clone(),
422 main_thread_worker: worker.clone(),
423 };
424
425 let runtime = unsafe {
426 Runtime::new_with_parent(Some(parent), Some(event_loop_sender.clone()))
427 };
428 let mut cx = unsafe { runtime.cx() };
432 let cx = &mut cx;
433 let debugger_global = DebuggerGlobalScope::new(
434 pipeline_id,
435 init.to_devtools_sender.clone(),
436 init.from_devtools_sender
437 .clone()
438 .expect("Guaranteed by Worker::Constructor"),
439 init.mem_profiler_chan.clone(),
440 init.time_profiler_chan.clone(),
441 init.script_to_constellation_chan.clone(),
442 init.script_to_embedder_chan.clone(),
443 init.resource_threads.clone(),
444 init.storage_threads.clone(),
445 #[cfg(feature = "webgpu")]
446 gpu_id_hub.clone(),
447 cx,
448 );
449 debugger_global.execute(cx);
450
451 let context_for_interrupt = runtime.thread_safe_js_context();
452 let _ = context_sender.send(context_for_interrupt);
453
454 let devtools_mpsc_port = from_devtools_receiver.route_preserving_errors();
455
456 if worker_url.scheme() == "data" {
464 if is_secure_context {
466 init.origin = ImmutableOrigin::new_opaque_data_url_worker();
467 } else {
468 init.origin = ImmutableOrigin::new_opaque();
469 }
470 }
471
472 let worker_id = init.worker_id;
473 let global = DedicatedWorkerGlobalScope::new(
474 init,
475 webview_id,
476 worker_name.into(),
477 worker_type,
478 worker_url.url(),
479 devtools_mpsc_port,
480 runtime,
481 parent_event_loop_sender,
482 own_sender,
483 receiver,
484 closing,
485 image_cache,
486 browsing_context,
487 #[cfg(feature = "webgpu")]
488 gpu_id_hub,
489 control_receiver,
490 insecure_requests_policy,
491 font_context,
492 &debugger_global,
493 cx,
494 );
495 debugger_global.fire_add_debuggee(
496 CanGc::from_cx(cx),
497 global.upcast(),
498 pipeline_id,
499 Some(worker_id),
500 );
501 let scope = global.upcast::<WorkerGlobalScope>();
502
503 let fetch_client = ModuleFetchClient {
504 insecure_requests_policy,
505 has_trustworthy_ancestor_origin: current_global_ancestor_trustworthy,
506 policy_container,
507 client: request_client,
508 pipeline_id,
509 origin,
510 https_state: current_global_https_state,
511 };
512
513 {
515 let _ar = AutoWorkerReset::new(&global, worker.clone());
516 match worker_type {
517 WorkerType::Classic => {
518 fetch_a_classic_worker_script(
519 scope,
520 worker_url,
521 fetch_client,
522 Destination::Worker,
523 webview_id,
524 referrer,
525 );
526 },
527 WorkerType::Module => {
528 fetch_a_module_worker_script_graph(
529 cx,
530 worker_url.url(),
531 fetch_client,
532 ModuleOwner::Worker(Trusted::new(scope)),
533 referrer,
534 credentials,
535 );
536 },
537 }
538
539 let reporter_name = format!("dedicated-worker-reporter-{}", worker_id);
540 scope
541 .upcast::<GlobalScope>()
542 .mem_profiler_chan()
543 .run_with_memory_reporting(
544 || {
545 while !scope.is_closing() {
551 run_worker_event_loop(&*global, Some(&worker), cx);
552 }
553 },
554 reporter_name,
555 event_loop_sender,
556 CommonScriptMsg::CollectReports,
557 );
558 }
559
560 scope.clear_js_runtime();
561 })
562 .expect("Thread spawning failed")
563 }
564
565 pub(crate) fn webview_id(&self) -> WebViewId {
566 self.webview_id
567 }
568
569 fn replace_worker(
574 &self,
575 new_worker: Option<TrustedWorkerAddress>,
576 ) -> Option<TrustedWorkerAddress> {
577 let old_worker = std::mem::replace(&mut *self.worker.borrow_mut(), new_worker);
578
579 self.upcast::<GlobalScope>()
585 .task_manager()
586 .set_sender(self.event_loop_sender());
587
588 old_worker
589 }
590
591 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
592 self.image_cache.clone()
593 }
594
595 pub(crate) fn event_loop_sender(&self) -> Option<ScriptEventLoopSender> {
596 Some(ScriptEventLoopSender::DedicatedWorker {
597 sender: self.own_sender.clone(),
598 main_thread_worker: self.worker.borrow().clone()?,
599 })
600 }
601
602 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
603 let (sender, receiver) = unbounded();
604 let main_thread_worker = self.worker.borrow().as_ref().unwrap().clone();
605 (
606 ScriptEventLoopSender::DedicatedWorker {
607 sender,
608 main_thread_worker,
609 },
610 ScriptEventLoopReceiver::DedicatedWorker(receiver),
611 )
612 }
613
614 pub(crate) fn fire_queued_messages(&self, can_gc: CanGc) {
615 let queue: Vec<_> = self.queued_worker_tasks.borrow_mut().drain(..).collect();
616 for msg in queue {
617 if self.upcast::<WorkerGlobalScope>().is_closing() {
618 return;
619 }
620 self.dispatch_message_event(msg, can_gc);
621 }
622 }
623
624 fn dispatch_message_event(&self, msg: MessageData, can_gc: CanGc) {
625 let scope = self.upcast::<WorkerGlobalScope>();
626 let target = self.upcast();
627 let _ac = enter_realm(self);
628 rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
629 if let Ok(ports) =
630 structuredclone::read(scope.upcast(), *msg.data, message.handle_mut(), can_gc)
631 {
632 MessageEvent::dispatch_jsval(
633 target,
634 scope.upcast(),
635 message.handle(),
636 Some(&msg.origin.ascii_serialization()),
637 None,
638 ports,
639 can_gc,
640 );
641 } else {
642 MessageEvent::dispatch_error(target, scope.upcast(), can_gc);
643 }
644 }
645
646 fn handle_script_event(&self, msg: WorkerScriptMsg, cx: &mut JSContext) {
647 match msg {
648 WorkerScriptMsg::DOMMessage(message_data) => {
649 if self.upcast::<WorkerGlobalScope>().is_execution_ready() {
650 self.dispatch_message_event(message_data, CanGc::from_cx(cx));
651 } else {
652 self.queued_worker_tasks.borrow_mut().push(message_data);
653 }
654 },
655 WorkerScriptMsg::Common(msg) => {
656 self.upcast::<WorkerGlobalScope>().process_event(msg, cx);
657 },
658 }
659 }
660
661 fn handle_mixed_message(&self, msg: MixedMessage, cx: &mut JSContext) -> bool {
662 if self.upcast::<WorkerGlobalScope>().is_closing() {
663 return false;
664 }
665 match msg {
667 MixedMessage::Devtools(msg) => match msg {
668 DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) => {
669 self.upcast::<GlobalScope>()
670 .set_devtools_wants_updates(bool_val);
671 },
672 DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, reply) => {
673 self.debugger_global.fire_eval(
674 CanGc::from_cx(cx),
675 code.into(),
676 id,
677 Some(self.upcast::<WorkerGlobalScope>().worker_id()),
678 frame_actor_id,
679 reply,
680 );
681 },
682 _ => debug!("got an unusable devtools control message inside the worker!"),
683 },
684 MixedMessage::Worker(DedicatedWorkerScriptMsg::CommonWorker(linked_worker, msg)) => {
685 let _ar = AutoWorkerReset::new(self, linked_worker);
686 self.handle_script_event(msg, cx);
687 },
688 MixedMessage::Worker(DedicatedWorkerScriptMsg::WakeUp) => {},
689 MixedMessage::Control(DedicatedWorkerControlMsg::Exit) => {
690 return false;
691 },
692 MixedMessage::Timer => {},
693 }
694 true
695 }
696
697 pub(crate) fn forward_error_to_worker_object(&self, error_info: ErrorInfo) {
699 let worker = self.worker.borrow().as_ref().unwrap().clone();
701 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
702 let task = Box::new(task!(forward_error_to_worker_object: move || {
703 let worker = worker.root();
704 let global = worker.global();
705
706 let event = ErrorEvent::new(
709 &global,
710 atom!("error"),
711 EventBubbles::DoesNotBubble,
712 EventCancelable::Cancelable,
713 error_info.message.as_str().into(),
714 error_info.filename.as_str().into(),
715 error_info.lineno,
716 error_info.column,
717 HandleValue::null(),
718 CanGc::note(),
719 );
720
721 if event.upcast::<Event>().fire(worker.upcast::<EventTarget>(), CanGc::note()) {
723 global.report_an_error(error_info, HandleValue::null(), CanGc::note());
724 }
725 }));
726 self.parent_event_loop_sender
727 .send(CommonScriptMsg::Task(
728 WorkerEvent,
729 task,
730 Some(pipeline_id),
731 TaskSourceName::DOMManipulation,
732 ))
733 .unwrap();
734 }
735
736 fn post_message_impl(
738 &self,
739 cx: &mut JSContext,
740 message: HandleValue,
741 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
742 ) -> ErrorResult {
743 let data = structuredclone::write(cx.into(), message, Some(transfer))?;
744 let worker = self.worker.borrow().as_ref().unwrap().clone();
745 let global_scope = self.upcast::<GlobalScope>();
746 let pipeline_id = global_scope.pipeline_id();
747 let task = Box::new(task!(post_worker_message: move |cx| {
748 Worker::handle_message(worker, data, cx);
749 }));
750 self.parent_event_loop_sender
751 .send(CommonScriptMsg::Task(
752 WorkerEvent,
753 task,
754 Some(pipeline_id),
755 TaskSourceName::DOMManipulation,
756 ))
757 .expect("Sending to parent failed");
758 Ok(())
759 }
760
761 pub(crate) fn browsing_context(&self) -> Option<BrowsingContextId> {
762 self.browsing_context
763 }
764
765 pub(crate) fn report_csp_violations(&self, violations: Vec<Violation>) {
766 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
767 self.parent_event_loop_sender
768 .send(CommonScriptMsg::ReportCspViolations(
769 pipeline_id,
770 violations,
771 ))
772 .expect("Sending to parent failed");
773 }
774
775 pub(crate) fn forward_simple_error_at_worker(&self) {
776 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
777 let worker = self.worker.borrow().clone().expect("worker must be set");
778 self.parent_event_loop_sender
779 .send(CommonScriptMsg::Task(
780 WorkerEvent,
781 Box::new(SimpleWorkerErrorHandler::new(worker)),
782 Some(pipeline_id),
783 TaskSourceName::DOMManipulation,
784 ))
785 .expect("Sending to parent failed");
786 }
787}
788
789#[expect(unsafe_code)]
790pub(crate) unsafe extern "C" fn interrupt_callback(cx: *mut RawJSContext) -> bool {
791 let in_realm_proof = AlreadyInRealm::assert_for_cx(unsafe { SafeJSContext::from_ptr(cx) });
792 let global = unsafe { GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof)) };
793
794 let Some(worker) = global.downcast::<WorkerGlobalScope>() else {
796 assert!(global.is::<DebuggerGlobalScope>());
797 return false;
798 };
799
800 assert!(worker.is::<DedicatedWorkerGlobalScope>());
802 !worker.is_closing()
803}
804
805fn fetch_a_classic_worker_script(
807 workerscope: &WorkerGlobalScope,
808 url_with_blob_lock: UrlWithBlobClaim,
809 fetch_client: ModuleFetchClient,
810 destination: Destination,
811 webview_id: WebViewId,
812 referrer: Referrer,
813) {
814 let request = RequestBuilder::new(Some(webview_id), url_with_blob_lock.clone(), referrer)
816 .insecure_requests_policy(fetch_client.insecure_requests_policy)
818 .has_trustworthy_ancestor_origin(fetch_client.has_trustworthy_ancestor_origin)
819 .policy_container(fetch_client.policy_container.clone())
820 .client(fetch_client.client)
821 .pipeline_id(Some(fetch_client.pipeline_id))
822 .origin(fetch_client.origin)
823 .https_state(fetch_client.https_state)
824 .destination(destination)
826 .mode(RequestMode::SameOrigin)
829 .credentials_mode(CredentialsMode::CredentialsSameOrigin)
831 .parser_metadata(ParserMetadata::NotParserInserted)
833 .use_url_credentials(true);
835
836 let context = ScriptFetchContext::new(
837 Trusted::new(workerscope),
838 url_with_blob_lock.url(),
839 fetch_client.policy_container,
840 );
841 let global = workerscope.upcast::<GlobalScope>();
842 let task_source = global.task_manager().networking_task_source().to_sendable();
843 global.fetch(request, context, task_source);
844}
845
846impl DedicatedWorkerGlobalScopeMethods<crate::DomTypeHolder> for DedicatedWorkerGlobalScope {
847 fn Name(&self) -> DOMString {
849 self.workerglobalscope.worker_name()
850 }
851
852 fn PostMessage(
854 &self,
855 cx: &mut JSContext,
856 message: HandleValue,
857 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
858 ) -> ErrorResult {
859 self.post_message_impl(cx, message, transfer)
860 }
861
862 fn PostMessage_(
864 &self,
865 cx: &mut JSContext,
866 message: HandleValue,
867 options: RootedTraceableBox<StructuredSerializeOptions>,
868 ) -> ErrorResult {
869 let mut rooted = CustomAutoRooter::new(
870 options
871 .transfer
872 .iter()
873 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
874 .collect(),
875 );
876 #[expect(unsafe_code)]
877 let guard = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
878 self.post_message_impl(cx, message, guard)
879 }
880
881 fn Close(&self) {
883 self.upcast::<WorkerGlobalScope>().close();
885 }
886
887 event_handler!(message, GetOnmessage, SetOnmessage);
889
890 event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
892}