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::sharedworkerglobalscope::SharedWorkerGlobalScope;
79use crate::dom::trustedtypes::trustedscripturl::TrustedScriptURL;
80use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
81use crate::dom::types::ImageBitmap;
82#[cfg(feature = "webgpu")]
83use crate::dom::webgpu::identityhub::IdentityHub;
84use crate::dom::window::{base64_atob, base64_btoa};
85use crate::dom::workerlocation::WorkerLocation;
86use crate::dom::workernavigator::WorkerNavigator;
87use crate::fetch::{CspViolationsProcessor, Fetch, RequestWithGlobalScope, load_whole_resource};
88use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
89use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask};
90use crate::network_listener::{FetchResponseListener, ResourceTimingListener, submit_timing};
91use crate::realms::enter_auto_realm;
92use crate::script_module::ScriptFetchOptions;
93use crate::script_runtime::{CanGc, IntroductionType, Runtime, get_reports};
94use crate::task::TaskCanceller;
95use crate::task_manager::TaskManager;
96use crate::timers::{IsInterval, OneshotTimers, TimerCallback};
97
98pub(crate) fn prepare_workerscope_init(
99 global: &GlobalScope,
100 devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
101 worker_id: Option<WorkerId>,
102 webgl_chan: Option<WebGLChan>,
103) -> WorkerGlobalScopeInit {
104 WorkerGlobalScopeInit {
105 resource_threads: global.resource_threads().clone(),
106 storage_threads: global.storage_threads().clone(),
107 mem_profiler_chan: global.mem_profiler_chan().clone(),
108 to_devtools_sender: global.devtools_chan().cloned(),
109 time_profiler_chan: global.time_profiler_chan().clone(),
110 from_devtools_sender: devtools_sender,
111 script_to_constellation_chan: global.script_to_constellation_chan().sender,
112 script_to_embedder_chan: global.script_to_embedder_chan().clone(),
113 worker_id: worker_id.unwrap_or_else(|| WorkerId(Uuid::new_v4())),
114 pipeline_id: global.pipeline_id(),
115 origin: global.origin().immutable().clone(),
116 inherited_secure_context: Some(global.is_secure_context()),
117 unminify_js: global.unminify_js(),
118 webgl_chan,
119 }
120}
121
122pub(crate) struct ScriptFetchContext {
123 scope: Trusted<WorkerGlobalScope>,
124 response: Option<Metadata>,
125 body_bytes: Vec<u8>,
126 url: ServoUrl,
127 policy_container: PolicyContainer,
128}
129
130impl ScriptFetchContext {
131 pub(crate) fn new(
132 scope: Trusted<WorkerGlobalScope>,
133 url: ServoUrl,
134 policy_container: PolicyContainer,
135 ) -> ScriptFetchContext {
136 ScriptFetchContext {
137 scope,
138 response: None,
139 body_bytes: Vec::new(),
140 url,
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_response(
150 &mut self,
151 _: &mut JSContext,
152 _request_id: RequestId,
153 metadata: Result<FetchMetadata, NetworkError>,
154 ) {
155 self.response = metadata.ok().map(|m| match m {
156 FetchMetadata::Unfiltered(m) => m,
157 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
158 });
159 }
160
161 fn process_response_chunk(&mut self, _: &mut JSContext, _: RequestId, mut chunk: Vec<u8>) {
162 self.body_bytes.append(&mut chunk);
163 }
164
165 fn process_response_eof(
166 mut self,
167 cx: &mut JSContext,
168 _request_id: RequestId,
169 response: Result<(), NetworkError>,
170 timing: ResourceFetchTiming,
171 ) {
172 let scope = self.scope.root();
173
174 if response
175 .as_ref()
176 .inspect_err(|e| error!("error loading script {} ({:?})", self.url, e))
177 .is_err() ||
178 self.response.is_none()
179 {
180 scope.on_complete(cx, None);
181 return;
182 }
183 let metadata = self.response.take().unwrap();
184
185 scope.process_response_for_workerscope(&metadata, &self.policy_container);
188
189 if !metadata.status.is_success() {
196 scope.on_complete(cx, None);
198 return;
199 }
200
201 let is_http_scheme = matches!(metadata.final_url.scheme(), "http" | "https");
204 let not_a_javascript_mime_type = !metadata.content_type.is_some_and(|ct| {
206 let mime: Mime = ct.into_inner().into();
207 SCRIPT_JS_MIMES.contains(&mime.essence_str())
208 });
209
210 if is_http_scheme && not_a_javascript_mime_type {
211 scope.on_complete(cx, None);
213 return;
214 }
215
216 let (source, _) = UTF_8.decode_with_bom_removal(&self.body_bytes);
218
219 let global_scope = scope.upcast::<GlobalScope>();
220
221 let script = global_scope.create_a_classic_script(
224 cx,
225 source,
226 scope.worker_url.borrow().clone(),
227 ScriptFetchOptions::default_classic_script(),
228 ErrorReporting::Unmuted,
229 Some(IntroductionType::WORKER),
230 1,
231 true,
232 );
233
234 scope.on_complete(cx, Some(Script::Classic(script)));
236
237 submit_timing(cx, &self, &response, &timing);
238 }
239
240 fn process_csp_violations(
241 &mut self,
242 _cx: &mut JSContext,
243 _request_id: RequestId,
244 violations: Vec<content_security_policy::Violation>,
245 ) {
246 let scope = self.scope.root();
247
248 if let Some(worker_scope) = scope.downcast::<DedicatedWorkerGlobalScope>() {
249 worker_scope.report_csp_violations(violations);
250 } else if let Some(worker_scope) = scope.downcast::<SharedWorkerGlobalScope>() {
251 worker_scope.report_csp_violations(violations);
252 }
253 }
254}
255
256impl ResourceTimingListener for ScriptFetchContext {
257 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
258 (InitiatorType::Other, self.url.clone())
259 }
260
261 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
262 self.scope.root().global()
263 }
264}
265
266#[dom_struct]
268pub(crate) struct WorkerGlobalScope {
269 globalscope: GlobalScope,
270
271 #[conditional_malloc_size_of]
273 microtask_queue: Rc<MicrotaskQueue>,
274
275 worker_name: DOMString,
276 worker_type: WorkerType,
277
278 #[no_trace]
279 worker_id: WorkerId,
280 #[no_trace]
281 worker_url: DomRefCell<ServoUrl>,
282 #[conditional_malloc_size_of]
283 closing: Arc<AtomicBool>,
284 execution_ready: AtomicBool,
285 #[ignore_malloc_size_of = "Defined in js"]
286 runtime: DomRefCell<Option<Runtime>>,
287 location: MutNullableDom<WorkerLocation>,
288 navigator: MutNullableDom<WorkerNavigator>,
289 crypto: MutNullableDom<Crypto>,
290 #[no_trace]
291 policy_container: DomRefCell<PolicyContainer>,
293
294 #[ignore_malloc_size_of = "Defined in base"]
295 #[no_trace]
296 _devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
299
300 #[ignore_malloc_size_of = "Defined in base"]
301 #[no_trace]
302 devtools_receiver: Option<RoutedReceiver<DevtoolScriptControlMsg>>,
304
305 #[no_trace]
306 navigation_start: CrossProcessInstant,
307 performance: MutNullableDom<Performance>,
308 trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
309
310 #[no_trace]
313 timer_scheduler: RefCell<TimerScheduler>,
314
315 timers: OnceCell<OneshotTimers>,
318
319 #[no_trace]
320 insecure_requests_policy: InsecureRequestsPolicy,
321
322 reporting_observer_list: DomRefCell<Vec<Dom<ReportingObserver>>>,
324
325 report_list: DomRefCell<Vec<Report>>,
327
328 #[no_trace]
330 endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
331
332 #[ignore_malloc_size_of = "Measured by the JS engine"]
337 debugger_global: Heap<Value>,
338
339 #[no_trace]
340 pipeline_id: PipelineId,
341
342 #[conditional_malloc_size_of]
344 task_manager: Rc<TaskManager>,
345
346 #[no_trace]
347 origin: MutableOrigin,
348}
349
350impl WorkerGlobalScope {
351 #[allow(clippy::too_many_arguments)]
352 pub(crate) fn new_inherited(
353 init: WorkerGlobalScopeInit,
354 worker_name: DOMString,
355 worker_type: WorkerType,
356 worker_url: ServoUrl,
357 runtime: Runtime,
358 devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
359 closing: Arc<AtomicBool>,
360 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
361 insecure_requests_policy: InsecureRequestsPolicy,
362 font_context: Option<Arc<FontContext>>,
363 event_loop_sender: Option<ScriptEventLoopSender>,
364 ) -> Self {
365 PipelineNamespace::auto_install();
367
368 let devtools_receiver = match init.from_devtools_sender {
369 Some(..) => Some(devtools_receiver),
370 None => None,
371 };
372
373 Self {
374 globalscope: GlobalScope::new_inherited(
375 init.to_devtools_sender,
376 init.mem_profiler_chan,
377 init.time_profiler_chan,
378 init.script_to_constellation_chan,
379 init.script_to_embedder_chan,
380 init.resource_threads,
381 init.storage_threads,
382 worker_url.clone(),
383 None,
384 #[cfg(feature = "webgpu")]
385 gpu_id_hub,
386 init.inherited_secure_context,
387 init.unminify_js,
388 font_context,
389 ),
390 microtask_queue: runtime.microtask_queue.clone(),
391 worker_id: init.worker_id,
392 worker_name,
393 worker_type,
394 worker_url: DomRefCell::new(worker_url),
395 closing: closing.clone(),
396 execution_ready: AtomicBool::new(false),
397 runtime: DomRefCell::new(Some(runtime)),
398 location: Default::default(),
399 navigator: Default::default(),
400 crypto: Default::default(),
401 policy_container: Default::default(),
402 devtools_receiver,
403 _devtools_sender: init.from_devtools_sender,
404 navigation_start: CrossProcessInstant::now(),
405 performance: Default::default(),
406 timer_scheduler: RefCell::default(),
407 timers: Default::default(),
408 insecure_requests_policy,
409 trusted_types: Default::default(),
410 reporting_observer_list: Default::default(),
411 report_list: Default::default(),
412 endpoints_list: Default::default(),
413 debugger_global: Default::default(),
414 pipeline_id: init.pipeline_id,
415 task_manager: Rc::new(TaskManager::new(
416 event_loop_sender,
417 init.pipeline_id,
418 Some(TaskCanceller { cancelled: closing }),
419 )),
420 origin: MutableOrigin::new(init.origin),
421 }
422 }
423
424 pub(crate) fn timers(&self) -> &OneshotTimers {
425 self.timers
426 .get_or_init(|| OneshotTimers::new(self.upcast()))
427 }
428
429 pub(crate) fn enqueue_microtask(&self, cx: &JSContext, job: Microtask) {
430 self.microtask_queue.enqueue(cx, job);
431 }
432
433 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut JSContext) {
435 if !self.is_closing() {
437 self.microtask_queue.checkpoint(
438 cx,
439 |_| Some(DomRoot::from_ref(&self.globalscope)),
440 vec![DomRoot::from_ref(&self.globalscope)],
441 );
442 }
443 }
444
445 pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
447 self.insecure_requests_policy
448 }
449
450 pub(crate) fn clear_js_runtime(&self) {
452 self.upcast::<GlobalScope>()
453 .remove_web_messaging_and_dedicated_workers_infra();
454
455 let runtime = self.runtime.borrow_mut().take();
457 drop(runtime);
458 }
459
460 pub(crate) fn runtime_handle(&self) -> ParentRuntime {
461 self.runtime
462 .borrow()
463 .as_ref()
464 .unwrap()
465 .prepare_for_new_child()
466 }
467
468 pub(crate) fn devtools_receiver(&self) -> Option<&RoutedReceiver<DevtoolScriptControlMsg>> {
469 self.devtools_receiver.as_ref()
470 }
471
472 pub(crate) fn is_closing(&self) -> bool {
473 self.closing.load(Ordering::SeqCst)
474 }
475
476 pub(crate) fn is_execution_ready(&self) -> bool {
477 self.execution_ready.load(Ordering::Relaxed)
478 }
479
480 pub(crate) fn get_url(&self) -> Ref<'_, ServoUrl> {
481 self.worker_url.borrow()
482 }
483
484 pub(crate) fn set_url(&self, url: ServoUrl) {
485 *self.worker_url.borrow_mut() = url;
486 }
487
488 pub(crate) fn worker_name(&self) -> DOMString {
489 self.worker_name.clone()
490 }
491
492 pub(crate) fn worker_id(&self) -> WorkerId {
493 self.worker_id
494 }
495
496 pub(crate) fn pipeline_id(&self) -> PipelineId {
497 self.pipeline_id
498 }
499
500 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
501 self.task_manager.clone()
502 }
503
504 pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
505 self.policy_container.borrow()
506 }
507
508 pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
509 self.policy_container.borrow_mut().set_csp_list(csp_list);
510 }
511
512 pub(crate) fn set_referrer_policy(&self, referrer_policy: ReferrerPolicy) {
513 self.policy_container
514 .borrow_mut()
515 .set_referrer_policy(referrer_policy);
516 }
517
518 pub(crate) fn append_reporting_observer(&self, reporting_observer: &ReportingObserver) {
519 self.reporting_observer_list
520 .borrow_mut()
521 .push(Dom::from_ref(reporting_observer));
522 }
523
524 pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
525 if let Some(index) = self
526 .reporting_observer_list
527 .borrow()
528 .iter()
529 .position(|observer| &**observer == reporting_observer)
530 {
531 self.reporting_observer_list.borrow_mut().remove(index);
532 }
533 }
534
535 pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
536 self.reporting_observer_list
537 .borrow()
538 .iter()
539 .map(|observer| DomRoot::from_ref(&**observer))
540 .collect()
541 }
542
543 pub(crate) fn append_report(&self, report: Report) {
544 self.report_list.borrow_mut().push(report);
545 let trusted_worker = Trusted::new(self);
546 self.upcast::<GlobalScope>()
547 .task_manager()
548 .dom_manipulation_task_source()
549 .queue(task!(send_to_reporting_endpoints: move || {
550 let worker = trusted_worker.root();
551 let reports = std::mem::take(&mut *worker.report_list.borrow_mut());
552 worker.upcast::<GlobalScope>().send_reports_to_endpoints(
553 reports,
554 worker.endpoints_list.borrow().clone(),
555 );
556 }));
557 }
558
559 pub(crate) fn buffered_reports(&self) -> Vec<Report> {
560 self.report_list.borrow().clone()
561 }
562
563 pub(crate) fn set_endpoints_list(&self, endpoints: Option<Vec<ReportingEndpoint>>) {
564 if let Some(endpoints) = endpoints {
565 *self.endpoints_list.borrow_mut() = endpoints;
566 }
567 }
568
569 pub(crate) fn timer_scheduler(&self) -> RefMut<'_, TimerScheduler> {
571 self.timer_scheduler.borrow_mut()
572 }
573
574 fn initialize_policy_container_for_worker_global_scope(
577 &self,
578 metadata: &Metadata,
579 parent_policy_container: &PolicyContainer,
580 ) {
581 if metadata.final_url.is_local_scheme() {
586 self.set_csp_list(parent_policy_container.csp_list.clone());
592 self.set_referrer_policy(parent_policy_container.get_referrer_policy());
593 return;
594 }
595 self.set_csp_list(parse_csp_list_from_metadata(&metadata.headers));
597 let referrer_policy = metadata
600 .headers
601 .as_ref()
602 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
603 .into();
604 self.set_referrer_policy(referrer_policy);
605 }
606
607 #[expect(unsafe_code)]
609 pub(crate) fn on_complete(&self, cx: &mut JSContext, script: Option<Script>) {
610 let script = match script {
612 Some(Script::Classic(script)) if script.record.is_ok() => Script::Classic(script),
613 Some(Script::Module(module_tree))
614 if module_tree.get_rethrow_error().borrow().is_none() =>
615 {
616 Script::Module(module_tree)
617 },
618 _ => {
619 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
622 dedicated.forward_simple_error_at_worker();
623 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
624 shared.forward_simple_error_at_worker();
625 }
626
627 return;
630 },
631 };
632
633 unsafe {
634 js::rust::wrappers2::JS_AddInterruptCallback(cx, Some(interrupt_callback));
636 }
637
638 if self.is_closing() {
639 return;
640 }
641
642 {
643 let mut realm = enter_auto_realm(cx, self);
644 let cx = &mut realm.current_realm();
645 define_all_exposed_interfaces(cx, self.upcast());
646 self.execution_ready.store(true, Ordering::Relaxed);
648 match script {
649 Script::Classic(script) => {
650 _ = self
651 .globalscope
652 .run_a_classic_script(cx, script, RethrowErrors::No);
653 },
654 Script::Module(module_tree) => {
655 self.globalscope.run_a_module_script(cx, module_tree, false);
656 },
657 _ => unreachable!(),
658 }
659 if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
660 dedicated.fire_queued_messages(cx);
661 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
662 shared.enable_outside_port_message_queue();
664 shared.fire_pending_connect(cx);
667 }
668 }
669 }
670
671 pub(crate) fn process_response_for_workerscope(
674 &self,
675 metadata: &Metadata,
676 policy_container: &PolicyContainer,
677 ) {
678 self.set_url(metadata.final_url.clone());
680
681 self.globalscope
683 .set_creation_url(metadata.final_url.clone());
684
685 self.initialize_policy_container_for_worker_global_scope(metadata, policy_container);
687 self.set_endpoints_list(ReportingEndpoint::parse_reporting_endpoints_header(
688 &metadata.final_url.clone(),
689 &metadata.headers,
690 ));
691 }
692
693 pub(crate) fn origin(&self) -> MutableOrigin {
694 self.origin.clone()
695 }
696}
697
698impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
699 fn Self_(&self) -> DomRoot<WorkerGlobalScope> {
701 DomRoot::from_ref(self)
702 }
703
704 fn IndexedDB(&self, cx: &mut JSContext) -> DomRoot<IDBFactory> {
706 self.upcast::<GlobalScope>().get_indexeddb(cx)
707 }
708
709 fn Location(&self) -> DomRoot<WorkerLocation> {
711 self.location.or_init(|| {
712 WorkerLocation::new(
713 self,
714 self.worker_url.borrow().clone(),
715 CanGc::deprecated_note(),
716 )
717 })
718 }
719
720 fn ImportScripts(
722 &self,
723 cx: &mut JSContext,
724 url_strings: Vec<TrustedScriptURLOrUSVString>,
725 ) -> ErrorResult {
726 if self.worker_type == WorkerType::Module {
729 return Err(Error::Type(
730 c"importScripts() is not allowed in module workers".to_owned(),
731 ));
732 }
733
734 let mut urls = Vec::with_capacity(url_strings.len());
736 for url in url_strings {
738 let url = TrustedScriptURL::get_trusted_type_compliant_string(
742 cx,
743 self.upcast::<GlobalScope>(),
744 url,
745 "WorkerGlobalScope importScripts",
746 )?;
747 let url = self.worker_url.borrow().join(&url.str());
748 match url {
749 Ok(url) => urls.push(url),
750 Err(_) => return Err(Error::Syntax(None)),
751 };
752 }
753
754 for url in urls {
755 let global_scope = self.upcast::<GlobalScope>();
756 let request = RequestBuilder::new(
757 global_scope.webview_id(),
758 UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
759 global_scope.get_referrer(),
760 )
761 .destination(Destination::Script)
762 .credentials_mode(CredentialsMode::Include)
763 .parser_metadata(ParserMetadata::NotParserInserted)
764 .use_url_credentials(true)
765 .with_global_scope(global_scope);
766
767 let (url, bytes, muted_errors) = match load_whole_resource(
769 request,
770 &global_scope.resource_threads().sender(),
771 global_scope,
772 &WorkerCspProcessor {
773 global_scope: DomRoot::from_ref(global_scope),
774 },
775 cx,
776 ) {
777 Err(_) => return Err(Error::Network(None)),
778 Ok((metadata, bytes, muted_errors)) => {
779 if !metadata.status.is_success() {
781 return Err(Error::Network(None));
782 }
783
784 let not_a_javascript_mime_type =
786 !metadata.content_type.clone().is_some_and(|ct| {
787 let mime: Mime = ct.into_inner().into();
788 SCRIPT_JS_MIMES.contains(&mime.essence_str())
789 });
790 if not_a_javascript_mime_type {
791 return Err(Error::Network(None));
792 }
793
794 (metadata.final_url, bytes, muted_errors)
795 },
796 };
797
798 let (source, _) = UTF_8.decode_with_bom_removal(&bytes);
800
801 let script = self.globalscope.create_a_classic_script(
807 cx,
808 source,
809 url,
810 ScriptFetchOptions::default_classic_script(),
811 ErrorReporting::from(muted_errors),
812 Some(IntroductionType::WORKER),
813 1,
814 true,
815 );
816
817 let result = self
819 .globalscope
820 .run_a_classic_script(cx, script, RethrowErrors::Yes);
821
822 if let Err(error) = result {
823 if self.is_closing() {
824 error!("evaluate_script failed (terminated)");
827 } else {
828 error!("evaluate_script failed");
829 return Err(error);
830 }
831 }
832 }
833
834 Ok(())
835 }
836
837 error_event_handler!(error, GetOnerror, SetOnerror);
839
840 event_handler!(languagechange, GetOnlanguagechange, SetOnlanguagechange);
842
843 event_handler!(offline, GetOnoffline, SetOnoffline);
845
846 event_handler!(online, GetOnonline, SetOnonline);
848
849 event_handler!(
851 rejectionhandled,
852 GetOnrejectionhandled,
853 SetOnrejectionhandled
854 );
855
856 event_handler!(
858 unhandledrejection,
859 GetOnunhandledrejection,
860 SetOnunhandledrejection
861 );
862
863 fn Navigator(&self) -> DomRoot<WorkerNavigator> {
865 self.navigator
866 .or_init(|| WorkerNavigator::new(self, CanGc::deprecated_note()))
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 ClearInterval(&self, handle: i32) {
950 self.ClearTimeout(handle);
951 }
952
953 fn QueueMicrotask(&self, cx: &mut JSContext, callback: Rc<VoidFunction>) {
955 self.enqueue_microtask(
956 cx,
957 Microtask::User(UserMicrotask {
958 callback,
959 pipeline: self.pipeline_id(),
960 }),
961 );
962 }
963
964 fn CreateImageBitmap(
966 &self,
967 realm: &mut CurrentRealm,
968 image: ImageBitmapSource,
969 options: &ImageBitmapOptions,
970 ) -> Rc<Promise> {
971 ImageBitmap::create_image_bitmap(self.upcast(), image, 0, 0, None, None, options, realm)
972 }
973
974 fn CreateImageBitmap_(
976 &self,
977 realm: &mut CurrentRealm,
978 image: ImageBitmapSource,
979 sx: i32,
980 sy: i32,
981 sw: i32,
982 sh: i32,
983 options: &ImageBitmapOptions,
984 ) -> Rc<Promise> {
985 ImageBitmap::create_image_bitmap(
986 self.upcast(),
987 image,
988 sx,
989 sy,
990 Some(sw),
991 Some(sh),
992 options,
993 realm,
994 )
995 }
996
997 fn Fetch(
999 &self,
1000 realm: &mut CurrentRealm,
1001 input: RequestOrUSVString,
1002 init: RootedTraceableBox<RequestInit>,
1003 ) -> Rc<Promise> {
1004 Fetch(self.upcast(), input, init, realm)
1005 }
1006
1007 fn Performance(&self) -> DomRoot<Performance> {
1009 self.performance.or_init(|| {
1010 let global_scope = self.upcast::<GlobalScope>();
1011 Performance::new(
1012 global_scope,
1013 self.navigation_start,
1014 CanGc::deprecated_note(),
1015 )
1016 })
1017 }
1018
1019 fn Origin(&self) -> USVString {
1021 USVString(
1022 self.upcast::<GlobalScope>()
1023 .origin()
1024 .immutable()
1025 .ascii_serialization(),
1026 )
1027 }
1028
1029 fn IsSecureContext(&self) -> bool {
1031 self.upcast::<GlobalScope>().is_secure_context()
1032 }
1033
1034 fn StructuredClone(
1036 &self,
1037 cx: &mut JSContext,
1038 value: HandleValue,
1039 options: RootedTraceableBox<StructuredSerializeOptions>,
1040 retval: MutableHandleValue,
1041 ) -> Fallible<()> {
1042 self.upcast::<GlobalScope>()
1043 .structured_clone(cx, value, options, retval)
1044 }
1045
1046 fn TrustedTypes(&self, cx: &mut JSContext) -> DomRoot<TrustedTypePolicyFactory> {
1048 self.trusted_types.or_init(|| {
1049 let global_scope = self.upcast::<GlobalScope>();
1050 TrustedTypePolicyFactory::new(cx, global_scope)
1051 })
1052 }
1053}
1054
1055impl WorkerGlobalScope {
1056 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
1057 let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
1058 if let Some(dedicated) = dedicated {
1059 dedicated.new_script_pair()
1060 } else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
1061 shared.new_script_pair()
1062 } else {
1063 panic!("need to implement a sender for ServiceWorker")
1064 }
1065 }
1066
1067 pub(crate) fn process_event(&self, msg: CommonScriptMsg, cx: &mut JSContext) -> bool {
1071 if self.is_closing() {
1072 return false;
1073 }
1074 match msg {
1075 CommonScriptMsg::Task(_, task, _, _) => task.run_box(cx),
1076 CommonScriptMsg::CollectReports(reports_chan) => {
1077 perform_memory_report(|ops| {
1078 let reports = get_reports(cx, format!("url({})", self.get_url()), ops);
1079 reports_chan.send(ProcessReports::new(reports));
1080 });
1081 },
1082 CommonScriptMsg::ReportCspViolations(_, violations) => {
1083 self.upcast::<GlobalScope>()
1084 .report_csp_violations(cx, violations, None, None);
1085 },
1086 }
1087 true
1088 }
1089
1090 pub(crate) fn close(&self) {
1091 self.closing.store(true, Ordering::SeqCst);
1092 self.upcast::<GlobalScope>()
1093 .task_manager()
1094 .cancel_all_tasks_and_ignore_future_tasks();
1095 if let Some(factory) = self.upcast::<GlobalScope>().get_existing_indexeddb() {
1096 factory.abort_pending_upgrades();
1097 }
1098 }
1099
1100 pub(crate) fn init_debugger_global(
1101 &self,
1102 debugger_global: &DebuggerGlobalScope,
1103 cx: &mut JSContext,
1104 ) {
1105 let mut realm = enter_auto_realm(cx, self);
1106 let cx = &mut realm.current_realm();
1107
1108 rooted!(&in(cx) let mut wrapped_global: Value);
1111 debugger_global
1112 .reflector()
1113 .safe_to_jsval(cx, wrapped_global.handle_mut());
1114 self.debugger_global.set(*wrapped_global);
1115 }
1116
1117 pub(crate) fn handle_devtools_message(&self, msg: DevtoolScriptControlMsg, cx: &mut JSContext) {
1118 match msg {
1119 DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _wants_updates) => {},
1120 DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, reply) => {
1121 let debugger_global_handle = rooted_heap_handle(self, |this| &this.debugger_global);
1122 let debugger_global =
1123 root_from_handlevalue::<DebuggerGlobalScope>(cx, debugger_global_handle)
1124 .expect("must be a debugger global scope");
1125
1126 debugger_global.fire_eval(
1127 cx,
1128 code.into(),
1129 id,
1130 Some(self.worker_id()),
1131 frame_actor_id,
1132 reply,
1133 );
1134 },
1135 _ => debug!("got an unusable devtools control message inside the worker!"),
1136 }
1137 }
1138}
1139
1140#[expect(unsafe_code)]
1141unsafe extern "C" fn interrupt_callback(cx: *mut RawJSContext) -> bool {
1142 let mut cx = unsafe { JSContext::from_ptr(std::ptr::NonNull::new(cx).unwrap()) };
1144 let realm = CurrentRealm::assert(&mut cx);
1145
1146 let global = GlobalScope::from_current_realm(&realm);
1147
1148 let Some(worker) = global.downcast::<WorkerGlobalScope>() else {
1150 return false;
1151 };
1152
1153 !worker.is_closing()
1155}
1156
1157struct WorkerCspProcessor {
1158 global_scope: DomRoot<GlobalScope>,
1159}
1160
1161impl CspViolationsProcessor for WorkerCspProcessor {
1162 fn process_csp_violations(&self, cx: &mut JSContext, violations: Vec<Violation>) {
1163 self.global_scope
1164 .report_csp_violations(cx, violations, None, None);
1165 }
1166}