1use std::collections::VecDeque;
6use std::io;
7use std::sync::Arc;
8use std::sync::atomic::AtomicBool;
9use std::thread::{self, JoinHandle};
10
11use content_security_policy::Violation;
12use crossbeam_channel::{Receiver, Sender, unbounded};
13use devtools_traits::DevtoolScriptControlMsg;
14use dom_struct::dom_struct;
15use fonts::FontContext;
16use js::context::JSContext;
17use js::jsval::UndefinedValue;
18use net_traits::blob_url_store::UrlWithBlobClaim;
19use net_traits::image_cache::ImageCache;
20use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
21use net_traits::request::{
22 CredentialsMode, Destination, InsecureRequestsPolicy, Origin, PreloadedResources, Referrer,
23 RequestClient,
24};
25use script_bindings::cell::DomRefCell;
26use script_bindings::conversions::SafeToJSValConvertible;
27use script_bindings::interfaces::HasOrigin;
28use servo_base::generic_channel::{GenericReceiver, RoutedReceiver};
29use servo_base::id::{BrowsingContextId, ScriptEventLoopId, WebViewId};
30use servo_constellation_traits::{MessagePortImpl, WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
31use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
32use style::thread_state::{self, ThreadState};
33use stylo_atoms::Atom;
34use uuid::Uuid;
35
36use crate::dom::abstractworker::{SimpleWorkerErrorHandler, WorkerScriptMsg};
37use crate::dom::abstractworkerglobalscope::{WorkerEventLoopMethods, run_worker_event_loop};
38use crate::dom::bindings::codegen::Bindings::SharedWorkerGlobalScopeBinding;
39use crate::dom::bindings::codegen::Bindings::SharedWorkerGlobalScopeBinding::SharedWorkerGlobalScopeMethods;
40use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
41use crate::dom::bindings::codegen::UnionTypes::WindowProxyOrMessagePortOrServiceWorker;
42use crate::dom::bindings::inheritance::Castable;
43use crate::dom::bindings::refcounted::Trusted;
44use crate::dom::bindings::root::{Dom, DomRoot};
45use crate::dom::bindings::str::DOMString;
46use crate::dom::bindings::trace::CustomTraceable;
47use crate::dom::dedicatedworkerglobalscope::fetch_a_classic_worker_script;
48use crate::dom::event::Event;
49use crate::dom::event::messageevent::MessageEvent;
50use crate::dom::eventtarget::EventTarget;
51use crate::dom::globalscope::GlobalScope;
52use crate::dom::html::htmlscriptelement::Script;
53use crate::dom::messageport::MessagePort;
54use crate::dom::sharedworker::{SharedWorker, SharedWorkerStorageKey, TrustedSharedWorkerAddress};
55use crate::dom::types::DebuggerGlobalScope;
56#[cfg(feature = "webgpu")]
57use crate::dom::webgpu::identityhub::IdentityHub;
58use crate::dom::workerglobalscope::WorkerGlobalScope;
59use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
60use crate::script_module::{ModuleFetchClient, fetch_a_module_worker_script_graph};
61use crate::script_runtime::ScriptThreadEventCategory::WorkerEvent;
62use crate::script_runtime::{CanGc, Runtime};
63use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
64use crate::task_source::TaskSourceName;
65
66pub(crate) enum SharedWorkerScriptMsg {
67 CommonWorker(WorkerScriptMsg),
68 Connect(MessagePortImpl),
69 WakeUp,
70}
71
72#[allow(dead_code)]
73pub(crate) enum SharedWorkerControlMsg {
74 Exit,
75}
76
77pub(crate) enum MixedMessage {
78 SharedWorker(SharedWorkerScriptMsg),
79 Devtools(DevtoolScriptControlMsg),
80 Control(SharedWorkerControlMsg),
81 Timer,
82}
83
84struct SharedWorkerRegistrationCleanup {
85 registration_id: Uuid,
86}
87
88impl Drop for SharedWorkerRegistrationCleanup {
89 fn drop(&mut self) {
90 SharedWorker::unregister_shared_worker(self.registration_id);
91 }
92}
93
94impl QueuedTaskConversion for SharedWorkerScriptMsg {
95 fn task_source_name(&self) -> Option<&TaskSourceName> {
96 let script_msg = match self {
97 SharedWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(script_msg)) => script_msg,
98 _ => return None,
99 };
100 match script_msg {
101 CommonScriptMsg::Task(_category, _boxed, _pipeline_id, task_source) => {
102 Some(task_source)
103 },
104 _ => None,
105 }
106 }
107
108 fn pipeline_id(&self) -> Option<servo_base::id::PipelineId> {
109 None
110 }
111
112 fn into_queued_task(self) -> Option<QueuedTask> {
113 let script_msg = match self {
114 SharedWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(script_msg)) => script_msg,
115 _ => return None,
116 };
117 let (event_category, task, pipeline_id, task_source) = match script_msg {
118 CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => {
119 (category, boxed, pipeline_id, task_source)
120 },
121 _ => return None,
122 };
123 Some(QueuedTask {
124 worker: None,
125 event_category,
126 task,
127 pipeline_id,
128 task_source,
129 })
130 }
131
132 fn from_queued_task(queued_task: QueuedTask) -> Self {
133 let script_msg = CommonScriptMsg::Task(
134 queued_task.event_category,
135 queued_task.task,
136 queued_task.pipeline_id,
137 queued_task.task_source,
138 );
139 SharedWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(script_msg))
140 }
141
142 fn inactive_msg() -> Self {
143 panic!("Workers should never receive messages marked as inactive");
144 }
145
146 fn wake_up_msg() -> Self {
147 SharedWorkerScriptMsg::WakeUp
148 }
149
150 fn is_wake_up(&self) -> bool {
151 matches!(self, SharedWorkerScriptMsg::WakeUp)
152 }
153}
154
155unsafe_no_jsmanaged_fields!(TaskQueue<SharedWorkerScriptMsg>);
156
157#[dom_struct]
159pub(crate) struct SharedWorkerGlobalScope {
160 workerglobalscope: WorkerGlobalScope,
161 #[no_trace]
163 webview_id: WebViewId,
164 #[ignore_malloc_size_of = "Defined in std"]
165 task_queue: TaskQueue<SharedWorkerScriptMsg>,
166 own_sender: Sender<SharedWorkerScriptMsg>,
167 worker: DomRefCell<Option<TrustedSharedWorkerAddress>>,
168 parent_event_loop_sender: ScriptEventLoopSender,
169 #[ignore_malloc_size_of = "ImageCache"]
170 #[no_trace]
171 image_cache: Arc<dyn ImageCache>,
172 #[no_trace]
173 browsing_context: Option<BrowsingContextId>,
174 pending_connect: DomRefCell<VecDeque<Dom<MessagePort>>>,
176 #[no_trace]
177 control_receiver: Receiver<SharedWorkerControlMsg>,
178 debugger_global: Dom<DebuggerGlobalScope>,
179 #[no_trace]
181 storage_key: SharedWorkerStorageKey,
182 #[no_trace]
183 constructor_origin: ImmutableOrigin,
184 #[no_trace]
185 constructor_url: ServoUrl,
186 #[no_trace]
187 credentials: CredentialsMode,
188 extended_lifetime: bool,
189 #[no_trace]
190 registration_id: Uuid,
191}
192
193impl WorkerEventLoopMethods for SharedWorkerGlobalScope {
194 type WorkerMsg = SharedWorkerScriptMsg;
195 type ControlMsg = SharedWorkerControlMsg;
196 type Event = MixedMessage;
197
198 fn task_queue(&self) -> &TaskQueue<SharedWorkerScriptMsg> {
199 &self.task_queue
200 }
201
202 fn handle_event(&self, event: MixedMessage, cx: &mut JSContext) -> bool {
203 self.handle_mixed_message(event, cx)
204 }
205
206 fn handle_worker_post_event(
207 &self,
208 _worker: &crate::dom::worker::TrustedWorkerAddress,
209 ) -> Option<crate::dom::dedicatedworkerglobalscope::AutoWorkerReset<'_>> {
210 None
211 }
212
213 fn from_control_msg(msg: SharedWorkerControlMsg) -> MixedMessage {
214 MixedMessage::Control(msg)
215 }
216
217 fn from_worker_msg(msg: SharedWorkerScriptMsg) -> MixedMessage {
218 MixedMessage::SharedWorker(msg)
219 }
220
221 fn from_devtools_msg(msg: DevtoolScriptControlMsg) -> MixedMessage {
222 MixedMessage::Devtools(msg)
223 }
224
225 fn from_timer_msg() -> MixedMessage {
226 MixedMessage::Timer
227 }
228
229 fn control_receiver(&self) -> &Receiver<SharedWorkerControlMsg> {
230 &self.control_receiver
231 }
232}
233
234impl SharedWorkerGlobalScope {
235 #[allow(clippy::too_many_arguments)]
236 fn new_inherited(
237 init: WorkerGlobalScopeInit,
238 webview_id: WebViewId,
239 worker_name: DOMString,
240 worker_type: WorkerType,
241 worker_url: ServoUrl,
242 worker: TrustedSharedWorkerAddress,
243 parent_event_loop_sender: ScriptEventLoopSender,
244 from_devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
245 runtime: Runtime,
246 own_sender: Sender<SharedWorkerScriptMsg>,
247 receiver: Receiver<SharedWorkerScriptMsg>,
248 closing: Arc<AtomicBool>,
249 image_cache: Arc<dyn ImageCache>,
250 browsing_context: Option<BrowsingContextId>,
251 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
252 control_receiver: Receiver<SharedWorkerControlMsg>,
253 insecure_requests_policy: InsecureRequestsPolicy,
254 font_context: Option<Arc<FontContext>>,
255 debugger_global: &DebuggerGlobalScope,
256 storage_key: SharedWorkerStorageKey,
257 constructor_origin: ImmutableOrigin,
258 constructor_url: ServoUrl,
259 credentials: CredentialsMode,
260 extended_lifetime: bool,
261 registration_id: Uuid,
262 ) -> SharedWorkerGlobalScope {
263 SharedWorkerGlobalScope {
264 workerglobalscope: WorkerGlobalScope::new_inherited(
265 init,
266 worker_name,
267 worker_type,
268 worker_url,
269 runtime,
270 from_devtools_receiver,
271 closing,
272 #[cfg(feature = "webgpu")]
273 gpu_id_hub,
274 insecure_requests_policy,
275 font_context,
276 Some(ScriptEventLoopSender::SharedWorker(own_sender.clone())),
277 ),
278 webview_id,
279 task_queue: TaskQueue::new(receiver, own_sender.clone()),
280 own_sender,
281 worker: DomRefCell::new(Some(worker)),
282 parent_event_loop_sender,
283 image_cache,
284 browsing_context,
285 pending_connect: DomRefCell::new(VecDeque::new()),
286 control_receiver,
287 debugger_global: Dom::from_ref(debugger_global),
288 storage_key,
289 constructor_origin,
290 constructor_url,
291 credentials,
292 extended_lifetime,
293 registration_id,
294 }
295 }
296
297 #[allow(clippy::too_many_arguments)]
298 pub(crate) fn new(
299 init: WorkerGlobalScopeInit,
300 webview_id: WebViewId,
301 worker_name: DOMString,
302 worker_type: WorkerType,
303 worker_url: ServoUrl,
304 worker: TrustedSharedWorkerAddress,
305 parent_event_loop_sender: ScriptEventLoopSender,
306 from_devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
307 runtime: Runtime,
308 own_sender: Sender<SharedWorkerScriptMsg>,
309 receiver: Receiver<SharedWorkerScriptMsg>,
310 closing: Arc<AtomicBool>,
311 image_cache: Arc<dyn ImageCache>,
312 browsing_context: Option<BrowsingContextId>,
313 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
314 control_receiver: Receiver<SharedWorkerControlMsg>,
315 insecure_requests_policy: InsecureRequestsPolicy,
316 font_context: Option<Arc<FontContext>>,
317 debugger_global: &DebuggerGlobalScope,
318 storage_key: SharedWorkerStorageKey,
319 constructor_origin: ImmutableOrigin,
320 constructor_url: ServoUrl,
321 credentials: CredentialsMode,
322 extended_lifetime: bool,
323 registration_id: Uuid,
324 cx: &mut js::context::JSContext,
325 ) -> DomRoot<SharedWorkerGlobalScope> {
326 let scope = Box::new(SharedWorkerGlobalScope::new_inherited(
327 init,
328 webview_id,
329 worker_name,
330 worker_type,
331 worker_url,
332 worker,
333 parent_event_loop_sender,
334 from_devtools_receiver,
335 runtime,
336 own_sender,
337 receiver,
338 closing,
339 image_cache,
340 browsing_context,
341 #[cfg(feature = "webgpu")]
342 gpu_id_hub,
343 control_receiver,
344 insecure_requests_policy,
345 font_context,
346 debugger_global,
347 storage_key,
348 constructor_origin,
349 constructor_url,
350 credentials,
351 extended_lifetime,
352 registration_id,
353 ));
354 SharedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(cx, &scope.origin(), scope)
355 }
356
357 #[expect(unsafe_code)]
359 #[allow(clippy::too_many_arguments)]
360 pub(crate) fn run_shared_worker_scope(
361 mut init: WorkerGlobalScopeInit,
362 webview_id: WebViewId,
363 browsing_context: Option<BrowsingContextId>,
364 worker_name: DOMString,
365 worker_type: WorkerType,
366 worker_url: UrlWithBlobClaim,
367 worker: TrustedSharedWorkerAddress,
368 parent_event_loop_sender: ScriptEventLoopSender,
369 from_devtools_receiver: GenericReceiver<DevtoolScriptControlMsg>,
370 own_sender: Sender<SharedWorkerScriptMsg>,
371 receiver: Receiver<SharedWorkerScriptMsg>,
372 worker_load_origin: WorkerScriptLoadOrigin,
373 closing: Arc<AtomicBool>,
374 image_cache: Arc<dyn ImageCache>,
375 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
376 control_receiver: Receiver<SharedWorkerControlMsg>,
377 setup_sender: Sender<bool>,
378 registration_receiver: Receiver<()>,
379 registration_id: Uuid,
380 credentials: CredentialsMode,
381 extended_lifetime: bool,
382 constructor_origin: ImmutableOrigin,
383 constructor_url: ServoUrl,
384 storage_key: SharedWorkerStorageKey,
385 insecure_requests_policy: InsecureRequestsPolicy,
386 policy_container: PolicyContainer,
387 font_context: Option<Arc<FontContext>>,
388 ) -> io::Result<JoinHandle<()>> {
389 let event_loop_id = ScriptEventLoopId::installed()
390 .expect("Should always be in a ScriptThread or in a worker");
391 let current_global = GlobalScope::current().expect("No current global object");
392 let origin = current_global.origin().immutable().clone();
393 let referrer = current_global.get_referrer();
394 let is_secure_context = current_global.is_secure_context();
395 let current_global_ancestor_trustworthy = current_global.has_trustworthy_ancestor_origin();
396 let is_nested_browsing_context = current_global.is_nested_browsing_context();
397 let worker_name = worker_name.to_string();
398
399 thread::Builder::new()
400 .name(format!("SWW:{}", worker_url.debug_compact()))
401 .spawn(move || {
402 thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
406 ScriptEventLoopId::install(event_loop_id);
407
408 let WorkerScriptLoadOrigin {
409 referrer_url,
410 pipeline_id,
411 ..
412 } = worker_load_origin;
413
414 let referrer = referrer_url.map(Referrer::ReferrerUrl).unwrap_or(referrer);
415
416 let request_client = RequestClient {
417 preloaded_resources: PreloadedResources::default(),
418 policy_container: RequestPolicyContainer::PolicyContainer(
419 policy_container.clone(),
420 ),
421 origin: Origin::Origin(origin.clone()),
422 is_nested_browsing_context,
423 insecure_requests_policy,
424 };
425
426 let event_loop_sender = ScriptEventLoopSender::SharedWorker(own_sender.clone());
427
428 let runtime = Runtime::new(Some(event_loop_sender.clone()));
432 let mut cx = unsafe { runtime.cx() };
436 let cx = &mut cx;
437 let debugger_global = DebuggerGlobalScope::new(
438 pipeline_id,
439 init.to_devtools_sender.clone(),
440 init.from_devtools_sender
441 .clone()
442 .expect("Guaranteed by SharedWorker::Constructor"),
443 init.mem_profiler_chan.clone(),
444 init.time_profiler_chan.clone(),
445 init.script_to_constellation_chan.clone(),
446 init.script_to_embedder_chan.clone(),
447 init.resource_threads.clone(),
448 init.storage_threads.clone(),
449 #[cfg(feature = "webgpu")]
450 gpu_id_hub.clone(),
451 cx,
452 );
453 debugger_global.execute(cx);
454
455 let devtools_mpsc_port = from_devtools_receiver.route_preserving_errors();
456
457 let worker_id = init.worker_id;
458 let devtools_enabled = init.to_devtools_sender.is_some();
459 if worker_url.scheme() == "data" {
461 if is_secure_context {
462 init.origin = ImmutableOrigin::new_opaque_data_url_worker();
463 } else {
464 init.origin = ImmutableOrigin::new_opaque();
465 }
466 }
467 let global = SharedWorkerGlobalScope::new(
470 init,
471 webview_id,
472 worker_name.into(),
473 worker_type,
474 worker_url.url(),
475 worker,
476 parent_event_loop_sender,
477 devtools_mpsc_port,
478 runtime,
479 own_sender,
480 receiver,
481 closing,
482 image_cache,
483 browsing_context,
484 #[cfg(feature = "webgpu")]
485 gpu_id_hub,
486 control_receiver,
487 insecure_requests_policy,
488 font_context,
489 &debugger_global,
490 storage_key,
491 constructor_origin,
492 constructor_url,
493 credentials,
494 extended_lifetime,
495 registration_id,
496 cx,
497 );
498 let scope = global.upcast::<WorkerGlobalScope>();
499 let global_scope = global.upcast::<GlobalScope>();
500 let worker_is_secure_context = global_scope.is_secure_context();
502 if devtools_enabled {
503 debugger_global.fire_add_debuggee(
504 cx,
505 global_scope,
506 pipeline_id,
507 Some(worker_id),
508 );
509 }
510
511 if setup_sender.send(worker_is_secure_context).is_err() {
512 scope.clear_js_runtime();
513 return;
514 }
515
516 if registration_receiver.recv().is_err() {
517 scope.clear_js_runtime();
518 return;
519 }
520 let _registration_cleanup = SharedWorkerRegistrationCleanup { registration_id };
523
524 let fetch_client = ModuleFetchClient {
525 insecure_requests_policy,
526 has_trustworthy_ancestor_origin: current_global_ancestor_trustworthy,
527 policy_container,
528 client: request_client,
529 pipeline_id,
530 origin,
531 };
532
533 match worker_type {
537 WorkerType::Classic => {
538 fetch_a_classic_worker_script(
539 scope,
540 worker_url,
541 fetch_client,
542 Destination::SharedWorker,
543 Some(webview_id),
544 referrer,
545 );
546 },
547 WorkerType::Module => {
548 let worker_scope = DomRoot::from_ref(scope);
549 fetch_a_module_worker_script_graph(
550 cx,
551 global_scope,
552 worker_url.url(),
553 fetch_client,
554 Destination::SharedWorker,
555 referrer,
556 credentials,
557 move |cx, module_tree| {
558 worker_scope.on_complete(cx, module_tree.map(Script::Module));
559 },
560 );
561 },
562 }
563
564 let reporter_name = format!("shared-worker-reporter-{}", worker_id);
565 scope
566 .upcast::<GlobalScope>()
567 .mem_profiler_chan()
568 .run_with_memory_reporting(
569 || {
570 while !scope.is_closing() {
572 run_worker_event_loop(&*global, None, cx);
573 }
574 },
575 reporter_name,
576 event_loop_sender,
577 CommonScriptMsg::CollectReports,
578 );
579
580 scope.clear_js_runtime();
581 })
582 }
583
584 pub(crate) fn event_loop_sender(&self) -> ScriptEventLoopSender {
585 ScriptEventLoopSender::SharedWorker(self.own_sender.clone())
586 }
587
588 pub(crate) fn webview_id(&self) -> WebViewId {
589 self.webview_id
590 }
591
592 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
593 self.image_cache.clone()
594 }
595
596 pub(crate) fn browsing_context(&self) -> Option<BrowsingContextId> {
597 self.browsing_context
598 }
599
600 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
601 let (sender, receiver) = unbounded();
602 (
603 ScriptEventLoopSender::SharedWorker(sender),
604 ScriptEventLoopReceiver::SharedWorker(receiver),
605 )
606 }
607
608 pub(crate) fn forward_simple_error_at_worker(&self) {
610 SharedWorker::unregister_shared_worker(self.registration_id);
611 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
612 let worker = self.worker.borrow().clone().expect("worker must be set");
613 self.parent_event_loop_sender
615 .send(CommonScriptMsg::Task(
616 WorkerEvent,
617 Box::new(SimpleWorkerErrorHandler::new(worker)),
618 Some(pipeline_id),
619 TaskSourceName::DOMManipulation,
620 ))
621 .expect("Sending to parent failed");
622 }
623
624 pub(crate) fn report_csp_violations(&self, violations: Vec<Violation>) {
625 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
626 self.parent_event_loop_sender
627 .send(CommonScriptMsg::ReportCspViolations(
628 pipeline_id,
629 violations,
630 ))
631 .unwrap_or_else(|error| {
632 log::warn!("Failed to send CSP violations to parent event loop: {error}");
633 });
634 }
635
636 pub(crate) fn enable_outside_port_message_queue(&self) {
638 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
639 let worker = self.worker.borrow().clone().expect("worker must be set");
640
641 self.parent_event_loop_sender
642 .send(CommonScriptMsg::Task(
643 WorkerEvent,
644 Box::new(
645 task!(sharedworker_enable_outside_port_message_queue: move |cx| {
646 SharedWorker::enable_outside_port_message_queue(worker, cx);
647 }),
648 ),
649 Some(pipeline_id),
650 TaskSourceName::DOMManipulation,
651 ))
652 .expect("Sending to parent failed");
653 }
654
655 fn handle_connect(
656 &self,
657 port_impl: MessagePortImpl,
658 cx: &mut JSContext,
659 ) -> DomRoot<MessagePort> {
660 let inside_port = MessagePort::new_transferred(
662 self.upcast::<GlobalScope>(),
663 *port_impl.message_port_id(),
664 port_impl.entangled_port_id(),
665 CanGc::from_cx(cx),
666 );
667 self.upcast::<GlobalScope>()
668 .track_message_port(&inside_port, Some(port_impl));
669 inside_port
670 }
671
672 fn dispatch_connect_event(&self, inside_port: &MessagePort) {
674 let worker_global = Trusted::new(self);
675 let inside_port = Trusted::new(inside_port);
676
677 self.upcast::<GlobalScope>()
678 .task_manager()
679 .dom_manipulation_task_source()
680 .queue(task!(sharedworker_connect_event: move |cx| {
681 let worker_global = worker_global.root();
682 let worker_global = &*worker_global;
683 let inside_port = inside_port.root();
684
685 rooted!(&in(cx) let mut data = UndefinedValue());
686 DOMString::from("").safe_to_jsval(cx,
687 data.handle_mut(),
688 );
689
690 let source = WindowProxyOrMessagePortOrServiceWorker::MessagePort(
691 inside_port.clone(),
692 );
693 let event = MessageEvent::new(
694 cx,
695 worker_global.upcast::<GlobalScope>(),
696 Atom::from("connect"),
697 false,
698 false,
699 data.handle(),
700 DOMString::from(""),
701 Some(&source),
702 DOMString::new(),
703 vec![inside_port],
704 );
705
706 event
707 .upcast::<Event>()
708 .fire(cx, worker_global.upcast::<EventTarget>());
709 }));
710 }
711
712 pub(crate) fn fire_pending_connect(&self, _cx: &mut JSContext) {
713 loop {
714 let inside_port = self
715 .pending_connect
716 .borrow_mut()
717 .pop_front()
718 .map(|inside_port| inside_port.as_rooted());
719 let Some(inside_port) = inside_port else {
720 break;
721 };
722 if self.upcast::<WorkerGlobalScope>().is_closing() {
723 return;
724 }
725 self.dispatch_connect_event(&inside_port);
727 }
728 }
729
730 fn handle_script_event(&self, msg: SharedWorkerScriptMsg, cx: &mut JSContext) {
731 match msg {
732 SharedWorkerScriptMsg::CommonWorker(WorkerScriptMsg::Common(msg)) => {
733 self.upcast::<WorkerGlobalScope>().process_event(msg, cx);
734 },
735 SharedWorkerScriptMsg::Connect(port_impl) => {
736 let inside_port = self.handle_connect(port_impl, cx);
737 if self.upcast::<WorkerGlobalScope>().is_execution_ready() {
738 self.dispatch_connect_event(&inside_port);
740 } else {
741 self.pending_connect
743 .borrow_mut()
744 .push_back(Dom::from_ref(&*inside_port));
745 }
746 },
747 SharedWorkerScriptMsg::CommonWorker(WorkerScriptMsg::DOMMessage(_)) => {
748 debug_assert!(
751 false,
752 "SharedWorkerGlobalScope does not support direct DOMMessage dispatch"
753 );
754 },
755 SharedWorkerScriptMsg::WakeUp => {},
756 }
757 }
758
759 fn handle_mixed_message(&self, msg: MixedMessage, cx: &mut JSContext) -> bool {
760 if self.upcast::<WorkerGlobalScope>().is_closing() {
761 return false;
762 }
763
764 match msg {
765 MixedMessage::Devtools(msg) => match msg {
766 DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _bool_val) => {},
767 DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, reply) => {
768 self.debugger_global.fire_eval(
769 cx,
770 code.into(),
771 id,
772 Some(self.upcast::<WorkerGlobalScope>().worker_id()),
773 frame_actor_id,
774 reply,
775 );
776 },
777 _ => debug!("got an unusable devtools control message inside the worker!"),
778 },
779 MixedMessage::SharedWorker(msg) => {
780 self.handle_script_event(msg, cx);
781 },
782 MixedMessage::Control(SharedWorkerControlMsg::Exit) => {
783 return false;
784 },
785 MixedMessage::Timer => {},
786 }
787
788 true
789 }
790}
791
792impl SharedWorkerGlobalScopeMethods<crate::DomTypeHolder> for SharedWorkerGlobalScope {
793 fn Name(&self) -> DOMString {
795 self.workerglobalscope.worker_name()
798 }
799
800 fn Close(&self) {
802 self.upcast::<WorkerGlobalScope>().close()
804 }
805
806 event_handler!(connect, GetOnconnect, SetOnconnect);
808}
809
810impl HasOrigin for SharedWorkerGlobalScope {
811 fn origin(&self) -> MutableOrigin {
812 self.upcast::<WorkerGlobalScope>().origin()
813 }
814}