1use std::sync::Arc;
6use std::sync::atomic::AtomicBool;
7use std::thread::{self, JoinHandle};
8
9use base::id::{BrowsingContextId, PipelineId, WebViewId};
10use constellation_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
11use crossbeam_channel::{Receiver, Sender, unbounded};
12use devtools_traits::DevtoolScriptControlMsg;
13use dom_struct::dom_struct;
14use fonts::FontContext;
15use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
16use ipc_channel::ipc::IpcReceiver;
17use ipc_channel::router::ROUTER;
18use js::jsapi::{Heap, JS_AddInterruptCallback, JSContext, JSObject};
19use js::jsval::UndefinedValue;
20use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
21use net_traits::image_cache::ImageCache;
22use net_traits::policy_container::PolicyContainer;
23use net_traits::request::{
24 CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, Referrer, RequestBuilder,
25 RequestMode,
26};
27use net_traits::{IpcSend, Metadata};
28use servo_rand::random;
29use servo_url::{ImmutableOrigin, ServoUrl};
30use style::thread_state::{self, ThreadState};
31
32use crate::devtools;
33use crate::dom::abstractworker::{SimpleWorkerErrorHandler, WorkerScriptMsg};
34use crate::dom::abstractworkerglobalscope::{WorkerEventLoopMethods, run_worker_event_loop};
35use crate::dom::bindings::cell::DomRefCell;
36use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
37use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
38use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
39use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
40use crate::dom::bindings::error::{ErrorInfo, ErrorResult};
41use crate::dom::bindings::inheritance::Castable;
42use crate::dom::bindings::reflector::DomGlobal;
43use crate::dom::bindings::root::DomRoot;
44use crate::dom::bindings::str::DOMString;
45use crate::dom::bindings::structuredclone;
46use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox};
47use crate::dom::bindings::utils::define_all_exposed_interfaces;
48use crate::dom::csp::{Violation, parse_csp_list_from_metadata};
49use crate::dom::errorevent::ErrorEvent;
50use crate::dom::event::{Event, EventBubbles, EventCancelable};
51use crate::dom::eventtarget::EventTarget;
52use crate::dom::globalscope::GlobalScope;
53use crate::dom::messageevent::MessageEvent;
54use crate::dom::reportingendpoint::ReportingEndpoint;
55use crate::dom::types::DebuggerGlobalScope;
56#[cfg(feature = "webgpu")]
57use crate::dom::webgpu::identityhub::IdentityHub;
58use crate::dom::worker::{TrustedWorkerAddress, Worker};
59use crate::dom::workerglobalscope::WorkerGlobalScope;
60use crate::fetch::{CspViolationsProcessor, load_whole_resource};
61use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
62use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
63use crate::script_runtime::ScriptThreadEventCategory::WorkerEvent;
64use crate::script_runtime::{CanGc, JSContext as SafeJSContext, Runtime, ThreadSafeJSContext};
65use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
66use crate::task_source::{SendableTaskSource, TaskSourceName};
67
68pub(crate) struct AutoWorkerReset<'a> {
73 workerscope: &'a DedicatedWorkerGlobalScope,
74 old_worker: Option<TrustedWorkerAddress>,
75}
76
77impl<'a> AutoWorkerReset<'a> {
78 fn new(
79 workerscope: &'a DedicatedWorkerGlobalScope,
80 worker: TrustedWorkerAddress,
81 ) -> AutoWorkerReset<'a> {
82 let old_worker = workerscope.replace_worker(Some(worker));
83 AutoWorkerReset {
84 workerscope,
85 old_worker,
86 }
87 }
88}
89
90impl Drop for AutoWorkerReset<'_> {
91 fn drop(&mut self) {
92 self.workerscope
93 .replace_worker(std::mem::take(&mut self.old_worker));
94 }
95}
96
97pub(crate) enum DedicatedWorkerControlMsg {
99 Exit,
101}
102
103pub(crate) enum DedicatedWorkerScriptMsg {
104 CommonWorker(TrustedWorkerAddress, WorkerScriptMsg),
106 WakeUp,
108}
109
110pub(crate) enum MixedMessage {
111 Worker(DedicatedWorkerScriptMsg),
112 Devtools(DevtoolScriptControlMsg),
113 Control(DedicatedWorkerControlMsg),
114 Timer,
115}
116
117impl QueuedTaskConversion for DedicatedWorkerScriptMsg {
118 fn task_source_name(&self) -> Option<&TaskSourceName> {
119 let common_worker_msg = match self {
120 DedicatedWorkerScriptMsg::CommonWorker(_, common_worker_msg) => common_worker_msg,
121 _ => return None,
122 };
123 let script_msg = match common_worker_msg {
124 WorkerScriptMsg::Common(script_msg) => script_msg,
125 _ => return None,
126 };
127 match script_msg {
128 CommonScriptMsg::Task(_category, _boxed, _pipeline_id, source_name) => {
129 Some(source_name)
130 },
131 _ => None,
132 }
133 }
134
135 fn pipeline_id(&self) -> Option<PipelineId> {
136 None
139 }
140
141 fn into_queued_task(self) -> Option<QueuedTask> {
142 let (worker, common_worker_msg) = match self {
143 DedicatedWorkerScriptMsg::CommonWorker(worker, common_worker_msg) => {
144 (worker, common_worker_msg)
145 },
146 _ => return None,
147 };
148 let script_msg = match common_worker_msg {
149 WorkerScriptMsg::Common(script_msg) => script_msg,
150 _ => return None,
151 };
152 let (category, boxed, pipeline_id, task_source) = match script_msg {
153 CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => {
154 (category, boxed, pipeline_id, task_source)
155 },
156 _ => return None,
157 };
158 Some((Some(worker), category, boxed, pipeline_id, task_source))
159 }
160
161 fn from_queued_task(queued_task: QueuedTask) -> Self {
162 let (worker, category, boxed, pipeline_id, task_source) = queued_task;
163 let script_msg = CommonScriptMsg::Task(category, boxed, pipeline_id, task_source);
164 DedicatedWorkerScriptMsg::CommonWorker(worker.unwrap(), WorkerScriptMsg::Common(script_msg))
165 }
166
167 fn inactive_msg() -> Self {
168 panic!("Workers should never receive messages marked as inactive");
170 }
171
172 fn wake_up_msg() -> Self {
173 DedicatedWorkerScriptMsg::WakeUp
174 }
175
176 fn is_wake_up(&self) -> bool {
177 matches!(self, DedicatedWorkerScriptMsg::WakeUp)
178 }
179}
180
181unsafe_no_jsmanaged_fields!(TaskQueue<DedicatedWorkerScriptMsg>);
182
183struct DedicatedWorkerCspProcessor {
184 parent_event_loop_sender: ScriptEventLoopSender,
185 pipeline_id: PipelineId,
186}
187
188impl CspViolationsProcessor for DedicatedWorkerCspProcessor {
189 fn process_csp_violations(&self, violations: Vec<Violation>) {
190 let _ = self
191 .parent_event_loop_sender
192 .send(CommonScriptMsg::ReportCspViolations(
193 self.pipeline_id,
194 violations,
195 ));
196 }
197}
198
199#[dom_struct]
201pub(crate) struct DedicatedWorkerGlobalScope {
202 workerglobalscope: WorkerGlobalScope,
203 #[ignore_malloc_size_of = "Defined in std"]
204 task_queue: TaskQueue<DedicatedWorkerScriptMsg>,
205 own_sender: Sender<DedicatedWorkerScriptMsg>,
206 #[ignore_malloc_size_of = "Trusted<T> has unclear ownership like Dom<T>"]
207 worker: DomRefCell<Option<TrustedWorkerAddress>>,
208 #[ignore_malloc_size_of = "Can't measure trait objects"]
209 parent_event_loop_sender: ScriptEventLoopSender,
211 #[ignore_malloc_size_of = "Arc"]
212 #[no_trace]
213 image_cache: Arc<dyn ImageCache>,
214 #[no_trace]
215 browsing_context: Option<BrowsingContextId>,
216 #[ignore_malloc_size_of = "Channels are hard"]
219 #[no_trace]
220 control_receiver: Receiver<DedicatedWorkerControlMsg>,
221}
222
223impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope {
224 type WorkerMsg = DedicatedWorkerScriptMsg;
225 type ControlMsg = DedicatedWorkerControlMsg;
226 type Event = MixedMessage;
227
228 fn task_queue(&self) -> &TaskQueue<DedicatedWorkerScriptMsg> {
229 &self.task_queue
230 }
231
232 fn handle_event(&self, event: MixedMessage, can_gc: CanGc) -> bool {
233 self.handle_mixed_message(event, can_gc)
234 }
235
236 fn handle_worker_post_event(
237 &self,
238 worker: &TrustedWorkerAddress,
239 ) -> Option<AutoWorkerReset<'_>> {
240 let ar = AutoWorkerReset::new(self, worker.clone());
241 Some(ar)
242 }
243
244 fn from_control_msg(msg: DedicatedWorkerControlMsg) -> MixedMessage {
245 MixedMessage::Control(msg)
246 }
247
248 fn from_worker_msg(msg: DedicatedWorkerScriptMsg) -> MixedMessage {
249 MixedMessage::Worker(msg)
250 }
251
252 fn from_devtools_msg(msg: DevtoolScriptControlMsg) -> MixedMessage {
253 MixedMessage::Devtools(msg)
254 }
255
256 fn from_timer_msg() -> MixedMessage {
257 MixedMessage::Timer
258 }
259
260 fn control_receiver(&self) -> &Receiver<DedicatedWorkerControlMsg> {
261 &self.control_receiver
262 }
263}
264
265impl DedicatedWorkerGlobalScope {
266 pub(crate) fn webview_id(&self) -> Option<WebViewId> {
267 WebViewId::installed()
268 }
269
270 #[allow(clippy::too_many_arguments)]
271 fn new_inherited(
272 init: WorkerGlobalScopeInit,
273 worker_name: DOMString,
274 worker_type: WorkerType,
275 worker_url: ServoUrl,
276 from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
277 runtime: Runtime,
278 parent_event_loop_sender: ScriptEventLoopSender,
279 own_sender: Sender<DedicatedWorkerScriptMsg>,
280 receiver: Receiver<DedicatedWorkerScriptMsg>,
281 closing: Arc<AtomicBool>,
282 image_cache: Arc<dyn ImageCache>,
283 browsing_context: Option<BrowsingContextId>,
284 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
285 control_receiver: Receiver<DedicatedWorkerControlMsg>,
286 insecure_requests_policy: InsecureRequestsPolicy,
287 font_context: Option<Arc<FontContext>>,
288 ) -> DedicatedWorkerGlobalScope {
289 DedicatedWorkerGlobalScope {
290 workerglobalscope: WorkerGlobalScope::new_inherited(
291 init,
292 worker_name,
293 worker_type,
294 worker_url,
295 runtime,
296 from_devtools_receiver,
297 closing,
298 #[cfg(feature = "webgpu")]
299 gpu_id_hub,
300 insecure_requests_policy,
301 font_context,
302 ),
303 task_queue: TaskQueue::new(receiver, own_sender.clone()),
304 own_sender,
305 parent_event_loop_sender,
306 worker: DomRefCell::new(None),
307 image_cache,
308 browsing_context,
309 control_receiver,
310 }
311 }
312
313 #[allow(unsafe_code, clippy::too_many_arguments)]
314 pub(crate) fn new(
315 init: WorkerGlobalScopeInit,
316 worker_name: DOMString,
317 worker_type: WorkerType,
318 worker_url: ServoUrl,
319 from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
320 runtime: Runtime,
321 parent_event_loop_sender: ScriptEventLoopSender,
322 own_sender: Sender<DedicatedWorkerScriptMsg>,
323 receiver: Receiver<DedicatedWorkerScriptMsg>,
324 closing: Arc<AtomicBool>,
325 image_cache: Arc<dyn ImageCache>,
326 browsing_context: Option<BrowsingContextId>,
327 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
328 control_receiver: Receiver<DedicatedWorkerControlMsg>,
329 insecure_requests_policy: InsecureRequestsPolicy,
330 font_context: Option<Arc<FontContext>>,
331 ) -> DomRoot<DedicatedWorkerGlobalScope> {
332 let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited(
333 init,
334 worker_name,
335 worker_type,
336 worker_url,
337 from_devtools_receiver,
338 runtime,
339 parent_event_loop_sender,
340 own_sender,
341 receiver,
342 closing,
343 image_cache,
344 browsing_context,
345 #[cfg(feature = "webgpu")]
346 gpu_id_hub,
347 control_receiver,
348 insecure_requests_policy,
349 font_context,
350 ));
351 DedicatedWorkerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(
352 GlobalScope::get_cx(),
353 scope,
354 )
355 }
356
357 #[allow(unsafe_code, clippy::too_many_arguments)]
359 pub(crate) fn run_worker_scope(
360 mut init: WorkerGlobalScopeInit,
361 worker_url: ServoUrl,
362 from_devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>,
363 worker: TrustedWorkerAddress,
364 parent_event_loop_sender: ScriptEventLoopSender,
365 own_sender: Sender<DedicatedWorkerScriptMsg>,
366 receiver: Receiver<DedicatedWorkerScriptMsg>,
367 worker_load_origin: WorkerScriptLoadOrigin,
368 worker_name: String,
369 worker_type: WorkerType,
370 closing: Arc<AtomicBool>,
371 image_cache: Arc<dyn ImageCache>,
372 browsing_context: Option<BrowsingContextId>,
373 #[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
374 control_receiver: Receiver<DedicatedWorkerControlMsg>,
375 context_sender: Sender<ThreadSafeJSContext>,
376 insecure_requests_policy: InsecureRequestsPolicy,
377 policy_container: PolicyContainer,
378 font_context: Option<Arc<FontContext>>,
379 ) -> JoinHandle<()> {
380 let serialized_worker_url = worker_url.to_string();
381 let webview_id = WebViewId::installed();
382 let current_global = GlobalScope::current().expect("No current global object");
383 let origin = current_global.origin().immutable().clone();
384 let referrer = current_global.get_referrer();
385 let parent = current_global.runtime_handle();
386 let current_global_https_state = current_global.get_https_state();
387 let current_global_ancestor_trustworthy = current_global.has_trustworthy_ancestor_origin();
388 let is_secure_context = current_global.is_secure_context();
389
390 thread::Builder::new()
391 .name(format!("WW:{}", worker_url.debug_compact()))
392 .spawn(move || {
393 thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
394
395 if let Some(webview_id) = webview_id {
396 WebViewId::install(webview_id);
397 }
398
399 let WorkerScriptLoadOrigin {
400 referrer_url,
401 referrer_policy,
402 pipeline_id,
403 } = worker_load_origin;
404
405 let referrer = referrer_url.map(Referrer::ReferrerUrl).unwrap_or(referrer);
406
407 let request = RequestBuilder::new(webview_id, worker_url.clone(), referrer)
408 .destination(Destination::Worker)
409 .mode(RequestMode::SameOrigin)
410 .credentials_mode(CredentialsMode::CredentialsSameOrigin)
411 .parser_metadata(ParserMetadata::NotParserInserted)
412 .use_url_credentials(true)
413 .pipeline_id(Some(pipeline_id))
414 .referrer_policy(referrer_policy)
415 .insecure_requests_policy(insecure_requests_policy)
416 .has_trustworthy_ancestor_origin(current_global_ancestor_trustworthy)
417 .policy_container(policy_container.clone())
418 .origin(origin);
419
420 let runtime = unsafe {
421 let task_source = SendableTaskSource {
422 sender: ScriptEventLoopSender::DedicatedWorker {
423 sender: own_sender.clone(),
424 main_thread_worker: worker.clone(),
425 },
426 pipeline_id,
427 name: TaskSourceName::Networking,
428 canceller: Default::default(),
429 };
430 Runtime::new_with_parent(Some(parent), Some(task_source))
431 };
432 let debugger_global = DebuggerGlobalScope::new(
433 pipeline_id,
434 init.to_devtools_sender.clone(),
435 init.from_devtools_sender
436 .clone()
437 .expect("Guaranteed by Worker::Constructor"),
438 init.mem_profiler_chan.clone(),
439 init.time_profiler_chan.clone(),
440 init.script_to_constellation_chan.clone(),
441 init.script_to_embedder_chan.clone(),
442 init.resource_threads.clone(),
443 #[cfg(feature = "webgpu")]
444 gpu_id_hub.clone(),
445 CanGc::note(),
446 );
447 debugger_global.execute(CanGc::note());
448
449 let context_for_interrupt = runtime.thread_safe_js_context();
450 let _ = context_sender.send(context_for_interrupt);
451
452 let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded();
453 ROUTER.route_ipc_receiver_to_crossbeam_sender(
454 from_devtools_receiver,
455 devtools_mpsc_chan,
456 );
457
458 if worker_url.scheme() == "data" {
466 if is_secure_context {
468 init.origin = ImmutableOrigin::new_opaque_data_url_worker();
469 } else {
470 init.origin = ImmutableOrigin::new_opaque();
471 }
472 }
473
474 let worker_id = init.worker_id;
475 let global = DedicatedWorkerGlobalScope::new(
476 init,
477 DOMString::from_string(worker_name),
478 worker_type,
479 worker_url,
480 devtools_mpsc_port,
481 runtime,
482 parent_event_loop_sender.clone(),
483 own_sender,
484 receiver,
485 closing,
486 image_cache,
487 browsing_context,
488 #[cfg(feature = "webgpu")]
489 gpu_id_hub,
490 control_receiver,
491 insecure_requests_policy,
492 font_context,
493 );
494 debugger_global.fire_add_debuggee(
495 CanGc::note(),
496 global.upcast(),
497 pipeline_id,
498 Some(worker_id),
499 );
500 let scope = global.upcast::<WorkerGlobalScope>();
503 let global_scope = global.upcast::<GlobalScope>();
504
505 global_scope.set_https_state(current_global_https_state);
506
507 let (metadata, bytes) = match load_whole_resource(
508 request,
509 &global_scope.resource_threads().sender(),
510 global_scope,
511 &DedicatedWorkerCspProcessor {
512 parent_event_loop_sender: parent_event_loop_sender.clone(),
513 pipeline_id,
514 },
515 CanGc::note(),
516 ) {
517 Err(e) => {
518 error!("error loading script {} ({:?})", serialized_worker_url, e);
519 parent_event_loop_sender
520 .send(CommonScriptMsg::Task(
521 WorkerEvent,
522 Box::new(SimpleWorkerErrorHandler::new(worker)),
523 Some(pipeline_id),
524 TaskSourceName::DOMManipulation,
525 ))
526 .unwrap();
527 scope.clear_js_runtime();
528 return;
529 },
530 Ok((metadata, bytes)) => (metadata, bytes),
531 };
532 scope.set_url(metadata.final_url.clone());
533 Self::initialize_policy_container_for_worker_global_scope(
534 scope,
535 &metadata,
536 &policy_container,
537 );
538 scope.set_endpoints_list(ReportingEndpoint::parse_reporting_endpoints_header(
539 &metadata.final_url.clone(),
540 &metadata.headers,
541 ));
542 global_scope.set_https_state(metadata.https_state);
543 let source = String::from_utf8_lossy(&bytes);
544
545 unsafe {
546 JS_AddInterruptCallback(*scope.get_cx(), Some(interrupt_callback));
548 }
549
550 if scope.is_closing() {
551 scope.clear_js_runtime();
552 return;
553 }
554
555 {
556 let _ar = AutoWorkerReset::new(&global, worker.clone());
557 let realm = enter_realm(scope);
558 define_all_exposed_interfaces(
559 global.upcast(),
560 InRealm::entered(&realm),
561 CanGc::note(),
562 );
563 scope.execute_script(DOMString::from(source), CanGc::note());
564 }
565
566 let reporter_name = format!("dedicated-worker-reporter-{}", random::<u64>());
567 scope
568 .upcast::<GlobalScope>()
569 .mem_profiler_chan()
570 .run_with_memory_reporting(
571 || {
572 while !scope.is_closing() {
578 run_worker_event_loop(&*global, Some(&worker), CanGc::note());
579 }
580 },
581 reporter_name,
582 parent_event_loop_sender,
583 CommonScriptMsg::CollectReports,
584 );
585
586 scope.clear_js_runtime();
587 })
588 .expect("Thread spawning failed")
589 }
590
591 fn initialize_policy_container_for_worker_global_scope(
594 scope: &WorkerGlobalScope,
595 metadata: &Metadata,
596 parent_policy_container: &PolicyContainer,
597 ) {
598 if metadata.final_url.is_local_scheme() {
603 scope.set_csp_list(parent_policy_container.csp_list.clone());
609 scope.set_referrer_policy(parent_policy_container.get_referrer_policy());
610 return;
611 }
612 scope.set_csp_list(parse_csp_list_from_metadata(&metadata.headers));
614 let referrer_policy = metadata
617 .headers
618 .as_ref()
619 .and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
620 .into();
621 scope.set_referrer_policy(referrer_policy);
622 }
623
624 fn replace_worker(
629 &self,
630 new_worker: Option<TrustedWorkerAddress>,
631 ) -> Option<TrustedWorkerAddress> {
632 let old_worker = std::mem::replace(&mut *self.worker.borrow_mut(), new_worker);
633
634 self.upcast::<GlobalScope>()
640 .task_manager()
641 .set_sender(self.event_loop_sender());
642
643 old_worker
644 }
645
646 pub(crate) fn image_cache(&self) -> Arc<dyn ImageCache> {
647 self.image_cache.clone()
648 }
649
650 pub(crate) fn event_loop_sender(&self) -> Option<ScriptEventLoopSender> {
651 Some(ScriptEventLoopSender::DedicatedWorker {
652 sender: self.own_sender.clone(),
653 main_thread_worker: self.worker.borrow().clone()?,
654 })
655 }
656
657 pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
658 let (sender, receiver) = unbounded();
659 let main_thread_worker = self.worker.borrow().as_ref().unwrap().clone();
660 (
661 ScriptEventLoopSender::DedicatedWorker {
662 sender,
663 main_thread_worker,
664 },
665 ScriptEventLoopReceiver::DedicatedWorker(receiver),
666 )
667 }
668
669 fn handle_script_event(&self, msg: WorkerScriptMsg, can_gc: CanGc) {
670 match msg {
671 WorkerScriptMsg::DOMMessage { origin, data } => {
672 let scope = self.upcast::<WorkerGlobalScope>();
673 let target = self.upcast();
674 let _ac = enter_realm(self);
675 rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
676 if let Ok(ports) =
677 structuredclone::read(scope.upcast(), *data, message.handle_mut())
678 {
679 MessageEvent::dispatch_jsval(
680 target,
681 scope.upcast(),
682 message.handle(),
683 Some(&origin.ascii_serialization()),
684 None,
685 ports,
686 can_gc,
687 );
688 } else {
689 MessageEvent::dispatch_error(target, scope.upcast(), can_gc);
690 }
691 },
692 WorkerScriptMsg::Common(msg) => {
693 self.upcast::<WorkerGlobalScope>().process_event(msg);
694 },
695 }
696 }
697
698 fn handle_mixed_message(&self, msg: MixedMessage, can_gc: CanGc) -> bool {
699 if self.upcast::<WorkerGlobalScope>().is_closing() {
700 return false;
701 }
702 match msg {
704 MixedMessage::Devtools(msg) => match msg {
705 DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => {
706 devtools::handle_evaluate_js(self.upcast(), string, sender, can_gc)
707 },
708 DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) => {
709 devtools::handle_wants_live_notifications(self.upcast(), bool_val)
710 },
711 _ => debug!("got an unusable devtools control message inside the worker!"),
712 },
713 MixedMessage::Worker(DedicatedWorkerScriptMsg::CommonWorker(linked_worker, msg)) => {
714 let _ar = AutoWorkerReset::new(self, linked_worker);
715 self.handle_script_event(msg, can_gc);
716 },
717 MixedMessage::Worker(DedicatedWorkerScriptMsg::WakeUp) => {},
718 MixedMessage::Control(DedicatedWorkerControlMsg::Exit) => {
719 return false;
720 },
721 MixedMessage::Timer => {},
722 }
723 true
724 }
725
726 #[allow(unsafe_code)]
728 pub(crate) fn forward_error_to_worker_object(&self, error_info: ErrorInfo) {
729 let worker = self.worker.borrow().as_ref().unwrap().clone();
730 let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
731 let task = Box::new(task!(forward_error_to_worker_object: move || {
732 let worker = worker.root();
733 let global = worker.global();
734
735 let event = ErrorEvent::new(
737 &global,
738 atom!("error"),
739 EventBubbles::DoesNotBubble,
740 EventCancelable::Cancelable,
741 error_info.message.as_str().into(),
742 error_info.filename.as_str().into(),
743 error_info.lineno,
744 error_info.column,
745 HandleValue::null(),
746 CanGc::note(),
747 );
748
749 if event.upcast::<Event>().fire(worker.upcast::<EventTarget>(), CanGc::note()) {
751 global.report_an_error(error_info, HandleValue::null(), CanGc::note());
752 }
753 }));
754 self.parent_event_loop_sender
755 .send(CommonScriptMsg::Task(
756 WorkerEvent,
757 task,
758 Some(pipeline_id),
759 TaskSourceName::DOMManipulation,
760 ))
761 .unwrap();
762 }
763
764 fn post_message_impl(
766 &self,
767 cx: SafeJSContext,
768 message: HandleValue,
769 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
770 ) -> ErrorResult {
771 let data = structuredclone::write(cx, message, Some(transfer))?;
772 let worker = self.worker.borrow().as_ref().unwrap().clone();
773 let global_scope = self.upcast::<GlobalScope>();
774 let pipeline_id = global_scope.pipeline_id();
775 let task = Box::new(task!(post_worker_message: move || {
776 Worker::handle_message(worker, data, CanGc::note());
777 }));
778 self.parent_event_loop_sender
779 .send(CommonScriptMsg::Task(
780 WorkerEvent,
781 task,
782 Some(pipeline_id),
783 TaskSourceName::DOMManipulation,
784 ))
785 .expect("Sending to parent failed");
786 Ok(())
787 }
788
789 pub(crate) fn browsing_context(&self) -> Option<BrowsingContextId> {
790 self.browsing_context
791 }
792}
793
794#[allow(unsafe_code)]
795unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool {
796 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
797 let global = GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof));
798 let worker =
799 DomRoot::downcast::<WorkerGlobalScope>(global).expect("global is not a worker scope");
800 assert!(worker.is::<DedicatedWorkerGlobalScope>());
801
802 !worker.is_closing()
804}
805
806impl DedicatedWorkerGlobalScopeMethods<crate::DomTypeHolder> for DedicatedWorkerGlobalScope {
807 fn PostMessage(
809 &self,
810 cx: SafeJSContext,
811 message: HandleValue,
812 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
813 ) -> ErrorResult {
814 self.post_message_impl(cx, message, transfer)
815 }
816
817 fn PostMessage_(
819 &self,
820 cx: SafeJSContext,
821 message: HandleValue,
822 options: RootedTraceableBox<StructuredSerializeOptions>,
823 ) -> ErrorResult {
824 let mut rooted = CustomAutoRooter::new(
825 options
826 .transfer
827 .iter()
828 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
829 .collect(),
830 );
831 let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
832 self.post_message_impl(cx, message, guard)
833 }
834
835 fn Close(&self) {
837 self.upcast::<WorkerGlobalScope>().close();
839 }
840
841 event_handler!(message, GetOnmessage, SetOnmessage);
843}