script/dom/workers/
dedicatedworkerglobalscope.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::sync::Arc;
6use std::sync::atomic::AtomicBool;
7use std::thread::{self, JoinHandle};
8
9use base::id::{BrowsingContextId, PipelineId, WebViewId};
10use constellation_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
11use crossbeam_channel::{Receiver, Sender, unbounded};
12use devtools_traits::DevtoolScriptControlMsg;
13use dom_struct::dom_struct;
14use fonts::FontContext;
15use ipc_channel::ipc::IpcReceiver;
16use ipc_channel::router::ROUTER;
17use js::jsapi::{Heap, JSContext, JSObject};
18use js::jsval::UndefinedValue;
19use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
20use net_traits::image_cache::ImageCache;
21use net_traits::policy_container::PolicyContainer;
22use net_traits::request::{
23    CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, Referrer, RequestBuilder,
24    RequestMode,
25};
26use servo_url::{ImmutableOrigin, ServoUrl};
27use style::thread_state::{self, ThreadState};
28
29use crate::devtools;
30use crate::dom::abstractworker::{MessageData, SimpleWorkerErrorHandler, WorkerScriptMsg};
31use crate::dom::abstractworkerglobalscope::{WorkerEventLoopMethods, run_worker_event_loop};
32use crate::dom::bindings::cell::DomRefCell;
33use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
34use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
35use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
36use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
37use crate::dom::bindings::error::{ErrorInfo, ErrorResult};
38use crate::dom::bindings::inheritance::Castable;
39use crate::dom::bindings::refcounted::Trusted;
40use crate::dom::bindings::reflector::DomGlobal;
41use crate::dom::bindings::root::DomRoot;
42use crate::dom::bindings::str::DOMString;
43use crate::dom::bindings::structuredclone;
44use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox};
45use crate::dom::csp::Violation;
46use crate::dom::errorevent::ErrorEvent;
47use crate::dom::event::{Event, EventBubbles, EventCancelable};
48use crate::dom::eventtarget::EventTarget;
49use crate::dom::globalscope::GlobalScope;
50use crate::dom::messageevent::MessageEvent;
51use crate::dom::types::DebuggerGlobalScope;
52#[cfg(feature = "webgpu")]
53use crate::dom::webgpu::identityhub::IdentityHub;
54use crate::dom::worker::{TrustedWorkerAddress, Worker};
55use crate::dom::workerglobalscope::{ScriptFetchContext, WorkerGlobalScope};
56use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
57use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
58use crate::script_runtime::ScriptThreadEventCategory::WorkerEvent;
59use crate::script_runtime::{CanGc, JSContext as SafeJSContext, Runtime, ThreadSafeJSContext};
60use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
61use crate::task_source::{SendableTaskSource, TaskSourceName};
62
63/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
64/// value for the duration of this object's lifetime. This ensures that the related Worker
65/// object only lives as long as necessary (ie. while events are being executed), while
66/// providing a reference that can be cloned freely.
67pub(crate) struct AutoWorkerReset<'a> {
68    workerscope: &'a DedicatedWorkerGlobalScope,
69    old_worker: Option<TrustedWorkerAddress>,
70}
71
72impl<'a> AutoWorkerReset<'a> {
73    pub(crate) fn new(
74        workerscope: &'a DedicatedWorkerGlobalScope,
75        worker: TrustedWorkerAddress,
76    ) -> AutoWorkerReset<'a> {
77        let old_worker = workerscope.replace_worker(Some(worker));
78        AutoWorkerReset {
79            workerscope,
80            old_worker,
81        }
82    }
83}
84
85impl Drop for AutoWorkerReset<'_> {
86    fn drop(&mut self) {
87        self.workerscope
88            .replace_worker(std::mem::take(&mut self.old_worker));
89    }
90}
91
92/// Messages sent from the owning global.
93pub(crate) enum DedicatedWorkerControlMsg {
94    /// Shutdown the worker.
95    Exit,
96}
97
98pub(crate) enum DedicatedWorkerScriptMsg {
99    /// Standard message from a worker.
100    CommonWorker(TrustedWorkerAddress, WorkerScriptMsg),
101    /// Wake-up call from the task queue.
102    WakeUp,
103}
104
105pub(crate) enum MixedMessage {
106    Worker(DedicatedWorkerScriptMsg),
107    Devtools(DevtoolScriptControlMsg),
108    Control(DedicatedWorkerControlMsg),
109    Timer,
110}
111
112impl QueuedTaskConversion for DedicatedWorkerScriptMsg {
113    fn task_source_name(&self) -> Option<&TaskSourceName> {
114        let common_worker_msg = match self {
115            DedicatedWorkerScriptMsg::CommonWorker(_, common_worker_msg) => common_worker_msg,
116            _ => return None,
117        };
118        let script_msg = match common_worker_msg {
119            WorkerScriptMsg::Common(script_msg) => script_msg,
120            _ => return None,
121        };
122        match script_msg {
123            CommonScriptMsg::Task(_category, _boxed, _pipeline_id, source_name) => {
124                Some(source_name)
125            },
126            _ => None,
127        }
128    }
129
130    fn pipeline_id(&self) -> Option<PipelineId> {
131        // Workers always return None, since the pipeline_id is only used to check for document activity,
132        // and this check does not apply to worker event-loops.
133        None
134    }
135
136    fn into_queued_task(self) -> Option<QueuedTask> {
137        let (worker, common_worker_msg) = match self {
138            DedicatedWorkerScriptMsg::CommonWorker(worker, common_worker_msg) => {
139                (worker, common_worker_msg)
140            },
141            _ => return None,
142        };
143        let script_msg = match common_worker_msg {
144            WorkerScriptMsg::Common(script_msg) => script_msg,
145            _ => return None,
146        };
147        let (category, boxed, pipeline_id, task_source) = match script_msg {
148            CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => {
149                (category, boxed, pipeline_id, task_source)
150            },
151            _ => return None,
152        };
153        Some((Some(worker), category, boxed, pipeline_id, task_source))
154    }
155
156    fn from_queued_task(queued_task: QueuedTask) -> Self {
157        let (worker, category, boxed, pipeline_id, task_source) = queued_task;
158        let script_msg = CommonScriptMsg::Task(category, boxed, pipeline_id, task_source);
159        DedicatedWorkerScriptMsg::CommonWorker(worker.unwrap(), WorkerScriptMsg::Common(script_msg))
160    }
161
162    fn inactive_msg() -> Self {
163        // Inactive is only relevant in the context of a browsing-context event-loop.
164        panic!("Workers should never receive messages marked as inactive");
165    }
166
167    fn wake_up_msg() -> Self {
168        DedicatedWorkerScriptMsg::WakeUp
169    }
170
171    fn is_wake_up(&self) -> bool {
172        matches!(self, DedicatedWorkerScriptMsg::WakeUp)
173    }
174}
175
176unsafe_no_jsmanaged_fields!(TaskQueue<DedicatedWorkerScriptMsg>);
177
178// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
179#[dom_struct]
180pub(crate) struct DedicatedWorkerGlobalScope {
181    workerglobalscope: WorkerGlobalScope,
182    #[ignore_malloc_size_of = "Defined in std"]
183    task_queue: TaskQueue<DedicatedWorkerScriptMsg>,
184    own_sender: Sender<DedicatedWorkerScriptMsg>,
185    worker: DomRefCell<Option<TrustedWorkerAddress>>,
186    /// Sender to the parent thread.
187    parent_event_loop_sender: ScriptEventLoopSender,
188    #[ignore_malloc_size_of = "ImageCache"]
189    #[no_trace]
190    image_cache: Arc<dyn ImageCache>,
191    #[no_trace]
192    browsing_context: Option<BrowsingContextId>,
193    /// A receiver of control messages,
194    /// currently only used to signal shutdown.
195    #[ignore_malloc_size_of = "Channels are hard"]
196    #[no_trace]
197    control_receiver: Receiver<DedicatedWorkerControlMsg>,
198    #[no_trace]
199    queued_worker_tasks: DomRefCell<Vec<MessageData>>,
200}
201
202impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope {
203    type WorkerMsg = DedicatedWorkerScriptMsg;
204    type ControlMsg = DedicatedWorkerControlMsg;
205    type Event = MixedMessage;
206
207    fn task_queue(&self) -> &TaskQueue<DedicatedWorkerScriptMsg> {
208        &self.task_queue
209    }
210
211    fn handle_event(&self, event: MixedMessage, can_gc: CanGc) -> bool {
212        self.handle_mixed_message(event, can_gc)
213    }
214
215    fn handle_worker_post_event(
216        &self,
217        worker: &TrustedWorkerAddress,
218    ) -> Option<AutoWorkerReset<'_>> {
219        let ar = AutoWorkerReset::new(self, worker.clone());
220        Some(ar)
221    }
222
223    fn from_control_msg(msg: DedicatedWorkerControlMsg) -> MixedMessage {
224        MixedMessage::Control(msg)
225    }
226
227    fn from_worker_msg(msg: DedicatedWorkerScriptMsg) -> MixedMessage {
228        MixedMessage::Worker(msg)
229    }
230
231    fn from_devtools_msg(msg: DevtoolScriptControlMsg) -> MixedMessage {
232        MixedMessage::Devtools(msg)
233    }
234
235    fn from_timer_msg() -> MixedMessage {
236        MixedMessage::Timer
237    }
238
239    fn control_receiver(&self) -> &Receiver<DedicatedWorkerControlMsg> {
240        &self.control_receiver
241    }
242}
243
244impl DedicatedWorkerGlobalScope {
245    pub(crate) fn webview_id(&self) -> Option<WebViewId> {
246        WebViewId::installed()
247    }
248
249    #[allow(clippy::too_many_arguments)]
250    fn new_inherited(
251        init: WorkerGlobalScopeInit,
252        worker_name: DOMString,
253        worker_type: WorkerType,
254        worker_url: ServoUrl,
255        from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
256        runtime: Runtime,
257        parent_event_loop_sender: ScriptEventLoopSender,
258        own_sender: Sender<DedicatedWorkerScriptMsg>,
259        receiver: Receiver<DedicatedWorkerScriptMsg>,
260        closing: Arc<AtomicBool>,
261        image_cache: Arc<dyn ImageCache>,
262        browsing_context: Option<BrowsingContextId>,
263        #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
264        control_receiver: Receiver<DedicatedWorkerControlMsg>,
265        insecure_requests_policy: InsecureRequestsPolicy,
266        font_context: Option<Arc<FontContext>>,
267    ) -> DedicatedWorkerGlobalScope {
268        DedicatedWorkerGlobalScope {
269            workerglobalscope: WorkerGlobalScope::new_inherited(
270                init,
271                worker_name,
272                worker_type,
273                worker_url,
274                runtime,
275                from_devtools_receiver,
276                closing,
277                #[cfg(feature = "webgpu")]
278                gpu_id_hub,
279                insecure_requests_policy,
280                font_context,
281            ),
282            task_queue: TaskQueue::new(receiver, own_sender.clone()),
283            own_sender,
284            parent_event_loop_sender,
285            worker: DomRefCell::new(None),
286            image_cache,
287            browsing_context,
288            control_receiver,
289            queued_worker_tasks: Default::default(),
290        }
291    }
292
293    #[allow(unsafe_code, clippy::too_many_arguments)]
294    pub(crate) fn new(
295        init: WorkerGlobalScopeInit,
296        worker_name: DOMString,
297        worker_type: WorkerType,
298        worker_url: ServoUrl,
299        from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
300        runtime: Runtime,
301        parent_event_loop_sender: ScriptEventLoopSender,
302        own_sender: Sender<DedicatedWorkerScriptMsg>,
303        receiver: Receiver<DedicatedWorkerScriptMsg>,
304        closing: Arc<AtomicBool>,
305        image_cache: Arc<dyn ImageCache>,
306        browsing_context: Option<BrowsingContextId>,
307        #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
308        control_receiver: Receiver<DedicatedWorkerControlMsg>,
309        insecure_requests_policy: InsecureRequestsPolicy,
310        font_context: Option<Arc<FontContext>>,
311    ) -> DomRoot<DedicatedWorkerGlobalScope> {
312        let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited(
313            init,
314            worker_name,
315            worker_type,
316            worker_url,
317            from_devtools_receiver,
318            runtime,
319            parent_event_loop_sender,
320            own_sender,
321            receiver,
322            closing,
323            image_cache,
324            browsing_context,
325            #[cfg(feature = "webgpu")]
326            gpu_id_hub,
327            control_receiver,
328            insecure_requests_policy,
329            font_context,
330        ));
331        DedicatedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(
332            GlobalScope::get_cx(),
333            scope,
334        )
335    }
336
337    /// <https://html.spec.whatwg.org/multipage/#run-a-worker>
338    #[allow(unsafe_code, clippy::too_many_arguments)]
339    pub(crate) fn run_worker_scope(
340        mut init: WorkerGlobalScopeInit,
341        worker_url: ServoUrl,
342        from_devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>,
343        worker: TrustedWorkerAddress,
344        parent_event_loop_sender: ScriptEventLoopSender,
345        own_sender: Sender<DedicatedWorkerScriptMsg>,
346        receiver: Receiver<DedicatedWorkerScriptMsg>,
347        worker_load_origin: WorkerScriptLoadOrigin,
348        worker_name: String,
349        worker_type: WorkerType,
350        closing: Arc<AtomicBool>,
351        image_cache: Arc<dyn ImageCache>,
352        browsing_context: Option<BrowsingContextId>,
353        #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
354        control_receiver: Receiver<DedicatedWorkerControlMsg>,
355        context_sender: Sender<ThreadSafeJSContext>,
356        insecure_requests_policy: InsecureRequestsPolicy,
357        policy_container: PolicyContainer,
358        font_context: Option<Arc<FontContext>>,
359    ) -> JoinHandle<()> {
360        let webview_id = WebViewId::installed();
361        let current_global = GlobalScope::current().expect("No current global object");
362        let origin = current_global.origin().immutable().clone();
363        let referrer = current_global.get_referrer();
364        let parent = current_global.runtime_handle();
365        let current_global_https_state = current_global.get_https_state();
366        let current_global_ancestor_trustworthy = current_global.has_trustworthy_ancestor_origin();
367        let is_secure_context = current_global.is_secure_context();
368
369        thread::Builder::new()
370            .name(format!("WW:{}", worker_url.debug_compact()))
371            .spawn(move || {
372                thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
373
374                if let Some(webview_id) = webview_id {
375                    WebViewId::install(webview_id);
376                }
377
378                let WorkerScriptLoadOrigin {
379                    referrer_url,
380                    referrer_policy,
381                    pipeline_id,
382                } = worker_load_origin;
383
384                let referrer = referrer_url.map(Referrer::ReferrerUrl).unwrap_or(referrer);
385
386                let request = RequestBuilder::new(webview_id, worker_url.clone(), referrer)
387                    .destination(Destination::Worker)
388                    .mode(RequestMode::SameOrigin)
389                    .credentials_mode(CredentialsMode::CredentialsSameOrigin)
390                    .parser_metadata(ParserMetadata::NotParserInserted)
391                    .use_url_credentials(true)
392                    .pipeline_id(Some(pipeline_id))
393                    .referrer_policy(referrer_policy)
394                    .insecure_requests_policy(insecure_requests_policy)
395                    .has_trustworthy_ancestor_origin(current_global_ancestor_trustworthy)
396                    .policy_container(policy_container.clone())
397                    .origin(origin);
398
399                let event_loop_sender = ScriptEventLoopSender::DedicatedWorker {
400                    sender: own_sender.clone(),
401                    main_thread_worker: worker.clone(),
402                };
403
404                let runtime = unsafe {
405                    Runtime::new_with_parent(Some(parent), Some(event_loop_sender.clone()))
406                };
407                let debugger_global = DebuggerGlobalScope::new(
408                    pipeline_id,
409                    init.to_devtools_sender.clone(),
410                    init.from_devtools_sender
411                        .clone()
412                        .expect("Guaranteed by Worker::Constructor"),
413                    init.mem_profiler_chan.clone(),
414                    init.time_profiler_chan.clone(),
415                    init.script_to_constellation_chan.clone(),
416                    init.script_to_embedder_chan.clone(),
417                    init.resource_threads.clone(),
418                    init.storage_threads.clone(),
419                    #[cfg(feature = "webgpu")]
420                    gpu_id_hub.clone(),
421                    CanGc::note(),
422                );
423                debugger_global.execute(CanGc::note());
424
425                let context_for_interrupt = runtime.thread_safe_js_context();
426                let _ = context_sender.send(context_for_interrupt);
427
428                let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded();
429                ROUTER.route_ipc_receiver_to_crossbeam_sender(
430                    from_devtools_receiver,
431                    devtools_mpsc_chan,
432                );
433
434                // Step 8 "Set up a worker environment settings object [...]"
435                //
436                // <https://html.spec.whatwg.org/multipage/#script-settings-for-workers>
437                //
438                // > The origin: Return a unique opaque origin if `worker global
439                // > scope`'s url's scheme is "data", and `inherited origin`
440                // > otherwise.
441                if worker_url.scheme() == "data" {
442                    // Workers created from a data: url are secure if they were created from secure contexts
443                    if is_secure_context {
444                        init.origin = ImmutableOrigin::new_opaque_data_url_worker();
445                    } else {
446                        init.origin = ImmutableOrigin::new_opaque();
447                    }
448                }
449
450                let worker_id = init.worker_id;
451                let global = DedicatedWorkerGlobalScope::new(
452                    init,
453                    DOMString::from_string(worker_name),
454                    worker_type,
455                    worker_url,
456                    devtools_mpsc_port,
457                    runtime,
458                    parent_event_loop_sender,
459                    own_sender,
460                    receiver,
461                    closing,
462                    image_cache,
463                    browsing_context,
464                    #[cfg(feature = "webgpu")]
465                    gpu_id_hub,
466                    control_receiver,
467                    insecure_requests_policy,
468                    font_context,
469                );
470                debugger_global.fire_add_debuggee(
471                    CanGc::note(),
472                    global.upcast(),
473                    pipeline_id,
474                    Some(worker_id),
475                );
476                let scope = global.upcast::<WorkerGlobalScope>();
477                let global_scope = global.upcast::<GlobalScope>();
478
479                global_scope.set_https_state(current_global_https_state);
480                let request = request.https_state(global_scope.get_https_state());
481
482                let task_source = SendableTaskSource {
483                    sender: event_loop_sender.clone(),
484                    pipeline_id,
485                    name: TaskSourceName::Networking,
486                    canceller: Default::default(),
487                };
488                let context = ScriptFetchContext::new(
489                    Trusted::new(scope),
490                    request.url.clone(),
491                    worker.clone(),
492                    policy_container,
493                );
494                global_scope.fetch(request, context, task_source);
495
496                let reporter_name = format!("dedicated-worker-reporter-{}", worker_id);
497                scope
498                    .upcast::<GlobalScope>()
499                    .mem_profiler_chan()
500                    .run_with_memory_reporting(
501                        || {
502                            // Step 27, Run the responsible event loop specified
503                            // by inside settings until it is destroyed.
504                            // The worker processing model remains on this step
505                            // until the event loop is destroyed,
506                            // which happens after the closing flag is set to true.
507                            while !scope.is_closing() {
508                                run_worker_event_loop(&*global, Some(&worker), CanGc::note());
509                            }
510                        },
511                        reporter_name,
512                        event_loop_sender,
513                        CommonScriptMsg::CollectReports,
514                    );
515                scope.clear_js_runtime();
516            })
517            .expect("Thread spawning failed")
518    }
519
520    /// The non-None value of the `worker` field can contain a rooted [`TrustedWorkerAddress`]
521    /// version of the main thread's worker object. This is set while handling messages and then
522    /// unset otherwise, ensuring that the main thread object can be garbage collected. See
523    /// [`AutoWorkerReset`].
524    fn replace_worker(
525        &self,
526        new_worker: Option<TrustedWorkerAddress>,
527    ) -> Option<TrustedWorkerAddress> {
528        let old_worker = std::mem::replace(&mut *self.worker.borrow_mut(), new_worker);
529
530        // The `TaskManager` maintains a handle to this `DedicatedWorkerGlobalScope`'s
531        // event_loop_sender, which might in turn have a `TrustedWorkerAddress` rooting of the main
532        // thread's worker, which prevents garbage collection. Resetting it here ensures that
533        // garbage collection of the main thread object can happen again (assuming the new `worker`
534        // is `None`).
535        self.upcast::<GlobalScope>()
536            .task_manager()
537            .set_sender(self.event_loop_sender());
538
539        old_worker
540    }
541
542    pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
543        self.image_cache.clone()
544    }
545
546    pub(crate) fn event_loop_sender(&self) -> Option<ScriptEventLoopSender> {
547        Some(ScriptEventLoopSender::DedicatedWorker {
548            sender: self.own_sender.clone(),
549            main_thread_worker: self.worker.borrow().clone()?,
550        })
551    }
552
553    pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
554        let (sender, receiver) = unbounded();
555        let main_thread_worker = self.worker.borrow().as_ref().unwrap().clone();
556        (
557            ScriptEventLoopSender::DedicatedWorker {
558                sender,
559                main_thread_worker,
560            },
561            ScriptEventLoopReceiver::DedicatedWorker(receiver),
562        )
563    }
564
565    pub(crate) fn fire_queued_messages(&self, can_gc: CanGc) {
566        let queue: Vec<_> = self.queued_worker_tasks.borrow_mut().drain(..).collect();
567        for msg in queue {
568            if self.upcast::<WorkerGlobalScope>().is_closing() {
569                return;
570            }
571            self.dispatch_message_event(msg, can_gc);
572        }
573    }
574
575    fn dispatch_message_event(&self, msg: MessageData, can_gc: CanGc) {
576        let scope = self.upcast::<WorkerGlobalScope>();
577        let target = self.upcast();
578        let _ac = enter_realm(self);
579        rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
580        if let Ok(ports) =
581            structuredclone::read(scope.upcast(), *msg.data, message.handle_mut(), can_gc)
582        {
583            MessageEvent::dispatch_jsval(
584                target,
585                scope.upcast(),
586                message.handle(),
587                Some(&msg.origin.ascii_serialization()),
588                None,
589                ports,
590                can_gc,
591            );
592        } else {
593            MessageEvent::dispatch_error(target, scope.upcast(), can_gc);
594        }
595    }
596
597    fn handle_script_event(&self, msg: WorkerScriptMsg, can_gc: CanGc) {
598        match msg {
599            WorkerScriptMsg::DOMMessage(message_data) => {
600                if self.upcast::<WorkerGlobalScope>().is_execution_ready() {
601                    self.dispatch_message_event(message_data, can_gc);
602                } else {
603                    self.queued_worker_tasks.borrow_mut().push(message_data);
604                }
605            },
606            WorkerScriptMsg::Common(msg) => {
607                self.upcast::<WorkerGlobalScope>().process_event(msg);
608            },
609        }
610    }
611
612    fn handle_mixed_message(&self, msg: MixedMessage, can_gc: CanGc) -> bool {
613        if self.upcast::<WorkerGlobalScope>().is_closing() {
614            return false;
615        }
616        // FIXME(#26324): `self.worker` is None in devtools messages.
617        match msg {
618            MixedMessage::Devtools(msg) => match msg {
619                DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => {
620                    devtools::handle_evaluate_js(self.upcast(), string, sender, can_gc)
621                },
622                DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) => {
623                    devtools::handle_wants_live_notifications(self.upcast(), bool_val)
624                },
625                _ => debug!("got an unusable devtools control message inside the worker!"),
626            },
627            MixedMessage::Worker(DedicatedWorkerScriptMsg::CommonWorker(linked_worker, msg)) => {
628                let _ar = AutoWorkerReset::new(self, linked_worker);
629                self.handle_script_event(msg, can_gc);
630            },
631            MixedMessage::Worker(DedicatedWorkerScriptMsg::WakeUp) => {},
632            MixedMessage::Control(DedicatedWorkerControlMsg::Exit) => {
633                return false;
634            },
635            MixedMessage::Timer => {},
636        }
637        true
638    }
639
640    // https://html.spec.whatwg.org/multipage/#runtime-script-errors-2
641    pub(crate) fn forward_error_to_worker_object(&self, error_info: ErrorInfo) {
642        let worker = self.worker.borrow().as_ref().unwrap().clone();
643        let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
644        let task = Box::new(task!(forward_error_to_worker_object: move || {
645            let worker = worker.root();
646            let global = worker.global();
647
648            // Step 1.
649            let event = ErrorEvent::new(
650                &global,
651                atom!("error"),
652                EventBubbles::DoesNotBubble,
653                EventCancelable::Cancelable,
654                error_info.message.as_str().into(),
655                error_info.filename.as_str().into(),
656                error_info.lineno,
657                error_info.column,
658                HandleValue::null(),
659                CanGc::note(),
660            );
661
662            // Step 2.
663            if event.upcast::<Event>().fire(worker.upcast::<EventTarget>(), CanGc::note()) {
664                global.report_an_error(error_info, HandleValue::null(), CanGc::note());
665            }
666        }));
667        self.parent_event_loop_sender
668            .send(CommonScriptMsg::Task(
669                WorkerEvent,
670                task,
671                Some(pipeline_id),
672                TaskSourceName::DOMManipulation,
673            ))
674            .unwrap();
675    }
676
677    /// <https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage>
678    fn post_message_impl(
679        &self,
680        cx: SafeJSContext,
681        message: HandleValue,
682        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
683    ) -> ErrorResult {
684        let data = structuredclone::write(cx, message, Some(transfer))?;
685        let worker = self.worker.borrow().as_ref().unwrap().clone();
686        let global_scope = self.upcast::<GlobalScope>();
687        let pipeline_id = global_scope.pipeline_id();
688        let task = Box::new(task!(post_worker_message: move || {
689            Worker::handle_message(worker, data, CanGc::note());
690        }));
691        self.parent_event_loop_sender
692            .send(CommonScriptMsg::Task(
693                WorkerEvent,
694                task,
695                Some(pipeline_id),
696                TaskSourceName::DOMManipulation,
697            ))
698            .expect("Sending to parent failed");
699        Ok(())
700    }
701
702    pub(crate) fn browsing_context(&self) -> Option<BrowsingContextId> {
703        self.browsing_context
704    }
705
706    pub(crate) fn report_csp_violations(&self, violations: Vec<Violation>) {
707        let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
708        self.parent_event_loop_sender
709            .send(CommonScriptMsg::ReportCspViolations(
710                pipeline_id,
711                violations,
712            ))
713            .expect("Sending to parent failed");
714    }
715
716    pub(crate) fn forward_simple_error_at_worker(&self, worker: TrustedWorkerAddress) {
717        let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
718        self.parent_event_loop_sender
719            .send(CommonScriptMsg::Task(
720                WorkerEvent,
721                Box::new(SimpleWorkerErrorHandler::new(worker)),
722                Some(pipeline_id),
723                TaskSourceName::DOMManipulation,
724            ))
725            .expect("Sending to parent failed");
726    }
727}
728
729#[expect(unsafe_code)]
730pub(crate) unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool {
731    let in_realm_proof = AlreadyInRealm::assert_for_cx(unsafe { SafeJSContext::from_ptr(cx) });
732    let global = unsafe { GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof)) };
733    let worker =
734        DomRoot::downcast::<WorkerGlobalScope>(global).expect("global is not a worker scope");
735    assert!(worker.is::<DedicatedWorkerGlobalScope>());
736
737    // A false response causes the script to terminate
738    !worker.is_closing()
739}
740
741impl DedicatedWorkerGlobalScopeMethods<crate::DomTypeHolder> for DedicatedWorkerGlobalScope {
742    /// <https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-name>
743    fn Name(&self) -> DOMString {
744        self.workerglobalscope.worker_name()
745    }
746
747    /// <https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage>
748    fn PostMessage(
749        &self,
750        cx: SafeJSContext,
751        message: HandleValue,
752        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
753    ) -> ErrorResult {
754        self.post_message_impl(cx, message, transfer)
755    }
756
757    /// <https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage>
758    fn PostMessage_(
759        &self,
760        cx: SafeJSContext,
761        message: HandleValue,
762        options: RootedTraceableBox<StructuredSerializeOptions>,
763    ) -> ErrorResult {
764        let mut rooted = CustomAutoRooter::new(
765            options
766                .transfer
767                .iter()
768                .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
769                .collect(),
770        );
771        let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
772        self.post_message_impl(cx, message, guard)
773    }
774
775    /// <https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-close>
776    fn Close(&self) {
777        // Step 2
778        self.upcast::<WorkerGlobalScope>().close();
779    }
780
781    // https://html.spec.whatwg.org/multipage/#handler-dedicatedworkerglobalscope-onmessage
782    event_handler!(message, GetOnmessage, SetOnmessage);
783
784    // https://html.spec.whatwg.org/multipage/#handler-dedicatedworkerglobalscope-onmessageerror
785    event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
786}