Skip to main content

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