1use std::rc::Rc;
6use std::sync::Arc;
7use std::sync::atomic::{AtomicBool, Ordering};
8
9use crossbeam_channel::Sender;
10use devtools_traits::ScriptToDevtoolsControlMsg;
11use dom_struct::dom_struct;
12use embedder_traits::ScriptToEmbedderChan;
13use js::context::JSContext;
14use net_traits::ResourceThreads;
15use net_traits::image_cache::ImageCache;
16use profile_traits::{mem, time};
17use script_traits::Painter;
18use servo_base::generic_channel::GenericCallback;
19use servo_base::id::PipelineId;
20use servo_constellation_traits::ScriptToConstellationSender;
21use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
22use storage_traits::StorageThreads;
23use stylo_atoms::Atom;
24
25use crate::dom::bindings::inheritance::Castable;
26use crate::dom::bindings::root::DomRoot;
27use crate::dom::bindings::trace::CustomTraceable;
28use crate::dom::bindings::utils::define_all_exposed_interfaces;
29use crate::dom::globalscope::GlobalScope;
30use crate::dom::paintworkletglobalscope::{PaintWorkletGlobalScope, PaintWorkletTask};
31#[cfg(feature = "testbinding")]
32use crate::dom::testworkletglobalscope::{TestWorkletGlobalScope, TestWorkletTask};
33#[cfg(feature = "webgpu")]
34use crate::dom::webgpu::identityhub::IdentityHub;
35use crate::dom::worklet::WorkletExecutor;
36use crate::messaging::MainThreadScriptMsg;
37use crate::microtask::MicrotaskQueue;
38use crate::realms::enter_auto_realm;
39use crate::tasks::task::TaskCanceller;
40use crate::tasks::task_manager::TaskManager;
41
42#[dom_struct]
43pub(crate) struct WorkletGlobalScope {
45 globalscope: GlobalScope,
47 #[no_trace]
49 base_url: ServoUrl,
50 to_script_thread_sender: Sender<MainThreadScriptMsg>,
52 executor: WorkletExecutor,
54
55 #[no_trace]
56 pipeline_id: PipelineId,
58
59 #[no_trace]
60 origin: MutableOrigin,
61
62 #[conditional_malloc_size_of]
64 task_manager: Rc<TaskManager>,
65
66 #[conditional_malloc_size_of]
69 microtask_queue: Rc<MicrotaskQueue>,
70
71 #[conditional_malloc_size_of]
72 closing: Arc<AtomicBool>,
73}
74
75impl WorkletGlobalScope {
76 #[allow(clippy::too_many_arguments)]
78 pub(crate) fn new(
79 scope_type: WorkletGlobalScopeType,
80 pipeline_id: PipelineId,
81 base_url: ServoUrl,
82 inherited_secure_context: Option<bool>,
83 executor: WorkletExecutor,
84 init: &WorkletGlobalScopeInit,
85 cx: &mut JSContext,
86 closing: Arc<AtomicBool>,
87 microtask_queue: Rc<MicrotaskQueue>,
88 ) -> DomRoot<WorkletGlobalScope> {
89 let scope: DomRoot<WorkletGlobalScope> = match scope_type {
90 #[cfg(feature = "testbinding")]
91 WorkletGlobalScopeType::Test => DomRoot::upcast(TestWorkletGlobalScope::new(
92 pipeline_id,
93 base_url,
94 inherited_secure_context,
95 executor,
96 init,
97 cx,
98 closing,
99 microtask_queue,
100 )),
101 WorkletGlobalScopeType::Paint => DomRoot::upcast(PaintWorkletGlobalScope::new(
102 cx,
103 pipeline_id,
104 base_url,
105 inherited_secure_context,
106 executor,
107 init,
108 closing,
109 microtask_queue,
110 )),
111 };
112
113 let mut realm = enter_auto_realm(cx, &*scope);
114 let mut realm = realm.current_realm();
115 define_all_exposed_interfaces(&mut realm, scope.upcast());
116
117 scope
118 }
119
120 pub(crate) fn new_inherited(
122 pipeline_id: PipelineId,
123 base_url: ServoUrl,
124 inherited_secure_context: Option<bool>,
125 executor: WorkletExecutor,
126 init: &WorkletGlobalScopeInit,
127 closing: Arc<AtomicBool>,
128 microtask_queue: Rc<MicrotaskQueue>,
129 ) -> Self {
130 let script_event_loop_sender = executor.event_loop_sender();
131
132 Self {
133 globalscope: GlobalScope::new_inherited(
134 init.devtools_chan.clone(),
135 init.mem_profiler_chan.clone(),
136 init.time_profiler_chan.clone(),
137 init.script_to_constellation_sender.clone(),
138 init.to_embedder_sender.clone(),
139 init.resource_threads.clone(),
140 init.storage_threads.clone(),
141 base_url.clone(),
142 None,
143 #[cfg(feature = "webgpu")]
144 init.gpu_id_hub.clone(),
145 inherited_secure_context,
146 false,
147 None, ),
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 microtask_queue,
162 closing,
163 }
164 }
165
166 pub(crate) fn origin(&self) -> MutableOrigin {
167 self.origin.clone()
168 }
169
170 pub(crate) fn pipeline_id(&self) -> PipelineId {
171 self.pipeline_id
172 }
173
174 pub(crate) fn register_paint_worklet(
176 &self,
177 name: Atom,
178 properties: Vec<Atom>,
179 painter: Box<dyn Painter>,
180 ) {
181 self.to_script_thread_sender
182 .send(MainThreadScriptMsg::RegisterPaintWorklet {
183 pipeline_id: self.globalscope.pipeline_id(),
184 name,
185 properties,
186 painter,
187 })
188 .expect("Worklet thread outlived script thread.");
189 }
190
191 pub(crate) fn base_url(&self) -> ServoUrl {
193 self.base_url.clone()
194 }
195
196 pub(crate) fn executor(&self) -> WorkletExecutor {
198 self.executor.clone()
199 }
200
201 pub(crate) fn perform_a_worklet_task(&self, cx: &mut JSContext, task: WorkletTask) {
203 match task {
204 #[cfg(feature = "testbinding")]
205 WorkletTask::Test(task) => match self.downcast::<TestWorkletGlobalScope>() {
206 Some(global) => global.perform_a_worklet_task(task),
207 None => warn!("This is not a test worklet."),
208 },
209 WorkletTask::Paint(task) => match self.downcast::<PaintWorkletGlobalScope>() {
210 Some(global) => global.perform_a_worklet_task(cx, task),
211 None => warn!("This is not a paint worklet."),
212 },
213 }
214 }
215
216 pub(crate) fn task_manager(&self) -> Rc<TaskManager> {
217 self.task_manager.clone()
218 }
219
220 pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut JSContext) {
221 if !self.closing.load(Ordering::SeqCst) {
222 self.microtask_queue
223 .checkpoint(cx, vec![DomRoot::from_ref(&self.globalscope)]);
224 }
225 }
226}
227
228#[derive(Clone)]
230pub(crate) struct WorkletGlobalScopeInit {
231 pub(crate) to_script_thread_sender: Sender<MainThreadScriptMsg>,
233 pub(crate) resource_threads: ResourceThreads,
235 pub(crate) storage_threads: StorageThreads,
237 pub(crate) mem_profiler_chan: mem::ProfilerChan,
239 pub(crate) time_profiler_chan: time::ProfilerChan,
241 pub(crate) devtools_chan: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
243 pub(crate) to_embedder_sender: ScriptToEmbedderChan,
245 pub(crate) image_cache: Arc<dyn ImageCache>,
247 #[cfg(feature = "webgpu")]
249 pub(crate) gpu_id_hub: Arc<IdentityHub>,
250 pub(crate) script_to_constellation_sender: ScriptToConstellationSender,
251}
252
253#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf)]
255pub(crate) enum WorkletGlobalScopeType {
256 #[cfg(feature = "testbinding")]
258 Test,
259 Paint,
261}
262
263pub(crate) enum WorkletTask {
265 #[cfg(feature = "testbinding")]
266 Test(TestWorkletTask),
267 Paint(PaintWorkletTask),
268}