Skip to main content

script/dom/worklet/
workletglobalscope.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::rc::Rc;
8use std::sync::Arc;
9use std::sync::atomic::{AtomicBool, Ordering};
10
11use crossbeam_channel::Sender;
12use devtools_traits::ScriptToDevtoolsControlMsg;
13use dom_struct::dom_struct;
14use embedder_traits::ScriptToEmbedderChan;
15use js::context::JSContext;
16use net_traits::ResourceThreads;
17use net_traits::image_cache::ImageCache;
18use profile_traits::{mem, time};
19use script_bindings::cell::DomRefCell;
20use script_traits::Painter;
21use servo_base::generic_channel::GenericCallback;
22use servo_base::id::PipelineId;
23use servo_constellation_traits::ScriptToConstellationSender;
24use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
25use storage_traits::StorageThreads;
26use stylo_atoms::Atom;
27
28use crate::dom::Window;
29use crate::dom::bindings::inheritance::Castable;
30use crate::dom::bindings::root::DomRoot;
31use crate::dom::bindings::trace::{CustomTraceable, HashMapTracedValues};
32use crate::dom::bindings::utils::define_all_exposed_interfaces;
33use crate::dom::globalscope::GlobalScope;
34use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
35#[cfg(feature = "testbinding")]
36use crate::dom::testworkletglobalscope::TestWorkletGlobalScope;
37#[cfg(feature = "webgpu")]
38use crate::dom::webgpu::identityhub::IdentityHub;
39use crate::dom::worklet::WorkletExecutor;
40use crate::messaging::MainThreadScriptMsg;
41use crate::modules::script_module::{ModuleRequest, ModuleStatus};
42use crate::realms::enter_auto_realm;
43use crate::runtime::job_queue::job_queue_microtask_checkpoint;
44use crate::tasks::task::TaskCanceller;
45use crate::tasks::task_manager::TaskManager;
46
47#[dom_struct]
48/// <https://drafts.css-houdini.org/worklets/#workletglobalscope>
49pub(crate) struct WorkletGlobalScope {
50    /// The global for this worklet.
51    globalscope: GlobalScope,
52    /// The base URL for this worklet.
53    #[no_trace]
54    base_url: ServoUrl,
55    /// Sender back to the script thread
56    to_script_thread_sender: Sender<MainThreadScriptMsg>,
57    /// Worklet task executor
58    executor: WorkletExecutor,
59
60    #[no_trace]
61    /// The pipeline that created this worklet.
62    pipeline_id: PipelineId,
63
64    #[no_trace]
65    origin: MutableOrigin,
66
67    /// The [`TaskManager`] for this [`WorkletGlobalScope`].
68    #[conditional_malloc_size_of]
69    task_manager: Rc<TaskManager>,
70
71    #[conditional_malloc_size_of]
72    closing: Arc<AtomicBool>,
73
74    /// module map is used when importing JavaScript modules
75    /// <https://html.spec.whatwg.org/multipage/#concept-settings-object-module-map>
76    #[ignore_malloc_size_of = "mozjs"]
77    module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
78}
79
80impl WorkletGlobalScope {
81    /// Create a new heap-allocated `WorkletGlobalScope`.
82    #[allow(clippy::too_many_arguments)]
83    pub(crate) fn new(
84        scope_type: WorkletGlobalScopeType,
85        pipeline_id: PipelineId,
86        base_url: ServoUrl,
87        inherited_secure_context: Option<bool>,
88        executor: WorkletExecutor,
89        init: &WorkletGlobalScopeInit,
90        cx: &mut JSContext,
91        closing: Arc<AtomicBool>,
92    ) -> DomRoot<WorkletGlobalScope> {
93        let scope: DomRoot<WorkletGlobalScope> = match scope_type {
94            #[cfg(feature = "testbinding")]
95            WorkletGlobalScopeType::Test => DomRoot::upcast(TestWorkletGlobalScope::new(
96                pipeline_id,
97                base_url,
98                inherited_secure_context,
99                executor,
100                init,
101                cx,
102                closing,
103            )),
104            WorkletGlobalScopeType::Paint => DomRoot::upcast(PaintWorkletGlobalScope::new(
105                cx,
106                pipeline_id,
107                base_url,
108                inherited_secure_context,
109                executor,
110                init,
111                closing,
112            )),
113        };
114
115        let mut realm = enter_auto_realm(cx, &*scope);
116        let mut realm = realm.current_realm();
117        define_all_exposed_interfaces(&mut realm, scope.upcast());
118
119        scope
120    }
121
122    /// Create a new stack-allocated `WorkletGlobalScope`.
123    pub(crate) fn new_inherited(
124        pipeline_id: PipelineId,
125        base_url: ServoUrl,
126        inherited_secure_context: Option<bool>,
127        executor: WorkletExecutor,
128        init: &WorkletGlobalScopeInit,
129        closing: Arc<AtomicBool>,
130    ) -> Self {
131        let script_event_loop_sender = executor.event_loop_sender();
132
133        Self {
134            globalscope: GlobalScope::new_inherited(
135                init.devtools_chan.clone(),
136                init.mem_profiler_chan.clone(),
137                init.time_profiler_chan.clone(),
138                init.script_to_constellation_sender.clone(),
139                init.to_embedder_sender.clone(),
140                init.resource_threads.clone(),
141                init.storage_threads.clone(),
142                base_url.clone(),
143                None,
144                #[cfg(feature = "webgpu")]
145                init.gpu_id_hub.clone(),
146                inherited_secure_context,
147                false,
148            ),
149            base_url,
150            to_script_thread_sender: init.to_script_thread_sender.clone(),
151            executor,
152            pipeline_id,
153            task_manager: Rc::new(TaskManager::new(
154                Some(script_event_loop_sender),
155                pipeline_id,
156                Some(TaskCanceller {
157                    cancelled: closing.clone(),
158                }),
159            )),
160            origin: MutableOrigin::new(ImmutableOrigin::new_opaque()),
161            closing,
162            module_map: Default::default(),
163        }
164    }
165
166    pub(crate) fn module_map(
167        &self,
168    ) -> &DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>> {
169        &self.module_map
170    }
171
172    pub(crate) fn origin(&self) -> MutableOrigin {
173        self.origin.clone()
174    }
175
176    pub(crate) fn pipeline_id(&self) -> PipelineId {
177        self.pipeline_id
178    }
179
180    /// Register a paint worklet to the script thread.
181    pub(crate) fn register_paint_worklet(
182        &self,
183        name: Atom,
184        properties: Vec<Atom>,
185        painter: Box<dyn Painter>,
186    ) {
187        self.to_script_thread_sender
188            .send(MainThreadScriptMsg::RegisterPaintWorklet {
189                pipeline_id: self.globalscope.pipeline_id(),
190                name,
191                properties,
192                painter,
193            })
194            .expect("Worklet thread outlived script thread.");
195    }
196
197    /// The base URL of this global.
198    pub(crate) fn base_url(&self) -> ServoUrl {
199        self.base_url.clone()
200    }
201
202    /// The worklet executor.
203    pub(crate) fn executor(&self) -> WorkletExecutor {
204        self.executor.clone()
205    }
206
207    pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
208        self.task_manager.clone()
209    }
210
211    pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut JSContext) {
212        if !self.closing.load(Ordering::SeqCst) {
213            job_queue_microtask_checkpoint(cx, vec![DomRoot::from_ref(&self.globalscope)]);
214        }
215    }
216}
217
218impl From<&Window> for WorkletGlobalScopeInit {
219    fn from(window: &Window) -> Self {
220        let global_scope = window.as_global_scope();
221
222        WorkletGlobalScopeInit {
223            to_script_thread_sender: window.main_thread_script_chan().clone(),
224            resource_threads: global_scope.resource_threads().clone(),
225            storage_threads: global_scope.storage_threads().clone(),
226            mem_profiler_chan: global_scope.mem_profiler_chan().clone(),
227            time_profiler_chan: global_scope.time_profiler_chan().clone(),
228            devtools_chan: global_scope.devtools_chan().cloned(),
229            script_to_constellation_sender: global_scope.script_to_constellation_chan().sender,
230            to_embedder_sender: global_scope.script_to_embedder_chan().clone(),
231            image_cache: global_scope.image_cache(),
232            #[cfg(feature = "webgpu")]
233            gpu_id_hub: global_scope.wgpu_id_hub(),
234        }
235    }
236}
237
238/// Resources required by workletglobalscopes
239#[derive(Clone)]
240pub(crate) struct WorkletGlobalScopeInit {
241    /// Channel to the main script thread
242    pub(crate) to_script_thread_sender: Sender<MainThreadScriptMsg>,
243    /// Channel to a resource thread
244    pub(crate) resource_threads: ResourceThreads,
245    /// Channels to the [`StorageThreads`].
246    pub(crate) storage_threads: StorageThreads,
247    /// Channel to the memory profiler
248    pub(crate) mem_profiler_chan: mem::ProfilerChan,
249    /// Channel to the time profiler
250    pub(crate) time_profiler_chan: time::ProfilerChan,
251    /// Channel to devtools
252    pub(crate) devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
253    /// Messages to send to the Embedder
254    pub(crate) to_embedder_sender: ScriptToEmbedderChan,
255    /// The image cache
256    pub(crate) image_cache: Arc<dyn ImageCache>,
257    /// Identity manager for WebGPU resources
258    #[cfg(feature = "webgpu")]
259    pub(crate) gpu_id_hub: Arc<IdentityHub>,
260    pub(crate) script_to_constellation_sender: ScriptToConstellationSender,
261}
262
263/// <https://drafts.css-houdini.org/worklets/#worklet-global-scope-type>
264#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
265pub(crate) enum WorkletGlobalScopeType {
266    /// A servo-specific testing worklet
267    #[cfg(feature = "testbinding")]
268    Test,
269    /// A paint worklet
270    Paint,
271}