Skip to main content

script/dom/workers/
workerglobalscope.rs

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