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