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