1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::cell::{OnceCell, RefCell, RefMut};
8use std::collections::HashSet;
9use std::default::Default;
10use std::rc::Rc;
11use std::sync::Arc;
12use std::sync::atomic::{AtomicBool, Ordering};
13use std::time::Duration;
14
15use bytes::{Bytes, BytesMut};
16use content_security_policy::CspList;
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::context::JSContext;
23use js::conversions::ToJSValConvertible;
24use js::jsapi::{Heap, JSContext as RawJSContext, Value};
25use js::realm::CurrentRealm;
26use js::rust::{HandleValue, MutableHandleValue, ParentRuntime};
27use mime::Mime;
28use net_traits::blob_url_store::UrlWithBlobClaim;
29use net_traits::policy_container::PolicyContainer;
30use net_traits::request::{
31 CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, RequestBuilder, RequestId,
32};
33use net_traits::{FetchMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming};
34use profile_traits::mem::{ProcessReports, perform_memory_report};
35use script_bindings::cell::{DomRefCell, Ref};
36use script_bindings::conversions::root_from_handlevalue;
37use script_bindings::reflector::DomObject;
38use script_bindings::root::rooted_heap_handle;
39use script_bindings::trace::CustomTraceable;
40use servo_base::cross_process_instant::CrossProcessInstant;
41use servo_base::generic_channel::{GenericSend, GenericSender, RoutedReceiver};
42use servo_base::id::{PipelineId, PipelineNamespace};
43#[cfg(feature = "webgl")]
44use servo_canvas_traits::webgl::WebGLChan;
45use servo_constellation_traits::WorkerGlobalScopeInit;
46use servo_url::{MutableOrigin, ServoUrl};
47use timers::TimerScheduler;
48use uuid::Uuid;
49
50use crate::dom::Window;
51use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
52 ImageBitmapOptions, ImageBitmapSource,
53};
54use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
55use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
56use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
57use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
58use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
59use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
60use crate::dom::bindings::codegen::UnionTypes::{
61 RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
62 TrustedScriptURLOrUSVString,
63};
64use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
65use crate::dom::bindings::inheritance::Castable;
66use crate::dom::bindings::refcounted::Trusted;
67use crate::dom::bindings::reflector::DomGlobal;
68use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
69use crate::dom::bindings::str::{DOMString, USVString};
70use crate::dom::bindings::trace::{HashMapTracedValues, RootedTraceableBox};
71use crate::dom::bindings::utils::define_all_exposed_interfaces;
72#[cfg(feature = "webcrypto")]
73use crate::dom::crypto::Crypto;
74use crate::dom::csp::{GlobalCspReporting, Violation, parse_csp_list_from_metadata};
75use crate::dom::debugger::debuggerglobalscope::DebuggerGlobalScope;
76use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
77use crate::dom::globalscope::GlobalScope;
78use crate::dom::globalscope::script_execution::RethrowErrors;
79use crate::dom::htmlscriptelement::{SCRIPT_JS_MIMES, Script};
80use crate::dom::idbfactory::IDBFactory;
81use crate::dom::performance::performance::Performance;
82use crate::dom::performance::performanceresourcetiming::InitiatorType;
83use crate::dom::promise::RootedPromise;
84use crate::dom::reporting::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
85use crate::dom::reporting::reportingobserver::ReportingObserver;
86use crate::dom::script_execution::ScriptOptions;
87use crate::dom::serviceworker::cachestorage::CacheStorage;
88use crate::dom::sharedworkerglobalscope::SharedWorkerGlobalScope;
89use crate::dom::trustedtypes::trustedscripturl::TrustedScriptURL;
90use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
91use crate::dom::types::ImageBitmap;
92#[cfg(feature = "webgpu")]
93use crate::dom::webgpu::identityhub::IdentityHub;
94use crate::dom::window::{base64_atob, base64_btoa};
95use crate::dom::workerlocation::WorkerLocation;
96use crate::dom::workernavigator::WorkerNavigator;
97use crate::event_loop::timers::{IsInterval, OneshotTimers, TimerCallback};
98use crate::fetch::fetch::{
99 CspViolationsProcessor, Fetch, RequestWithGlobalScope, load_whole_resource,
100};
101use crate::fetch::network_listener::{
102 FetchResponseListener, ResourceTimingListener, submit_timing,
103};
104use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
105use crate::modules::script_module::{ModuleRequest, ModuleStatus, ScriptFetchOptions};
106use crate::realms::enter_auto_realm;
107use crate::runtime::job_queue::{MicrotaskRunnable, UserMicrotask, job_queue_microtask_checkpoint};
108use crate::runtime::script_runtime::{IntroductionType, Runtime, get_reports};
109use crate::tasks::task::TaskCanceller;
110use crate::tasks::task_manager::TaskManager;
111
112pub(crate) fn prepare_workerscope_init(
114 global: &GlobalScope,
115 devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
116 worker_id: Option<WorkerId>,
117 #[cfg(feature = "webgl")] webgl_chan: Option<WebGLChan>,
118) -> WorkerGlobalScopeInit {
119 let animation_frame_provider_supported = if global.downcast::<Window>().is_some() {
124 true
125 } else if let Some(dedicated) = global.downcast::<DedicatedWorkerGlobalScope>() {
126 dedicated.animation_frame_provider_supported()
127 } else {
128 false
129 };
130
131 WorkerGlobalScopeInit {
132 resource_threads: global.resource_threads().clone(),
133 storage_threads: global.storage_threads().clone(),
134 mem_profiler_chan: global.mem_profiler_chan().clone(),
135 to_devtools_sender: global.devtools_chan().cloned(),
136 time_profiler_chan: global.time_profiler_chan().clone(),
137 from_devtools_sender: devtools_sender,
138 script_to_constellation_chan: global.script_to_constellation_chan().sender,
139 script_to_embedder_chan: global.script_to_embedder_chan().clone(),
140 worker_id: worker_id.unwrap_or_else(|| WorkerId(Uuid::new_v4())),
141 animation_frame_provider_supported,
142 pipeline_id: global.pipeline_id(),
143 origin: global.origin().immutable().clone(),
144 inherited_secure_context: Some(global.is_secure_context()),
145 unminify_js: global.unminify_js(),
146 #[cfg(feature = "webgl")]
147 webgl_chan,
148 }
149}
150
151pub(crate) struct ScriptFetchContext {
152 scope: Trusted<WorkerGlobalScope>,
153 response: Option<Metadata>,
154 body_bytes: BytesMut,
155 url: ServoUrl,
156 policy_container: PolicyContainer,
157}
158
159impl ScriptFetchContext {
160 pub(crate) fn new(
161 scope: Trusted<WorkerGlobalScope>,
162 url: ServoUrl,
163 policy_container: PolicyContainer,
164 ) -> ScriptFetchContext {
165 ScriptFetchContext {
166 scope,
167 response: None,
168 body_bytes: BytesMut::new(),
169 url,
170 policy_container,
171 }
172 }
173}
174
175impl FetchResponseListener for ScriptFetchContext {
176 fn process_request_body(&mut self, _request_id: RequestId) {}
177
178 fn process_response(
179 &mut self,
180 _: &mut JSContext,
181 _request_id: RequestId,
182 metadata: Result<FetchMetadata, NetworkError>,
183 ) {
184 self.response = metadata.ok().map(|m| match m {
185 FetchMetadata::Unfiltered(m) => m,
186 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
187 });
188 }
189
190 fn process_response_chunk(&mut self, _: &mut JSContext, _: RequestId, chunk: Bytes) {
191 self.body_bytes.extend_from_slice(&chunk);
192 }
193
194 fn process_response_eof(
195 mut self,
196 cx: &mut JSContext,
197 _request_id: RequestId,
198 response: Result<(), NetworkError>,
199 timing: ResourceFetchTiming,
200 ) {
201 let scope = self.scope.root();
202
203 if response
204 .as_ref()
205 .inspect_err(|e| error!("error loading script {} ({:?})", self.url, e))
206 .is_err() ||
207 self.response.is_none()
208 {
209 scope.on_complete(cx, None);
210 return;
211 }
212 let metadata = self.response.take().unwrap();
213
214 scope.process_response_for_workerscope(&metadata, &self.policy_container);
217
218 if !metadata.status.is_success() {
225 scope.on_complete(cx, None);
227 return;
228 }
229
230 let is_http_scheme = matches!(metadata.final_url.scheme(), "http" | "https");
233 let not_a_javascript_mime_type = !metadata.content_type.is_some_and(|ct| {
235 let mime: Mime = ct.into_inner().into();
236 SCRIPT_JS_MIMES.contains(&mime.essence_str())
237 });
238
239 if is_http_scheme && not_a_javascript_mime_type {
240 scope.on_complete(cx, None);
242 return;
243 }
244
245 let (source, _) = UTF_8.decode_with_bom_removal(&self.body_bytes);
247
248 let global_scope = scope.upcast::<GlobalScope>();
249
250 let script = global_scope.create_a_classic_script(
253 cx,
254 source,
255 scope.worker_url.borrow().clone(),
256 ScriptOptions::External,
257 ScriptFetchOptions::default_classic_script(),
258 Some(IntroductionType::WORKER),
259 1,
260 );
261
262 scope.on_complete(cx, Some(Script::Classic(script)));
264
265 submit_timing(cx, &self, &response, &timing);
266 }
267
268 fn process_csp_violations(
269 &mut self,
270 _cx: &mut JSContext,
271 _request_id: RequestId,
272 violations: Vec<content_security_policy::Violation>,
273 ) {
274 let scope = self.scope.root();
275
276 if let Some(worker_scope) = scope.downcast::<DedicatedWorkerGlobalScope>() {
277 worker_scope.report_csp_violations(violations);
278 } else if let Some(worker_scope) = scope.downcast::<SharedWorkerGlobalScope>() {
279 worker_scope.report_csp_violations(violations);
280 }
281 }
282
283 fn process_content_length(&mut self, _request_id: RequestId, size: usize) {
284 self.body_bytes
285 .reserve(size.saturating_sub(self.body_bytes.len()));
286 }
287}
288
289impl ResourceTimingListener for ScriptFetchContext {
290 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
291 (InitiatorType::Other, self.url.clone())
292 }
293
294 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
295 self.scope.root().global()
296 }
297}
298
299#[dom_struct]
301pub(crate) struct WorkerGlobalScope {
302 globalscope: GlobalScope,
303
304 worker_name: DOMString,
305 worker_type: WorkerType,
306
307 #[no_trace]
308 worker_id: WorkerId,
309 #[no_trace]
310 worker_url: DomRefCell<ServoUrl>,
311 #[conditional_malloc_size_of]
312 closing: Arc<AtomicBool>,
313 execution_ready: AtomicBool,
314 #[ignore_malloc_size_of = "Defined in js"]
315 runtime: DomRefCell<Option<Runtime>>,
316 location: MutNullableDom<WorkerLocation>,
317 navigator: MutNullableDom<WorkerNavigator>,
318 #[cfg(feature = "webcrypto")]
319 crypto: MutNullableDom<Crypto>,
320 #[no_trace]
321 policy_container: DomRefCell<PolicyContainer>,
323
324 #[ignore_malloc_size_of = "Defined in base"]
325 #[no_trace]
326 _devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
329
330 #[ignore_malloc_size_of = "Defined in base"]
331 #[no_trace]
332 devtools_receiver: Option<RoutedReceiver<DevtoolScriptControlMsg>>,
334
335 #[no_trace]
336 navigation_start: CrossProcessInstant,
337 performance: MutNullableDom<Performance>,
338 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
339
340 #[no_trace]
343 timer_scheduler: RefCell<TimerScheduler>,
344
345 timers: OnceCell<OneshotTimers>,
348
349 #[no_trace]
350 insecure_requests_policy: InsecureRequestsPolicy,
351
352 reporting_observer_list: DomRefCell<Vec<Dom<ReportingObserver>>>,
354
355 report_list: DomRefCell<Vec<Report>>,
357
358 #[no_trace]
360 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
361
362 #[ignore_malloc_size_of = "Measured by the JS engine"]
367 debugger_global: Heap<Value>,
368
369 #[no_trace]
370 pipeline_id: PipelineId,
371
372 caches: MutNullableDom<CacheStorage>,
374
375 #[conditional_malloc_size_of]
377 task_manager: Rc<TaskManager>,
378
379 #[no_trace]
380 origin: MutableOrigin,
381
382 #[conditional_malloc_size_of]
384 #[no_trace]
385 font_context: Arc<FontContext>,
386
387 #[ignore_malloc_size_of = "mozjs"]
390 module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
391}
392
393impl WorkerGlobalScope {
394 #[allow(clippy::too_many_arguments)]
395 pub(crate) fn new_inherited(
396 init: WorkerGlobalScopeInit,
397 worker_name: DOMString,
398 worker_type: WorkerType,
399 worker_url: ServoUrl,
400 runtime: Runtime,
401 devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
402 closing: Arc<AtomicBool>,
403 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
404 insecure_requests_policy: InsecureRequestsPolicy,
405 font_context: Arc<FontContext>,
406 event_loop_sender: Option<ScriptEventLoopSender>,
407 ) -> Self {
408 PipelineNamespace::auto_install();
410
411 let devtools_receiver = match init.from_devtools_sender {
412 Some(..) => Some(devtools_receiver),
413 None => None,
414 };
415
416 Self {
417 globalscope: GlobalScope::new_inherited(
418 init.to_devtools_sender,
419 init.mem_profiler_chan,
420 init.time_profiler_chan,
421 init.script_to_constellation_chan,
422 init.script_to_embedder_chan,
423 init.resource_threads,
424 init.storage_threads,
425 worker_url.clone(),
426 None,
427 #[cfg(feature = "webgpu")]
428 gpu_id_hub,
429 init.inherited_secure_context,
430 init.unminify_js,
431 ),
432 caches: Default::default(),
433 worker_id: init.worker_id,
434 worker_name,
435 worker_type,
436 worker_url: DomRefCell::new(worker_url),
437 closing: closing.clone(),
438 execution_ready: AtomicBool::new(false),
439 runtime: DomRefCell::new(Some(runtime)),
440 location: Default::default(),
441 navigator: Default::default(),
442 #[cfg(feature = "webcrypto")]
443 crypto: Default::default(),
444 policy_container: Default::default(),
445 devtools_receiver,
446 _devtools_sender: init.from_devtools_sender,
447 navigation_start: CrossProcessInstant::now(),
448 performance: Default::default(),
449 timer_scheduler: RefCell::default(),
450 timers: Default::default(),
451 insecure_requests_policy,
452 trusted_types: Default::default(),
453 reporting_observer_list: Default::default(),
454 report_list: Default::default(),
455 endpoints_list: Default::default(),
456 debugger_global: Default::default(),
457 pipeline_id: init.pipeline_id,
458 task_manager: Rc::new(TaskManager::new(
459 event_loop_sender,
460 init.pipeline_id,
461 Some(TaskCanceller { cancelled: closing }),
462 )),
463 origin: MutableOrigin::new(init.origin),
464 font_context,
465 module_map: Default::default(),
466 }
467 }
468
469 pub(crate) fn module_map(
470 &self,
471 ) -> &DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>> {
472 &self.module_map
473 }
474
475 pub(crate) fn font_context(&self) -> Arc<FontContext> {
476 self.font_context.clone()
477 }
478
479 pub(crate) fn timers(&self) -> &OneshotTimers {
480 self.timers
481 .get_or_init(|| OneshotTimers::new(self.upcast()))
482 }
483
484 pub(crate) fn enqueue_microtask(&self, cx: &JSContext, job: Box<dyn MicrotaskRunnable>) {
485 crate::runtime::job_queue::enqueue(cx, job);
486 }
487
488 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut JSContext) {
490 if !self.is_closing() {
492 job_queue_microtask_checkpoint(cx, vec![DomRoot::from_ref(&self.globalscope)]);
493 }
494 }
495
496 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
498 self.insecure_requests_policy
499 }
500
501 pub(crate) fn clear_js_runtime(&self) {
503 self.upcast::<GlobalScope>()
504 .remove_web_messaging_and_dedicated_workers_infra();
505
506 let runtime = self.runtime.borrow_mut().take();
508 drop(runtime);
509 }
510
511 pub(crate) fn runtime_handle(&self) -> ParentRuntime {
512 self.runtime
513 .borrow()
514 .as_ref()
515 .unwrap()
516 .prepare_for_new_child()
517 }
518
519 pub(crate) fn devtools_receiver(&self) -> Option<&RoutedReceiver<DevtoolScriptControlMsg>> {
520 self.devtools_receiver.as_ref()
521 }
522
523 pub(crate) fn is_closing(&self) -> bool {
524 self.closing.load(Ordering::SeqCst)
525 }
526
527 pub(crate) fn is_execution_ready(&self) -> bool {
528 self.execution_ready.load(Ordering::Relaxed)
529 }
530
531 pub(crate) fn get_url(&self) -> Ref<'_, ServoUrl> {
532 self.worker_url.borrow()
533 }
534
535 pub(crate) fn set_url(&self, url: ServoUrl) {
536 *self.worker_url.borrow_mut() = url;
537 }
538
539 pub(crate) fn worker_name(&self) -> DOMString {
540 self.worker_name.clone()
541 }
542
543 pub(crate) fn worker_id(&self) -> WorkerId {
544 self.worker_id
545 }
546
547 pub(crate) fn pipeline_id(&self) -> PipelineId {
548 self.pipeline_id
549 }
550
551 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
552 self.task_manager.clone()
553 }
554
555 pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
556 self.policy_container.borrow()
557 }
558
559 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
560 self.policy_container.borrow_mut().set_csp_list(csp_list);
561 }
562
563 pub(crate) fn set_referrer_policy(&self, referrer_policy: ReferrerPolicy) {
564 self.policy_container
565 .borrow_mut()
566 .set_referrer_policy(referrer_policy);
567 }
568
569 pub(crate) fn append_reporting_observer(&self, reporting_observer: &ReportingObserver) {
570 self.reporting_observer_list
571 .borrow_mut()
572 .push(Dom::from_ref(reporting_observer));
573 }
574
575 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
576 if let Some(index) = self
577 .reporting_observer_list
578 .borrow()
579 .iter()
580 .position(|observer| &**observer == reporting_observer)
581 {
582 self.reporting_observer_list.borrow_mut().remove(index);
583 }
584 }
585
586 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
587 self.reporting_observer_list
588 .borrow()
589 .iter()
590 .map(|observer| DomRoot::from_ref(&**observer))
591 .collect()
592 }
593
594 pub(crate) fn append_report(&self, report: Report) {
595 self.report_list.borrow_mut().push(report);
596 let trusted_worker = Trusted::new(self);
597 self.upcast::<GlobalScope>()
598 .task_manager()
599 .dom_manipulation_task_source()
600 .queue(task!(send_to_reporting_endpoints: move || {
601 let worker = trusted_worker.root();
602 let reports = std::mem::take(&mut *worker.report_list.borrow_mut());
603 worker.upcast::<GlobalScope>().send_reports_to_endpoints(
604 reports,
605 worker.endpoints_list.borrow().clone(),
606 );
607 }));
608 }
609
610 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
611 self.report_list.borrow().clone()
612 }
613
614 pub(crate) fn set_endpoints_list(&self, endpoints: Option<Vec<ReportingEndpoint>>) {
615 if let Some(endpoints) = endpoints {
616 *self.endpoints_list.borrow_mut() = endpoints;
617 }
618 }
619
620 pub(crate) fn timer_scheduler(&self) -> RefMut<'_, TimerScheduler> {
622 self.timer_scheduler.borrow_mut()
623 }
624
625 fn initialize_policy_container_for_worker_global_scope(
628 &self,
629 metadata: &Metadata,
630 parent_policy_container: &PolicyContainer,
631 ) {
632 if metadata.final_url.is_local_scheme() {
637 self.set_csp_list(parent_policy_container.csp_list.clone());
643 self.set_referrer_policy(parent_policy_container.get_referrer_policy());
644 return;
645 }
646 self.set_csp_list(parse_csp_list_from_metadata(&metadata.headers));
648 let referrer_policy = metadata
651 .headers
652 .as_ref()
653 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
654 .into();
655 self.set_referrer_policy(referrer_policy);
656 }
657
658 #[expect(unsafe_code)]
660 pub(crate) fn on_complete(&self, cx: &mut JSContext, script: Option<Script>) {
661 let script = match script {
663 Some(Script::Classic(script)) if script.record.is_ok() => Script::Classic(script),
664 Some(Script::Module(module_tree))
665 if module_tree.get_rethrow_error().borrow().is_none() =>
666 {
667 Script::Module(module_tree)
668 },
669 _ => {
670 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
673 dedicated.forward_simple_error_at_worker();
674 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
675 shared.forward_simple_error_at_worker();
676 }
677
678 return;
681 },
682 };
683
684 unsafe {
685 js::rust::wrappers2::JS_AddInterruptCallback(cx, Some(interrupt_callback));
687 }
688
689 if self.is_closing() {
690 return;
691 }
692
693 {
694 let mut realm = enter_auto_realm(cx, self);
695 let cx = &mut realm.current_realm();
696 define_all_exposed_interfaces(cx, self.upcast());
697 self.execution_ready.store(true, Ordering::Relaxed);
699 match script {
700 Script::Classic(script) => {
701 _ = self.globalscope.run_a_classic_script(
702 cx,
703 script,
704 RethrowErrors::No,
705 None, );
707 },
708 Script::Module(module_tree) => {
709 self.globalscope.run_a_module_script(cx, module_tree, false);
710 },
711 _ => unreachable!(),
712 }
713 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
714 dedicated.fire_queued_messages(cx);
715 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
716 shared.enable_outside_port_message_queue();
718 shared.fire_pending_connect(cx);
721 }
722 }
723 }
724
725 pub(crate) fn process_response_for_workerscope(
728 &self,
729 metadata: &Metadata,
730 policy_container: &PolicyContainer,
731 ) {
732 self.set_url(metadata.final_url.clone());
734
735 self.globalscope
737 .set_creation_url(metadata.final_url.clone());
738
739 self.initialize_policy_container_for_worker_global_scope(metadata, policy_container);
741 self.set_endpoints_list(ReportingEndpoint::parse_reporting_endpoints_header(
742 &metadata.final_url.clone(),
743 &metadata.headers,
744 ));
745 }
746
747 pub(crate) fn origin(&self) -> MutableOrigin {
748 self.origin.clone()
749 }
750}
751
752impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
753 fn Self_(&self) -> DomRoot<WorkerGlobalScope> {
755 DomRoot::from_ref(self)
756 }
757
758 fn IndexedDB(&self, cx: &mut JSContext) -> DomRoot<IDBFactory> {
760 self.upcast::<GlobalScope>().ensure_indexeddb_factory(cx)
761 }
762
763 fn Location(&self, cx: &mut JSContext) -> DomRoot<WorkerLocation> {
765 self.location
766 .or_init(|| WorkerLocation::new(cx, self, self.worker_url.borrow().clone()))
767 }
768
769 fn ImportScripts(
771 &self,
772 cx: &mut JSContext,
773 url_strings: Vec<TrustedScriptURLOrUSVString>,
774 ) -> ErrorResult {
775 if self.worker_type == WorkerType::Module {
778 return Err(Error::Type(
779 c"importScripts() is not allowed in module workers".to_owned(),
780 ));
781 }
782
783 let mut urls = Vec::with_capacity(url_strings.len());
785 for url in url_strings {
787 let url = TrustedScriptURL::get_trusted_type_compliant_string(
791 cx,
792 self.upcast::<GlobalScope>(),
793 url,
794 "WorkerGlobalScope importScripts",
795 )?;
796 let url = self.worker_url.borrow().join(&url.str());
797 match url {
798 Ok(url) => urls.push(url),
799 Err(_) => return Err(Error::Syntax(None)),
800 };
801 }
802
803 for url in urls {
804 let global_scope = self.upcast::<GlobalScope>();
805 let request = RequestBuilder::new(
806 global_scope.webview_id(),
807 UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
808 global_scope.get_referrer(),
809 )
810 .destination(Destination::Script)
811 .credentials_mode(CredentialsMode::Include)
812 .parser_metadata(ParserMetadata::NotParserInserted)
813 .use_url_credentials(true)
814 .with_global_scope(global_scope);
815
816 let (url, bytes, muted_errors) = match load_whole_resource(
818 request,
819 &global_scope.resource_threads().sender(),
820 global_scope,
821 &WorkerCspProcessor {
822 global_scope: DomRoot::from_ref(global_scope),
823 },
824 cx,
825 ) {
826 Err(_) => return Err(Error::Network(None)),
827 Ok((metadata, bytes, muted_errors)) => {
828 if !metadata.status.is_success() {
830 return Err(Error::Network(None));
831 }
832
833 let not_a_javascript_mime_type =
835 !metadata.content_type.clone().is_some_and(|ct| {
836 let mime: Mime = ct.into_inner().into();
837 SCRIPT_JS_MIMES.contains(&mime.essence_str())
838 });
839 if not_a_javascript_mime_type {
840 return Err(Error::Network(None));
841 }
842
843 (metadata.final_url, bytes, muted_errors)
844 },
845 };
846
847 let (source, _) = UTF_8.decode_with_bom_removal(&bytes);
849
850 let mut script_options = ScriptOptions::External;
856 script_options.set(ScriptOptions::MutedErrors, muted_errors);
857 let script = self.globalscope.create_a_classic_script(
858 cx,
859 source,
860 url,
861 script_options,
862 ScriptFetchOptions::default_classic_script(),
863 Some(IntroductionType::WORKER),
864 1,
865 );
866
867 let result = self.globalscope.run_a_classic_script(
869 cx,
870 script,
871 RethrowErrors::Yes,
872 None, );
874
875 if let Err(error) = result {
876 if self.is_closing() {
877 error!("evaluate_script failed (terminated)");
880 } else {
881 error!("evaluate_script failed");
882 return Err(error);
883 }
884 }
885 }
886
887 Ok(())
888 }
889
890 error_event_handler!(error, GetOnerror, SetOnerror);
892
893 event_handler!(languagechange, GetOnlanguagechange, SetOnlanguagechange);
895
896 event_handler!(offline, GetOnoffline, SetOnoffline);
898
899 event_handler!(online, GetOnonline, SetOnonline);
901
902 event_handler!(
904 rejectionhandled,
905 GetOnrejectionhandled,
906 SetOnrejectionhandled
907 );
908
909 event_handler!(
911 unhandledrejection,
912 GetOnunhandledrejection,
913 SetOnunhandledrejection
914 );
915
916 fn Navigator(&self, cx: &mut JSContext) -> DomRoot<WorkerNavigator> {
918 self.navigator.or_init(|| WorkerNavigator::new(cx, self))
919 }
920
921 #[cfg(feature = "webcrypto")]
923 fn Crypto(&self, cx: &mut JSContext) -> DomRoot<Crypto> {
924 self.crypto
925 .or_init(|| Crypto::new(cx, self.upcast::<GlobalScope>()))
926 }
927
928 fn ReportError(&self, cx: &mut JSContext, error: HandleValue) {
930 self.upcast::<GlobalScope>().report_an_exception(cx, error);
931 }
932
933 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
935 base64_btoa(btoa)
936 }
937
938 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
940 base64_atob(atob)
941 }
942
943 fn SetTimeout(
945 &self,
946 cx: &mut JSContext,
947 callback: TrustedScriptOrStringOrFunction,
948 timeout: i32,
949 args: Vec<HandleValue>,
950 ) -> Fallible<i32> {
951 let callback = match callback {
952 TrustedScriptOrStringOrFunction::String(i) => {
953 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
954 },
955 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
956 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
957 },
958 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
959 };
960 self.upcast::<GlobalScope>().set_timeout_or_interval(
961 cx,
962 callback,
963 args,
964 Duration::from_millis(timeout.max(0) as u64),
965 IsInterval::NonInterval,
966 )
967 }
968
969 fn ClearTimeout(&self, handle: i32) {
971 self.upcast::<GlobalScope>()
972 .clear_timeout_or_interval(handle);
973 }
974
975 fn SetInterval(
977 &self,
978 cx: &mut JSContext,
979 callback: TrustedScriptOrStringOrFunction,
980 timeout: i32,
981 args: Vec<HandleValue>,
982 ) -> Fallible<i32> {
983 let callback = match callback {
984 TrustedScriptOrStringOrFunction::String(i) => {
985 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
986 },
987 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
988 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
989 },
990 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
991 };
992 self.upcast::<GlobalScope>().set_timeout_or_interval(
993 cx,
994 callback,
995 args,
996 Duration::from_millis(timeout.max(0) as u64),
997 IsInterval::Interval,
998 )
999 }
1000
1001 fn Caches(&self, cx: &mut JSContext) -> DomRoot<CacheStorage> {
1003 self.caches
1004 .or_init(|| CacheStorage::new(cx, self.upcast::<GlobalScope>()))
1005 }
1006
1007 fn ClearInterval(&self, handle: i32) {
1009 self.ClearTimeout(handle);
1010 }
1011
1012 fn QueueMicrotask(&self, cx: &mut JSContext, callback: Rc<VoidFunction>) {
1014 self.enqueue_microtask(
1015 cx,
1016 Box::new(UserMicrotask {
1017 callback,
1018 global: Dom::from_ref(&self.globalscope),
1019 }),
1020 );
1021 }
1022
1023 fn CreateImageBitmap(
1025 &self,
1026 realm: &mut CurrentRealm,
1027 image: ImageBitmapSource,
1028 options: &ImageBitmapOptions,
1029 ) -> RootedPromise {
1030 ImageBitmap::create_image_bitmap(self.upcast(), image, 0, 0, None, None, options, realm)
1031 }
1032
1033 fn CreateImageBitmap_(
1035 &self,
1036 realm: &mut CurrentRealm,
1037 image: ImageBitmapSource,
1038 sx: i32,
1039 sy: i32,
1040 sw: i32,
1041 sh: i32,
1042 options: &ImageBitmapOptions,
1043 ) -> RootedPromise {
1044 ImageBitmap::create_image_bitmap(
1045 self.upcast(),
1046 image,
1047 sx,
1048 sy,
1049 Some(sw),
1050 Some(sh),
1051 options,
1052 realm,
1053 )
1054 }
1055
1056 fn Fetch(
1058 &self,
1059 realm: &mut CurrentRealm,
1060 input: RequestOrUSVString,
1061 init: RootedTraceableBox<RequestInit>,
1062 ) -> RootedPromise {
1063 Fetch(self.upcast(), input, init, realm)
1064 }
1065
1066 fn Performance(&self, cx: &mut JSContext) -> DomRoot<Performance> {
1068 self.performance.or_init(|| {
1069 let global_scope = self.upcast::<GlobalScope>();
1070 Performance::new(cx, global_scope, self.navigation_start)
1071 })
1072 }
1073
1074 fn Origin(&self) -> USVString {
1076 USVString(
1077 self.upcast::<GlobalScope>()
1078 .origin()
1079 .immutable()
1080 .ascii_serialization()
1081 .into_owned(),
1082 )
1083 }
1084
1085 fn IsSecureContext(&self) -> bool {
1087 self.upcast::<GlobalScope>().is_secure_context()
1088 }
1089
1090 fn StructuredClone(
1092 &self,
1093 cx: &mut JSContext,
1094 value: HandleValue,
1095 options: RootedTraceableBox<StructuredSerializeOptions>,
1096 retval: MutableHandleValue,
1097 ) -> Fallible<()> {
1098 self.upcast::<GlobalScope>()
1099 .structured_clone(cx, value, options, retval)
1100 }
1101
1102 fn TrustedTypes(&self, cx: &mut JSContext) -> DomRoot<TrustedTypePolicyFactory> {
1104 self.trusted_types.or_init(|| {
1105 let global_scope = self.upcast::<GlobalScope>();
1106 TrustedTypePolicyFactory::new(cx, global_scope)
1107 })
1108 }
1109}
1110
1111impl WorkerGlobalScope {
1112 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
1113 let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
1114 if let Some(dedicated) = dedicated {
1115 dedicated.new_script_pair()
1116 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
1117 shared.new_script_pair()
1118 } else {
1119 panic!("need to implement a sender for ServiceWorker")
1120 }
1121 }
1122
1123 pub(crate) fn process_event(&self, msg: CommonScriptMsg, cx: &mut JSContext) -> bool {
1127 if self.is_closing() {
1128 return false;
1129 }
1130 match msg {
1131 CommonScriptMsg::Task(_, task, _, _) => task.run_box(cx),
1132 CommonScriptMsg::CollectReports(reports_chan) => {
1133 perform_memory_report(|ops| {
1134 let reports =
1135 get_reports(cx, format!("url({})", self.get_url()), ops, HashSet::new());
1136 reports_chan.send(ProcessReports::new(reports));
1137 });
1138 },
1139 CommonScriptMsg::ReportCspViolations(_, violations) => {
1140 self.upcast::<GlobalScope>()
1141 .report_csp_violations(cx, violations, None, None);
1142 },
1143 }
1144 true
1145 }
1146
1147 pub(crate) fn close(&self) {
1149 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
1154 dedicated.clear_animation_frame_callbacks_and_unregister();
1155 }
1156
1157 self.closing.store(true, Ordering::SeqCst);
1160 self.upcast::<GlobalScope>()
1161 .task_manager()
1162 .cancel_all_tasks_and_ignore_future_tasks();
1163
1164 if let Some(factory) = self.upcast::<GlobalScope>().indexeddb_factory() {
1169 factory.abort_pending_upgrades_and_close_databases();
1170 }
1171 }
1172
1173 pub(crate) fn init_debugger_global(
1174 &self,
1175 debugger_global: &DebuggerGlobalScope,
1176 cx: &mut JSContext,
1177 ) {
1178 let mut realm = enter_auto_realm(cx, self);
1179 let cx = &mut realm.current_realm();
1180
1181 rooted!(&in(cx) let mut wrapped_global: Value);
1184 debugger_global
1185 .reflector()
1186 .to_jsval(cx, wrapped_global.handle_mut());
1187 self.debugger_global.set(*wrapped_global);
1188 }
1189
1190 pub(crate) fn handle_devtools_message(&self, msg: DevtoolScriptControlMsg, cx: &mut JSContext) {
1191 match msg {
1192 DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _wants_updates) => {},
1193 DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, eager, reply) => {
1194 let debugger_global_handle = rooted_heap_handle(self, |this| &this.debugger_global);
1195 let debugger_global =
1196 root_from_handlevalue::<DebuggerGlobalScope>(cx, debugger_global_handle)
1197 .expect("must be a debugger global scope");
1198
1199 debugger_global.fire_eval(
1200 cx,
1201 code.into(),
1202 id,
1203 Some(self.worker_id()),
1204 frame_actor_id,
1205 eager,
1206 reply,
1207 );
1208 },
1209 _ => debug!("got an unusable devtools control message inside the worker!"),
1210 }
1211 }
1212}
1213
1214#[expect(unsafe_code)]
1215unsafe extern "C" fn interrupt_callback(cx: *mut RawJSContext) -> bool {
1216 let mut cx = unsafe { JSContext::from_ptr(std::ptr::NonNull::new(cx).unwrap()) };
1218 let mut realm = CurrentRealm::assert(&mut cx);
1219
1220 let global = GlobalScope::from_current_realm(&mut realm);
1221
1222 let Some(worker) = global.downcast::<WorkerGlobalScope>() else {
1224 return false;
1225 };
1226
1227 !worker.is_closing()
1229}
1230
1231struct WorkerCspProcessor {
1232 global_scope: DomRoot<GlobalScope>,
1233}
1234
1235impl CspViolationsProcessor for WorkerCspProcessor {
1236 fn process_csp_violations(&self, cx: &mut JSContext, violations: Vec<Violation>) {
1237 self.global_scope
1238 .report_csp_violations(cx, violations, None, None);
1239 }
1240}