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
5use std::borrow::Cow;
6use std::sync::Arc;
7
8use crossbeam_channel::Sender;
9use devtools_traits::ScriptToDevtoolsControlMsg;
10use dom_struct::dom_struct;
11use embedder_traits::{JavaScriptEvaluationError, ScriptToEmbedderChan};
12use js::context::JSContext;
13use net_traits::ResourceThreads;
14use net_traits::image_cache::ImageCache;
15use profile_traits::{mem, time};
16use script_traits::Painter;
17use servo_base::generic_channel::GenericCallback;
18use servo_base::id::PipelineId;
19use servo_constellation_traits::ScriptToConstellationSender;
20use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
21use storage_traits::StorageThreads;
22use stylo_atoms::Atom;
23
24use crate::dom::bindings::inheritance::Castable;
25use crate::dom::bindings::root::DomRoot;
26use crate::dom::bindings::trace::CustomTraceable;
27use crate::dom::bindings::utils::define_all_exposed_interfaces;
28use crate::dom::globalscope::GlobalScope;
29use crate::dom::paintworkletglobalscope::{PaintWorkletGlobalScope, PaintWorkletTask};
30#[cfg(feature = "testbinding")]
31use crate::dom::testworkletglobalscope::{TestWorkletGlobalScope, TestWorkletTask};
32#[cfg(feature = "webgpu")]
33use crate::dom::webgpu::identityhub::IdentityHub;
34use crate::dom::worklet::WorkletExecutor;
35use crate::messaging::MainThreadScriptMsg;
36use crate::realms::enter_auto_realm;
37use crate::script_runtime::IntroductionType;
38
39#[dom_struct]
40/// <https://drafts.css-houdini.org/worklets/#workletglobalscope>
41pub(crate) struct WorkletGlobalScope {
42    /// The global for this worklet.
43    globalscope: GlobalScope,
44    /// The base URL for this worklet.
45    #[no_trace]
46    base_url: ServoUrl,
47    /// Sender back to the script thread
48    to_script_thread_sender: Sender<MainThreadScriptMsg>,
49    /// Worklet task executor
50    executor: WorkletExecutor,
51
52    #[no_trace]
53    /// The pipeline that created this worklet.
54    pipeline_id: PipelineId,
55
56    #[no_trace]
57    origin: MutableOrigin,
58}
59
60impl WorkletGlobalScope {
61    /// Create a new heap-allocated `WorkletGlobalScope`.
62    pub(crate) fn new(
63        scope_type: WorkletGlobalScopeType,
64        pipeline_id: PipelineId,
65        base_url: ServoUrl,
66        inherited_secure_context: Option<bool>,
67        executor: WorkletExecutor,
68        init: &WorkletGlobalScopeInit,
69        cx: &mut JSContext,
70    ) -> DomRoot<WorkletGlobalScope> {
71        let scope: DomRoot<WorkletGlobalScope> = match scope_type {
72            #[cfg(feature = "testbinding")]
73            WorkletGlobalScopeType::Test => DomRoot::upcast(TestWorkletGlobalScope::new(
74                pipeline_id,
75                base_url,
76                inherited_secure_context,
77                executor,
78                init,
79                cx,
80            )),
81            WorkletGlobalScopeType::Paint => DomRoot::upcast(PaintWorkletGlobalScope::new(
82                pipeline_id,
83                base_url,
84                inherited_secure_context,
85                executor,
86                init,
87                cx,
88            )),
89        };
90
91        let mut realm = enter_auto_realm(cx, &*scope);
92        let mut realm = realm.current_realm();
93        define_all_exposed_interfaces(&mut realm, scope.upcast());
94
95        scope
96    }
97
98    /// Create a new stack-allocated `WorkletGlobalScope`.
99    pub(crate) fn new_inherited(
100        pipeline_id: PipelineId,
101        base_url: ServoUrl,
102        inherited_secure_context: Option<bool>,
103        executor: WorkletExecutor,
104        init: &WorkletGlobalScopeInit,
105    ) -> Self {
106        Self {
107            globalscope: GlobalScope::new_inherited(
108                init.devtools_chan.clone(),
109                init.mem_profiler_chan.clone(),
110                init.time_profiler_chan.clone(),
111                init.script_to_constellation_sender.clone(),
112                init.to_embedder_sender.clone(),
113                init.resource_threads.clone(),
114                init.storage_threads.clone(),
115                base_url.clone(),
116                None,
117                #[cfg(feature = "webgpu")]
118                init.gpu_id_hub.clone(),
119                inherited_secure_context,
120                false,
121                None, // font_context
122            ),
123            base_url,
124            to_script_thread_sender: init.to_script_thread_sender.clone(),
125            executor,
126            pipeline_id,
127            origin: MutableOrigin::new(ImmutableOrigin::new_opaque()),
128        }
129    }
130
131    pub(crate) fn origin(&self) -> MutableOrigin {
132        self.origin.clone()
133    }
134
135    pub(crate) fn pipeline_id(&self) -> PipelineId {
136        self.pipeline_id
137    }
138
139    /// Evaluate a JS script in this global.
140    pub(crate) fn evaluate_js(
141        &self,
142        script: Cow<'_, str>,
143        cx: &mut JSContext,
144    ) -> Result<(), JavaScriptEvaluationError> {
145        let mut realm = enter_auto_realm(cx, self);
146        let cx = &mut realm.current_realm();
147
148        debug!("Evaluating Dom in a worklet.");
149        self.globalscope.evaluate_js_on_global(
150            cx,
151            script,
152            "",
153            Some(IntroductionType::WORKLET),
154            None,
155        )
156    }
157
158    /// Register a paint worklet to the script thread.
159    pub(crate) fn register_paint_worklet(
160        &self,
161        name: Atom,
162        properties: Vec<Atom>,
163        painter: Box<dyn Painter>,
164    ) {
165        self.to_script_thread_sender
166            .send(MainThreadScriptMsg::RegisterPaintWorklet {
167                pipeline_id: self.globalscope.pipeline_id(),
168                name,
169                properties,
170                painter,
171            })
172            .expect("Worklet thread outlived script thread.");
173    }
174
175    /// The base URL of this global.
176    pub(crate) fn base_url(&self) -> ServoUrl {
177        self.base_url.clone()
178    }
179
180    /// The worklet executor.
181    pub(crate) fn executor(&self) -> WorkletExecutor {
182        self.executor.clone()
183    }
184
185    /// Perform a worklet task
186    pub(crate) fn perform_a_worklet_task(&self, cx: &mut JSContext, task: WorkletTask) {
187        match task {
188            #[cfg(feature = "testbinding")]
189            WorkletTask::Test(task) => match self.downcast::<TestWorkletGlobalScope>() {
190                Some(global) => global.perform_a_worklet_task(task),
191                None => warn!("This is not a test worklet."),
192            },
193            WorkletTask::Paint(task) => match self.downcast::<PaintWorkletGlobalScope>() {
194                Some(global) => global.perform_a_worklet_task(cx, task),
195                None => warn!("This is not a paint worklet."),
196            },
197        }
198    }
199}
200
201/// Resources required by workletglobalscopes
202#[derive(Clone)]
203pub(crate) struct WorkletGlobalScopeInit {
204    /// Channel to the main script thread
205    pub(crate) to_script_thread_sender: Sender<MainThreadScriptMsg>,
206    /// Channel to a resource thread
207    pub(crate) resource_threads: ResourceThreads,
208    /// Channels to the [`StorageThreads`].
209    pub(crate) storage_threads: StorageThreads,
210    /// Channel to the memory profiler
211    pub(crate) mem_profiler_chan: mem::ProfilerChan,
212    /// Channel to the time profiler
213    pub(crate) time_profiler_chan: time::ProfilerChan,
214    /// Channel to devtools
215    pub(crate) devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
216    /// Messages to send to the Embedder
217    pub(crate) to_embedder_sender: ScriptToEmbedderChan,
218    /// The image cache
219    pub(crate) image_cache: Arc<dyn ImageCache>,
220    /// Identity manager for WebGPU resources
221    #[cfg(feature = "webgpu")]
222    pub(crate) gpu_id_hub: Arc<IdentityHub>,
223    pub(crate) script_to_constellation_sender: ScriptToConstellationSender,
224}
225
226/// <https://drafts.css-houdini.org/worklets/#worklet-global-scope-type>
227#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
228pub(crate) enum WorkletGlobalScopeType {
229    /// A servo-specific testing worklet
230    #[cfg(feature = "testbinding")]
231    Test,
232    /// A paint worklet
233    Paint,
234}
235
236/// A task which can be performed in the context of a worklet global.
237pub(crate) enum WorkletTask {
238    #[cfg(feature = "testbinding")]
239    Test(TestWorkletTask),
240    Paint(PaintWorkletTask),
241}