1#![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]
49pub(crate) struct WorkletGlobalScope {
51 globalscope: GlobalScope,
53 #[no_trace]
55 base_url: ServoUrl,
56 to_script_thread_sender: Sender<MainThreadScriptMsg>,
58 executor: WorkletExecutor,
60
61 #[no_trace]
62 pipeline_id: PipelineId,
64
65 #[no_trace]
66 origin: MutableOrigin,
67
68 #[conditional_malloc_size_of]
70 task_manager: Rc<TaskManager>,
71
72 #[conditional_malloc_size_of]
73 closing: Arc<AtomicBool>,
74
75 #[ignore_malloc_size_of = "mozjs"]
78 module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
79}
80
81impl WorkletGlobalScope {
82 #[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 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 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 pub(crate) fn base_url(&self) -> ServoUrl {
201 self.base_url.clone()
202 }
203
204 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#[derive(Clone)]
243pub(crate) struct WorkletGlobalScopeInit {
244 pub(crate) to_script_thread_sender: Sender<MainThreadScriptMsg>,
246 pub(crate) resource_threads: ResourceThreads,
248 pub(crate) storage_threads: StorageThreads,
250 pub(crate) mem_profiler_chan: mem::ProfilerChan,
252 pub(crate) time_profiler_chan: time::ProfilerChan,
254 #[cfg(feature = "devtools")]
256 pub(crate) devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
257 pub(crate) to_embedder_sender: ScriptToEmbedderChan,
259 pub(crate) image_cache: Arc<dyn ImageCache>,
261 #[cfg(feature = "webgpu")]
263 pub(crate) gpu_id_hub: Arc<IdentityHub>,
264 pub(crate) script_to_constellation_sender: ScriptToConstellationSender,
265}
266
267#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
269pub(crate) enum WorkletGlobalScopeType {
270 #[cfg(feature = "testbinding")]
272 Test,
273 Paint,
275}