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