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