Skip to main content

script/dom/workers/
workerglobalscope.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
5#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::cell::{OnceCell, RefCell, RefMut};
8use std::collections::HashSet;
9use std::default::Default;
10use std::rc::Rc;
11use std::sync::Arc;
12use std::sync::atomic::{AtomicBool, Ordering};
13use std::time::Duration;
14
15use bytes::{Bytes, BytesMut};
16use content_security_policy::CspList;
17#[cfg(feature = "devtools")]
18use devtools_traits::DevtoolScriptControlMsg;
19use dom_struct::dom_struct;
20use encoding_rs::UTF_8;
21use fonts::FontContext;
22use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
23use js::context::JSContext;
24use js::conversions::ToJSValConvertible;
25use js::jsapi::{Heap, JSContext as RawJSContext, Value};
26use js::realm::CurrentRealm;
27use js::rust::{HandleValue, MutableHandleValue, ParentRuntime};
28use mime::Mime;
29use net_traits::blob_url_store::UrlWithBlobClaim;
30use net_traits::policy_container::PolicyContainer;
31use net_traits::request::{
32    CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, RequestBuilder, RequestId,
33};
34use net_traits::{FetchMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming};
35use profile_traits::mem::{ProcessReports, perform_memory_report};
36use script_bindings::callback::RootedCallback;
37use script_bindings::cell::{DomRefCell, Ref};
38use script_bindings::conversions::root_from_handlevalue;
39use script_bindings::reflector::DomObject;
40use script_bindings::root::rooted_heap_handle;
41use script_bindings::trace::CustomTraceable;
42use servo_base::cross_process_instant::CrossProcessInstant;
43use servo_base::generic_channel::{GenericSend, GenericSender, RoutedReceiver};
44use servo_base::id::{PipelineId, PipelineNamespace, WorkerId};
45#[cfg(feature = "webgl")]
46use servo_canvas_traits::webgl::WebGLChan;
47use servo_constellation_traits::WorkerGlobalScopeInit;
48use servo_url::{MutableOrigin, ServoUrl};
49use timers::TimerScheduler;
50use uuid::Uuid;
51
52use crate::dom::Window;
53use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
54    ImageBitmapOptions, ImageBitmapSource,
55};
56use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
57use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
58use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
59use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
60use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
61use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
62use crate::dom::bindings::codegen::UnionTypes::{
63    RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
64    TrustedScriptURLOrUSVString,
65};
66use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
67use crate::dom::bindings::inheritance::Castable;
68use crate::dom::bindings::refcounted::Trusted;
69use crate::dom::bindings::reflector::DomGlobal;
70use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
71use crate::dom::bindings::str::{DOMString, USVString};
72use crate::dom::bindings::trace::HashMapTracedValues;
73use crate::dom::bindings::utils::define_all_exposed_interfaces;
74#[cfg(feature = "webcrypto")]
75use crate::dom::crypto::Crypto;
76use crate::dom::csp::{GlobalCspReporting, Violation, parse_csp_list_from_metadata};
77#[cfg(feature = "devtools")]
78use crate::dom::debugger::debuggerglobalscope::DebuggerGlobalScope;
79use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
80use crate::dom::globalscope::GlobalScope;
81use crate::dom::htmlscriptelement::{SCRIPT_JS_MIMES, Script};
82use crate::dom::idbfactory::IDBFactory;
83use crate::dom::performance::performance::Performance;
84use crate::dom::performance::performanceresourcetiming::InitiatorType;
85use crate::dom::promise::RootedPromise;
86use crate::dom::reporting::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
87use crate::dom::reporting::reportingobserver::ReportingObserver;
88use crate::dom::serviceworker::cachestorage::CacheStorage;
89use crate::dom::sharedworkerglobalscope::SharedWorkerGlobalScope;
90use crate::dom::trustedtypes::trustedscripturl::TrustedScriptURL;
91use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
92use crate::dom::types::ImageBitmap;
93#[cfg(feature = "webgpu")]
94use crate::dom::webgpu::identityhub::IdentityHub;
95use crate::dom::window::{base64_atob, base64_btoa};
96use crate::dom::workerlocation::WorkerLocation;
97use crate::dom::workernavigator::WorkerNavigator;
98use crate::event_loop::timers::{IsInterval, OneshotTimers, TimerCallback};
99use crate::fetch::fetch::{
100    CspViolationsProcessor, Fetch, RequestWithGlobalScope, load_whole_resource,
101};
102use crate::fetch::network_listener::{
103    FetchResponseListener, ResourceTimingListener, submit_timing,
104};
105use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
106use crate::realms::enter_auto_realm;
107use crate::runtime::job_queue::{MicrotaskRunnable, UserMicrotask, job_queue_microtask_checkpoint};
108use crate::runtime::script_runtime::{IntroductionType, Runtime, get_reports};
109use crate::scripting::fetching_options::ScriptFetchOptions;
110use crate::scripting::script_execution::{RethrowErrors, ScriptOptions};
111use crate::scripting::script_module::{ModuleRequest, ModuleStatus};
112use crate::tasks::task::TaskCanceller;
113use crate::tasks::task_manager::TaskManager;
114
115#[cfg(feature = "devtools")]
116pub(crate) type WorkerDevtoolsControlMsg = DevtoolScriptControlMsg;
117#[cfg(not(feature = "devtools"))]
118pub(crate) type WorkerDevtoolsControlMsg = ();
119
120#[cfg(feature = "devtools")]
121pub(crate) type WorkerDebuggerGlobalScope = DebuggerGlobalScope;
122#[cfg(not(feature = "devtools"))]
123pub(crate) type WorkerDebuggerGlobalScope = ();
124
125/// <https://html.spec.whatwg.org/multipage/#animation-frames>
126pub(crate) fn prepare_workerscope_init(
127    global: &GlobalScope,
128    #[cfg(feature = "devtools")] devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
129    worker_id: Option<WorkerId>,
130    #[cfg(feature = "webgl")] webgl_chan: Option<WebGLChan>,
131) -> WorkerGlobalScopeInit {
132    // An AnimationFrameProvider provider is considered supported if any of the following are true:
133    // - provider is a Window.
134    // - provider's owner set contains a Document object.
135    // - Any of the DedicatedWorkerGlobalScope objects in provider's owner set are supported.
136    let animation_frame_provider_supported = if global.downcast::<Window>().is_some() {
137        true
138    } else if let Some(dedicated) = global.downcast::<DedicatedWorkerGlobalScope>() {
139        dedicated.animation_frame_provider_supported()
140    } else {
141        false
142    };
143
144    WorkerGlobalScopeInit {
145        resource_threads: global.resource_threads().clone(),
146        storage_threads: global.storage_threads().clone(),
147        mem_profiler_chan: global.mem_profiler_chan().clone(),
148        #[cfg(feature = "devtools")]
149        to_devtools_sender: global.devtools_chan().cloned(),
150        time_profiler_chan: global.time_profiler_chan().clone(),
151        #[cfg(feature = "devtools")]
152        from_devtools_sender: devtools_sender,
153        script_to_constellation_chan: global.script_to_constellation_chan().sender,
154        script_to_embedder_chan: global.script_to_embedder_chan().clone(),
155        worker_id: worker_id.unwrap_or_else(|| WorkerId(Uuid::new_v4())),
156        animation_frame_provider_supported,
157        pipeline_id: global.pipeline_id(),
158        origin: global.origin().immutable().clone(),
159        inherited_secure_context: Some(global.is_secure_context()),
160        unminify_js: global.unminify_js(),
161        #[cfg(feature = "webgl")]
162        webgl_chan,
163    }
164}
165
166pub(crate) struct ScriptFetchContext {
167    scope: Trusted<WorkerGlobalScope>,
168    response: Option<Metadata>,
169    body_bytes: BytesMut,
170    url: ServoUrl,
171    policy_container: Arc<PolicyContainer>,
172}
173
174impl ScriptFetchContext {
175    pub(crate) fn new(
176        scope: Trusted<WorkerGlobalScope>,
177        url: ServoUrl,
178        policy_container: Arc<PolicyContainer>,
179    ) -> ScriptFetchContext {
180        ScriptFetchContext {
181            scope,
182            response: None,
183            body_bytes: BytesMut::new(),
184            url,
185            policy_container,
186        }
187    }
188}
189
190impl FetchResponseListener for ScriptFetchContext {
191    fn process_request_body(&mut self, _request_id: RequestId) {}
192
193    fn process_response(
194        &mut self,
195        _: &mut JSContext,
196        _request_id: RequestId,
197        metadata: Result<FetchMetadata, NetworkError>,
198    ) {
199        self.response = metadata.ok().map(Into::into);
200    }
201
202    fn process_response_chunk(&mut self, _: &mut JSContext, _: RequestId, chunk: Bytes) {
203        self.body_bytes.extend_from_slice(&chunk);
204    }
205
206    fn process_response_eof(
207        mut self,
208        cx: &mut JSContext,
209        _request_id: RequestId,
210        response: Result<(), NetworkError>,
211        timing: ResourceFetchTiming,
212    ) {
213        let scope = self.scope.root();
214
215        if response
216            .as_ref()
217            .inspect_err(|e| error!("error loading script {} ({:?})", self.url, e))
218            .is_err() ||
219            self.response.is_none()
220        {
221            scope.on_complete(cx, None);
222            return;
223        }
224        let metadata = self.response.take().unwrap();
225
226        // The processResponseConsumeBody steps defined inside
227        // [run a worker](https://html.spec.whatwg.org/multipage/#run-a-worker)
228        scope.process_response_for_workerscope(&metadata, &self.policy_container);
229
230        // The processResponseConsumeBody steps defined inside
231        // [fetch a classic worker script](https://html.spec.whatwg.org/multipage/#fetch-a-classic-worker-script)
232
233        // Step 1 Set response to response's unsafe response. Done in process_response
234
235        // Step 2 If any of the following are true: bodyBytes is null or failure; or response's status is not an ok status,
236        if !metadata.status.is_success() {
237            // then run onComplete given null, and abort these steps.
238            scope.on_complete(cx, None);
239            return;
240        }
241
242        // Step 3 If all of the following are true:
243        // response's URL's scheme is an HTTP(S) scheme;
244        let is_http_scheme = matches!(metadata.final_url.scheme(), "http" | "https");
245        // and the result of extracting a MIME type from response's header list is not a JavaScript MIME type,
246        let not_a_javascript_mime_type = !metadata.content_type.is_some_and(|ct| {
247            let mime: Mime = ct.into_inner().into();
248            SCRIPT_JS_MIMES.contains(&mime.essence_str())
249        });
250
251        if is_http_scheme && not_a_javascript_mime_type {
252            // then run onComplete given null, and abort these steps.
253            scope.on_complete(cx, None);
254            return;
255        }
256
257        // Step 4 Let sourceText be the result of UTF-8 decoding bodyBytes.
258        let (source, _) = UTF_8.decode_with_bom_removal(&self.body_bytes);
259
260        let global_scope = scope.upcast::<GlobalScope>();
261
262        // Step 5 Let script be the result of creating a classic script using
263        // sourceText, settingsObject, response's URL, and the default script fetch options.
264        let script = global_scope.create_a_classic_script(
265            cx,
266            source,
267            scope.worker_url.borrow().clone(),
268            ScriptOptions::External,
269            ScriptFetchOptions::default_classic_script(),
270            Some(IntroductionType::WORKER),
271            1,
272        );
273
274        // Step 6 Run onComplete given script.
275        scope.on_complete(cx, Some(Script::Classic(script)));
276
277        submit_timing(cx, &self, &response, &timing);
278    }
279
280    fn process_csp_violations(
281        &mut self,
282        _cx: &mut JSContext,
283        _request_id: RequestId,
284        violations: Vec<content_security_policy::Violation>,
285    ) {
286        let scope = self.scope.root();
287
288        if let Some(worker_scope) = scope.downcast::<DedicatedWorkerGlobalScope>() {
289            worker_scope.report_csp_violations(violations);
290        } else if let Some(worker_scope) = scope.downcast::<SharedWorkerGlobalScope>() {
291            worker_scope.report_csp_violations(violations);
292        }
293    }
294
295    fn process_content_length(&mut self, _request_id: RequestId, size: usize) {
296        self.body_bytes
297            .reserve(size.saturating_sub(self.body_bytes.len()));
298    }
299}
300
301impl ResourceTimingListener for ScriptFetchContext {
302    fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
303        (InitiatorType::Other, self.url.clone())
304    }
305
306    fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
307        self.scope.root().global()
308    }
309}
310
311/// <https://html.spec.whatwg.org/multipage/#the-workerglobalscope-common-interface>
312#[dom_struct]
313pub(crate) struct WorkerGlobalScope {
314    globalscope: GlobalScope,
315
316    worker_name: DOMString,
317    worker_type: WorkerType,
318
319    #[no_trace]
320    worker_id: WorkerId,
321    #[no_trace]
322    worker_url: DomRefCell<ServoUrl>,
323    #[conditional_malloc_size_of]
324    closing: Arc<AtomicBool>,
325    execution_ready: AtomicBool,
326    #[ignore_malloc_size_of = "Defined in js"]
327    runtime: DomRefCell<Option<Runtime>>,
328    location: MutNullableDom<WorkerLocation>,
329    navigator: MutNullableDom<WorkerNavigator>,
330    #[cfg(feature = "webcrypto")]
331    crypto: MutNullableDom<Crypto>,
332    #[no_trace]
333    /// <https://html.spec.whatwg.org/multipage/#the-workerglobalscope-common-interface:policy-container>
334    #[conditional_malloc_size_of]
335    policy_container: DomRefCell<Arc<PolicyContainer>>,
336
337    #[ignore_malloc_size_of = "Defined in base"]
338    #[no_trace]
339    /// A `Sender` for sending messages to devtools. This is unused but is stored here to
340    /// keep the channel alive.
341    #[cfg(feature = "devtools")]
342    _devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
343
344    #[ignore_malloc_size_of = "Defined in base"]
345    #[no_trace]
346    /// A `Receiver` for receiving messages from devtools.
347    #[cfg(feature = "devtools")]
348    devtools_receiver: Option<RoutedReceiver<DevtoolScriptControlMsg>>,
349
350    #[no_trace]
351    navigation_start: CrossProcessInstant,
352    performance: MutNullableDom<Performance>,
353    trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
354
355    /// A [`TimerScheduler`] used to schedule timers for this [`WorkerGlobalScope`].
356    /// Timers are handled in the service worker event loop.
357    #[no_trace]
358    timer_scheduler: RefCell<TimerScheduler>,
359
360    /// The mechanism by which time-outs and intervals are scheduled.
361    /// <https://html.spec.whatwg.org/multipage/#timers>
362    timers: OnceCell<OneshotTimers>,
363
364    #[no_trace]
365    insecure_requests_policy: InsecureRequestsPolicy,
366
367    /// <https://w3c.github.io/reporting/#windoworworkerglobalscope-registered-reporting-observer-list>
368    reporting_observer_list: DomRefCell<Vec<Dom<ReportingObserver>>>,
369
370    /// <https://w3c.github.io/reporting/#windoworworkerglobalscope-reports>
371    report_list: DomRefCell<Vec<Report>>,
372
373    /// <https://w3c.github.io/reporting/#windoworworkerglobalscope-endpoints>
374    #[no_trace]
375    endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
376
377    /// The debugger global object associated with this worker global.
378    /// All traced members of DOM objects must be same-compartment with the
379    /// realm being traced, so this is the debugger global object wrapped into
380    /// this global's compartment.
381    #[ignore_malloc_size_of = "Measured by the JS engine"]
382    debugger_global: Heap<Value>,
383
384    #[no_trace]
385    pipeline_id: PipelineId,
386
387    /// <https://w3c.github.io/ServiceWorker/#global-caches-attribute>
388    caches: MutNullableDom<CacheStorage>,
389
390    /// The [`TaskManager`] for this [`WorkerGlobalScope`].
391    #[conditional_malloc_size_of]
392    task_manager: Rc<TaskManager>,
393
394    #[no_trace]
395    origin: MutableOrigin,
396
397    /// The [`FontContext`] for this worker, used by any offscreen canvas.
398    #[conditional_malloc_size_of]
399    #[no_trace]
400    font_context: Arc<FontContext>,
401
402    /// module map is used when importing JavaScript modules
403    /// <https://html.spec.whatwg.org/multipage/#concept-settings-object-module-map>
404    #[ignore_malloc_size_of = "mozjs"]
405    module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
406}
407
408impl WorkerGlobalScope {
409    #[allow(clippy::too_many_arguments)]
410    pub(crate) fn new_inherited(
411        init: WorkerGlobalScopeInit,
412        worker_name: DOMString,
413        worker_type: WorkerType,
414        worker_url: ServoUrl,
415        runtime: Runtime,
416        devtools_receiver: Option<RoutedReceiver<WorkerDevtoolsControlMsg>>,
417        closing: Arc<AtomicBool>,
418        #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
419        insecure_requests_policy: InsecureRequestsPolicy,
420        font_context: Arc<FontContext>,
421        event_loop_sender: Option<ScriptEventLoopSender>,
422    ) -> Self {
423        // Install a pipeline-namespace in the current thread.
424        PipelineNamespace::auto_install();
425
426        #[cfg(feature = "devtools")]
427        let devtools_receiver = init.from_devtools_sender.as_ref().and(devtools_receiver);
428
429        Self {
430            globalscope: GlobalScope::new_inherited(
431                #[cfg(feature = "devtools")]
432                init.to_devtools_sender,
433                init.mem_profiler_chan,
434                init.time_profiler_chan,
435                init.script_to_constellation_chan,
436                init.script_to_embedder_chan,
437                init.resource_threads,
438                init.storage_threads,
439                worker_url.clone(),
440                None,
441                #[cfg(feature = "webgpu")]
442                gpu_id_hub,
443                init.inherited_secure_context,
444                init.unminify_js,
445            ),
446            caches: Default::default(),
447            worker_id: init.worker_id,
448            worker_name,
449            worker_type,
450            worker_url: DomRefCell::new(worker_url),
451            closing: closing.clone(),
452            execution_ready: AtomicBool::new(false),
453            runtime: DomRefCell::new(Some(runtime)),
454            location: Default::default(),
455            navigator: Default::default(),
456            #[cfg(feature = "webcrypto")]
457            crypto: Default::default(),
458            policy_container: Default::default(),
459            #[cfg(feature = "devtools")]
460            devtools_receiver,
461            #[cfg(feature = "devtools")]
462            _devtools_sender: init.from_devtools_sender,
463            navigation_start: CrossProcessInstant::now(),
464            performance: Default::default(),
465            timer_scheduler: RefCell::default(),
466            timers: Default::default(),
467            insecure_requests_policy,
468            trusted_types: Default::default(),
469            reporting_observer_list: Default::default(),
470            report_list: Default::default(),
471            endpoints_list: Default::default(),
472            debugger_global: Default::default(),
473            pipeline_id: init.pipeline_id,
474            task_manager: Rc::new(TaskManager::new(
475                event_loop_sender,
476                init.pipeline_id,
477                Some(TaskCanceller { cancelled: closing }),
478            )),
479            origin: MutableOrigin::new(init.origin),
480            font_context,
481            module_map: Default::default(),
482        }
483    }
484
485    pub(crate) fn module_map(
486        &self,
487    ) -> &DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>> {
488        &self.module_map
489    }
490
491    pub(crate) fn font_context(&self) -> Arc<FontContext> {
492        self.font_context.clone()
493    }
494
495    pub(crate) fn timers(&self) -> &OneshotTimers {
496        self.timers
497            .get_or_init(|| OneshotTimers::new(self.upcast()))
498    }
499
500    pub(crate) fn enqueue_microtask(&self, cx: &JSContext, job: Box<dyn MicrotaskRunnable>) {
501        crate::runtime::job_queue::enqueue(cx, job);
502    }
503
504    /// Perform a microtask checkpoint.
505    pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut JSContext) {
506        // Only perform the checkpoint if we're not shutting down.
507        if !self.is_closing() {
508            job_queue_microtask_checkpoint(cx, vec![DomRoot::from_ref(&self.globalscope)]);
509        }
510    }
511
512    /// Returns a policy value that should be used by fetches initiated by this worker.
513    pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
514        self.insecure_requests_policy
515    }
516
517    /// Clear various items when the worker event-loop shuts-down.
518    pub(crate) fn clear_js_runtime(&self) {
519        self.upcast::<GlobalScope>()
520            .remove_web_messaging_and_dedicated_workers_infra();
521
522        // Drop the runtime.
523        let runtime = self.runtime.borrow_mut().take();
524        drop(runtime);
525    }
526
527    pub(crate) fn runtime_handle(&self) -> ParentRuntime {
528        self.runtime
529            .borrow()
530            .as_ref()
531            .unwrap()
532            .prepare_for_new_child()
533    }
534
535    #[cfg(feature = "devtools")]
536    pub(crate) fn devtools_receiver(&self) -> Option<&RoutedReceiver<DevtoolScriptControlMsg>> {
537        self.devtools_receiver.as_ref()
538    }
539
540    pub(crate) fn is_closing(&self) -> bool {
541        self.closing.load(Ordering::SeqCst)
542    }
543
544    pub(crate) fn is_execution_ready(&self) -> bool {
545        self.execution_ready.load(Ordering::Relaxed)
546    }
547
548    pub(crate) fn get_url(&self) -> Ref<'_, ServoUrl> {
549        self.worker_url.borrow()
550    }
551
552    pub(crate) fn set_url(&self, url: ServoUrl) {
553        *self.worker_url.borrow_mut() = url;
554    }
555
556    pub(crate) fn worker_name(&self) -> DOMString {
557        self.worker_name.clone()
558    }
559
560    pub(crate) fn worker_id(&self) -> WorkerId {
561        self.worker_id
562    }
563
564    pub(crate) fn pipeline_id(&self) -> PipelineId {
565        self.pipeline_id
566    }
567
568    pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
569        self.task_manager.clone()
570    }
571
572    pub(crate) fn policy_container(&self) -> Ref<'_, Arc<PolicyContainer>> {
573        self.policy_container.borrow()
574    }
575
576    pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
577        Arc::make_mut(&mut *self.policy_container.borrow_mut()).set_csp_list(csp_list);
578    }
579
580    pub(crate) fn set_referrer_policy(&self, referrer_policy: ReferrerPolicy) {
581        Arc::make_mut(&mut *self.policy_container.borrow_mut())
582            .set_referrer_policy(referrer_policy);
583    }
584
585    pub(crate) fn append_reporting_observer(&self, reporting_observer: &ReportingObserver) {
586        self.reporting_observer_list
587            .borrow_mut()
588            .push(Dom::from_ref(reporting_observer));
589    }
590
591    pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
592        if let Some(index) = self
593            .reporting_observer_list
594            .borrow()
595            .iter()
596            .position(|observer| &**observer == reporting_observer)
597        {
598            self.reporting_observer_list.borrow_mut().remove(index);
599        }
600    }
601
602    pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
603        self.reporting_observer_list
604            .borrow()
605            .iter()
606            .map(|observer| DomRoot::from_ref(&**observer))
607            .collect()
608    }
609
610    pub(crate) fn append_report(&self, report: Report) {
611        self.report_list.borrow_mut().push(report);
612        let trusted_worker = Trusted::new(self);
613        self.upcast::<GlobalScope>()
614            .task_manager()
615            .dom_manipulation_task_source()
616            .queue(task!(send_to_reporting_endpoints: move || {
617                let worker = trusted_worker.root();
618                let reports = std::mem::take(&mut *worker.report_list.borrow_mut());
619                worker.upcast::<GlobalScope>().send_reports_to_endpoints(
620                    reports,
621                    worker.endpoints_list.borrow().clone(),
622                );
623            }));
624    }
625
626    pub(crate) fn buffered_reports(&self) -> Vec<Report> {
627        self.report_list.borrow().clone()
628    }
629
630    pub(crate) fn set_endpoints_list(&self, endpoints: Option<Vec<ReportingEndpoint>>) {
631        if let Some(endpoints) = endpoints {
632            *self.endpoints_list.borrow_mut() = endpoints;
633        }
634    }
635
636    /// Get a mutable reference to the [`TimerScheduler`] for this [`ServiceWorkerGlobalScope`].
637    pub(crate) fn timer_scheduler(&self) -> RefMut<'_, TimerScheduler> {
638        self.timer_scheduler.borrow_mut()
639    }
640
641    /// <https://html.spec.whatwg.org/multipage/#initialize-worker-policy-container> and
642    /// <https://html.spec.whatwg.org/multipage/#creating-a-policy-container-from-a-fetch-response>
643    fn initialize_policy_container_for_worker_global_scope(
644        &self,
645        metadata: &Metadata,
646        parent_policy_container: &PolicyContainer,
647    ) {
648        // Step 1. If workerGlobalScope's url is local but its scheme is not "blob":
649        //
650        // Note that we also allow for blob here, as the parent_policy_container is in both cases
651        // the container that we need to clone.
652        if metadata.final_url.is_local_scheme() {
653            // Step 1.2. Set workerGlobalScope's policy container to a clone of workerGlobalScope's
654            // owner set[0]'s relevant settings object's policy container.
655            //
656            // Step 1. If response's URL's scheme is "blob", then return a clone of response's URL's
657            // blob URL entry's environment's policy container.
658            self.set_csp_list(parent_policy_container.csp_list.clone());
659            self.set_referrer_policy(parent_policy_container.get_referrer_policy());
660            return;
661        }
662        // Step 3. Set result's CSP list to the result of parsing a response's Content Security Policies given response.
663        self.set_csp_list(parse_csp_list_from_metadata(&metadata.headers));
664        // Step 5. Set result's referrer policy to the result of parsing the `Referrer-Policy`
665        // header given response. [REFERRERPOLICY]
666        let referrer_policy = metadata
667            .headers
668            .as_ref()
669            .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
670            .into();
671        self.set_referrer_policy(referrer_policy);
672    }
673
674    /// onComplete algorithm defined inside <https://html.spec.whatwg.org/multipage/#run-a-worker>
675    #[expect(unsafe_code)]
676    pub(crate) fn on_complete(&self, cx: &mut JSContext, script: Option<Script>) {
677        // Step 1. If script is null or if script's error to rethrow is non-null, then:
678        let script = match script {
679            Some(Script::Classic(script)) if script.record.is_ok() => Script::Classic(script),
680            Some(Script::Module(module_tree))
681                if module_tree.get_rethrow_error().borrow().is_none() =>
682            {
683                Script::Module(module_tree)
684            },
685            _ => {
686                // Step 1.1. Queue a global task on the DOM manipulation task source given
687                // worker's relevant global object to fire an event named error at worker.
688                if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
689                    dedicated.forward_simple_error_at_worker();
690                } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
691                    shared.forward_simple_error_at_worker();
692                }
693
694                // TODO Step 1.2. Run the environment discarding steps for inside settings.
695                // Step 1.3 Abort these steps.
696                return;
697            },
698        };
699
700        unsafe {
701            // Handle interrupt requests
702            js::rust::wrappers2::JS_AddInterruptCallback(cx, Some(interrupt_callback));
703        }
704
705        if self.is_closing() {
706            return;
707        }
708
709        {
710            let mut realm = enter_auto_realm(cx, self);
711            let cx = &mut realm.current_realm();
712            define_all_exposed_interfaces(cx, self.upcast());
713            // Step 9. Set inside settings's execution ready flag.
714            self.execution_ready.store(true, Ordering::Relaxed);
715            match script {
716                Script::Classic(script) => {
717                    _ = self.globalscope.run_a_classic_script(
718                        cx,
719                        script,
720                        RethrowErrors::No,
721                        None, // return_value
722                    );
723                },
724                Script::Module(module_tree) => {
725                    self.globalscope.run_a_module_script(cx, module_tree, false);
726                },
727                _ => unreachable!(),
728            }
729            if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
730                dedicated.fire_queued_messages(cx);
731            } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
732                // Step 11. Enable outside port's port message queue.
733                shared.enable_outside_port_message_queue();
734                // Step 13. If the initial Connect arrived before execution-ready,
735                // queue the global task to fire its connect event now.
736                shared.fire_pending_connect(cx);
737            }
738        }
739    }
740
741    // The processResponseConsumeBody steps defined inside
742    // [run a worker](https://html.spec.whatwg.org/multipage/#run-a-worker)
743    pub(crate) fn process_response_for_workerscope(
744        &self,
745        metadata: &Metadata,
746        policy_container: &PolicyContainer,
747    ) {
748        // Step 1. Set worker global scope's url to response's url.
749        self.set_url(metadata.final_url.clone());
750
751        // Step 2. Set inside settings's creation URL to response's url.
752        self.globalscope
753            .set_creation_url(metadata.final_url.clone());
754
755        // Step 3. Initialize worker global scope's policy container given worker global scope, response, and inside settings.
756        self.initialize_policy_container_for_worker_global_scope(metadata, policy_container);
757        self.set_endpoints_list(ReportingEndpoint::parse_reporting_endpoints_header(
758            &metadata.final_url.clone(),
759            &metadata.headers,
760        ));
761    }
762
763    pub(crate) fn origin(&self) -> MutableOrigin {
764        self.origin.clone()
765    }
766}
767
768impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
769    /// <https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-self>
770    fn Self_(&self) -> DomRoot<WorkerGlobalScope> {
771        DomRoot::from_ref(self)
772    }
773
774    /// <https://w3c.github.io/IndexedDB/#factory-interface>
775    fn IndexedDB(&self, cx: &mut JSContext) -> DomRoot<IDBFactory> {
776        self.upcast::<GlobalScope>().ensure_indexeddb_factory(cx)
777    }
778
779    /// <https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-location>
780    fn Location(&self, cx: &mut JSContext) -> DomRoot<WorkerLocation> {
781        self.location
782            .or_init(|| WorkerLocation::new(cx, self, self.worker_url.borrow().clone()))
783    }
784
785    /// <https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-importscripts>
786    fn ImportScripts(
787        &self,
788        cx: &mut JSContext,
789        url_strings: Vec<TrustedScriptURLOrUSVString>,
790    ) -> ErrorResult {
791        // https://html.spec.whatwg.org/multipage/#import-scripts-into-worker-global-scope
792        // Step 1: If worker global scope's type is "module", throw a TypeError exception.
793        if self.worker_type == WorkerType::Module {
794            return Err(Error::Type(
795                c"importScripts() is not allowed in module workers".to_owned(),
796            ));
797        }
798
799        // Step 4: Let urlStrings be « ».
800        let mut urls = Vec::with_capacity(url_strings.len());
801        // Step 5: For each url of urls:
802        for url in url_strings {
803            // Step 3: Append the result of invoking the Get Trusted Type compliant string algorithm
804            // with TrustedScriptURL, this's relevant global object, url, "WorkerGlobalScope importScripts",
805            // and "script" to urlStrings.
806            let url = TrustedScriptURL::get_trusted_type_compliant_string(
807                cx,
808                self.upcast::<GlobalScope>(),
809                url,
810                "WorkerGlobalScope importScripts",
811            )?;
812            let url = self.worker_url.borrow().join(&url.str());
813            match url {
814                Ok(url) => urls.push(url),
815                Err(_) => return Err(Error::Syntax(None)),
816            };
817        }
818
819        for url in urls {
820            let global_scope = self.upcast::<GlobalScope>();
821            let request = RequestBuilder::new(
822                global_scope.webview_id(),
823                UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
824                global_scope.get_referrer(),
825            )
826            .destination(Destination::Script)
827            .credentials_mode(CredentialsMode::Include)
828            .parser_metadata(ParserMetadata::NotParserInserted)
829            .use_url_credentials(true)
830            .with_global_scope(global_scope);
831
832            // https://html.spec.whatwg.org/multipage/#fetch-a-classic-worker-imported-script
833            let (url, bytes, muted_errors) = match load_whole_resource(
834                request,
835                &global_scope.resource_threads().sender(),
836                global_scope,
837                &WorkerCspProcessor {
838                    global_scope: DomRoot::from_ref(global_scope),
839                },
840                cx,
841            ) {
842                Err(_) => return Err(Error::Network(None)),
843                Ok((metadata, bytes, muted_errors)) => {
844                    // Step 7: Check if response status is not an ok status
845                    if !metadata.status.is_success() {
846                        return Err(Error::Network(None));
847                    }
848
849                    // Step 7: Check if the MIME type is not a JavaScript MIME type
850                    let not_a_javascript_mime_type =
851                        !metadata.content_type.clone().is_some_and(|ct| {
852                            let mime: Mime = ct.into_inner().into();
853                            SCRIPT_JS_MIMES.contains(&mime.essence_str())
854                        });
855                    if not_a_javascript_mime_type {
856                        return Err(Error::Network(None));
857                    }
858
859                    (metadata.final_url, bytes, muted_errors)
860                },
861            };
862
863            // Step 8. Let sourceText be the result of UTF-8 decoding bodyBytes.
864            let (source, _) = UTF_8.decode_with_bom_removal(&bytes);
865
866            // Step 9. Let mutedErrors be true if response was CORS-cross-origin, and false otherwise.
867            // Note: done inside load_whole_resource
868
869            // Step 10. Let script be the result of creating a classic script
870            // given sourceText, settingsObject, response's URL, the default script fetch options, and mutedErrors.
871            let mut script_options = ScriptOptions::External;
872            script_options.set(ScriptOptions::MutedErrors, muted_errors);
873            let script = self.globalscope.create_a_classic_script(
874                cx,
875                source,
876                url,
877                script_options,
878                ScriptFetchOptions::default_classic_script(),
879                Some(IntroductionType::WORKER),
880                1,
881            );
882
883            // Run the classic script script, with rethrow errors set to true.
884            let result = self.globalscope.run_a_classic_script(
885                cx,
886                script,
887                RethrowErrors::Yes,
888                None, // return_value
889            );
890
891            if let Err(error) = result {
892                if self.is_closing() {
893                    // Don't return JSFailed as we might not have
894                    // any pending exceptions.
895                    error!("evaluate_script failed (terminated)");
896                } else {
897                    error!("evaluate_script failed");
898                    return Err(error);
899                }
900            }
901        }
902
903        Ok(())
904    }
905
906    // https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onerror
907    error_event_handler!(error, GetOnerror, SetOnerror);
908
909    // https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onlanguagechange
910    event_handler!(languagechange, GetOnlanguagechange, SetOnlanguagechange);
911
912    // https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onoffline
913    event_handler!(offline, GetOnoffline, SetOnoffline);
914
915    // https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-ononline
916    event_handler!(online, GetOnonline, SetOnonline);
917
918    // https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onrejectionhandled
919    event_handler!(
920        rejectionhandled,
921        GetOnrejectionhandled,
922        SetOnrejectionhandled
923    );
924
925    // https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onunhandledrejection
926    event_handler!(
927        unhandledrejection,
928        GetOnunhandledrejection,
929        SetOnunhandledrejection
930    );
931
932    /// <https://html.spec.whatwg.org/multipage/#dom-worker-navigator>
933    fn Navigator(&self, cx: &mut JSContext) -> DomRoot<WorkerNavigator> {
934        self.navigator.or_init(|| WorkerNavigator::new(cx, self))
935    }
936
937    /// <https://html.spec.whatwg.org/multipage/#dfn-Crypto>
938    #[cfg(feature = "webcrypto")]
939    fn Crypto(&self, cx: &mut JSContext) -> DomRoot<Crypto> {
940        self.crypto
941            .or_init(|| Crypto::new(cx, self.upcast::<GlobalScope>()))
942    }
943
944    /// <https://html.spec.whatwg.org/multipage/#dom-reporterror>
945    fn ReportError(&self, cx: &mut JSContext, error: HandleValue) {
946        self.upcast::<GlobalScope>().report_an_exception(cx, error);
947    }
948
949    /// <https://html.spec.whatwg.org/multipage/#dom-windowbase64-btoa>
950    fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
951        base64_btoa(btoa)
952    }
953
954    /// <https://html.spec.whatwg.org/multipage/#dom-windowbase64-atob>
955    fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
956        base64_atob(atob)
957    }
958
959    /// <https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout>
960    fn SetTimeout(
961        &self,
962        cx: &mut JSContext,
963        callback: TrustedScriptOrStringOrFunction,
964        timeout: i32,
965        args: Vec<HandleValue>,
966    ) -> Fallible<i32> {
967        let callback = match callback {
968            TrustedScriptOrStringOrFunction::String(i) => {
969                TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
970            },
971            TrustedScriptOrStringOrFunction::TrustedScript(i) => {
972                TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
973            },
974            TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
975        };
976        self.upcast::<GlobalScope>().set_timeout_or_interval(
977            cx,
978            callback,
979            args,
980            Duration::from_millis(timeout.max(0) as u64),
981            IsInterval::NonInterval,
982        )
983    }
984
985    /// <https://html.spec.whatwg.org/multipage/#dom-windowtimers-cleartimeout>
986    fn ClearTimeout(&self, handle: i32) {
987        self.upcast::<GlobalScope>()
988            .clear_timeout_or_interval(handle);
989    }
990
991    /// <https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval>
992    fn SetInterval(
993        &self,
994        cx: &mut JSContext,
995        callback: TrustedScriptOrStringOrFunction,
996        timeout: i32,
997        args: Vec<HandleValue>,
998    ) -> Fallible<i32> {
999        let callback = match callback {
1000            TrustedScriptOrStringOrFunction::String(i) => {
1001                TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1002            },
1003            TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1004                TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1005            },
1006            TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1007        };
1008        self.upcast::<GlobalScope>().set_timeout_or_interval(
1009            cx,
1010            callback,
1011            args,
1012            Duration::from_millis(timeout.max(0) as u64),
1013            IsInterval::Interval,
1014        )
1015    }
1016
1017    /// <https://w3c.github.io/ServiceWorker/#global-caches-attribute>
1018    fn Caches(&self, cx: &mut JSContext) -> DomRoot<CacheStorage> {
1019        self.caches
1020            .or_init(|| CacheStorage::new(cx, self.upcast::<GlobalScope>()))
1021    }
1022
1023    /// <https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval>
1024    fn ClearInterval(&self, handle: i32) {
1025        self.ClearTimeout(handle);
1026    }
1027
1028    /// <https://html.spec.whatwg.org/multipage/#dom-queuemicrotask>
1029    fn QueueMicrotask(&self, cx: &mut JSContext, callback: RootedCallback<VoidFunction>) {
1030        self.enqueue_microtask(
1031            cx,
1032            Box::new(UserMicrotask {
1033                callback: callback.to_traced(),
1034                global: Dom::from_ref(&self.globalscope),
1035            }),
1036        );
1037    }
1038
1039    /// <https://html.spec.whatwg.org/multipage/#dom-createimagebitmap>
1040    fn CreateImageBitmap(
1041        &self,
1042        realm: &mut CurrentRealm,
1043        image: ImageBitmapSource,
1044        options: &ImageBitmapOptions,
1045    ) -> RootedPromise {
1046        ImageBitmap::create_image_bitmap(self.upcast(), image, 0, 0, None, None, options, realm)
1047    }
1048
1049    /// <https://html.spec.whatwg.org/multipage/#dom-createimagebitmap>
1050    fn CreateImageBitmap_(
1051        &self,
1052        realm: &mut CurrentRealm,
1053        image: ImageBitmapSource,
1054        sx: i32,
1055        sy: i32,
1056        sw: i32,
1057        sh: i32,
1058        options: &ImageBitmapOptions,
1059    ) -> RootedPromise {
1060        ImageBitmap::create_image_bitmap(
1061            self.upcast(),
1062            image,
1063            sx,
1064            sy,
1065            Some(sw),
1066            Some(sh),
1067            options,
1068            realm,
1069        )
1070    }
1071
1072    /// <https://fetch.spec.whatwg.org/#dom-global-fetch>
1073    fn Fetch(
1074        &self,
1075        realm: &mut CurrentRealm,
1076        input: RequestOrUSVString,
1077        init: &RequestInit,
1078    ) -> RootedPromise {
1079        Fetch(self.upcast(), input, init, realm)
1080    }
1081
1082    /// <https://w3c.github.io/hr-time/#the-performance-attribute>
1083    fn Performance(&self, cx: &mut JSContext) -> DomRoot<Performance> {
1084        self.performance.or_init(|| {
1085            let global_scope = self.upcast::<GlobalScope>();
1086            Performance::new(cx, global_scope, self.navigation_start)
1087        })
1088    }
1089
1090    /// <https://html.spec.whatwg.org/multipage/#dom-origin>
1091    fn Origin(&self) -> USVString {
1092        USVString(
1093            self.upcast::<GlobalScope>()
1094                .origin()
1095                .immutable()
1096                .ascii_serialization()
1097                .into_owned(),
1098        )
1099    }
1100
1101    /// <https://w3c.github.io/webappsec-secure-contexts/#dom-windoworworkerglobalscope-issecurecontext>
1102    fn IsSecureContext(&self) -> bool {
1103        self.upcast::<GlobalScope>().is_secure_context()
1104    }
1105
1106    /// <https://html.spec.whatwg.org/multipage/#dom-structuredclone>
1107    fn StructuredClone(
1108        &self,
1109        cx: &mut JSContext,
1110        value: HandleValue,
1111        options: &StructuredSerializeOptions,
1112        retval: MutableHandleValue,
1113    ) -> Fallible<()> {
1114        self.upcast::<GlobalScope>()
1115            .structured_clone(cx, value, options, retval)
1116    }
1117
1118    /// <https://www.w3.org/TR/trusted-types/#dom-windoworworkerglobalscope-trustedtypes>
1119    fn TrustedTypes(&self, cx: &mut JSContext) -> DomRoot<TrustedTypePolicyFactory> {
1120        self.trusted_types.or_init(|| {
1121            let global_scope = self.upcast::<GlobalScope>();
1122            TrustedTypePolicyFactory::new(cx, global_scope)
1123        })
1124    }
1125}
1126
1127impl WorkerGlobalScope {
1128    pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
1129        let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
1130        if let Some(dedicated) = dedicated {
1131            dedicated.new_script_pair()
1132        } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
1133            shared.new_script_pair()
1134        } else {
1135            panic!("need to implement a sender for ServiceWorker")
1136        }
1137    }
1138
1139    /// Process a single event as if it were the next event
1140    /// in the queue for this worker event-loop.
1141    /// Returns a boolean indicating whether further events should be processed.
1142    pub(crate) fn process_event(&self, msg: CommonScriptMsg, cx: &mut JSContext) -> bool {
1143        if self.is_closing() {
1144            return false;
1145        }
1146        match msg {
1147            CommonScriptMsg::Task(_, task, _, _) => task.run_box(cx),
1148            CommonScriptMsg::CollectReports(reports_chan) => {
1149                perform_memory_report(|ops| {
1150                    let reports =
1151                        get_reports(cx, format!("url({})", self.get_url()), ops, HashSet::new());
1152                    reports_chan.send(ProcessReports::new(reports));
1153                });
1154            },
1155            CommonScriptMsg::ReportCspViolations(_, violations) => {
1156                self.upcast::<GlobalScope>()
1157                    .report_csp_violations(cx, violations, None, None);
1158            },
1159        }
1160        true
1161    }
1162
1163    /// <https://html.spec.whatwg.org/multipage/#close-a-worker>
1164    pub(crate) fn close(&self) {
1165        // Step 1. Discard any tasks that have been added to workerGlobal's relevant
1166        // agent's event loop's task queues.
1167        //
1168        // Worker rAF callbacks are stored outside the task queues.
1169        if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
1170            dedicated.clear_animation_frame_callbacks_and_unregister();
1171        }
1172
1173        // Step 2. Set workerGlobal's closing flag to true. (This prevents any
1174        // further tasks from being queued.)
1175        self.closing.store(true, Ordering::SeqCst);
1176        self.upcast::<GlobalScope>()
1177            .task_manager()
1178            .cancel_all_tasks_and_ignore_future_tasks();
1179
1180        // From <https://w3c.github.io/IndexedDB/#database-connection>
1181        // > The connection can be closed through several means. If the execution context where
1182        // > the connection was created is destroyed (for example due to the user navigating away
1183        // > from that page), the connection is closed.
1184        if let Some(factory) = self.upcast::<GlobalScope>().indexeddb_factory() {
1185            factory.abort_pending_upgrades_and_close_databases();
1186        }
1187    }
1188}
1189
1190#[cfg(feature = "devtools")]
1191impl WorkerGlobalScope {
1192    pub(crate) fn init_debugger_global(
1193        &self,
1194        debugger_global: Option<&DebuggerGlobalScope>,
1195        cx: &mut JSContext,
1196    ) {
1197        let Some(debugger_global) = debugger_global else {
1198            return;
1199        };
1200
1201        let mut realm = enter_auto_realm(cx, self);
1202        let cx = &mut realm.current_realm();
1203
1204        // Convert the debugger global’s reflector to a Value, wrapping it from its originating realm (debugger realm)
1205        // into the active realm (debuggee realm) so that it can be passed across compartments.
1206        rooted!(&in(cx) let mut wrapped_global: Value);
1207        debugger_global
1208            .reflector()
1209            .to_jsval(cx, wrapped_global.handle_mut());
1210        self.debugger_global.set(*wrapped_global);
1211    }
1212
1213    pub(crate) fn handle_devtools_message(&self, msg: DevtoolScriptControlMsg, cx: &mut JSContext) {
1214        match msg {
1215            DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _wants_updates) => {},
1216            DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, eager, reply) => {
1217                let debugger_global_handle = rooted_heap_handle(self, |this| &this.debugger_global);
1218                let debugger_global =
1219                    root_from_handlevalue::<DebuggerGlobalScope>(cx, debugger_global_handle)
1220                        .expect("must be a debugger global scope");
1221
1222                debugger_global.fire_eval(
1223                    cx,
1224                    code.into(),
1225                    id,
1226                    Some(self.worker_id()),
1227                    frame_actor_id,
1228                    eager,
1229                    reply,
1230                );
1231            },
1232            _ => debug!("got an unusable devtools control message inside the worker!"),
1233        }
1234    }
1235}
1236
1237#[expect(unsafe_code)]
1238unsafe extern "C" fn interrupt_callback(cx: *mut RawJSContext) -> bool {
1239    // SAFETY: it is safe to construct a JSContext from engine hook.
1240    let mut cx = unsafe { JSContext::from_ptr(std::ptr::NonNull::new(cx).unwrap()) };
1241    let mut realm = CurrentRealm::assert(&mut cx);
1242
1243    let global = GlobalScope::from_current_realm(&mut realm);
1244
1245    // If we are running the debugger script, just exit immediately.
1246    let Some(worker) = global.downcast::<WorkerGlobalScope>() else {
1247        return false;
1248    };
1249
1250    // A false response causes the script to terminate.
1251    !worker.is_closing()
1252}
1253
1254struct WorkerCspProcessor {
1255    global_scope: DomRoot<GlobalScope>,
1256}
1257
1258impl CspViolationsProcessor for WorkerCspProcessor {
1259    fn process_csp_violations(&self, cx: &mut JSContext, violations: Vec<Violation>) {
1260        self.global_scope
1261            .report_csp_violations(cx, violations, None, None);
1262    }
1263}