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