1use std::borrow::Cow;
6use std::cell::{RefCell, RefMut};
7use std::default::Default;
8use std::rc::Rc;
9use std::sync::Arc;
10use std::sync::atomic::{AtomicBool, Ordering};
11use std::time::Duration;
12
13use base::IpcSend;
14use base::cross_process_instant::CrossProcessInstant;
15use base::id::{PipelineId, PipelineNamespace};
16use constellation_traits::WorkerGlobalScopeInit;
17use content_security_policy::CspList;
18use crossbeam_channel::Receiver;
19use devtools_traits::{DevtoolScriptControlMsg, WorkerId};
20use dom_struct::dom_struct;
21use encoding_rs::UTF_8;
22use fonts::FontContext;
23use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
24use ipc_channel::ipc::IpcSender;
25use js::jsapi::JS_AddInterruptCallback;
26use js::jsval::UndefinedValue;
27use js::panic::maybe_resume_unwind;
28use js::rust::{CompileOptionsWrapper, HandleValue, MutableHandleValue, ParentRuntime};
29use mime::Mime;
30use net_traits::policy_container::PolicyContainer;
31use net_traits::request::{
32 CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata,
33 RequestBuilder as NetRequestInit, RequestId,
34};
35use net_traits::{FetchMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming};
36use profile_traits::mem::{ProcessReports, perform_memory_report};
37use servo_url::{MutableOrigin, ServoUrl};
38use timers::TimerScheduler;
39use uuid::Uuid;
40
41use crate::dom::bindings::cell::{DomRefCell, Ref};
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, DomObject};
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::dedicatedworkerglobalscope::{
66 AutoWorkerReset, DedicatedWorkerGlobalScope, interrupt_callback,
67};
68use crate::dom::globalscope::GlobalScope;
69use crate::dom::htmlscriptelement::{SCRIPT_JS_MIMES, ScriptOrigin, ScriptType};
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::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
75use crate::dom::reportingobserver::ReportingObserver;
76use crate::dom::trustedscripturl::TrustedScriptURL;
77use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
78use crate::dom::types::ImageBitmap;
79#[cfg(feature = "webgpu")]
80use crate::dom::webgpu::identityhub::IdentityHub;
81use crate::dom::window::{base64_atob, base64_btoa};
82use crate::dom::worker::TrustedWorkerAddress;
83use crate::dom::workerlocation::WorkerLocation;
84use crate::dom::workernavigator::WorkerNavigator;
85use crate::fetch::{CspViolationsProcessor, Fetch, 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::{InRealm, enter_realm};
90use crate::script_module::ScriptFetchOptions;
91use crate::script_runtime::{CanGc, IntroductionType, JSContext, JSContextHelper, Runtime};
92use crate::task::TaskCanceller;
93use crate::timers::{IsInterval, TimerCallback};
94use crate::unminify::unminify_js;
95
96pub(crate) fn prepare_workerscope_init(
97 global: &GlobalScope,
98 devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>,
99 worker_id: Option<WorkerId>,
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 creation_url: global.creation_url().clone(),
114 inherited_secure_context: Some(global.is_secure_context()),
115 unminify_js: global.unminify_js(),
116 }
117}
118
119pub(crate) struct ScriptFetchContext {
120 scope: Trusted<WorkerGlobalScope>,
121 response: Option<Metadata>,
122 body_bytes: Vec<u8>,
123 url: ServoUrl,
124 worker: TrustedWorkerAddress,
125 policy_container: PolicyContainer,
126}
127
128impl ScriptFetchContext {
129 pub(crate) fn new(
130 scope: Trusted<WorkerGlobalScope>,
131 url: ServoUrl,
132 worker: TrustedWorkerAddress,
133 policy_container: PolicyContainer,
134 ) -> ScriptFetchContext {
135 ScriptFetchContext {
136 scope,
137 response: None,
138 body_bytes: Vec::new(),
139 url,
140 worker,
141 policy_container,
142 }
143 }
144}
145
146impl FetchResponseListener for ScriptFetchContext {
147 fn process_request_body(&mut self, _request_id: RequestId) {}
148
149 fn process_request_eof(&mut self, _request_id: RequestId) {}
150
151 fn process_response(
152 &mut self,
153 _request_id: RequestId,
154 metadata: Result<FetchMetadata, NetworkError>,
155 ) {
156 self.response = metadata.ok().map(|m| match m {
157 FetchMetadata::Unfiltered(m) => m,
158 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
159 });
160 }
161
162 fn process_response_chunk(&mut self, _request_id: RequestId, mut chunk: Vec<u8>) {
163 self.body_bytes.append(&mut chunk);
164 }
165
166 fn process_response_eof(
167 mut self,
168 _request_id: RequestId,
169 response: Result<ResourceFetchTiming, NetworkError>,
170 ) {
171 let scope = self.scope.root();
172
173 if response
174 .as_ref()
175 .inspect_err(|e| error!("error loading script {} ({:?})", self.url, e))
176 .is_err() ||
177 self.response.is_none()
178 {
179 scope.on_complete(None, self.worker.clone(), CanGc::note());
180 return;
181 }
182 let metadata = self.response.take().unwrap();
183
184 scope.set_url(metadata.final_url.clone());
189
190 scope
192 .initialize_policy_container_for_worker_global_scope(&metadata, &self.policy_container);
193 scope.set_endpoints_list(ReportingEndpoint::parse_reporting_endpoints_header(
194 &metadata.final_url.clone(),
195 &metadata.headers,
196 ));
197 scope
198 .upcast::<GlobalScope>()
199 .set_https_state(metadata.https_state);
200
201 if !metadata.status.is_success() {
208 scope.on_complete(None, self.worker.clone(), CanGc::note());
210 return;
211 }
212
213 let is_http_scheme = matches!(metadata.final_url.scheme(), "http" | "https");
216 let not_a_javascript_mime_type = !metadata.content_type.clone().is_some_and(|ct| {
218 let mime: Mime = ct.into_inner().into();
219 SCRIPT_JS_MIMES.contains(&mime.essence_str())
220 });
221
222 if is_http_scheme && not_a_javascript_mime_type {
223 scope.on_complete(None, self.worker.clone(), CanGc::note());
225 return;
226 }
227
228 let (source, _, _) = UTF_8.decode(&self.body_bytes);
230
231 scope.on_complete(Some(source), self.worker.clone(), CanGc::note());
236
237 if let Ok(response) = response {
238 submit_timing(&self, &response, CanGc::note());
239 }
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 #[no_trace]
289 policy_container: DomRefCell<PolicyContainer>,
291
292 #[ignore_malloc_size_of = "Defined in ipc-channel"]
293 #[no_trace]
294 _devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>,
297
298 #[ignore_malloc_size_of = "Defined in crossbeam"]
299 #[no_trace]
300 devtools_receiver: Option<Receiver<DevtoolScriptControlMsg>>,
302
303 #[no_trace]
304 navigation_start: CrossProcessInstant,
305 performance: MutNullableDom<Performance>,
306 indexeddb: MutNullableDom<IDBFactory>,
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
328impl WorkerGlobalScope {
329 #[allow(clippy::too_many_arguments)]
330 pub(crate) fn new_inherited(
331 init: WorkerGlobalScopeInit,
332 worker_name: DOMString,
333 worker_type: WorkerType,
334 worker_url: ServoUrl,
335 runtime: Runtime,
336 devtools_receiver: Receiver<DevtoolScriptControlMsg>,
337 closing: Arc<AtomicBool>,
338 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
339 insecure_requests_policy: InsecureRequestsPolicy,
340 font_context: Option<Arc<FontContext>>,
341 ) -> Self {
342 PipelineNamespace::auto_install();
344
345 let devtools_receiver = match init.from_devtools_sender {
346 Some(..) => Some(devtools_receiver),
347 None => None,
348 };
349
350 Self {
351 globalscope: GlobalScope::new_inherited(
352 init.pipeline_id,
353 init.to_devtools_sender,
354 init.mem_profiler_chan,
355 init.time_profiler_chan,
356 init.script_to_constellation_chan,
357 init.script_to_embedder_chan,
358 init.resource_threads,
359 init.storage_threads,
360 MutableOrigin::new(init.origin),
361 init.creation_url,
362 None,
363 #[cfg(feature = "webgpu")]
364 gpu_id_hub,
365 init.inherited_secure_context,
366 init.unminify_js,
367 font_context,
368 ),
369 microtask_queue: runtime.microtask_queue.clone(),
370 worker_id: init.worker_id,
371 worker_name,
372 worker_type,
373 worker_url: DomRefCell::new(worker_url),
374 closing,
375 execution_ready: AtomicBool::new(false),
376 runtime: DomRefCell::new(Some(runtime)),
377 location: Default::default(),
378 navigator: Default::default(),
379 policy_container: Default::default(),
380 devtools_receiver,
381 _devtools_sender: init.from_devtools_sender,
382 navigation_start: CrossProcessInstant::now(),
383 performance: Default::default(),
384 indexeddb: Default::default(),
385 timer_scheduler: RefCell::default(),
386 insecure_requests_policy,
387 trusted_types: Default::default(),
388 reporting_observer_list: Default::default(),
389 report_list: Default::default(),
390 endpoints_list: Default::default(),
391 }
392 }
393
394 pub(crate) fn enqueue_microtask(&self, job: Microtask) {
395 self.microtask_queue.enqueue(job, GlobalScope::get_cx());
396 }
397
398 pub(crate) fn perform_a_microtask_checkpoint(&self, can_gc: CanGc) {
400 if !self.is_closing() {
402 self.microtask_queue.checkpoint(
403 GlobalScope::get_cx(),
404 |_| Some(DomRoot::from_ref(&self.globalscope)),
405 vec![DomRoot::from_ref(&self.globalscope)],
406 can_gc,
407 );
408 }
409 }
410
411 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
413 self.insecure_requests_policy
414 }
415
416 pub(crate) fn clear_js_runtime(&self) {
418 self.upcast::<GlobalScope>()
419 .remove_web_messaging_and_dedicated_workers_infra();
420
421 let runtime = self.runtime.borrow_mut().take();
423 drop(runtime);
424 }
425
426 pub(crate) fn runtime_handle(&self) -> ParentRuntime {
427 self.runtime
428 .borrow()
429 .as_ref()
430 .unwrap()
431 .prepare_for_new_child()
432 }
433
434 pub(crate) fn devtools_receiver(&self) -> Option<&Receiver<DevtoolScriptControlMsg>> {
435 self.devtools_receiver.as_ref()
436 }
437
438 #[expect(unsafe_code)]
439 pub(crate) fn get_cx(&self) -> JSContext {
440 unsafe { JSContext::from_ptr(js::rust::Runtime::get().unwrap().as_ptr()) }
441 }
442
443 pub(crate) fn is_closing(&self) -> bool {
444 self.closing.load(Ordering::SeqCst)
445 }
446
447 pub(crate) fn is_execution_ready(&self) -> bool {
448 self.execution_ready.load(Ordering::Relaxed)
449 }
450
451 pub(crate) fn get_url(&self) -> Ref<'_, ServoUrl> {
452 self.worker_url.borrow()
453 }
454
455 pub(crate) fn set_url(&self, url: ServoUrl) {
456 *self.worker_url.borrow_mut() = url;
457 }
458
459 pub(crate) fn worker_name(&self) -> DOMString {
460 self.worker_name.clone()
461 }
462
463 pub(crate) fn worker_id(&self) -> WorkerId {
464 self.worker_id
465 }
466
467 pub(crate) fn pipeline_id(&self) -> PipelineId {
468 self.globalscope.pipeline_id()
469 }
470
471 pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
472 self.policy_container.borrow()
473 }
474
475 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
476 self.policy_container.borrow_mut().set_csp_list(csp_list);
477 }
478
479 pub(crate) fn set_referrer_policy(&self, referrer_policy: ReferrerPolicy) {
480 self.policy_container
481 .borrow_mut()
482 .set_referrer_policy(referrer_policy);
483 }
484
485 pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
486 self.reporting_observer_list
487 .borrow_mut()
488 .push(reporting_observer);
489 }
490
491 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
492 if let Some(index) = self
493 .reporting_observer_list
494 .borrow()
495 .iter()
496 .position(|observer| &**observer == reporting_observer)
497 {
498 self.reporting_observer_list.borrow_mut().remove(index);
499 }
500 }
501
502 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
503 self.reporting_observer_list.borrow().clone()
504 }
505
506 pub(crate) fn append_report(&self, report: Report) {
507 self.report_list.borrow_mut().push(report);
508 let trusted_worker = Trusted::new(self);
509 self.upcast::<GlobalScope>()
510 .task_manager()
511 .dom_manipulation_task_source()
512 .queue(task!(send_to_reporting_endpoints: move || {
513 let worker = trusted_worker.root();
514 let reports = std::mem::take(&mut *worker.report_list.borrow_mut());
515 worker.upcast::<GlobalScope>().send_reports_to_endpoints(
516 reports,
517 worker.endpoints_list.borrow().clone(),
518 );
519 }));
520 }
521
522 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
523 self.report_list.borrow().clone()
524 }
525
526 pub(crate) fn set_endpoints_list(&self, endpoints: Option<Vec<ReportingEndpoint>>) {
527 if let Some(endpoints) = endpoints {
528 *self.endpoints_list.borrow_mut() = endpoints;
529 }
530 }
531
532 pub(crate) fn timer_scheduler(&self) -> RefMut<'_, TimerScheduler> {
534 self.timer_scheduler.borrow_mut()
535 }
536
537 pub(crate) fn shared_task_canceller(&self) -> TaskCanceller {
540 TaskCanceller {
541 cancelled: self.closing.clone(),
542 }
543 }
544
545 fn initialize_policy_container_for_worker_global_scope(
548 &self,
549 metadata: &Metadata,
550 parent_policy_container: &PolicyContainer,
551 ) {
552 if metadata.final_url.is_local_scheme() {
557 self.set_csp_list(parent_policy_container.csp_list.clone());
563 self.set_referrer_policy(parent_policy_container.get_referrer_policy());
564 return;
565 }
566 self.set_csp_list(parse_csp_list_from_metadata(&metadata.headers));
568 let referrer_policy = metadata
571 .headers
572 .as_ref()
573 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
574 .into();
575 self.set_referrer_policy(referrer_policy);
576 }
577
578 #[expect(unsafe_code)]
580 fn on_complete(
581 &self,
582 script: Option<Cow<'_, str>>,
583 worker: TrustedWorkerAddress,
584 can_gc: CanGc,
585 ) {
586 let dedicated_worker_scope = self
587 .downcast::<DedicatedWorkerGlobalScope>()
588 .expect("Only DedicatedWorkerGlobalScope is supported for now");
589
590 let Some(script) = script else {
591 dedicated_worker_scope.forward_simple_error_at_worker(worker.clone());
594
595 return;
597 };
598
599 unsafe {
600 JS_AddInterruptCallback(*self.get_cx(), Some(interrupt_callback));
602 }
603
604 if self.is_closing() {
605 return;
606 }
607
608 {
609 let _ar = AutoWorkerReset::new(dedicated_worker_scope, worker);
610 let realm = enter_realm(self);
611 define_all_exposed_interfaces(
612 dedicated_worker_scope.upcast(),
613 InRealm::entered(&realm),
614 can_gc,
615 );
616 self.execution_ready.store(true, Ordering::Relaxed);
617 self.execute_script(DOMString::from(script), can_gc);
618 dedicated_worker_scope.fire_queued_messages(can_gc);
619 }
620 }
621}
622
623impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
624 fn Self_(&self) -> DomRoot<WorkerGlobalScope> {
626 DomRoot::from_ref(self)
627 }
628
629 fn IndexedDB(&self) -> DomRoot<IDBFactory> {
631 self.indexeddb.or_init(|| {
632 let global_scope = self.upcast::<GlobalScope>();
633 IDBFactory::new(global_scope, CanGc::note())
634 })
635 }
636
637 fn Location(&self) -> DomRoot<WorkerLocation> {
639 self.location
640 .or_init(|| WorkerLocation::new(self, self.worker_url.borrow().clone(), CanGc::note()))
641 }
642
643 fn ImportScripts(
645 &self,
646 url_strings: Vec<TrustedScriptURLOrUSVString>,
647 can_gc: CanGc,
648 ) -> ErrorResult {
649 if self.worker_type == WorkerType::Module {
652 return Err(Error::Type(
653 "importScripts() is not allowed in module workers".to_string(),
654 ));
655 }
656
657 let mut urls = Vec::with_capacity(url_strings.len());
659 for url in url_strings {
661 let url = TrustedScriptURL::get_trusted_script_url_compliant_string(
665 self.upcast::<GlobalScope>(),
666 url,
667 "WorkerGlobalScope",
668 "importScripts",
669 can_gc,
670 )?;
671 let url = self.worker_url.borrow().join(&url.str());
672 match url {
673 Ok(url) => urls.push(url),
674 Err(_) => return Err(Error::Syntax(None)),
675 };
676 }
677
678 rooted!(in(*self.get_cx()) let mut rval = UndefinedValue());
679 for url in urls {
680 let global_scope = self.upcast::<GlobalScope>();
681 let request = NetRequestInit::new(
682 global_scope.webview_id(),
683 url.clone(),
684 global_scope.get_referrer(),
685 )
686 .destination(Destination::Script)
687 .credentials_mode(CredentialsMode::Include)
688 .parser_metadata(ParserMetadata::NotParserInserted)
689 .use_url_credentials(true)
690 .origin(global_scope.origin().immutable().clone())
691 .insecure_requests_policy(self.insecure_requests_policy())
692 .policy_container(global_scope.policy_container())
693 .has_trustworthy_ancestor_origin(
694 global_scope.has_trustworthy_ancestor_or_current_origin(),
695 )
696 .pipeline_id(Some(self.upcast::<GlobalScope>().pipeline_id()));
697
698 let (url, source) = match load_whole_resource(
699 request,
700 &global_scope.resource_threads().sender(),
701 global_scope,
702 &WorkerCspProcessor {
703 global_scope: DomRoot::from_ref(global_scope),
704 },
705 can_gc,
706 ) {
707 Err(_) => return Err(Error::Network(None)),
708 Ok((metadata, bytes)) => {
709 if !metadata.status.is_success() {
712 return Err(Error::Network(None));
713 }
714
715 let not_a_javascript_mime_type =
717 !metadata.content_type.clone().is_some_and(|ct| {
718 let mime: Mime = ct.into_inner().into();
719 SCRIPT_JS_MIMES.contains(&mime.essence_str())
720 });
721 if not_a_javascript_mime_type {
722 return Err(Error::Network(None));
723 }
724
725 (metadata.final_url, String::from_utf8(bytes).unwrap())
726 },
727 };
728
729 #[expect(unsafe_code)]
730 let mut cx =
731 unsafe { js::context::JSContext::from_ptr(js::rust::Runtime::get().unwrap()) };
732 let options = CompileOptionsWrapper::new(&cx, url.as_str(), 1);
733 let result = js::rust::evaluate_script(
734 &mut cx,
735 self.reflector().get_jsobject(),
736 &source,
737 rval.handle_mut(),
738 options,
739 );
740
741 maybe_resume_unwind();
742
743 match result {
744 Ok(_) => (),
745 Err(_) => {
746 if self.is_closing() {
747 error!("evaluate_script failed (terminated)");
750 } else {
751 error!("evaluate_script failed");
752 return Err(Error::JSFailed);
753 }
754 },
755 }
756 }
757
758 Ok(())
759 }
760
761 error_event_handler!(error, GetOnerror, SetOnerror);
763
764 event_handler!(languagechange, GetOnlanguagechange, SetOnlanguagechange);
766
767 event_handler!(offline, GetOnoffline, SetOnoffline);
769
770 event_handler!(online, GetOnonline, SetOnonline);
772
773 event_handler!(
775 rejectionhandled,
776 GetOnrejectionhandled,
777 SetOnrejectionhandled
778 );
779
780 event_handler!(
782 unhandledrejection,
783 GetOnunhandledrejection,
784 SetOnunhandledrejection
785 );
786
787 fn Navigator(&self) -> DomRoot<WorkerNavigator> {
789 self.navigator
790 .or_init(|| WorkerNavigator::new(self, CanGc::note()))
791 }
792
793 fn Crypto(&self) -> DomRoot<Crypto> {
795 self.upcast::<GlobalScope>().crypto(CanGc::note())
796 }
797
798 fn ReportError(&self, cx: JSContext, error: HandleValue, can_gc: CanGc) {
800 self.upcast::<GlobalScope>()
801 .report_an_exception(cx, error, can_gc);
802 }
803
804 fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
806 base64_btoa(btoa)
807 }
808
809 fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
811 base64_atob(atob)
812 }
813
814 fn SetTimeout(
816 &self,
817 _cx: JSContext,
818 callback: TrustedScriptOrStringOrFunction,
819 timeout: i32,
820 args: Vec<HandleValue>,
821 can_gc: CanGc,
822 ) -> Fallible<i32> {
823 let callback = match callback {
824 TrustedScriptOrStringOrFunction::String(i) => {
825 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
826 },
827 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
828 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
829 },
830 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
831 };
832 self.upcast::<GlobalScope>().set_timeout_or_interval(
833 callback,
834 args,
835 Duration::from_millis(timeout.max(0) as u64),
836 IsInterval::NonInterval,
837 can_gc,
838 )
839 }
840
841 fn ClearTimeout(&self, handle: i32) {
843 self.upcast::<GlobalScope>()
844 .clear_timeout_or_interval(handle);
845 }
846
847 fn SetInterval(
849 &self,
850 _cx: JSContext,
851 callback: TrustedScriptOrStringOrFunction,
852 timeout: i32,
853 args: Vec<HandleValue>,
854 can_gc: CanGc,
855 ) -> Fallible<i32> {
856 let callback = match callback {
857 TrustedScriptOrStringOrFunction::String(i) => {
858 TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
859 },
860 TrustedScriptOrStringOrFunction::TrustedScript(i) => {
861 TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
862 },
863 TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
864 };
865 self.upcast::<GlobalScope>().set_timeout_or_interval(
866 callback,
867 args,
868 Duration::from_millis(timeout.max(0) as u64),
869 IsInterval::Interval,
870 can_gc,
871 )
872 }
873
874 fn ClearInterval(&self, handle: i32) {
876 self.ClearTimeout(handle);
877 }
878
879 fn QueueMicrotask(&self, callback: Rc<VoidFunction>) {
881 self.enqueue_microtask(Microtask::User(UserMicrotask {
882 callback,
883 pipeline: self.pipeline_id(),
884 }));
885 }
886
887 fn CreateImageBitmap(
889 &self,
890 image: ImageBitmapSource,
891 options: &ImageBitmapOptions,
892 can_gc: CanGc,
893 ) -> Rc<Promise> {
894 ImageBitmap::create_image_bitmap(self.upcast(), image, 0, 0, None, None, options, can_gc)
895 }
896
897 fn CreateImageBitmap_(
899 &self,
900 image: ImageBitmapSource,
901 sx: i32,
902 sy: i32,
903 sw: i32,
904 sh: i32,
905 options: &ImageBitmapOptions,
906 can_gc: CanGc,
907 ) -> Rc<Promise> {
908 ImageBitmap::create_image_bitmap(
909 self.upcast(),
910 image,
911 sx,
912 sy,
913 Some(sw),
914 Some(sh),
915 options,
916 can_gc,
917 )
918 }
919
920 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
921 fn Fetch(
923 &self,
924 input: RequestOrUSVString,
925 init: RootedTraceableBox<RequestInit>,
926 comp: InRealm,
927 can_gc: CanGc,
928 ) -> Rc<Promise> {
929 Fetch(self.upcast(), input, init, comp, can_gc)
930 }
931
932 fn Performance(&self) -> DomRoot<Performance> {
934 self.performance.or_init(|| {
935 let global_scope = self.upcast::<GlobalScope>();
936 Performance::new(global_scope, self.navigation_start, CanGc::note())
937 })
938 }
939
940 fn Origin(&self) -> USVString {
942 USVString(
943 self.upcast::<GlobalScope>()
944 .origin()
945 .immutable()
946 .ascii_serialization(),
947 )
948 }
949
950 fn IsSecureContext(&self) -> bool {
952 self.upcast::<GlobalScope>().is_secure_context()
953 }
954
955 fn StructuredClone(
957 &self,
958 cx: JSContext,
959 value: HandleValue,
960 options: RootedTraceableBox<StructuredSerializeOptions>,
961 can_gc: CanGc,
962 retval: MutableHandleValue,
963 ) -> Fallible<()> {
964 self.upcast::<GlobalScope>()
965 .structured_clone(cx, value, options, retval, can_gc)
966 }
967
968 fn TrustedTypes(&self, can_gc: CanGc) -> DomRoot<TrustedTypePolicyFactory> {
970 self.trusted_types.or_init(|| {
971 let global_scope = self.upcast::<GlobalScope>();
972 TrustedTypePolicyFactory::new(global_scope, can_gc)
973 })
974 }
975}
976
977impl WorkerGlobalScope {
978 pub(crate) fn execute_script(&self, source: DOMString, can_gc: CanGc) {
979 let global = self.upcast::<GlobalScope>();
980 let mut script = ScriptOrigin::external(
981 Rc::new(source),
982 self.worker_url.borrow().clone(),
983 ScriptFetchOptions::default_classic_script(global),
984 ScriptType::Classic,
985 global.unminified_js_dir(),
986 );
987 unminify_js(&mut script);
988
989 global.run_a_classic_script(&script, 1, Some(IntroductionType::WORKER), can_gc);
990 }
991
992 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
993 let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
994 if let Some(dedicated) = dedicated {
995 dedicated.new_script_pair()
996 } else {
997 panic!("need to implement a sender for SharedWorker/ServiceWorker")
998 }
999 }
1000
1001 pub(crate) fn process_event(&self, msg: CommonScriptMsg) -> bool {
1005 if self.is_closing() {
1006 return false;
1007 }
1008 match msg {
1009 CommonScriptMsg::Task(_, task, _, _) => task.run_box(),
1010 CommonScriptMsg::CollectReports(reports_chan) => {
1011 let cx = self.get_cx();
1012 perform_memory_report(|ops| {
1013 let reports = cx.get_reports(format!("url({})", self.get_url()), ops);
1014 reports_chan.send(ProcessReports::new(reports));
1015 });
1016 },
1017 CommonScriptMsg::ReportCspViolations(_, violations) => {
1018 self.upcast::<GlobalScope>()
1019 .report_csp_violations(violations, None, None);
1020 },
1021 }
1022 true
1023 }
1024
1025 pub(crate) fn close(&self) {
1026 self.closing.store(true, Ordering::SeqCst);
1027 self.upcast::<GlobalScope>()
1028 .task_manager()
1029 .cancel_all_tasks_and_ignore_future_tasks();
1030 }
1031}
1032
1033struct WorkerCspProcessor {
1034 global_scope: DomRoot<GlobalScope>,
1035}
1036
1037impl CspViolationsProcessor for WorkerCspProcessor {
1038 fn process_csp_violations(&self, violations: Vec<Violation>) {
1039 self.global_scope
1040 .report_csp_violations(violations, None, None);
1041 }
1042}