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