1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::cell::Cell;
8use std::collections::VecDeque;
9use std::sync::Arc;
10use std::sync::atomic::{AtomicBool, Ordering};
11use std::thread::{self, JoinHandle};
12
13use crossbeam_channel::{Receiver, Sender, unbounded};
14#[cfg(feature = "devtools")]
15use devtools_traits::DevtoolScriptControlMsg;
16use dom_struct::dom_struct;
17use fonts::FontContext;
18use js::context::JSContext;
19use js::jsapi::JSObject;
20use js::jsval::UndefinedValue;
21use js::rust::{CustomAutoRooterGuard, HandleValue};
22use net_traits::blob_url_store::UrlWithBlobClaim;
23use net_traits::image_cache::ImageCache;
24use net_traits::policy_container::PolicyContainer;
25use net_traits::request::{
26 CredentialsMode, Destination, InsecureRequestsPolicy, Origin, ParserMetadata,
27 PreloadedResources, Referrer, RequestBuilder, RequestClient, RequestMode,
28};
29use script_bindings::cell::DomRefCell;
30use script_bindings::interfaces::HasOrigin;
31use servo_base::generic_channel::{self, GenericReceiver, GenericSender, RoutedReceiver};
32use servo_base::id::{BrowsingContextId, PipelineId, ScriptEventLoopId, WebViewId};
33use servo_constellation_traits::{
34 ScriptToConstellationMessage, WorkerAnimationFrameTick, WorkerGlobalScopeInit,
35 WorkerScriptLoadOrigin,
36};
37use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
38use style::thread_state::{self, ThreadState};
39
40use crate::conversions::Convert;
41use crate::dom::abstractworker::{MessageData, SimpleWorkerErrorHandler, WorkerScriptMsg};
42use crate::dom::abstractworkerglobalscope::{WorkerEventLoopMethods, run_worker_event_loop};
43use crate::dom::bindings::callback::{ExceptionHandling, RootedCallback, TracedCallback};
44use crate::dom::bindings::codegen::Bindings::AnimationFrameProviderBinding::FrameRequestCallback;
45use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
46use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
47use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
48use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
49use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerOptions, WorkerType};
50use crate::dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible};
51use crate::dom::bindings::inheritance::Castable;
52use crate::dom::bindings::num::Finite;
53use crate::dom::bindings::reflector::DomGlobal;
54use crate::dom::bindings::root::DomRoot;
55use crate::dom::bindings::str::DOMString;
56use crate::dom::bindings::structuredclone;
57use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox};
58use crate::dom::csp::Violation;
59use crate::dom::errorevent::ErrorEvent;
60use crate::dom::event::{Event, EventBubbles, EventCancelable};
61use crate::dom::eventtarget::EventTarget;
62use crate::dom::globalscope::GlobalScope;
63use crate::dom::html::htmlscriptelement::Script;
64use crate::dom::messageevent::MessageEvent;
65#[cfg(feature = "devtools")]
66use crate::dom::types::DebuggerGlobalScope;
67#[cfg(feature = "webgpu")]
68use crate::dom::webgpu::identityhub::IdentityHub;
69use crate::dom::worker::{TrustedWorkerAddress, Worker};
70use crate::dom::workerglobalscope::{
71 ScriptFetchContext, WorkerDevtoolsControlMsg, WorkerGlobalScope,
72};
73use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
74use crate::realms::enter_auto_realm;
75use crate::runtime::script_runtime::ScriptThreadEventCategory::WorkerEvent;
76use crate::runtime::script_runtime::{IntroductionType, Runtime, ThreadSafeJSContext};
77use crate::scripting::script_module::fetch_a_module_script_graph;
78use crate::tasks::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
79use crate::tasks::task_source::TaskSourceName;
80
81pub(crate) struct AutoWorkerReset<'a> {
86 workerscope: &'a DedicatedWorkerGlobalScope,
87 old_worker: Option<TrustedWorkerAddress>,
88}
89
90impl<'a> AutoWorkerReset<'a> {
91 pub(crate) fn new(
92 workerscope: &'a DedicatedWorkerGlobalScope,
93 worker: TrustedWorkerAddress,
94 ) -> AutoWorkerReset<'a> {
95 let old_worker = workerscope.replace_worker(Some(worker));
96 AutoWorkerReset {
97 workerscope,
98 old_worker,
99 }
100 }
101}
102
103impl Drop for AutoWorkerReset<'_> {
104 fn drop(&mut self) {
105 self.workerscope
106 .replace_worker(std::mem::take(&mut self.old_worker));
107 }
108}
109
110pub(crate) enum DedicatedWorkerControlMsg {
112 Exit,
114 AnimationFrameProviderUnsupported,
115 SetNetworkOnlineState(bool),
116}
117
118#[derive(MallocSizeOf)]
119pub(crate) enum DedicatedWorkerScriptMsg {
120 CommonWorker(TrustedWorkerAddress, WorkerScriptMsg),
122 WakeUp,
124}
125
126pub(crate) enum MixedMessage {
127 Worker(DedicatedWorkerScriptMsg),
128 #[cfg(feature = "devtools")]
129 Devtools(DevtoolScriptControlMsg),
130 Control(DedicatedWorkerControlMsg),
131 AnimationFrameTick(WorkerAnimationFrameTick),
132 Timer,
133}
134
135impl QueuedTaskConversion for DedicatedWorkerScriptMsg {
136 fn pipeline_id(&self) -> Option<PipelineId> {
137 None
140 }
141
142 fn into_queued_task(self) -> Option<QueuedTask> {
143 let (worker, common_worker_msg) = match self {
144 DedicatedWorkerScriptMsg::CommonWorker(worker, common_worker_msg) => {
145 (worker, common_worker_msg)
146 },
147 _ => return None,
148 };
149 let script_msg = match common_worker_msg {
150 WorkerScriptMsg::Common(script_msg) => script_msg,
151 _ => return None,
152 };
153 let (event_category, task, pipeline_id, task_source) = match script_msg {
154 CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => {
155 (category, boxed, pipeline_id, task_source)
156 },
157 _ => return None,
158 };
159 Some(QueuedTask {
160 worker: Some(worker),
161 event_category,
162 task,
163 pipeline_id,
164 task_source,
165 })
166 }
167
168 fn from_queued_task(queued_task: QueuedTask) -> Self {
169 let script_msg = CommonScriptMsg::Task(
170 queued_task.event_category,
171 queued_task.task,
172 queued_task.pipeline_id,
173 queued_task.task_source,
174 );
175 DedicatedWorkerScriptMsg::CommonWorker(
176 queued_task.worker.unwrap(),
177 WorkerScriptMsg::Common(script_msg),
178 )
179 }
180
181 fn inactive_msg() -> Self {
182 panic!("Workers should never receive messages marked as inactive");
184 }
185
186 fn wake_up_msg() -> Self {
187 DedicatedWorkerScriptMsg::WakeUp
188 }
189
190 fn is_wake_up(&self) -> bool {
191 matches!(self, DedicatedWorkerScriptMsg::WakeUp)
192 }
193}
194
195unsafe_no_jsmanaged_fields!(TaskQueue<DedicatedWorkerScriptMsg>);
196
197#[dom_struct]
199pub(crate) struct DedicatedWorkerGlobalScope {
200 workerglobalscope: WorkerGlobalScope,
201 #[no_trace]
203 webview_id: WebViewId,
204 task_queue: TaskQueue<DedicatedWorkerScriptMsg>,
205 own_sender: Sender<DedicatedWorkerScriptMsg>,
206 worker: DomRefCell<Option<TrustedWorkerAddress>>,
207 parent_event_loop_sender: ScriptEventLoopSender,
209 #[ignore_malloc_size_of = "ImageCache"]
210 #[no_trace]
211 image_cache: Arc<dyn ImageCache>,
212 #[no_trace]
213 browsing_context: Option<BrowsingContextId>,
214 #[conditional_malloc_size_of]
215 animation_frame_provider_supported: Arc<AtomicBool>,
216 animation_frame_provider_registered: Cell<bool>,
217 animation_frame_ident: Cell<u32>,
219 #[ignore_malloc_size_of = "closures are hard"]
221 animation_frame_list: DomRefCell<VecDeque<(u32, TracedCallback<FrameRequestCallback>)>>,
222 #[ignore_malloc_size_of = "closures are hard"]
224 current_animation_frame_list: DomRefCell<VecDeque<(u32, TracedCallback<FrameRequestCallback>)>>,
225 running_animation_callbacks: Cell<bool>,
227 animation_frame_callbacks_active: Cell<bool>,
229 #[no_trace]
231 animation_frame_tick_sender: Option<GenericSender<WorkerAnimationFrameTick>>,
232 #[no_trace]
234 animation_frame_tick_receiver: Option<RoutedReceiver<WorkerAnimationFrameTick>>,
235 #[no_trace]
237 control_receiver: Receiver<DedicatedWorkerControlMsg>,
238 #[no_trace]
239 queued_worker_tasks: DomRefCell<Vec<MessageData>>,
240}
241
242impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope {
243 type WorkerMsg = DedicatedWorkerScriptMsg;
244 type ControlMsg = DedicatedWorkerControlMsg;
245 type Event = MixedMessage;
246
247 fn task_queue(&self) -> &TaskQueue<DedicatedWorkerScriptMsg> {
248 &self.task_queue
249 }
250
251 fn handle_event(&self, event: MixedMessage, cx: &mut JSContext) -> bool {
252 self.handle_mixed_message(event, cx)
253 }
254
255 fn handle_worker_post_event(
256 &self,
257 worker: &TrustedWorkerAddress,
258 ) -> Option<AutoWorkerReset<'_>> {
259 let ar = AutoWorkerReset::new(self, worker.clone());
260 Some(ar)
261 }
262
263 fn from_control_msg(msg: DedicatedWorkerControlMsg) -> MixedMessage {
264 MixedMessage::Control(msg)
265 }
266
267 fn from_worker_msg(msg: DedicatedWorkerScriptMsg) -> MixedMessage {
268 MixedMessage::Worker(msg)
269 }
270
271 #[cfg(feature = "devtools")]
272 fn from_devtools_msg(msg: DevtoolScriptControlMsg) -> MixedMessage {
273 MixedMessage::Devtools(msg)
274 }
275
276 fn from_timer_msg() -> MixedMessage {
277 MixedMessage::Timer
278 }
279
280 fn from_animation_frame_tick_msg(msg: WorkerAnimationFrameTick) -> Option<MixedMessage> {
281 Some(MixedMessage::AnimationFrameTick(msg))
282 }
283
284 fn animation_frame_tick_receiver(&self) -> Option<&RoutedReceiver<WorkerAnimationFrameTick>> {
285 self.animation_frame_tick_receiver.as_ref()
286 }
287
288 fn control_receiver(&self) -> &Receiver<DedicatedWorkerControlMsg> {
289 &self.control_receiver
290 }
291}
292
293impl DedicatedWorkerGlobalScope {
294 #[allow(clippy::too_many_arguments)]
295 fn new_inherited(
296 init: WorkerGlobalScopeInit,
297 webview_id: WebViewId,
298 worker_name: DOMString,
299 worker_type: WorkerType,
300 worker_url: ServoUrl,
301 from_devtools_receiver: Option<RoutedReceiver<WorkerDevtoolsControlMsg>>,
302 runtime: Runtime,
303 parent_event_loop_sender: ScriptEventLoopSender,
304 own_sender: Sender<DedicatedWorkerScriptMsg>,
305 receiver: Receiver<DedicatedWorkerScriptMsg>,
306 closing: Arc<AtomicBool>,
307 animation_frame_provider_supported: Arc<AtomicBool>,
308 image_cache: Arc<dyn ImageCache>,
309 browsing_context: Option<BrowsingContextId>,
310 animation_frame_tick_sender: Option<GenericSender<WorkerAnimationFrameTick>>,
311 animation_frame_tick_receiver: Option<RoutedReceiver<WorkerAnimationFrameTick>>,
312 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
313 control_receiver: Receiver<DedicatedWorkerControlMsg>,
314 insecure_requests_policy: InsecureRequestsPolicy,
315 font_context: Arc<FontContext>,
316 ) -> DedicatedWorkerGlobalScope {
317 DedicatedWorkerGlobalScope {
318 workerglobalscope: WorkerGlobalScope::new_inherited(
319 init,
320 worker_name,
321 worker_type,
322 worker_url,
323 runtime,
324 from_devtools_receiver,
325 closing,
326 #[cfg(feature = "webgpu")]
327 gpu_id_hub,
328 insecure_requests_policy,
329 font_context,
330 None,
331 ),
332 webview_id,
333 task_queue: TaskQueue::new(receiver),
334 own_sender,
335 parent_event_loop_sender,
336 worker: DomRefCell::new(None),
337 image_cache,
338 browsing_context,
339 animation_frame_provider_supported,
340 animation_frame_provider_registered: Cell::new(false),
341 animation_frame_ident: Cell::new(0),
342 animation_frame_list: DomRefCell::new(VecDeque::new()),
343 current_animation_frame_list: DomRefCell::new(VecDeque::new()),
344 running_animation_callbacks: Cell::new(false),
345 animation_frame_callbacks_active: Cell::new(false),
346 animation_frame_tick_sender,
347 animation_frame_tick_receiver,
348 control_receiver,
349 queued_worker_tasks: Default::default(),
350 }
351 }
352
353 #[expect(clippy::too_many_arguments)]
354 pub(crate) fn new(
355 init: WorkerGlobalScopeInit,
356 webview_id: WebViewId,
357 worker_name: DOMString,
358 worker_type: WorkerType,
359 worker_url: ServoUrl,
360 from_devtools_receiver: Option<RoutedReceiver<WorkerDevtoolsControlMsg>>,
361 runtime: Runtime,
362 parent_event_loop_sender: ScriptEventLoopSender,
363 own_sender: Sender<DedicatedWorkerScriptMsg>,
364 receiver: Receiver<DedicatedWorkerScriptMsg>,
365 closing: Arc<AtomicBool>,
366 animation_frame_provider_supported: Arc<AtomicBool>,
367 image_cache: Arc<dyn ImageCache>,
368 browsing_context: Option<BrowsingContextId>,
369 animation_frame_tick_sender: Option<GenericSender<WorkerAnimationFrameTick>>,
370 animation_frame_tick_receiver: Option<RoutedReceiver<WorkerAnimationFrameTick>>,
371 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
372 control_receiver: Receiver<DedicatedWorkerControlMsg>,
373 insecure_requests_policy: InsecureRequestsPolicy,
374 font_context: Arc<FontContext>,
375 #[cfg(feature = "devtools")] debugger_global: &DebuggerGlobalScope,
376 cx: &mut js::context::JSContext,
377 ) -> DomRoot<DedicatedWorkerGlobalScope> {
378 let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited(
379 init,
380 webview_id,
381 worker_name,
382 worker_type,
383 worker_url,
384 from_devtools_receiver,
385 runtime,
386 parent_event_loop_sender,
387 own_sender,
388 receiver,
389 closing,
390 animation_frame_provider_supported,
391 image_cache,
392 browsing_context,
393 animation_frame_tick_sender,
394 animation_frame_tick_receiver,
395 #[cfg(feature = "webgpu")]
396 gpu_id_hub,
397 control_receiver,
398 insecure_requests_policy,
399 font_context,
400 ));
401 let scope = DedicatedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(
402 cx,
403 &scope.origin(),
404 scope,
405 );
406 #[cfg(feature = "devtools")]
407 scope
408 .upcast::<WorkerGlobalScope>()
409 .init_debugger_global(Some(debugger_global), cx);
410
411 scope
412 }
413
414 #[expect(unsafe_code)]
416 #[allow(clippy::too_many_arguments)]
417 pub(crate) fn run_worker_scope(
418 mut init: WorkerGlobalScopeInit,
419 webview_id: WebViewId,
420 worker_url: UrlWithBlobClaim,
421 #[cfg(feature = "devtools")] from_devtools_receiver: GenericReceiver<
422 DevtoolScriptControlMsg,
423 >,
424 worker: TrustedWorkerAddress,
425 parent_event_loop_sender: ScriptEventLoopSender,
426 own_sender: Sender<DedicatedWorkerScriptMsg>,
427 receiver: Receiver<DedicatedWorkerScriptMsg>,
428 worker_load_origin: WorkerScriptLoadOrigin,
429 worker_options: &WorkerOptions,
430 closing: Arc<AtomicBool>,
431 animation_frame_provider_supported: Arc<AtomicBool>,
432 image_cache: Arc<dyn ImageCache>,
433 browsing_context: Option<BrowsingContextId>,
434 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
435 control_receiver: Receiver<DedicatedWorkerControlMsg>,
436 context_sender: Sender<ThreadSafeJSContext>,
437 insecure_requests_policy: InsecureRequestsPolicy,
438 policy_container: Arc<PolicyContainer>,
439 font_context: Arc<FontContext>,
440 ) -> JoinHandle<()> {
441 let event_loop_id = ScriptEventLoopId::installed()
442 .expect("Should always be in a ScriptThread or in a dedicated worker");
443 let current_global = GlobalScope::current().expect("No current global object");
444 let origin = current_global.origin().immutable().clone();
445 let referrer = current_global.get_referrer();
446 let parent = current_global.runtime_handle();
447 let current_global_ancestor_trustworthy = current_global.has_trustworthy_ancestor_origin();
448 let is_secure_context = current_global.is_secure_context();
449 let is_nested_browsing_context = current_global.is_nested_browsing_context();
450
451 let worker_type = worker_options.type_;
452 let worker_name = worker_options.name.to_string();
453 let credentials = worker_options.credentials.convert();
454
455 thread::Builder::new()
456 .name(format!("WW:{}", worker_url.debug_compact()))
457 .spawn(move || {
458 thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
459 ScriptEventLoopId::install(event_loop_id);
460
461 let WorkerScriptLoadOrigin {
462 referrer_url,
463 pipeline_id,
464 ..
465 } = worker_load_origin;
466
467 let referrer = referrer_url.map(Referrer::ReferrerUrl).unwrap_or(referrer);
468
469 let request_client = RequestClient {
470 preloaded_resources: PreloadedResources::default(),
471 policy_container,
472 origin: Origin::Origin(origin.clone()),
473 is_nested_browsing_context,
474 insecure_requests_policy,
475 has_trustworthy_ancestor_origin: current_global_ancestor_trustworthy,
476 network_partition_key: None,
477 };
478
479 let event_loop_sender = ScriptEventLoopSender::DedicatedWorker {
480 sender: own_sender.clone(),
481 main_thread_worker: worker.clone(),
482 };
483
484 let runtime = unsafe {
485 Runtime::new_with_parent(Some(parent), Some(event_loop_sender.clone()))
486 };
487 let mut cx = unsafe { runtime.cx() };
491 let cx = &mut cx;
492 #[cfg(feature = "devtools")]
493 let debugger_global = DebuggerGlobalScope::new(
494 pipeline_id,
495 init.to_devtools_sender.clone(),
496 init.from_devtools_sender
497 .clone()
498 .expect("Guaranteed by Worker::Constructor"),
499 init.mem_profiler_chan.clone(),
500 init.time_profiler_chan.clone(),
501 init.script_to_constellation_chan.clone(),
502 init.script_to_embedder_chan.clone(),
503 init.resource_threads.clone(),
504 init.storage_threads.clone(),
505 #[cfg(feature = "webgpu")]
506 gpu_id_hub.clone(),
507 cx,
508 );
509 #[cfg(feature = "devtools")]
510 debugger_global.execute(cx);
511
512 let context_for_interrupt = runtime.thread_safe_js_context();
513 let _ = context_sender.send(context_for_interrupt);
514
515 #[cfg(feature = "devtools")]
516 let devtools_mpsc_port = Some(from_devtools_receiver.route_preserving_errors());
517 #[cfg(not(feature = "devtools"))]
518 let devtools_mpsc_port = None;
519 let animation_frame_channel = init
520 .animation_frame_provider_supported
521 .then(|| generic_channel::channel().expect("Failed to create generic channel"));
522 let (animation_frame_tick_sender, animation_frame_tick_receiver) =
523 match animation_frame_channel {
524 Some((sender, receiver)) => {
525 (Some(sender), Some(receiver.route_preserving_errors()))
526 },
527 None => (None, None),
528 };
529
530 if worker_url.scheme() == "data" {
538 if is_secure_context {
540 init.origin = ImmutableOrigin::new_opaque_data_url_worker();
541 } else {
542 init.origin = ImmutableOrigin::new_opaque();
543 }
544 }
545
546 let worker_id = init.worker_id;
547 #[cfg(feature = "devtools")]
548 let devtools_enabled = init.to_devtools_sender.is_some();
549 let dedicated_worker_global_scope = DedicatedWorkerGlobalScope::new(
550 init,
551 webview_id,
552 worker_name.into(),
553 worker_type,
554 worker_url.url(),
555 devtools_mpsc_port,
556 runtime,
557 parent_event_loop_sender,
558 own_sender,
559 receiver,
560 closing,
561 animation_frame_provider_supported,
562 image_cache,
563 browsing_context,
564 animation_frame_tick_sender,
565 animation_frame_tick_receiver,
566 #[cfg(feature = "webgpu")]
567 gpu_id_hub,
568 control_receiver,
569 insecure_requests_policy,
570 font_context,
571 #[cfg(feature = "devtools")]
572 &debugger_global,
573 cx,
574 );
575
576 #[cfg(feature = "devtools")]
577 if devtools_enabled {
578 debugger_global.fire_add_debuggee(
579 cx,
580 dedicated_worker_global_scope.upcast(),
581 pipeline_id,
582 Some(worker_id),
583 );
584 }
585 let worker_global_scope =
586 dedicated_worker_global_scope.upcast::<WorkerGlobalScope>();
587 let global_scope = dedicated_worker_global_scope.upcast::<GlobalScope>();
588
589 {
591 let _ar = AutoWorkerReset::new(&dedicated_worker_global_scope, worker.clone());
592 match worker_type {
593 WorkerType::Classic => {
594 fetch_a_classic_worker_script(
595 worker_global_scope,
596 worker_url,
597 request_client,
598 Destination::Worker,
599 Some(webview_id),
600 referrer,
601 );
602 },
603 WorkerType::Module => {
604 let worker_scope = DomRoot::from_ref(worker_global_scope);
605 fetch_a_module_script_graph(
606 cx,
607 global_scope,
608 worker_url,
609 request_client,
610 Destination::Worker,
611 referrer,
612 credentials,
613 Some(IntroductionType::WORKER),
614 move |cx, module_tree| {
615 let module_script = module_tree.map(|module| {
616 Script::Module(RootedTraceableBox::new(module))
617 });
618 worker_scope.on_complete(cx, module_script);
619 },
620 );
621 },
622 }
623
624 let reporter_name = format!("dedicated-worker-reporter-{}", worker_id);
625 global_scope.mem_profiler_chan().run_with_memory_reporting(
626 || {
627 while !worker_global_scope.is_closing() {
633 run_worker_event_loop(
634 &*dedicated_worker_global_scope,
635 Some(&worker),
636 cx,
637 );
638 }
639 },
640 reporter_name,
641 event_loop_sender,
642 CommonScriptMsg::CollectReports,
643 );
644 }
645
646 dedicated_worker_global_scope.clear_animation_frame_callbacks_and_unregister();
648 global_scope.tear_down();
649 worker_global_scope.clear_js_runtime();
650 })
651 .expect("Thread spawning failed")
652 }
653
654 pub(crate) fn webview_id(&self) -> WebViewId {
655 self.webview_id
656 }
657
658 pub(crate) fn animation_frame_provider_supported(&self) -> bool {
659 self.animation_frame_provider_supported
660 .load(Ordering::SeqCst)
661 }
662
663 pub(crate) fn animation_frame_provider_supported_flag(&self) -> Arc<AtomicBool> {
664 self.animation_frame_provider_supported.clone()
665 }
666
667 fn register_animation_frame_provider(&self) {
668 if !self.animation_frame_provider_supported() {
669 return;
670 }
671
672 let Some(sender) = self.animation_frame_tick_sender.clone() else {
673 return;
674 };
675
676 if self.animation_frame_provider_registered.replace(true) {
677 return;
678 }
679
680 let worker_id = self.upcast::<WorkerGlobalScope>().worker_id();
681 log::debug!(
682 "Registering dedicated worker animation frame provider: worker={worker_id:?} ---->"
683 );
684 let _ = self
685 .upcast::<GlobalScope>()
686 .script_to_constellation_chan()
687 .send(
688 ScriptToConstellationMessage::RegisterWorkerAnimationFrameProvider(
689 worker_id, sender,
690 ),
691 );
692 }
693
694 fn unregister_animation_frame_provider(&self) {
695 if !self.animation_frame_provider_registered.replace(false) {
696 return;
697 }
698
699 self.animation_frame_callbacks_active.set(false);
700 let worker_id = self.upcast::<WorkerGlobalScope>().worker_id();
701 log::debug!(
702 "Unregistering dedicated worker animation frame provider: worker={worker_id:?} ---->"
703 );
704 let _ = self
705 .upcast::<GlobalScope>()
706 .script_to_constellation_chan()
707 .send(ScriptToConstellationMessage::UnregisterWorkerAnimationFrameProvider(worker_id));
708 }
709
710 fn send_animation_frame_callbacks_state(&self, active: bool) {
711 if !self.animation_frame_provider_supported() {
712 return;
713 }
714
715 let worker_id = self.upcast::<WorkerGlobalScope>().worker_id();
716 log::debug!(
717 "Sending dedicated worker animation frame state: worker={worker_id:?}, active={active} ---->",
718 );
719 let _ = self
720 .upcast::<GlobalScope>()
721 .script_to_constellation_chan()
722 .send(
723 ScriptToConstellationMessage::ChangeWorkerAnimationFrameProviderState(
724 worker_id, active,
725 ),
726 );
727 }
728
729 fn set_animation_frame_callbacks_active(&self, active: bool) {
730 if self.animation_frame_callbacks_active.replace(active) == active {
731 return;
732 }
733
734 self.send_animation_frame_callbacks_state(active);
735 }
736
737 fn remove_animation_frame_callback_from(
738 list: &DomRefCell<VecDeque<(u32, TracedCallback<FrameRequestCallback>)>>,
739 ident: u32,
740 ) {
741 let mut list = list.borrow_mut();
742 if let Some(position) = list.iter().position(|(handle, _)| *handle == ident) {
743 list.remove(position);
744 }
745 }
746
747 fn has_animation_frame_callbacks(&self) -> bool {
748 !self.animation_frame_list.borrow().is_empty() ||
749 !self.current_animation_frame_list.borrow().is_empty()
750 }
751
752 pub(crate) fn request_animation_frame(
754 &self,
755 callback: RootedCallback<FrameRequestCallback>,
756 ) -> Fallible<u32> {
757 if !self.animation_frame_provider_supported() {
759 return Err(Error::NotSupported(Some(
760 "Animation frame callbacks are not supported for this worker".to_owned(),
761 )));
762 }
763
764 self.register_animation_frame_provider();
765
766 let ident = self.animation_frame_ident.get() + 1;
770 self.animation_frame_ident.set(ident);
771
772 self.animation_frame_list
775 .borrow_mut()
776 .push_back((ident, callback.to_traced()));
777 log::debug!("Queued dedicated worker animation frame callback: handle={ident} ---->");
778 self.set_animation_frame_callbacks_active(true);
779
780 Ok(ident)
782 }
783
784 pub(crate) fn cancel_animation_frame(&self, ident: u32) -> ErrorResult {
786 if !self.animation_frame_provider_supported() {
788 return Err(Error::NotSupported(Some(
789 "Animation frame callbacks are not supported for this worker".to_owned(),
790 )));
791 }
792
793 Self::remove_animation_frame_callback_from(&self.animation_frame_list, ident);
796
797 Self::remove_animation_frame_callback_from(&self.current_animation_frame_list, ident);
798 log::debug!("Cancelled dedicated worker animation frame callback: handle={ident} ---->");
799
800 if !self.running_animation_callbacks.get() && !self.has_animation_frame_callbacks() {
801 self.set_animation_frame_callbacks_active(false);
802 }
803
804 Ok(())
805 }
806
807 pub(crate) fn run_the_animation_frame_callbacks(&self, cx: &mut JSContext) {
809 if !self.animation_frame_provider_supported() ||
810 self.upcast::<WorkerGlobalScope>().is_closing()
811 {
812 return;
813 }
814
815 let callback_count = self.animation_frame_list.borrow().len();
818 log::debug!("Running dedicated worker animation frame callbacks: count={callback_count}");
819 {
820 let mut pending = self.animation_frame_list.borrow_mut();
821 let mut current = self.current_animation_frame_list.borrow_mut();
822 for _ in 0..callback_count {
823 if let Some(ref callback) = pending.pop_front() {
824 current.push_back(callback.clone());
825 }
826 }
827 }
828
829 self.running_animation_callbacks.set(true);
830 let timing = self.upcast::<GlobalScope>().performance(cx).Now();
831
832 for _ in 0..callback_count {
834 rooted!(&in(cx) let callback = self
837 .current_animation_frame_list
838 .borrow_mut()
839 .pop_front()
840 .map(|(_, callback)| callback));
841
842 if let Some(ref callback) = *callback {
843 let _ = callback.Call__(cx, Finite::wrap(*timing), ExceptionHandling::Report);
845 }
846 }
847
848 self.current_animation_frame_list.borrow_mut().clear();
849 self.running_animation_callbacks.set(false);
850
851 if !self.has_animation_frame_callbacks() {
852 self.set_animation_frame_callbacks_active(false);
853 } else {
854 self.send_animation_frame_callbacks_state(true);
857 }
858 }
859
860 pub(crate) fn clear_animation_frame_callbacks_and_unregister(&self) {
861 let worker_id = self.upcast::<WorkerGlobalScope>().worker_id();
863 log::debug!(
864 "Clearing dedicated worker animation frame callbacks: worker={worker_id:?} ---->"
865 );
866 self.animation_frame_list.borrow_mut().clear();
867 self.current_animation_frame_list.borrow_mut().clear();
868 self.running_animation_callbacks.set(false);
869 self.animation_frame_callbacks_active.set(false);
870 self.unregister_animation_frame_provider();
871 }
872
873 fn replace_worker(
878 &self,
879 new_worker: Option<TrustedWorkerAddress>,
880 ) -> Option<TrustedWorkerAddress> {
881 let old_worker = std::mem::replace(&mut *self.worker.borrow_mut(), new_worker);
882
883 self.upcast::<GlobalScope>()
889 .task_manager()
890 .set_sender(self.event_loop_sender());
891
892 old_worker
893 }
894
895 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
896 self.image_cache.clone()
897 }
898
899 pub(crate) fn event_loop_sender(&self) -> Option<ScriptEventLoopSender> {
900 Some(ScriptEventLoopSender::DedicatedWorker {
901 sender: self.own_sender.clone(),
902 main_thread_worker: self.worker.borrow().clone()?,
903 })
904 }
905
906 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
907 let (sender, receiver) = unbounded();
908 let main_thread_worker = self.worker.borrow().as_ref().unwrap().clone();
909 (
910 ScriptEventLoopSender::DedicatedWorker {
911 sender,
912 main_thread_worker,
913 },
914 ScriptEventLoopReceiver::DedicatedWorker(receiver),
915 )
916 }
917
918 pub(crate) fn fire_queued_messages(&self, cx: &mut JSContext) {
919 let queue: Vec<_> = self.queued_worker_tasks.borrow_mut().drain(..).collect();
920 for msg in queue {
921 if self.upcast::<WorkerGlobalScope>().is_closing() {
922 return;
923 }
924 self.dispatch_message_event(cx, msg);
925 }
926 }
927
928 fn dispatch_message_event(&self, cx: &mut JSContext, msg: MessageData) {
929 let scope = self.upcast::<WorkerGlobalScope>();
930 let target = self.upcast();
931 let mut realm = enter_auto_realm(cx, self);
932 let cx = &mut realm;
933 rooted!(&in(cx) let mut message = UndefinedValue());
934 if let Ok(ports) =
935 structuredclone::read(cx, scope.upcast(), *msg.data, message.handle_mut())
936 {
937 MessageEvent::dispatch_jsval(
938 cx,
939 target,
940 scope.upcast(),
941 message.handle(),
942 Some(msg.origin.ascii_serialization().as_ref()),
943 None,
944 ports,
945 );
946 } else {
947 MessageEvent::dispatch_error(cx, target, scope.upcast());
948 }
949 }
950
951 fn handle_script_event(&self, msg: WorkerScriptMsg, cx: &mut JSContext) {
952 match msg {
953 WorkerScriptMsg::DOMMessage(message_data) => {
954 if self.upcast::<WorkerGlobalScope>().is_execution_ready() {
955 self.dispatch_message_event(cx, message_data);
956 } else {
957 self.queued_worker_tasks.borrow_mut().push(message_data);
958 }
959 },
960 WorkerScriptMsg::Common(msg) => {
961 self.upcast::<WorkerGlobalScope>().process_event(msg, cx);
962 },
963 }
964 }
965
966 fn handle_mixed_message(&self, msg: MixedMessage, cx: &mut JSContext) -> bool {
968 if self.upcast::<WorkerGlobalScope>().is_closing() {
969 return false;
970 }
971 match msg {
973 #[cfg(feature = "devtools")]
974 MixedMessage::Devtools(msg) => self
975 .upcast::<WorkerGlobalScope>()
976 .handle_devtools_message(msg, cx),
977 MixedMessage::Worker(DedicatedWorkerScriptMsg::CommonWorker(linked_worker, msg)) => {
978 let _ar = AutoWorkerReset::new(self, linked_worker);
979 self.handle_script_event(msg, cx);
980 },
981 MixedMessage::Worker(DedicatedWorkerScriptMsg::WakeUp) => {},
982 MixedMessage::Control(DedicatedWorkerControlMsg::Exit) => {
983 return false;
984 },
985 MixedMessage::Control(DedicatedWorkerControlMsg::AnimationFrameProviderUnsupported) => {
986 self.clear_animation_frame_callbacks_and_unregister();
987 self.upcast::<GlobalScope>()
988 .disable_owned_worker_animation_frame_providers();
989 },
990 MixedMessage::Control(DedicatedWorkerControlMsg::SetNetworkOnlineState(online)) => {
991 if self
992 .upcast::<WorkerGlobalScope>()
993 .set_network_online_state(online)
994 {
995 self.upcast::<GlobalScope>()
996 .set_network_online_state_on_dedicated_workers(online);
997 }
998 },
999 MixedMessage::AnimationFrameTick(_) => {
1000 self.run_the_animation_frame_callbacks(cx);
1003 },
1004 MixedMessage::Timer => {},
1005 }
1006 true
1007 }
1008
1009 pub(crate) fn forward_error_to_worker_object(&self, error_info: ErrorInfo) {
1011 let worker = self.worker.borrow().as_ref().unwrap().clone();
1013 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
1014 let task = Box::new(task!(forward_error_to_worker_object: move |cx| {
1015 let worker = worker.root();
1016 let global = worker.global();
1017
1018 let event = ErrorEvent::new(
1021 cx,
1022 &global,
1023 atom!("error"),
1024 EventBubbles::DoesNotBubble,
1025 EventCancelable::Cancelable,
1026 error_info.message.as_str().into(),
1027 error_info.filename.as_str().into(),
1028 error_info.lineno,
1029 error_info.column,
1030 HandleValue::null(),
1031 );
1032
1033 if event.upcast::<Event>().fire(cx, worker.upcast::<EventTarget>()) {
1035 global.report_an_error(cx, error_info, HandleValue::null());
1036 }
1037 }));
1038 self.parent_event_loop_sender
1039 .send(CommonScriptMsg::Task(
1040 WorkerEvent,
1041 task,
1042 Some(pipeline_id),
1043 TaskSourceName::DOMManipulation,
1044 ))
1045 .unwrap();
1046 }
1047
1048 fn post_message_impl(
1050 &self,
1051 cx: &mut JSContext,
1052 message: HandleValue,
1053 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1054 ) -> ErrorResult {
1055 let data = structuredclone::write(cx, message, Some(transfer))?;
1056 let worker = self.worker.borrow().as_ref().unwrap().clone();
1057 let global_scope = self.upcast::<GlobalScope>();
1058 let pipeline_id = global_scope.pipeline_id();
1059 let task = Box::new(task!(post_worker_message: move |cx| {
1060 Worker::handle_message(worker, data, cx);
1061 }));
1062 self.parent_event_loop_sender
1063 .send(CommonScriptMsg::Task(
1064 WorkerEvent,
1065 task,
1066 Some(pipeline_id),
1067 TaskSourceName::DOMManipulation,
1068 ))
1069 .expect("Sending to parent failed");
1070 Ok(())
1071 }
1072
1073 pub(crate) fn browsing_context(&self) -> Option<BrowsingContextId> {
1074 self.browsing_context
1075 }
1076
1077 pub(crate) fn report_csp_violations(&self, violations: Vec<Violation>) {
1078 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
1079 self.parent_event_loop_sender
1080 .send(CommonScriptMsg::ReportCspViolations(
1081 pipeline_id,
1082 violations,
1083 ))
1084 .unwrap_or_else(|error| {
1085 log::warn!("Failed to send CSP violations to parent event loop: {error}");
1086 });
1087 }
1088
1089 pub(crate) fn forward_simple_error_at_worker(&self) {
1090 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
1091 let worker = self.worker.borrow().clone().expect("worker must be set");
1092 self.parent_event_loop_sender
1093 .send(CommonScriptMsg::Task(
1094 WorkerEvent,
1095 Box::new(SimpleWorkerErrorHandler::new(worker)),
1096 Some(pipeline_id),
1097 TaskSourceName::DOMManipulation,
1098 ))
1099 .expect("Sending to parent failed");
1100 }
1101}
1102
1103pub(crate) fn fetch_a_classic_worker_script(
1105 workerscope: &WorkerGlobalScope,
1106 url_with_blob_lock: UrlWithBlobClaim,
1107 fetch_client: RequestClient,
1108 destination: Destination,
1109 webview_id: Option<WebViewId>,
1110 referrer: Referrer,
1111) {
1112 let policy_container = fetch_client.policy_container.clone();
1113
1114 let request = RequestBuilder::new(webview_id, url_with_blob_lock.clone(), referrer)
1116 .client(fetch_client)
1118 .pipeline_id(Some(workerscope.pipeline_id()))
1119 .destination(destination)
1121 .mode(RequestMode::SameOrigin)
1124 .credentials_mode(CredentialsMode::CredentialsSameOrigin)
1126 .parser_metadata(ParserMetadata::NotParserInserted)
1128 .use_url_credentials(true);
1130
1131 let context = ScriptFetchContext::new(url_with_blob_lock.url(), policy_container);
1132 let global = workerscope.upcast::<GlobalScope>();
1133 let task_source = global.task_manager().networking_task_source().to_sendable();
1134 global.fetch(request, context, task_source);
1135}
1136
1137impl DedicatedWorkerGlobalScopeMethods<crate::DomTypeHolder> for DedicatedWorkerGlobalScope {
1138 fn Name(&self) -> DOMString {
1140 self.workerglobalscope.worker_name()
1141 }
1142
1143 fn PostMessage(
1145 &self,
1146 cx: &mut JSContext,
1147 message: HandleValue,
1148 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
1149 ) -> ErrorResult {
1150 self.post_message_impl(cx, message, transfer)
1151 }
1152
1153 fn PostMessage_(
1155 &self,
1156 cx: &mut JSContext,
1157 message: HandleValue,
1158 options: &StructuredSerializeOptions,
1159 ) -> ErrorResult {
1160 auto_root!(&in(cx) let guard =
1161 options
1162 .transfer
1163 .iter()
1164 .map(|js| js.get())
1165 .collect::<Vec<_>>());
1166 self.post_message_impl(cx, message, guard)
1167 }
1168
1169 fn Close(&self) {
1171 self.upcast::<WorkerGlobalScope>().close();
1173 }
1174
1175 fn RequestAnimationFrame(
1177 &self,
1178 callback: RootedCallback<FrameRequestCallback>,
1179 ) -> Fallible<u32> {
1180 self.request_animation_frame(callback)
1181 }
1182
1183 fn CancelAnimationFrame(&self, ident: u32) -> ErrorResult {
1185 self.cancel_animation_frame(ident)
1186 }
1187
1188 event_handler!(message, GetOnmessage, SetOnmessage);
1190
1191 event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
1193}
1194
1195impl HasOrigin for DedicatedWorkerGlobalScope {
1196 fn origin(&self) -> MutableOrigin {
1197 self.upcast::<WorkerGlobalScope>().origin()
1198 }
1199}