script/dom/workers/
workerglobalscope.rs

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