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