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