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;
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]
48pub(crate) struct WorkletGlobalScope {
50 globalscope: GlobalScope,
52 #[no_trace]
54 base_url: ServoUrl,
55 to_script_thread_sender: Sender<MainThreadScriptMsg>,
57 executor: WorkletExecutor,
59
60 #[no_trace]
61 pipeline_id: PipelineId,
63
64 #[no_trace]
65 origin: MutableOrigin,
66
67 #[conditional_malloc_size_of]
69 task_manager: Rc<TaskManager>,
70
71 #[conditional_malloc_size_of]
72 closing: Arc<AtomicBool>,
73
74 #[ignore_malloc_size_of = "mozjs"]
77 module_map: DomRefCell<HashMapTracedValues<ModuleRequest, ModuleStatus>>,
78}
79
80impl WorkletGlobalScope {
81 #[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 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 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 pub(crate) fn base_url(&self) -> ServoUrl {
199 self.base_url.clone()
200 }
201
202 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#[derive(Clone)]
240pub(crate) struct WorkletGlobalScopeInit {
241 pub(crate) to_script_thread_sender: Sender<MainThreadScriptMsg>,
243 pub(crate) resource_threads: ResourceThreads,
245 pub(crate) storage_threads: StorageThreads,
247 pub(crate) mem_profiler_chan: mem::ProfilerChan,
249 pub(crate) time_profiler_chan: time::ProfilerChan,
251 pub(crate) devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
253 pub(crate) to_embedder_sender: ScriptToEmbedderChan,
255 pub(crate) image_cache: Arc<dyn ImageCache>,
257 #[cfg(feature = "webgpu")]
259 pub(crate) gpu_id_hub: Arc<IdentityHub>,
260 pub(crate) script_to_constellation_sender: ScriptToConstellationSender,
261}
262
263#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
265pub(crate) enum WorkletGlobalScopeType {
266 #[cfg(feature = "testbinding")]
268 Test,
269 Paint,
271}