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