script/dom/
dedicatedworkerglobalscope.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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
68/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
69/// value for the duration of this object's lifetime. This ensures that the related Worker
70/// object only lives as long as necessary (ie. while events are being executed), while
71/// providing a reference that can be cloned freely.
72pub(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
97/// Messages sent from the owning global.
98pub(crate) enum DedicatedWorkerControlMsg {
99    /// Shutdown the worker.
100    Exit,
101}
102
103pub(crate) enum DedicatedWorkerScriptMsg {
104    /// Standard message from a worker.
105    CommonWorker(TrustedWorkerAddress, WorkerScriptMsg),
106    /// Wake-up call from the task queue.
107    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        // Workers always return None, since the pipeline_id is only used to check for document activity,
137        // and this check does not apply to worker event-loops.
138        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        // Inactive is only relevant in the context of a browsing-context event-loop.
169        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// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
200#[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    /// Sender to the parent thread.
210    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    /// A receiver of control messages,
217    /// currently only used to signal shutdown.
218    #[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    /// <https://html.spec.whatwg.org/multipage/#run-a-worker>
358    #[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                // Step 8 "Set up a worker environment settings object [...]"
459                //
460                // <https://html.spec.whatwg.org/multipage/#script-settings-for-workers>
461                //
462                // > The origin: Return a unique opaque origin if `worker global
463                // > scope`'s url's scheme is "data", and `inherited origin`
464                // > otherwise.
465                if worker_url.scheme() == "data" {
466                    // Workers created from a data: url are secure if they were created from secure contexts
467                    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                // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
501                // registration (#6631), so we instead use a random number and cross our fingers.
502                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                    // Handle interrupt requests
547                    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                            // Step 27, Run the responsible event loop specified
573                            // by inside settings until it is destroyed.
574                            // The worker processing model remains on this step
575                            // until the event loop is destroyed,
576                            // which happens after the closing flag is set to true.
577                            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    /// <https://html.spec.whatwg.org/multipage/#initialize-worker-policy-container> and
592    /// <https://html.spec.whatwg.org/multipage/#creating-a-policy-container-from-a-fetch-response>
593    fn initialize_policy_container_for_worker_global_scope(
594        scope: &WorkerGlobalScope,
595        metadata: &Metadata,
596        parent_policy_container: &PolicyContainer,
597    ) {
598        // Step 1. If workerGlobalScope's url is local but its scheme is not "blob":
599        //
600        // Note that we also allow for blob here, as the parent_policy_container is in both cases
601        // the container that we need to clone.
602        if metadata.final_url.is_local_scheme() {
603            // Step 1.2. Set workerGlobalScope's policy container to a clone of workerGlobalScope's
604            // owner set[0]'s relevant settings object's policy container.
605            //
606            // Step 1. If response's URL's scheme is "blob", then return a clone of response's URL's
607            // blob URL entry's environment's policy container.
608            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        // Step 3. Set result's CSP list to the result of parsing a response's Content Security Policies given response.
613        scope.set_csp_list(parse_csp_list_from_metadata(&metadata.headers));
614        // Step 5. Set result's referrer policy to the result of parsing the `Referrer-Policy`
615        // header given response. [REFERRERPOLICY]
616        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    /// The non-None value of the `worker` field can contain a rooted [`TrustedWorkerAddress`]
625    /// version of the main thread's worker object. This is set while handling messages and then
626    /// unset otherwise, ensuring that the main thread object can be garbage collected. See
627    /// [`AutoWorkerReset`].
628    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        // The `TaskManager` maintains a handle to this `DedicatedWorkerGlobalScope`'s
635        // event_loop_sender, which might in turn have a `TrustedWorkerAddress` rooting of the main
636        // thread's worker, which prevents garbage collection. Resetting it here ensures that
637        // garbage collection of the main thread object can happen again (assuming the new `worker`
638        // is `None`).
639        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        // FIXME(#26324): `self.worker` is None in devtools messages.
703        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    // https://html.spec.whatwg.org/multipage/#runtime-script-errors-2
727    #[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            // Step 1.
736            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            // Step 2.
750            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    // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
765    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    // A false response causes the script to terminate
803    !worker.is_closing()
804}
805
806impl DedicatedWorkerGlobalScopeMethods<crate::DomTypeHolder> for DedicatedWorkerGlobalScope {
807    /// <https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage>
808    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    /// <https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage>
818    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    // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-close
836    fn Close(&self) {
837        // Step 2
838        self.upcast::<WorkerGlobalScope>().close();
839    }
840
841    // https://html.spec.whatwg.org/multipage/#handler-dedicatedworkerglobalscope-onmessage
842    event_handler!(message, GetOnmessage, SetOnmessage);
843}