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