1use std::cell::{RefCell, RefMut};
6use std::default::Default;
7use std::rc::Rc;
8use std::sync::Arc;
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::time::Duration;
11
12use content_security_policy::CspList;
13use devtools_traits::{DevtoolScriptControlMsg, WorkerId};
14use dom_struct::dom_struct;
15use encoding_rs::UTF_8;
16use fonts::FontContext;
17use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
18use js::jsapi::{Heap, JSContext as RawJSContext, Value};
19use js::realm::CurrentRealm;
20use js::rust::{HandleValue, MutableHandleValue, ParentRuntime};
21use mime::Mime;
22use net_traits::blob_url_store::UrlWithBlobClaim;
23use net_traits::policy_container::PolicyContainer;
24use net_traits::request::{
25 CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, RequestBuilder, RequestId,
26};
27use net_traits::response::HttpsState;
28use net_traits::{FetchMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming};
29use profile_traits::mem::{ProcessReports, perform_memory_report};
30use script_bindings::conversions::{SafeToJSValConvertible, root_from_handlevalue};
31use script_bindings::reflector::DomObject;
32use script_bindings::root::rooted_heap_handle;
33use servo_base::cross_process_instant::CrossProcessInstant;
34use servo_base::generic_channel::{GenericSend, GenericSender, RoutedReceiver};
35use servo_base::id::{PipelineId, PipelineNamespace};
36use servo_canvas_traits::webgl::WebGLChan;
37use servo_constellation_traits::WorkerGlobalScopeInit;
38use servo_url::{MutableOrigin, ServoUrl};
39use timers::TimerScheduler;
40use uuid::Uuid;
41
42use crate::dom::bindings::cell::{DomRefCell, Ref};
43use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
44 ImageBitmapOptions, ImageBitmapSource,
45};
46use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
47use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
48use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
49use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
50use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
51use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
52use crate::dom::bindings::codegen::UnionTypes::{
53 RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
54 TrustedScriptURLOrUSVString,
55};
56use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
57use crate::dom::bindings::inheritance::Castable;
58use crate::dom::bindings::refcounted::Trusted;
59use crate::dom::bindings::reflector::DomGlobal;
60use crate::dom::bindings::root::{DomRoot, MutNullableDom};
61use crate::dom::bindings::str::{DOMString, USVString};
62use crate::dom::bindings::trace::RootedTraceableBox;
63use crate::dom::bindings::utils::define_all_exposed_interfaces;
64use crate::dom::crypto::Crypto;
65use crate::dom::csp::{GlobalCspReporting, Violation, parse_csp_list_from_metadata};
66use crate::dom::debugger::debuggerglobalscope::DebuggerGlobalScope;
67use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
68use crate::dom::global_scope_script_execution::{ErrorReporting, RethrowErrors};
69use crate::dom::globalscope::GlobalScope;
70use crate::dom::htmlscriptelement::{SCRIPT_JS_MIMES, Script};
71use crate::dom::idbfactory::IDBFactory;
72use crate::dom::performance::performance::Performance;
73use crate::dom::performance::performanceresourcetiming::InitiatorType;
74use crate::dom::promise::Promise;
75use crate::dom::reporting::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
76use crate::dom::reporting::reportingobserver::ReportingObserver;
77use crate::dom::sharedworkerglobalscope::SharedWorkerGlobalScope;
78use crate::dom::trustedtypes::trustedscripturl::TrustedScriptURL;
79use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
80use crate::dom::types::ImageBitmap;
81#[cfg(feature = "webgpu")]
82use crate::dom::webgpu::identityhub::IdentityHub;
83use crate::dom::window::{base64_atob, base64_btoa};
84use crate::dom::workerlocation::WorkerLocation;
85use crate::dom::workernavigator::WorkerNavigator;
86use crate::fetch::{CspViolationsProcessor, Fetch, RequestWithGlobalScope, load_whole_resource};
87use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
88use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask};
89use crate::network_listener::{FetchResponseListener, ResourceTimingListener, submit_timing};
90use crate::realms::{AlreadyInRealm, InRealm, enter_auto_realm};
91use crate::script_module::ScriptFetchOptions;
92use crate::script_runtime::{CanGc, IntroductionType, JSContext, JSContextHelper, Runtime};
93use crate::task::TaskCanceller;
94use crate::timers::{IsInterval, TimerCallback};
95
96pub(crate) fn prepare_workerscope_init(
97 global: &GlobalScope,
98 devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
99 worker_id: Option<WorkerId>,
100 webgl_chan: Option<WebGLChan>,
101) -> WorkerGlobalScopeInit {
102 WorkerGlobalScopeInit {
103 resource_threads: global.resource_threads().clone(),
104 storage_threads: global.storage_threads().clone(),
105 mem_profiler_chan: global.mem_profiler_chan().clone(),
106 to_devtools_sender: global.devtools_chan().cloned(),
107 time_profiler_chan: global.time_profiler_chan().clone(),
108 from_devtools_sender: devtools_sender,
109 script_to_constellation_chan: global.script_to_constellation_chan().clone(),
110 script_to_embedder_chan: global.script_to_embedder_chan().clone(),
111 worker_id: worker_id.unwrap_or_else(|| WorkerId(Uuid::new_v4())),
112 pipeline_id: global.pipeline_id(),
113 origin: global.origin().immutable().clone(),
114 inherited_secure_context: Some(global.is_secure_context()),
115 unminify_js: global.unminify_js(),
116 webgl_chan,
117 }
118}
119
120pub(crate) struct ScriptFetchContext {
121 scope: Trusted<WorkerGlobalScope>,
122 response: Option<Metadata>,
123 body_bytes: Vec<u8>,
124 url: ServoUrl,
125 policy_container: PolicyContainer,
126}
127
128impl ScriptFetchContext {
129 pub(crate) fn new(
130 scope: Trusted<WorkerGlobalScope>,
131 url: ServoUrl,
132 policy_container: PolicyContainer,
133 ) -> ScriptFetchContext {
134 ScriptFetchContext {
135 scope,
136 response: None,
137 body_bytes: Vec::new(),
138 url,
139 policy_container,
140 }
141 }
142}
143
144impl FetchResponseListener for ScriptFetchContext {
145 fn process_request_body(&mut self, _request_id: RequestId) {}
146
147 fn process_response(
148 &mut self,
149 _: &mut js::context::JSContext,
150 _request_id: RequestId,
151 metadata: Result<FetchMetadata, NetworkError>,
152 ) {
153 self.response = metadata.ok().map(|m| match m {
154 FetchMetadata::Unfiltered(m) => m,
155 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
156 });
157 }
158
159 fn process_response_chunk(
160 &mut self,
161 _: &mut js::context::JSContext,
162 _: RequestId,
163 mut chunk: Vec<u8>,
164 ) {
165 self.body_bytes.append(&mut chunk);
166 }
167
168 fn process_response_eof(
169 mut self,
170 cx: &mut js::context::JSContext,
171 _request_id: RequestId,
172 response: Result<(), NetworkError>,
173 timing: ResourceFetchTiming,
174 ) {
175 let scope = self.scope.root();
176
177 if response
178 .as_ref()
179 .inspect_err(|e| error!("error loading script {} ({:?})", self.url, e))
180 .is_err() ||
181 self.response.is_none()
182 {
183 scope.on_complete(cx, None);
184 return;
185 }
186 let metadata = self.response.take().unwrap();
187
188 scope.process_response_for_workerscope(&metadata, &self.policy_container);
191
192 if !metadata.status.is_success() {
199 scope.on_complete(cx, None);
201 return;
202 }
203
204 let is_http_scheme = matches!(metadata.final_url.scheme(), "http" | "https");
207 let not_a_javascript_mime_type = !metadata.content_type.is_some_and(|ct| {
209 let mime: Mime = ct.into_inner().into();
210 SCRIPT_JS_MIMES.contains(&mime.essence_str())
211 });
212
213 if is_http_scheme && not_a_javascript_mime_type {
214 scope.on_complete(cx, None);
216 return;
217 }
218
219 let (source, _) = UTF_8.decode_with_bom_removal(&self.body_bytes);
221
222 let global_scope = scope.upcast::<GlobalScope>();
223
224 let script = global_scope.create_a_classic_script(
227 cx,
228 source,
229 scope.worker_url.borrow().clone(),
230 ScriptFetchOptions::default_classic_script(),
231 ErrorReporting::Unmuted,
232 Some(IntroductionType::WORKER),
233 1,
234 true,
235 );
236
237 scope.on_complete(cx, Some(Script::Classic(script)));
239
240 submit_timing(cx, &self, &response, &timing);
241 }
242
243 fn process_csp_violations(
244 &mut self,
245 _request_id: RequestId,
246 violations: Vec<content_security_policy::Violation>,
247 ) {
248 let scope = self.scope.root();
249
250 if let Some(worker_scope) = scope.downcast::<DedicatedWorkerGlobalScope>() {
251 worker_scope.report_csp_violations(violations);
252 }
253 }
254}
255
256impl ResourceTimingListener for ScriptFetchContext {
257 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
258 (InitiatorType::Other, self.url.clone())
259 }
260
261 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
262 self.scope.root().global()
263 }
264}
265
266#[dom_struct]
268pub(crate) struct WorkerGlobalScope {
269 globalscope: GlobalScope,
270
271 #[conditional_malloc_size_of]
273 microtask_queue: Rc<MicrotaskQueue>,
274
275 worker_name: DOMString,
276 worker_type: WorkerType,
277
278 #[no_trace]
279 worker_id: WorkerId,
280 #[no_trace]
281 worker_url: DomRefCell<ServoUrl>,
282 #[conditional_malloc_size_of]
283 closing: Arc<AtomicBool>,
284 execution_ready: AtomicBool,
285 #[ignore_malloc_size_of = "Defined in js"]
286 runtime: DomRefCell<Option<Runtime>>,
287 location: MutNullableDom<WorkerLocation>,
288 navigator: MutNullableDom<WorkerNavigator>,
289 crypto: MutNullableDom<Crypto>,
290 #[no_trace]
291 policy_container: DomRefCell<PolicyContainer>,
293
294 #[ignore_malloc_size_of = "Defined in base"]
295 #[no_trace]
296 _devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
299
300 #[ignore_malloc_size_of = "Defined in base"]
301 #[no_trace]
302 devtools_receiver: Option<RoutedReceiver<DevtoolScriptControlMsg>>,
304
305 #[no_trace]
306 navigation_start: CrossProcessInstant,
307 performance: MutNullableDom<Performance>,
308 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
309
310 #[no_trace]
313 timer_scheduler: RefCell<TimerScheduler>,
314
315 #[no_trace]
316 insecure_requests_policy: InsecureRequestsPolicy,
317
318 reporting_observer_list: DomRefCell<Vec<DomRoot<ReportingObserver>>>,
320
321 report_list: DomRefCell<Vec<Report>>,
323
324 #[no_trace]
326 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
327
328 #[ignore_malloc_size_of = "Measured by the JS engine"]
333 debugger_global: Heap<Value>,
334}
335
336impl WorkerGlobalScope {
337 #[allow(clippy::too_many_arguments)]
338 pub(crate) fn new_inherited(
339 init: WorkerGlobalScopeInit,
340 worker_name: DOMString,
341 worker_type: WorkerType,
342 worker_url: ServoUrl,
343 runtime: Runtime,
344 devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
345 closing: Arc<AtomicBool>,
346 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
347 insecure_requests_policy: InsecureRequestsPolicy,
348 font_context: Option<Arc<FontContext>>,
349 ) -> Self {
350 PipelineNamespace::auto_install();
352
353 let devtools_receiver = match init.from_devtools_sender {
354 Some(..) => Some(devtools_receiver),
355 None => None,
356 };
357
358 Self {
359 globalscope: GlobalScope::new_inherited(
360 init.pipeline_id,
361 init.to_devtools_sender,
362 init.mem_profiler_chan,
363 init.time_profiler_chan,
364 init.script_to_constellation_chan,
365 init.script_to_embedder_chan,
366 init.resource_threads,
367 init.storage_threads,
368 MutableOrigin::new(init.origin),
369 worker_url.clone(),
370 None,
371 #[cfg(feature = "webgpu")]
372 gpu_id_hub,
373 init.inherited_secure_context,
374 init.unminify_js,
375 font_context,
376 HttpsState::None,
377 ),
378 microtask_queue: runtime.microtask_queue.clone(),
379 worker_id: init.worker_id,
380 worker_name,
381 worker_type,
382 worker_url: DomRefCell::new(worker_url),
383 closing,
384 execution_ready: AtomicBool::new(false),
385 runtime: DomRefCell::new(Some(runtime)),
386 location: Default::default(),
387 navigator: Default::default(),
388 crypto: Default::default(),
389 policy_container: Default::default(),
390 devtools_receiver,
391 _devtools_sender: init.from_devtools_sender,
392 navigation_start: CrossProcessInstant::now(),
393 performance: Default::default(),
394 timer_scheduler: RefCell::default(),
395 insecure_requests_policy,
396 trusted_types: Default::default(),
397 reporting_observer_list: Default::default(),
398 report_list: Default::default(),
399 endpoints_list: Default::default(),
400 debugger_global: Default::default(),
401 }
402 }
403
404 pub(crate) fn enqueue_microtask(&self, job: Microtask) {
405 self.microtask_queue.enqueue(job, GlobalScope::get_cx());
406 }
407
408 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
410 if !self.is_closing() {
412 self.microtask_queue.checkpoint(
413 cx,
414 |_| Some(DomRoot::from_ref(&self.globalscope)),
415 vec![DomRoot::from_ref(&self.globalscope)],
416 );
417 }
418 }
419
420 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
422 self.insecure_requests_policy
423 }
424
425 pub(crate) fn clear_js_runtime(&self) {
427 self.upcast::<GlobalScope>()
428 .remove_web_messaging_and_dedicated_workers_infra();
429
430 let runtime = self.runtime.borrow_mut().take();
432 drop(runtime);
433 }
434
435 pub(crate) fn runtime_handle(&self) -> ParentRuntime {
436 self.runtime
437 .borrow()
438 .as_ref()
439 .unwrap()
440 .prepare_for_new_child()
441 }
442
443 pub(crate) fn devtools_receiver(&self) -> Option<&RoutedReceiver<DevtoolScriptControlMsg>> {
444 self.devtools_receiver.as_ref()
445 }
446
447 pub(crate) fn is_closing(&self) -> bool {
448 self.closing.load(Ordering::SeqCst)
449 }
450
451 pub(crate) fn is_execution_ready(&self) -> bool {
452 self.execution_ready.load(Ordering::Relaxed)
453 }
454
455 pub(crate) fn get_url(&self) -> Ref<'_, ServoUrl> {
456 self.worker_url.borrow()
457 }
458
459 pub(crate) fn set_url(&self, url: ServoUrl) {
460 *self.worker_url.borrow_mut() = url;
461 }
462
463 pub(crate) fn worker_name(&self) -> DOMString {
464 self.worker_name.clone()
465 }
466
467 pub(crate) fn worker_id(&self) -> WorkerId {
468 self.worker_id
469 }
470
471 pub(crate) fn pipeline_id(&self) -> PipelineId {
472 self.globalscope.pipeline_id()
473 }
474
475 pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
476 self.policy_container.borrow()
477 }
478
479 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
480 self.policy_container.borrow_mut().set_csp_list(csp_list);
481 }
482
483 pub(crate) fn set_referrer_policy(&self, referrer_policy: ReferrerPolicy) {
484 self.policy_container
485 .borrow_mut()
486 .set_referrer_policy(referrer_policy);
487 }
488
489 pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
490 self.reporting_observer_list
491 .borrow_mut()
492 .push(reporting_observer);
493 }
494
495 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
496 if let Some(index) = self
497 .reporting_observer_list
498 .borrow()
499 .iter()
500 .position(|observer| &**observer == reporting_observer)
501 {
502 self.reporting_observer_list.borrow_mut().remove(index);
503 }
504 }
505
506 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
507 self.reporting_observer_list.borrow().clone()
508 }
509
510 pub(crate) fn append_report(&self, report: Report) {
511 self.report_list.borrow_mut().push(report);
512 let trusted_worker = Trusted::new(self);
513 self.upcast::<GlobalScope>()
514 .task_manager()
515 .dom_manipulation_task_source()
516 .queue(task!(send_to_reporting_endpoints: move || {
517 let worker = trusted_worker.root();
518 let reports = std::mem::take(&mut *worker.report_list.borrow_mut());
519 worker.upcast::<GlobalScope>().send_reports_to_endpoints(
520 reports,
521 worker.endpoints_list.borrow().clone(),
522 );
523 }));
524 }
525
526 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
527 self.report_list.borrow().clone()
528 }
529
530 pub(crate) fn set_endpoints_list(&self, endpoints: Option<Vec<ReportingEndpoint>>) {
531 if let Some(endpoints) = endpoints {
532 *self.endpoints_list.borrow_mut() = endpoints;
533 }
534 }
535
536 pub(crate) fn timer_scheduler(&self) -> RefMut<'_, TimerScheduler> {
538 self.timer_scheduler.borrow_mut()
539 }
540
541 pub(crate) fn shared_task_canceller(&self) -> TaskCanceller {
544 TaskCanceller {
545 cancelled: self.closing.clone(),
546 }
547 }
548
549 fn initialize_policy_container_for_worker_global_scope(
552 &self,
553 metadata: &Metadata,
554 parent_policy_container: &PolicyContainer,
555 ) {
556 if metadata.final_url.is_local_scheme() {
561 self.set_csp_list(parent_policy_container.csp_list.clone());
567 self.set_referrer_policy(parent_policy_container.get_referrer_policy());
568 return;
569 }
570 self.set_csp_list(parse_csp_list_from_metadata(&metadata.headers));
572 let referrer_policy = metadata
575 .headers
576 .as_ref()
577 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
578 .into();
579 self.set_referrer_policy(referrer_policy);
580 }
581
582 #[expect(unsafe_code)]
584 pub(crate) fn on_complete(&self, cx: &mut js::context::JSContext, script: Option<Script>) {
585 let script = match script {
587 Some(Script::Classic(script)) if script.record.is_ok() => Script::Classic(script),
588 Some(Script::Module(module_tree))
589 if module_tree.get_rethrow_error().borrow().is_none() =>
590 {
591 Script::Module(module_tree)
592 },
593 _ => {
594 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
597 dedicated.forward_simple_error_at_worker();
598 }
599
600 return;
603 },
604 };
605
606 unsafe {
607 js::rust::wrappers2::JS_AddInterruptCallback(cx, Some(interrupt_callback));
609 }
610
611 if self.is_closing() {
612 return;
613 }
614
615 {
616 let mut realm = enter_auto_realm(cx, self);
617 let cx = &mut realm.current_realm();
618 define_all_exposed_interfaces(cx, self.upcast());
619 self.execution_ready.store(true, Ordering::Relaxed);
620 match script {
621 Script::Classic(script) => {
622 _ = self
623 .globalscope
624 .run_a_classic_script(cx, script, RethrowErrors::No);
625 },
626 Script::Module(module_tree) => {
627 self.globalscope.run_a_module_script(cx, module_tree, false);
628 },
629 _ => unreachable!(),
630 }
631 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
632 dedicated.fire_queued_messages(cx);
633 }
634 }
635 }
636
637 pub(crate) fn process_response_for_workerscope(
640 &self,
641 metadata: &Metadata,
642 policy_container: &PolicyContainer,
643 ) {
644 self.set_url(metadata.final_url.clone());
646
647 self.globalscope
649 .set_creation_url(metadata.final_url.clone());
650
651 self.initialize_policy_container_for_worker_global_scope(metadata, policy_container);
653 self.set_endpoints_list(ReportingEndpoint::parse_reporting_endpoints_header(
654 &metadata.final_url.clone(),
655 &metadata.headers,
656 ));
657 self.globalscope.set_https_state(metadata.https_state);
658 }
659}
660
661impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
662 fn Self_(&self) -> DomRoot<WorkerGlobalScope> {
664 DomRoot::from_ref(self)
665 }
666
667 fn IndexedDB(&self) -> DomRoot<IDBFactory> {
669 self.upcast::<GlobalScope>().get_indexeddb()
670 }
671
672 fn Location(&self) -> DomRoot<WorkerLocation> {
674 self.location.or_init(|| {
675 WorkerLocation::new(
676 self,
677 self.worker_url.borrow().clone(),
678 CanGc::deprecated_note(),
679 )
680 })
681 }
682
683 fn ImportScripts(
685 &self,
686 cx: &mut js::context::JSContext,
687 url_strings: Vec<TrustedScriptURLOrUSVString>,
688 ) -> ErrorResult {
689 if self.worker_type == WorkerType::Module {
692 return Err(Error::Type(
693 c"importScripts() is not allowed in module workers".to_owned(),
694 ));
695 }
696
697 let mut urls = Vec::with_capacity(url_strings.len());
699 for url in url_strings {
701 let url = TrustedScriptURL::get_trusted_type_compliant_string(
705 cx,
706 self.upcast::<GlobalScope>(),
707 url,
708 "WorkerGlobalScope importScripts",
709 )?;
710 let url = self.worker_url.borrow().join(&url.str());
711 match url {
712 Ok(url) => urls.push(url),
713 Err(_) => return Err(Error::Syntax(None)),
714 };
715 }
716
717 for url in urls {
718 let global_scope = self.upcast::<GlobalScope>();
719 let request = RequestBuilder::new(
720 global_scope.webview_id(),
721 UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
722 global_scope.get_referrer(),
723 )
724 .destination(Destination::Script)
725 .credentials_mode(CredentialsMode::Include)
726 .parser_metadata(ParserMetadata::NotParserInserted)
727 .use_url_credentials(true)
728 .with_global_scope(global_scope);
729
730 let (url, bytes, muted_errors) = match load_whole_resource(
732 request,
733 &global_scope.resource_threads().sender(),
734 global_scope,
735 &WorkerCspProcessor {
736 global_scope: DomRoot::from_ref(global_scope),
737 },
738 cx,
739 ) {
740 Err(_) => return Err(Error::Network(None)),
741 Ok((metadata, bytes, muted_errors)) => {
742 if !metadata.status.is_success() {
744 return Err(Error::Network(None));
745 }
746
747 let not_a_javascript_mime_type =
749 !metadata.content_type.clone().is_some_and(|ct| {
750 let mime: Mime = ct.into_inner().into();
751 SCRIPT_JS_MIMES.contains(&mime.essence_str())
752 });
753 if not_a_javascript_mime_type {
754 return Err(Error::Network(None));
755 }
756
757 (metadata.final_url, bytes, muted_errors)
758 },
759 };
760
761 let (source, _) = UTF_8.decode_with_bom_removal(&bytes);
763
764 let script = self.globalscope.create_a_classic_script(
770 cx,
771 source,
772 url,
773 ScriptFetchOptions::default_classic_script(),
774 ErrorReporting::from(muted_errors),
775 Some(IntroductionType::WORKER),
776 1,
777 true,
778 );
779
780 let result = self
782 .globalscope
783 .run_a_classic_script(cx, script, RethrowErrors::Yes);
784
785 if let Err(error) = result {
786 if self.is_closing() {
787 error!("evaluate_script failed (terminated)");
790 } else {
791 error!("evaluate_script failed");
792 return Err(error);
793 }
794 }
795 }
796
797 Ok(())
798 }
799
800 error_event_handler!(error, GetOnerror, SetOnerror);
802
803 event_handler!(languagechange, GetOnlanguagechange, SetOnlanguagechange);
805
806 event_handler!(offline, GetOnoffline, SetOnoffline);
808
809 event_handler!(online, GetOnonline, SetOnonline);
811
812 event_handler!(
814 rejectionhandled,
815 GetOnrejectionhandled,
816 SetOnrejectionhandled
817 );
818
819 event_handler!(
821 unhandledrejection,
822 GetOnunhandledrejection,
823 SetOnunhandledrejection
824 );
825
826 fn Navigator(&self) -> DomRoot<WorkerNavigator> {
828 self.navigator
829 .or_init(|| WorkerNavigator::new(self, CanGc::deprecated_note()))
830 }
831
832 fn Crypto(&self) -> DomRoot<Crypto> {
834 self.crypto
835 .or_init(|| Crypto::new(self.upcast::<GlobalScope>(), CanGc::deprecated_note()))
836 }
837
838 fn ReportError(&self, cx: JSContext, error: HandleValue, can_gc: CanGc) {
840 self.upcast::<GlobalScope>()
841 .report_an_exception(cx, error, can_gc);
842 }
843
844 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
846 base64_btoa(btoa)
847 }
848
849 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
851 base64_atob(atob)
852 }
853
854 fn SetTimeout(
856 &self,
857 cx: &mut js::context::JSContext,
858 callback: TrustedScriptOrStringOrFunction,
859 timeout: i32,
860 args: Vec<HandleValue>,
861 ) -> Fallible<i32> {
862 let callback = match callback {
863 TrustedScriptOrStringOrFunction::String(i) => {
864 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
865 },
866 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
867 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
868 },
869 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
870 };
871 self.upcast::<GlobalScope>().set_timeout_or_interval(
872 cx,
873 callback,
874 args,
875 Duration::from_millis(timeout.max(0) as u64),
876 IsInterval::NonInterval,
877 )
878 }
879
880 fn ClearTimeout(&self, handle: i32) {
882 self.upcast::<GlobalScope>()
883 .clear_timeout_or_interval(handle);
884 }
885
886 fn SetInterval(
888 &self,
889 cx: &mut js::context::JSContext,
890 callback: TrustedScriptOrStringOrFunction,
891 timeout: i32,
892 args: Vec<HandleValue>,
893 ) -> Fallible<i32> {
894 let callback = match callback {
895 TrustedScriptOrStringOrFunction::String(i) => {
896 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
897 },
898 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
899 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
900 },
901 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
902 };
903 self.upcast::<GlobalScope>().set_timeout_or_interval(
904 cx,
905 callback,
906 args,
907 Duration::from_millis(timeout.max(0) as u64),
908 IsInterval::Interval,
909 )
910 }
911
912 fn ClearInterval(&self, handle: i32) {
914 self.ClearTimeout(handle);
915 }
916
917 fn QueueMicrotask(&self, callback: Rc<VoidFunction>) {
919 self.enqueue_microtask(Microtask::User(UserMicrotask {
920 callback,
921 pipeline: self.pipeline_id(),
922 }));
923 }
924
925 fn CreateImageBitmap(
927 &self,
928 realm: &mut CurrentRealm,
929 image: ImageBitmapSource,
930 options: &ImageBitmapOptions,
931 ) -> Rc<Promise> {
932 ImageBitmap::create_image_bitmap(self.upcast(), image, 0, 0, None, None, options, realm)
933 }
934
935 fn CreateImageBitmap_(
937 &self,
938 realm: &mut CurrentRealm,
939 image: ImageBitmapSource,
940 sx: i32,
941 sy: i32,
942 sw: i32,
943 sh: i32,
944 options: &ImageBitmapOptions,
945 ) -> Rc<Promise> {
946 ImageBitmap::create_image_bitmap(
947 self.upcast(),
948 image,
949 sx,
950 sy,
951 Some(sw),
952 Some(sh),
953 options,
954 realm,
955 )
956 }
957
958 fn Fetch(
960 &self,
961 realm: &mut CurrentRealm,
962 input: RequestOrUSVString,
963 init: RootedTraceableBox<RequestInit>,
964 ) -> Rc<Promise> {
965 Fetch(self.upcast(), input, init, realm)
966 }
967
968 fn Performance(&self) -> DomRoot<Performance> {
970 self.performance.or_init(|| {
971 let global_scope = self.upcast::<GlobalScope>();
972 Performance::new(
973 global_scope,
974 self.navigation_start,
975 CanGc::deprecated_note(),
976 )
977 })
978 }
979
980 fn Origin(&self) -> USVString {
982 USVString(
983 self.upcast::<GlobalScope>()
984 .origin()
985 .immutable()
986 .ascii_serialization(),
987 )
988 }
989
990 fn IsSecureContext(&self) -> bool {
992 self.upcast::<GlobalScope>().is_secure_context()
993 }
994
995 fn StructuredClone(
997 &self,
998 cx: &mut js::context::JSContext,
999 value: HandleValue,
1000 options: RootedTraceableBox<StructuredSerializeOptions>,
1001 retval: MutableHandleValue,
1002 ) -> Fallible<()> {
1003 self.upcast::<GlobalScope>()
1004 .structured_clone(cx, value, options, retval)
1005 }
1006
1007 fn TrustedTypes(&self, cx: &mut js::context::JSContext) -> DomRoot<TrustedTypePolicyFactory> {
1009 self.trusted_types.or_init(|| {
1010 let global_scope = self.upcast::<GlobalScope>();
1011 TrustedTypePolicyFactory::new(cx, global_scope)
1012 })
1013 }
1014}
1015
1016impl WorkerGlobalScope {
1017 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
1018 let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
1019 if let Some(dedicated) = dedicated {
1020 dedicated.new_script_pair()
1021 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
1022 shared.new_script_pair()
1023 } else {
1024 panic!("need to implement a sender for ServiceWorker")
1025 }
1026 }
1027
1028 pub(crate) fn process_event(
1032 &self,
1033 msg: CommonScriptMsg,
1034 cx: &mut js::context::JSContext,
1035 ) -> bool {
1036 if self.is_closing() {
1037 return false;
1038 }
1039 match msg {
1040 CommonScriptMsg::Task(_, task, _, _) => task.run_box(cx),
1041 CommonScriptMsg::CollectReports(reports_chan) => {
1042 let cx: JSContext = cx.into();
1043 perform_memory_report(|ops| {
1044 let reports = cx.get_reports(format!("url({})", self.get_url()), ops);
1045 reports_chan.send(ProcessReports::new(reports));
1046 });
1047 },
1048 CommonScriptMsg::ReportCspViolations(_, violations) => {
1049 self.upcast::<GlobalScope>()
1050 .report_csp_violations(violations, None, None);
1051 },
1052 }
1053 true
1054 }
1055
1056 pub(crate) fn close(&self) {
1057 self.closing.store(true, Ordering::SeqCst);
1058 self.upcast::<GlobalScope>()
1059 .task_manager()
1060 .cancel_all_tasks_and_ignore_future_tasks();
1061 if let Some(factory) = self.upcast::<GlobalScope>().get_existing_indexeddb() {
1062 factory.abort_pending_upgrades();
1063 }
1064 }
1065
1066 pub(super) fn init_debugger_global(
1067 &self,
1068 debugger_global: &DebuggerGlobalScope,
1069 cx: &mut js::context::JSContext,
1070 ) {
1071 let mut realm = enter_auto_realm(cx, self);
1072 let cx = &mut realm.current_realm();
1073
1074 rooted!(&in(cx) let mut wrapped_global: Value);
1077 debugger_global.reflector().safe_to_jsval(
1078 cx.into(),
1079 wrapped_global.handle_mut(),
1080 CanGc::from_cx(cx),
1081 );
1082 self.debugger_global.set(*wrapped_global);
1083 }
1084
1085 pub(super) fn handle_devtools_message(
1086 &self,
1087 msg: DevtoolScriptControlMsg,
1088 cx: &mut js::context::JSContext,
1089 ) {
1090 match msg {
1091 DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _wants_updates) => {},
1092 DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, reply) => {
1093 let debugger_global_handle = rooted_heap_handle(self, |this| &this.debugger_global);
1094 let debugger_global =
1095 root_from_handlevalue::<DebuggerGlobalScope>(debugger_global_handle, cx.into())
1096 .expect("must be a debugger global scope");
1097
1098 debugger_global.fire_eval(
1099 cx,
1100 code.into(),
1101 id,
1102 Some(self.worker_id()),
1103 frame_actor_id,
1104 reply,
1105 );
1106 },
1107 _ => debug!("got an unusable devtools control message inside the worker!"),
1108 }
1109 }
1110}
1111
1112#[expect(unsafe_code)]
1113unsafe extern "C" fn interrupt_callback(cx: *mut RawJSContext) -> bool {
1114 let in_realm_proof = AlreadyInRealm::assert_for_cx(unsafe { JSContext::from_ptr(cx) });
1115 let global = unsafe { GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof)) };
1116
1117 let Some(worker) = global.downcast::<WorkerGlobalScope>() else {
1119 return false;
1120 };
1121
1122 !worker.is_closing()
1124}
1125
1126struct WorkerCspProcessor {
1127 global_scope: DomRoot<GlobalScope>,
1128}
1129
1130impl CspViolationsProcessor for WorkerCspProcessor {
1131 fn process_csp_violations(&self, violations: Vec<Violation>) {
1132 self.global_scope
1133 .report_csp_violations(violations, None, None);
1134 }
1135}