1#![allow(clippy::new_without_default)]
8
9use std::cell::Cell;
10use std::fmt;
11use std::marker::PhantomData;
12use std::num::NonZeroU32;
13use std::sync::atomic::AtomicU32;
14use std::sync::{Arc, LazyLock};
15
16use malloc_size_of::MallocSizeOfOps;
17use malloc_size_of_derive::MallocSizeOf;
18use parking_lot::Mutex;
19use regex::Regex;
20use serde::{Deserialize, Serialize};
21use webrender_api::{ExternalScrollId, PipelineId as WebRenderPipelineId};
22
23use crate::generic_channel::{self, GenericReceiver, GenericSender};
24
25macro_rules! size_of_test {
27 ($t: ty, $expected_size: expr) => {
28 const _: () = assert!(std::mem::size_of::<$t>() == $expected_size);
29 };
30}
31
32pub trait Indexable {
35 const DISPLAY_PREFIX: &'static str;
38}
39
40#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
41pub struct Index<T>(pub NonZeroU32, pub PhantomData<T>);
43
44#[derive(Debug)]
45pub struct ZeroIndex;
48
49impl<T> Index<T> {
50 pub fn new(value: u32) -> Result<Index<T>, ZeroIndex> {
53 Ok(Index(NonZeroU32::new(value).ok_or(ZeroIndex)?, PhantomData))
54 }
55}
56
57impl<T> malloc_size_of::MallocSizeOf for Index<T> {
58 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
59 0
60 }
61}
62
63#[derive(
64 Clone, Copy, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
65)]
66pub struct NamespaceIndex<T> {
68 pub namespace_id: PipelineNamespaceId,
69 pub index: Index<T>,
70}
71
72impl<T> fmt::Debug for NamespaceIndex<T> {
73 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
74 let PipelineNamespaceId(namespace_id) = self.namespace_id;
75 let Index(index, _) = self.index;
76 write!(fmt, "({},{})", namespace_id, index.get())
77 }
78}
79
80impl<T: Indexable> fmt::Display for NamespaceIndex<T> {
81 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
82 write!(fmt, "{}{:?}", T::DISPLAY_PREFIX, self)
83 }
84}
85
86macro_rules! namespace_id {
87 ($id_name:ident, $index_name:ident, $display_prefix:literal) => {
88 #[derive(
89 Clone,
90 Copy,
91 Debug,
92 Deserialize,
93 Eq,
94 Hash,
95 Ord,
96 PartialEq,
97 PartialOrd,
98 Serialize,
99 MallocSizeOf,
100 )]
101 pub struct $index_name;
102 impl Indexable for $index_name {
103 const DISPLAY_PREFIX: &'static str = $display_prefix;
104 }
105 pub type $id_name = NamespaceIndex<$index_name>;
106 impl $id_name {
107 pub fn new() -> $id_name {
108 PIPELINE_NAMESPACE.with(|tls| {
109 let mut namespace = tls.get().expect("No namespace set for this thread!");
110 let next_id = namespace.next_namespace_index();
111 tls.set(Some(namespace));
112 next_id
113 })
114 }
115 }
116 };
117}
118
119#[derive(Debug, Deserialize, Serialize)]
120pub struct PipelineNamespaceRequest(pub GenericSender<PipelineNamespaceId>);
122
123pub struct PipelineNamespaceInstaller {
125 request_sender: Option<GenericSender<PipelineNamespaceRequest>>,
126 namespace_sender: GenericSender<PipelineNamespaceId>,
127 namespace_receiver: GenericReceiver<PipelineNamespaceId>,
128}
129
130impl Default for PipelineNamespaceInstaller {
131 fn default() -> Self {
132 let (namespace_sender, namespace_receiver) =
133 generic_channel::channel().expect("PipelineNamespaceInstaller channel failure");
134 Self {
135 request_sender: None,
136 namespace_sender,
137 namespace_receiver,
138 }
139 }
140}
141
142impl PipelineNamespaceInstaller {
143 pub fn set_sender(&mut self, sender: GenericSender<PipelineNamespaceRequest>) {
145 self.request_sender = Some(sender);
146 }
147
148 pub fn install_namespace(&self) {
150 match self.request_sender.as_ref() {
151 Some(sender) => {
152 let _ = sender.send(PipelineNamespaceRequest(self.namespace_sender.clone()));
153 let namespace_id = self
154 .namespace_receiver
155 .recv()
156 .expect("The constellation to make a pipeline namespace id available");
157 PipelineNamespace::install(namespace_id);
158 },
159 None => unreachable!("PipelineNamespaceInstaller should have a request_sender setup"),
160 }
161 }
162}
163
164static PIPELINE_NAMESPACE_INSTALLER: LazyLock<Arc<Mutex<PipelineNamespaceInstaller>>> =
172 LazyLock::new(|| Arc::new(Mutex::new(PipelineNamespaceInstaller::default())));
173
174#[derive(Clone, Copy)]
194pub struct PipelineNamespace {
195 id: PipelineNamespaceId,
196 index: u32,
197}
198
199impl PipelineNamespace {
200 pub fn install(namespace_id: PipelineNamespaceId) {
202 PIPELINE_NAMESPACE.with(|tls| {
203 assert!(tls.get().is_none());
204 tls.set(Some(PipelineNamespace {
205 id: namespace_id,
206 index: 0,
207 }));
208 });
209 }
210
211 pub fn set_installer_sender(sender: GenericSender<PipelineNamespaceRequest>) {
214 PIPELINE_NAMESPACE_INSTALLER.lock().set_sender(sender);
215 }
216
217 pub fn auto_install() {
220 PIPELINE_NAMESPACE_INSTALLER.lock().install_namespace();
228 }
229
230 fn next_index(&mut self) -> NonZeroU32 {
231 self.index += 1;
232 NonZeroU32::new(self.index).expect("pipeline id index wrapped!")
233 }
234
235 fn next_namespace_index<T>(&mut self) -> NamespaceIndex<T> {
236 NamespaceIndex {
237 namespace_id: self.id,
238 index: Index(self.next_index(), PhantomData),
239 }
240 }
241}
242
243thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = const { Cell::new(None) });
244
245#[derive(
246 Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
247)]
248pub struct PipelineNamespaceId(pub u32);
249
250namespace_id! {PipelineId, PipelineIndex, "Pipeline"}
251
252size_of_test!(PipelineId, 8);
253size_of_test!(Option<PipelineId>, 8);
254
255impl PipelineId {
256 pub fn root_scroll_id(&self) -> webrender_api::ExternalScrollId {
257 ExternalScrollId(0, self.into())
258 }
259}
260
261impl From<WebRenderPipelineId> for PipelineId {
262 #[allow(unsafe_code)]
263 fn from(pipeline: WebRenderPipelineId) -> Self {
264 let WebRenderPipelineId(namespace_id, index) = pipeline;
265 unsafe {
266 PipelineId {
267 namespace_id: PipelineNamespaceId(namespace_id),
268 index: Index(NonZeroU32::new_unchecked(index), PhantomData),
269 }
270 }
271 }
272}
273
274impl From<PipelineId> for WebRenderPipelineId {
275 fn from(value: PipelineId) -> Self {
276 let PipelineNamespaceId(namespace_id) = value.namespace_id;
277 let Index(index, _) = value.index;
278 WebRenderPipelineId(namespace_id, index.get())
279 }
280}
281
282impl From<&PipelineId> for WebRenderPipelineId {
283 fn from(value: &PipelineId) -> Self {
284 (*value).into()
285 }
286}
287
288namespace_id! {BrowsingContextId, BrowsingContextIndex, "BrowsingContext"}
289
290size_of_test!(BrowsingContextId, 8);
291size_of_test!(Option<BrowsingContextId>, 8);
292
293#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
294pub struct BrowsingContextGroupId(pub u32);
295impl fmt::Display for BrowsingContextGroupId {
296 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297 write!(f, "BrowsingContextGroup{:?}", self)
298 }
299}
300
301impl BrowsingContextId {
302 pub fn from_string(str: &str) -> Option<BrowsingContextId> {
303 let re = Regex::new(r"^BrowsingContext\((\d+),(\d+)\)$").ok()?;
304 let caps = re.captures(str)?;
305 let namespace_id = caps.get(1)?.as_str().parse::<u32>().ok()?;
306 let index = caps.get(2)?.as_str().parse::<u32>().ok()?;
307
308 let result = BrowsingContextId {
309 namespace_id: PipelineNamespaceId(namespace_id),
310 index: Index::new(index).ok()?,
311 };
312 assert_eq!(result.to_string(), str.to_string());
313 Some(result)
314 }
315}
316
317thread_local!(pub static WEBVIEW_ID: Cell<Option<WebViewId>> =
318 const { Cell::new(None) });
319
320#[derive(
321 Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
322)]
323pub struct WebViewId(PainterId, BrowsingContextId);
324
325size_of_test!(WebViewId, 12);
326size_of_test!(Option<WebViewId>, 12);
327
328impl fmt::Display for WebViewId {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 write!(f, "{}, TopLevel{}", self.0, self.1)
331 }
332}
333
334impl WebViewId {
335 pub fn new(painter_id: PainterId) -> WebViewId {
336 WebViewId(painter_id, BrowsingContextId::new())
337 }
338
339 pub fn install(id: WebViewId) {
342 WEBVIEW_ID.with(|tls| tls.set(Some(id)))
343 }
344
345 pub fn installed() -> Option<WebViewId> {
346 WEBVIEW_ID.with(|tls| tls.get())
347 }
348
349 pub fn mock_for_testing(browsing_context_id: BrowsingContextId) -> WebViewId {
350 WebViewId(PainterId(0), browsing_context_id)
351 }
352}
353
354impl From<WebViewId> for BrowsingContextId {
355 fn from(id: WebViewId) -> BrowsingContextId {
356 id.1
357 }
358}
359
360impl From<WebViewId> for PainterId {
361 fn from(id: WebViewId) -> PainterId {
362 id.0
363 }
364}
365
366impl PartialEq<WebViewId> for BrowsingContextId {
367 fn eq(&self, rhs: &WebViewId) -> bool {
368 self.eq(&rhs.1)
369 }
370}
371
372impl PartialEq<BrowsingContextId> for WebViewId {
373 fn eq(&self, rhs: &BrowsingContextId) -> bool {
374 self.1.eq(rhs)
375 }
376}
377
378namespace_id! {MessagePortId, MessagePortIndex, "MessagePort"}
379
380namespace_id! {MessagePortRouterId, MessagePortRouterIndex, "MessagePortRouter"}
381
382namespace_id! {BroadcastChannelRouterId, BroadcastChannelRouterIndex, "BroadcastChannelRouter"}
383
384namespace_id! {ServiceWorkerId, ServiceWorkerIndex, "ServiceWorker"}
385
386namespace_id! {ServiceWorkerRegistrationId, ServiceWorkerRegistrationIndex, "ServiceWorkerRegistration"}
387
388namespace_id! {BlobId, BlobIndex, "Blob"}
389
390namespace_id! {DomPointId, DomPointIndex, "DomPoint"}
391
392namespace_id! {DomRectId, DomRectIndex, "DomRect"}
393
394namespace_id! {DomQuadId, DomQuadIndex, "DomQuad"}
395
396namespace_id! {DomMatrixId, DomMatrixIndex, "DomMatrix"}
397
398namespace_id! {DomExceptionId, DomExceptionIndex, "DomException"}
399
400namespace_id! {QuotaExceededErrorId, QuotaExceededErrorIndex, "QuotaExceededError"}
401
402namespace_id! {HistoryStateId, HistoryStateIndex, "HistoryState"}
403
404namespace_id! {ImageBitmapId, ImageBitmapIndex, "ImageBitmap"}
405
406namespace_id! {OffscreenCanvasId, OffscreenCanvasIndex, "OffscreenCanvas"}
407
408namespace_id! {CookieStoreId, CookieStoreIndex, "CookieStore"}
409
410namespace_id! {ImageDataId, ImageDataIndex, "ImageData"}
411
412pub const TEST_NAMESPACE: PipelineNamespaceId = PipelineNamespaceId(1234);
414pub const TEST_PIPELINE_INDEX: Index<PipelineIndex> =
415 Index(NonZeroU32::new(5678).unwrap(), PhantomData);
416pub const TEST_PIPELINE_ID: PipelineId = PipelineId {
417 namespace_id: TEST_NAMESPACE,
418 index: TEST_PIPELINE_INDEX,
419};
420pub const TEST_BROWSING_CONTEXT_INDEX: Index<BrowsingContextIndex> =
421 Index(NonZeroU32::new(8765).unwrap(), PhantomData);
422pub const TEST_BROWSING_CONTEXT_ID: BrowsingContextId = BrowsingContextId {
423 namespace_id: TEST_NAMESPACE,
424 index: TEST_BROWSING_CONTEXT_INDEX,
425};
426
427pub const TEST_PAINTER_ID: PainterId = PainterId(0);
428pub const TEST_WEBVIEW_ID: WebViewId = WebViewId(TEST_PAINTER_ID, TEST_BROWSING_CONTEXT_ID);
429
430#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
434pub struct ScrollTreeNodeId {
435 pub index: usize,
437}
438
439#[derive(
440 Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Hash, Eq, Serialize, Deserialize, MallocSizeOf,
441)]
442pub struct PainterId(u32);
443
444impl fmt::Display for PainterId {
445 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446 write!(f, "PainterId: {}", self.0)
447 }
448}
449
450static PAINTER_ID: AtomicU32 = AtomicU32::new(0);
451
452impl PainterId {
453 pub fn next() -> Self {
454 Self(PAINTER_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
455 }
456
457 pub fn first_for_system_font_service() -> Self {
459 Self(0)
460 }
461}