1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::cell::{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::domstring::TracedDOMString;
40use script_bindings::reflector::DomObject;
41use script_bindings::root::rooted_heap_handle;
42use script_bindings::trace::CustomTraceable;
43use servo_base::cross_process_instant::CrossProcessInstant;
44use servo_base::generic_channel::{GenericSend, GenericSender, RoutedReceiver};
45use servo_base::id::{PipelineId, PipelineNamespace, WorkerId};
46#[cfg(feature = "webgl")]
47use servo_canvas_traits::webgl::WebGLChan;
48use servo_constellation_traits::WorkerGlobalScopeInit;
49use servo_url::{MutableOrigin, ServoUrl};
50use style::Atom;
51use timers::TimerScheduler;
52use uuid::Uuid;
53
54use crate::dom::Window;
55use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
56 ImageBitmapOptions, ImageBitmapSource,
57};
58use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
59use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
60use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
61use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
62use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
63use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
64use crate::dom::bindings::codegen::UnionTypes::{
65 RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
66 TrustedScriptURLOrUSVString,
67};
68use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
69use crate::dom::bindings::inheritance::Castable;
70use crate::dom::bindings::refcounted::Trusted;
71use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
72use crate::dom::bindings::str::{DOMString, USVString};
73use crate::dom::bindings::trace::HashMapTracedValues;
74use crate::dom::bindings::utils::define_all_exposed_interfaces;
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::{EventTarget, 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 online: global.online(),
164 }
165}
166
167pub(crate) struct ScriptFetchContext {
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(url: ServoUrl, policy_container: Arc<PolicyContainer>) -> ScriptFetchContext {
176 ScriptFetchContext {
177 response: None,
178 body_bytes: BytesMut::new(),
179 url,
180 policy_container,
181 }
182 }
183}
184
185impl FetchResponseListener for ScriptFetchContext {
186 fn process_request_body(&mut self, _: &GlobalScope, _request_id: RequestId) {}
187
188 fn process_response(
189 &mut self,
190 _: &mut JSContext,
191 _: &GlobalScope,
192 _request_id: RequestId,
193 metadata: Result<FetchMetadata, NetworkError>,
194 ) {
195 self.response = metadata.ok().map(Into::into);
196 }
197
198 fn process_response_chunk(
199 &mut self,
200 _: &mut JSContext,
201 _: &GlobalScope,
202 _: RequestId,
203 chunk: Bytes,
204 ) {
205 self.body_bytes.extend_from_slice(&chunk);
206 }
207
208 fn process_response_eof(
209 mut self,
210 cx: &mut JSContext,
211 global: &GlobalScope,
212 _request_id: RequestId,
213 response: Result<(), NetworkError>,
214 timing: ResourceFetchTiming,
215 ) {
216 let scope = global
217 .downcast::<WorkerGlobalScope>()
218 .expect("Should always be a WorkerGlobalScope here");
219 if response
220 .as_ref()
221 .inspect_err(|e| error!("error loading script {} ({:?})", self.url, e))
222 .is_err() ||
223 self.response.is_none()
224 {
225 scope.on_complete(cx, None);
226 return;
227 }
228 let metadata = self.response.take().unwrap();
229
230 scope.process_response_for_workerscope(&metadata, &self.policy_container);
233
234 if !metadata.status.is_success() {
241 scope.on_complete(cx, None);
243 return;
244 }
245
246 let is_http_scheme = matches!(metadata.final_url.scheme(), "http" | "https");
249 let not_a_javascript_mime_type = !metadata.content_type.is_some_and(|ct| {
251 let mime: Mime = ct.into_inner().into();
252 SCRIPT_JS_MIMES.contains(&mime.essence_str())
253 });
254
255 if is_http_scheme && not_a_javascript_mime_type {
256 scope.on_complete(cx, None);
258 return;
259 }
260
261 let (source, _) = UTF_8.decode_with_bom_removal(&self.body_bytes);
263
264 let script = global.create_a_classic_script(
267 cx,
268 source,
269 scope.worker_url.borrow().clone(),
270 ScriptOptions::External,
271 ScriptFetchOptions::default_classic_script(),
272 Some(IntroductionType::WORKER),
273 1,
274 );
275
276 scope.on_complete(cx, Some(Script::Classic(script)));
278
279 submit_timing(cx, global, &self, &response, &timing);
280 }
281
282 fn process_csp_violations(
283 &mut self,
284 _cx: &mut JSContext,
285 global: &GlobalScope,
286 _request_id: RequestId,
287 violations: Vec<content_security_policy::Violation>,
288 ) {
289 if let Some(worker_scope) = global.downcast::<DedicatedWorkerGlobalScope>() {
290 worker_scope.report_csp_violations(violations);
291 } else if let Some(worker_scope) = global.downcast::<SharedWorkerGlobalScope>() {
292 worker_scope.report_csp_violations(violations);
293 }
294 }
295
296 fn process_content_length(&mut self, _request_id: RequestId, size: usize) {
297 self.body_bytes
298 .reserve(size.saturating_sub(self.body_bytes.len()));
299 }
300}
301
302impl ResourceTimingListener for ScriptFetchContext {
303 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
304 (InitiatorType::Other, self.url.clone())
305 }
306}
307
308#[dom_struct]
310pub(crate) struct WorkerGlobalScope {
311 globalscope: GlobalScope,
312
313 worker_name: TracedDOMString,
314 worker_type: WorkerType,
315
316 #[no_trace]
317 worker_id: WorkerId,
318 #[no_trace]
319 worker_url: DomRefCell<ServoUrl>,
320 #[conditional_malloc_size_of]
321 closing: Arc<AtomicBool>,
322 execution_ready: AtomicBool,
323 #[ignore_malloc_size_of = "Defined in js"]
324 runtime: DomRefCell<Option<Runtime>>,
325 location: MutNullableDom<WorkerLocation>,
326 navigator: MutNullableDom<WorkerNavigator>,
327 crypto: MutNullableDom<Crypto>,
328 #[no_trace]
329 #[conditional_malloc_size_of]
331 policy_container: DomRefCell<Arc<PolicyContainer>>,
332
333 #[no_trace]
334 #[cfg(feature = "devtools")]
337 _devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
338
339 #[no_trace]
340 #[cfg(feature = "devtools")]
342 devtools_receiver: Option<RoutedReceiver<DevtoolScriptControlMsg>>,
343
344 #[no_trace]
345 navigation_start: CrossProcessInstant,
346 performance: MutNullableDom<Performance>,
347 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
348
349 #[no_trace]
352 timer_scheduler: RefCell<TimerScheduler>,
353
354 timers: OnceCell<OneshotTimers>,
357
358 #[no_trace]
359 insecure_requests_policy: InsecureRequestsPolicy,
360
361 reporting_observer_list: DomRefCell<Vec<Dom<ReportingObserver>>>,
363
364 report_list: DomRefCell<Vec<Report>>,
366
367 #[no_trace]
369 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
370
371 #[ignore_malloc_size_of = "Measured by the JS engine"]
376 debugger_global: Heap<Value>,
377
378 #[no_trace]
379 pipeline_id: PipelineId,
380
381 caches: MutNullableDom<CacheStorage>,
383
384 #[conditional_malloc_size_of]
386 task_manager: Rc<TaskManager>,
387
388 #[no_trace]
389 origin: MutableOrigin,
390
391 #[conditional_malloc_size_of]
393 #[no_trace]
394 font_context: Arc<FontContext>,
395
396 #[ignore_malloc_size_of = "mozjs"]
399 module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
400
401 online: Cell<bool>,
403}
404
405impl WorkerGlobalScope {
406 #[allow(clippy::too_many_arguments)]
407 pub(crate) fn new_inherited(
408 init: WorkerGlobalScopeInit,
409 worker_name: DOMString,
410 worker_type: WorkerType,
411 worker_url: ServoUrl,
412 runtime: Runtime,
413 devtools_receiver: Option<RoutedReceiver<WorkerDevtoolsControlMsg>>,
414 closing: Arc<AtomicBool>,
415 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
416 insecure_requests_policy: InsecureRequestsPolicy,
417 font_context: Arc<FontContext>,
418 event_loop_sender: Option<ScriptEventLoopSender>,
419 ) -> Self {
420 PipelineNamespace::auto_install();
422
423 #[cfg(feature = "devtools")]
424 let devtools_receiver = init.from_devtools_sender.as_ref().and(devtools_receiver);
425
426 Self {
427 globalscope: GlobalScope::new_inherited(
428 #[cfg(feature = "devtools")]
429 init.to_devtools_sender,
430 init.mem_profiler_chan,
431 init.time_profiler_chan,
432 init.script_to_constellation_chan,
433 init.script_to_embedder_chan,
434 init.resource_threads,
435 init.storage_threads,
436 worker_url.clone(),
437 None,
438 #[cfg(feature = "webgpu")]
439 gpu_id_hub,
440 init.inherited_secure_context,
441 init.unminify_js,
442 ),
443 caches: Default::default(),
444 worker_id: init.worker_id,
445 worker_name: worker_name.traced(),
446 worker_type,
447 worker_url: DomRefCell::new(worker_url),
448 closing: closing.clone(),
449 execution_ready: AtomicBool::new(false),
450 runtime: DomRefCell::new(Some(runtime)),
451 location: Default::default(),
452 navigator: Default::default(),
453 crypto: Default::default(),
454 policy_container: Default::default(),
455 #[cfg(feature = "devtools")]
456 devtools_receiver,
457 #[cfg(feature = "devtools")]
458 _devtools_sender: init.from_devtools_sender,
459 navigation_start: CrossProcessInstant::now(),
460 performance: Default::default(),
461 timer_scheduler: RefCell::default(),
462 timers: Default::default(),
463 insecure_requests_policy,
464 trusted_types: Default::default(),
465 reporting_observer_list: Default::default(),
466 report_list: Default::default(),
467 endpoints_list: Default::default(),
468 debugger_global: Default::default(),
469 pipeline_id: init.pipeline_id,
470 task_manager: Rc::new(TaskManager::new(
471 event_loop_sender,
472 init.pipeline_id,
473 Some(TaskCanceller { cancelled: closing }),
474 )),
475 origin: MutableOrigin::new(init.origin),
476 font_context,
477 module_map: Default::default(),
478 online: init.online.into(),
479 }
480 }
481
482 pub(crate) fn online(&self) -> bool {
483 self.online.get()
484 }
485
486 pub(crate) fn module_map(
487 &self,
488 ) -> &DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>> {
489 &self.module_map
490 }
491
492 pub(crate) fn font_context(&self) -> Arc<FontContext> {
493 self.font_context.clone()
494 }
495
496 pub(crate) fn timers(&self) -> &OneshotTimers {
497 self.timers
498 .get_or_init(|| OneshotTimers::new(self.upcast()))
499 }
500
501 pub(crate) fn enqueue_microtask(&self, cx: &JSContext, job: Box<dyn MicrotaskRunnable>) {
502 crate::runtime::job_queue::enqueue(cx, job);
503 }
504
505 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut JSContext) {
507 if !self.is_closing() {
509 job_queue_microtask_checkpoint(cx, vec![DomRoot::from_ref(&self.globalscope)]);
510 }
511 }
512
513 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
515 self.insecure_requests_policy
516 }
517
518 pub(crate) fn clear_js_runtime(&self) {
520 self.upcast::<GlobalScope>()
521 .remove_web_messaging_and_dedicated_workers_infra();
522
523 let runtime = self.runtime.borrow_mut().take();
525 drop(runtime);
526 }
527
528 pub(crate) fn runtime_handle(&self) -> ParentRuntime {
529 self.runtime
530 .borrow()
531 .as_ref()
532 .unwrap()
533 .prepare_for_new_child()
534 }
535
536 #[cfg(feature = "devtools")]
537 pub(crate) fn devtools_receiver(&self) -> Option<&RoutedReceiver<DevtoolScriptControlMsg>> {
538 self.devtools_receiver.as_ref()
539 }
540
541 pub(crate) fn is_closing(&self) -> bool {
542 self.closing.load(Ordering::SeqCst)
543 }
544
545 pub(crate) fn is_execution_ready(&self) -> bool {
546 self.execution_ready.load(Ordering::Relaxed)
547 }
548
549 pub(crate) fn get_url(&self) -> Ref<'_, ServoUrl> {
550 self.worker_url.borrow()
551 }
552
553 pub(crate) fn set_url(&self, url: ServoUrl) {
554 *self.worker_url.borrow_mut() = url;
555 }
556
557 pub(crate) fn worker_name(&self) -> DOMString {
558 self.worker_name.clone().root()
559 }
560
561 pub(crate) fn worker_id(&self) -> WorkerId {
562 self.worker_id
563 }
564
565 pub(crate) fn pipeline_id(&self) -> PipelineId {
566 self.pipeline_id
567 }
568
569 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
570 self.task_manager.clone()
571 }
572
573 pub(crate) fn policy_container(&self) -> Ref<'_, Arc<PolicyContainer>> {
574 self.policy_container.borrow()
575 }
576
577 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
578 Arc::make_mut(&mut *self.policy_container.borrow_mut()).set_csp_list(csp_list);
579 }
580
581 pub(crate) fn set_referrer_policy(&self, referrer_policy: ReferrerPolicy) {
582 Arc::make_mut(&mut *self.policy_container.borrow_mut())
583 .set_referrer_policy(referrer_policy);
584 }
585
586 pub(crate) fn append_reporting_observer(&self, reporting_observer: &ReportingObserver) {
587 self.reporting_observer_list
588 .borrow_mut()
589 .push(Dom::from_ref(reporting_observer));
590 }
591
592 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
593 if let Some(index) = self
594 .reporting_observer_list
595 .borrow()
596 .iter()
597 .position(|observer| &**observer == reporting_observer)
598 {
599 self.reporting_observer_list.borrow_mut().remove(index);
600 }
601 }
602
603 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
604 self.reporting_observer_list
605 .borrow()
606 .iter()
607 .map(|observer| DomRoot::from_ref(&**observer))
608 .collect()
609 }
610
611 pub(crate) fn append_report(&self, report: Report) {
612 self.report_list.borrow_mut().push(report);
613 let trusted_worker = Trusted::new(self);
614 self.upcast::<GlobalScope>()
615 .task_manager()
616 .dom_manipulation_task_source()
617 .queue(task!(send_to_reporting_endpoints: move || {
618 let worker = trusted_worker.root();
619 let reports = std::mem::take(&mut *worker.report_list.borrow_mut());
620 worker.upcast::<GlobalScope>().send_reports_to_endpoints(
621 reports,
622 worker.endpoints_list.borrow().clone(),
623 );
624 }));
625 }
626
627 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
628 self.report_list.borrow().clone()
629 }
630
631 pub(crate) fn set_endpoints_list(&self, endpoints: Option<Vec<ReportingEndpoint>>) {
632 if let Some(endpoints) = endpoints {
633 *self.endpoints_list.borrow_mut() = endpoints;
634 }
635 }
636
637 pub(crate) fn timer_scheduler(&self) -> RefMut<'_, TimerScheduler> {
639 self.timer_scheduler.borrow_mut()
640 }
641
642 fn initialize_policy_container_for_worker_global_scope(
645 &self,
646 metadata: &Metadata,
647 parent_policy_container: &PolicyContainer,
648 ) {
649 if metadata.final_url.is_local_scheme() {
654 self.set_csp_list(parent_policy_container.csp_list.clone());
660 self.set_referrer_policy(parent_policy_container.get_referrer_policy());
661 return;
662 }
663 self.set_csp_list(parse_csp_list_from_metadata(&metadata.headers));
665 let referrer_policy = metadata
668 .headers
669 .as_ref()
670 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
671 .into();
672 self.set_referrer_policy(referrer_policy);
673 }
674
675 #[expect(unsafe_code)]
677 pub(crate) fn on_complete(&self, cx: &mut JSContext, script: Option<Script>) {
678 let script = match script {
680 Some(Script::Classic(script)) if !script.has_rethrow_error() => Script::Classic(script),
681 Some(Script::Module(module)) if !module.has_rethrow_error() => Script::Module(module),
682 _ => {
683 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
686 dedicated.forward_simple_error_at_worker();
687 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
688 shared.forward_simple_error_at_worker();
689 }
690
691 return;
694 },
695 };
696
697 unsafe {
698 js::rust::wrappers2::JS_AddInterruptCallback(cx, Some(interrupt_callback));
700 }
701
702 if self.is_closing() {
703 return;
704 }
705
706 {
707 let mut realm = enter_auto_realm(cx, self);
708 let cx = &mut realm.current_realm();
709 define_all_exposed_interfaces(cx, self.upcast());
710 self.execution_ready.store(true, Ordering::Relaxed);
712 match script {
713 Script::Classic(script) => {
714 _ = self.globalscope.run_a_classic_script(
715 cx,
716 script,
717 RethrowErrors::No,
718 None, );
720 },
721 Script::Module(module_tree) => {
722 self.globalscope.run_a_module_script(cx, module_tree, false);
723 },
724 _ => unreachable!(),
725 }
726 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
727 dedicated.fire_queued_messages(cx);
728 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
729 shared.enable_outside_port_message_queue();
731 shared.fire_pending_connect(cx);
734 }
735 }
736 }
737
738 pub(crate) fn process_response_for_workerscope(
741 &self,
742 metadata: &Metadata,
743 policy_container: &PolicyContainer,
744 ) {
745 self.set_url(metadata.final_url.clone());
747
748 self.globalscope
750 .set_creation_url(metadata.final_url.clone());
751
752 self.initialize_policy_container_for_worker_global_scope(metadata, policy_container);
754 self.set_endpoints_list(ReportingEndpoint::parse_reporting_endpoints_header(
755 &metadata.final_url,
756 &metadata.headers,
757 ));
758 }
759
760 pub(crate) fn origin(&self) -> MutableOrigin {
761 self.origin.clone()
762 }
763
764 pub(crate) fn set_network_online_state(&self, online: bool) -> bool {
767 let previously_online = self.online.replace(online);
768 if previously_online == online {
769 return false;
770 }
771
772 let event_name = Atom::from(if online { "online" } else { "offline" });
773 self.globalscope
774 .task_manager()
775 .networking_task_source()
776 .queue_simple_event(self.globalscope.upcast::<EventTarget>(), event_name);
777 true
778 }
779}
780
781impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
782 fn Self_(&self) -> DomRoot<WorkerGlobalScope> {
784 DomRoot::from_ref(self)
785 }
786
787 fn IndexedDB(&self, cx: &mut JSContext) -> DomRoot<IDBFactory> {
789 self.upcast::<GlobalScope>().ensure_indexeddb_factory(cx)
790 }
791
792 fn Location(&self, cx: &mut JSContext) -> DomRoot<WorkerLocation> {
794 self.location
795 .or_init(|| WorkerLocation::new(cx, self, self.worker_url.borrow().clone()))
796 }
797
798 fn ImportScripts(
800 &self,
801 cx: &mut JSContext,
802 url_strings: Vec<TrustedScriptURLOrUSVString>,
803 ) -> ErrorResult {
804 if self.worker_type == WorkerType::Module {
807 return Err(Error::Type(
808 c"importScripts() is not allowed in module workers".to_owned(),
809 ));
810 }
811
812 let mut urls = Vec::with_capacity(url_strings.len());
814 for url in url_strings {
816 let url = TrustedScriptURL::get_trusted_type_compliant_string(
820 cx,
821 self.upcast::<GlobalScope>(),
822 url,
823 "WorkerGlobalScope importScripts",
824 )?;
825 let url = self.worker_url.borrow().join(&url.str());
826 match url {
827 Ok(url) => urls.push(url),
828 Err(_) => return Err(Error::Syntax(None)),
829 };
830 }
831
832 for url in urls {
833 let global_scope = self.upcast::<GlobalScope>();
834 let request = RequestBuilder::new(
835 global_scope.webview_id(),
836 UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
837 global_scope.get_referrer(),
838 )
839 .destination(Destination::Script)
840 .credentials_mode(CredentialsMode::Include)
841 .parser_metadata(ParserMetadata::NotParserInserted)
842 .use_url_credentials(true)
843 .with_global_scope(global_scope);
844
845 let (url, bytes, muted_errors) = match load_whole_resource(
847 request,
848 &global_scope.resource_threads().sender(),
849 global_scope,
850 &WorkerCspProcessor {
851 global_scope: DomRoot::from_ref(global_scope),
852 },
853 cx,
854 ) {
855 Err(_) => return Err(Error::Network(None)),
856 Ok((metadata, bytes, muted_errors)) => {
857 if !metadata.status.is_success() {
859 return Err(Error::Network(None));
860 }
861
862 let not_a_javascript_mime_type =
864 !metadata.content_type.clone().is_some_and(|ct| {
865 let mime: Mime = ct.into_inner().into();
866 SCRIPT_JS_MIMES.contains(&mime.essence_str())
867 });
868 if not_a_javascript_mime_type {
869 return Err(Error::Network(None));
870 }
871
872 (metadata.final_url, bytes, muted_errors)
873 },
874 };
875
876 let (source, _) = UTF_8.decode_with_bom_removal(&bytes);
878
879 let mut script_options = ScriptOptions::External;
885 script_options.set(ScriptOptions::MutedErrors, muted_errors);
886 let script = self.globalscope.create_a_classic_script(
887 cx,
888 source,
889 url,
890 script_options,
891 ScriptFetchOptions::default_classic_script(),
892 Some(IntroductionType::WORKER),
893 1,
894 );
895
896 let result = self.globalscope.run_a_classic_script(
898 cx,
899 script,
900 RethrowErrors::Yes,
901 None, );
903
904 if let Err(error) = result {
905 if self.is_closing() {
906 error!("evaluate_script failed (terminated)");
909 } else {
910 error!("evaluate_script failed");
911 return Err(error);
912 }
913 }
914 }
915
916 Ok(())
917 }
918
919 error_event_handler!(error, GetOnerror, SetOnerror);
921
922 event_handler!(languagechange, GetOnlanguagechange, SetOnlanguagechange);
924
925 event_handler!(offline, GetOnoffline, SetOnoffline);
927
928 event_handler!(online, GetOnonline, SetOnonline);
930
931 event_handler!(
933 rejectionhandled,
934 GetOnrejectionhandled,
935 SetOnrejectionhandled
936 );
937
938 event_handler!(
940 unhandledrejection,
941 GetOnunhandledrejection,
942 SetOnunhandledrejection
943 );
944
945 fn Navigator(&self, cx: &mut JSContext) -> DomRoot<WorkerNavigator> {
947 self.navigator.or_init(|| WorkerNavigator::new(cx, self))
948 }
949
950 fn Crypto(&self, cx: &mut JSContext) -> DomRoot<Crypto> {
952 self.crypto
953 .or_init(|| Crypto::new(cx, self.upcast::<GlobalScope>()))
954 }
955
956 fn ReportError(&self, cx: &mut JSContext, error: HandleValue) {
958 self.upcast::<GlobalScope>().report_an_exception(cx, error);
959 }
960
961 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
963 base64_btoa(btoa)
964 }
965
966 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
968 base64_atob(atob)
969 }
970
971 fn SetTimeout(
973 &self,
974 cx: &mut JSContext,
975 callback: TrustedScriptOrStringOrFunction,
976 timeout: i32,
977 args: Vec<HandleValue>,
978 ) -> Fallible<i32> {
979 let callback = match callback {
980 TrustedScriptOrStringOrFunction::String(i) => {
981 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
982 },
983 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
984 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
985 },
986 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
987 };
988 self.upcast::<GlobalScope>().set_timeout_or_interval(
989 cx,
990 callback,
991 args,
992 Duration::from_millis(timeout.max(0) as u64),
993 IsInterval::NonInterval,
994 )
995 }
996
997 fn ClearTimeout(&self, handle: i32) {
999 self.upcast::<GlobalScope>()
1000 .clear_timeout_or_interval(handle);
1001 }
1002
1003 fn SetInterval(
1005 &self,
1006 cx: &mut JSContext,
1007 callback: TrustedScriptOrStringOrFunction,
1008 timeout: i32,
1009 args: Vec<HandleValue>,
1010 ) -> Fallible<i32> {
1011 let callback = match callback {
1012 TrustedScriptOrStringOrFunction::String(i) => {
1013 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
1014 },
1015 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
1016 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
1017 },
1018 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
1019 };
1020 self.upcast::<GlobalScope>().set_timeout_or_interval(
1021 cx,
1022 callback,
1023 args,
1024 Duration::from_millis(timeout.max(0) as u64),
1025 IsInterval::Interval,
1026 )
1027 }
1028
1029 fn Caches(&self, cx: &mut JSContext) -> DomRoot<CacheStorage> {
1031 self.caches
1032 .or_init(|| CacheStorage::new(cx, self.upcast::<GlobalScope>()))
1033 }
1034
1035 fn ClearInterval(&self, handle: i32) {
1037 self.ClearTimeout(handle);
1038 }
1039
1040 fn QueueMicrotask(&self, cx: &mut JSContext, callback: RootedCallback<VoidFunction>) {
1042 self.enqueue_microtask(
1043 cx,
1044 Box::new(UserMicrotask {
1045 callback: callback.to_traced(),
1046 global: Dom::from_ref(&self.globalscope),
1047 }),
1048 );
1049 }
1050
1051 fn CreateImageBitmap(
1053 &self,
1054 realm: &mut CurrentRealm,
1055 image: ImageBitmapSource,
1056 options: &ImageBitmapOptions,
1057 ) -> RootedPromise {
1058 ImageBitmap::create_image_bitmap(self.upcast(), image, 0, 0, None, None, options, realm)
1059 }
1060
1061 fn CreateImageBitmap_(
1063 &self,
1064 realm: &mut CurrentRealm,
1065 image: ImageBitmapSource,
1066 sx: i32,
1067 sy: i32,
1068 sw: i32,
1069 sh: i32,
1070 options: &ImageBitmapOptions,
1071 ) -> RootedPromise {
1072 ImageBitmap::create_image_bitmap(
1073 self.upcast(),
1074 image,
1075 sx,
1076 sy,
1077 Some(sw),
1078 Some(sh),
1079 options,
1080 realm,
1081 )
1082 }
1083
1084 fn Fetch(
1086 &self,
1087 realm: &mut CurrentRealm,
1088 input: RequestOrUSVString,
1089 init: &RequestInit,
1090 ) -> RootedPromise {
1091 Fetch(self.upcast(), input, init, realm)
1092 }
1093
1094 fn Performance(&self, cx: &mut JSContext) -> DomRoot<Performance> {
1096 self.performance.or_init(|| {
1097 let global_scope = self.upcast::<GlobalScope>();
1098 Performance::new(cx, global_scope, self.navigation_start)
1099 })
1100 }
1101
1102 fn Origin(&self) -> USVString {
1104 USVString(
1105 self.upcast::<GlobalScope>()
1106 .origin()
1107 .immutable()
1108 .ascii_serialization()
1109 .into_owned(),
1110 )
1111 }
1112
1113 fn IsSecureContext(&self) -> bool {
1115 self.upcast::<GlobalScope>().is_secure_context()
1116 }
1117
1118 fn StructuredClone(
1120 &self,
1121 cx: &mut JSContext,
1122 value: HandleValue,
1123 options: &StructuredSerializeOptions,
1124 retval: MutableHandleValue,
1125 ) -> Fallible<()> {
1126 self.upcast::<GlobalScope>()
1127 .structured_clone(cx, value, options, retval)
1128 }
1129
1130 fn TrustedTypes(&self, cx: &mut JSContext) -> DomRoot<TrustedTypePolicyFactory> {
1132 self.trusted_types.or_init(|| {
1133 let global_scope = self.upcast::<GlobalScope>();
1134 TrustedTypePolicyFactory::new(cx, global_scope)
1135 })
1136 }
1137}
1138
1139impl WorkerGlobalScope {
1140 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
1141 let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
1142 if let Some(dedicated) = dedicated {
1143 dedicated.new_script_pair()
1144 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
1145 shared.new_script_pair()
1146 } else {
1147 panic!("need to implement a sender for ServiceWorker")
1148 }
1149 }
1150
1151 pub(crate) fn process_event(&self, msg: CommonScriptMsg, cx: &mut JSContext) -> bool {
1155 if self.is_closing() {
1156 return false;
1157 }
1158
1159 let mut realm = enter_auto_realm(cx, self);
1160 let cx = &mut realm;
1161
1162 match msg {
1163 CommonScriptMsg::Task(_, task, _, _) => task.run_box(cx),
1164 CommonScriptMsg::CollectReports(reports_chan) => {
1165 perform_memory_report(|ops| {
1166 let reports =
1167 get_reports(cx, format!("url({})", self.get_url()), ops, HashSet::new());
1168 reports_chan.send(ProcessReports::new(reports));
1169 });
1170 },
1171 CommonScriptMsg::ReportCspViolations(_, violations) => {
1172 self.upcast::<GlobalScope>()
1173 .report_csp_violations(cx, violations, None, None);
1174 },
1175 }
1176 true
1177 }
1178
1179 pub(crate) fn close(&self) {
1181 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
1186 dedicated.clear_animation_frame_callbacks_and_unregister();
1187 }
1188
1189 self.closing.store(true, Ordering::SeqCst);
1192 self.upcast::<GlobalScope>()
1193 .task_manager()
1194 .cancel_all_tasks_and_ignore_future_tasks();
1195
1196 if let Some(factory) = self.upcast::<GlobalScope>().indexeddb_factory() {
1201 factory.abort_pending_upgrades_and_close_databases();
1202 }
1203 }
1204}
1205
1206#[cfg(feature = "devtools")]
1207impl WorkerGlobalScope {
1208 pub(crate) fn init_debugger_global(
1209 &self,
1210 debugger_global: Option<&DebuggerGlobalScope>,
1211 cx: &mut JSContext,
1212 ) {
1213 let Some(debugger_global) = debugger_global else {
1214 return;
1215 };
1216
1217 let mut realm = enter_auto_realm(cx, self);
1218 let cx = &mut realm.current_realm();
1219
1220 rooted!(&in(cx) let mut wrapped_global: Value);
1223 debugger_global
1224 .reflector()
1225 .to_jsval(cx, wrapped_global.handle_mut());
1226 self.debugger_global.set(*wrapped_global);
1227 }
1228
1229 pub(crate) fn handle_devtools_message(&self, msg: DevtoolScriptControlMsg, cx: &mut JSContext) {
1230 match msg {
1231 DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _wants_updates) => {},
1232 DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, eager, reply) => {
1233 let debugger_global_handle = rooted_heap_handle(self, |this| &this.debugger_global);
1234 let debugger_global =
1235 root_from_handlevalue::<DebuggerGlobalScope>(cx, debugger_global_handle)
1236 .expect("must be a debugger global scope");
1237
1238 debugger_global.fire_eval(
1239 cx,
1240 code.into(),
1241 id,
1242 Some(self.worker_id()),
1243 frame_actor_id,
1244 eager,
1245 reply,
1246 );
1247 },
1248 _ => debug!("got an unusable devtools control message inside the worker!"),
1249 }
1250 }
1251}
1252
1253#[expect(unsafe_code)]
1254unsafe extern "C" fn interrupt_callback(cx: *mut RawJSContext) -> bool {
1255 let mut cx = unsafe { JSContext::from_ptr(std::ptr::NonNull::new(cx).unwrap()) };
1257 let mut realm = CurrentRealm::assert(&mut cx);
1258
1259 let global = GlobalScope::from_current_realm(&mut realm);
1260
1261 let Some(worker) = global.downcast::<WorkerGlobalScope>() else {
1263 return false;
1264 };
1265
1266 !worker.is_closing()
1268}
1269
1270struct WorkerCspProcessor {
1271 global_scope: DomRoot<GlobalScope>,
1272}
1273
1274impl CspViolationsProcessor for WorkerCspProcessor {
1275 fn process_csp_violations(&self, cx: &mut JSContext, violations: Vec<Violation>) {
1276 self.global_scope
1277 .report_csp_violations(cx, violations, None, None);
1278 }
1279}