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