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