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