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