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, AtomicUsize, Ordering};
14use std::sync::{Arc, LazyLock};
15
16use accesskit::{TreeId, Uuid};
17use malloc_size_of::MallocSizeOfOps;
18use malloc_size_of_derive::MallocSizeOf;
19use parking_lot::Mutex;
20use regex::Regex;
21use serde::{Deserialize, Serialize};
22use webrender_api::{
23 ExternalScrollId, FontInstanceKey, FontKey, IdNamespace, ImageKey,
24 PipelineId as WebRenderPipelineId,
25};
26
27use crate::generic_channel::{self, GenericReceiver, GenericSender};
28
29macro_rules! size_of_test {
31 ($t: ty, $expected_size: expr) => {
32 const _: () = assert!(std::mem::size_of::<$t>() == $expected_size);
33 };
34}
35
36pub trait Indexable {
39 const DISPLAY_PREFIX: &'static str;
42}
43
44#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
45pub struct Index<T>(pub NonZeroU32, pub PhantomData<T>);
47
48#[derive(Debug)]
49pub struct ZeroIndex;
52
53impl<T> Index<T> {
54 pub fn new(value: u32) -> Result<Index<T>, ZeroIndex> {
57 Ok(Index(NonZeroU32::new(value).ok_or(ZeroIndex)?, PhantomData))
58 }
59}
60
61impl<T> malloc_size_of::MallocSizeOf for Index<T> {
62 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
63 0
64 }
65}
66
67#[derive(
68 Clone, Copy, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
69)]
70pub struct NamespaceIndex<T> {
72 pub namespace_id: PipelineNamespaceId,
73 pub index: Index<T>,
74}
75
76impl<T> fmt::Debug for NamespaceIndex<T> {
77 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
78 let PipelineNamespaceId(namespace_id) = self.namespace_id;
79 let Index(index, _) = self.index;
80 write!(fmt, "({},{})", namespace_id, index.get())
81 }
82}
83
84impl<T: Indexable> fmt::Display for NamespaceIndex<T> {
85 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
86 write!(fmt, "{}{:?}", T::DISPLAY_PREFIX, self)
87 }
88}
89
90macro_rules! namespace_id {
91 ($id_name:ident, $index_name:ident, $display_prefix:literal) => {
92 #[derive(
93 Clone,
94 Copy,
95 Debug,
96 Deserialize,
97 Eq,
98 Hash,
99 Ord,
100 PartialEq,
101 PartialOrd,
102 Serialize,
103 MallocSizeOf,
104 )]
105 pub struct $index_name;
106 impl Indexable for $index_name {
107 const DISPLAY_PREFIX: &'static str = $display_prefix;
108 }
109 pub type $id_name = NamespaceIndex<$index_name>;
110 impl $id_name {
111 pub fn new() -> $id_name {
112 PIPELINE_NAMESPACE.with(|tls| {
113 let mut namespace = tls.get().expect("No namespace set for this thread!");
114 let next_id = namespace.next_namespace_index();
115 tls.set(Some(namespace));
116 next_id
117 })
118 }
119 }
120 };
121}
122
123#[derive(Debug, Deserialize, Serialize)]
124pub struct PipelineNamespaceRequest(pub GenericSender<PipelineNamespaceId>);
126
127pub struct PipelineNamespaceInstaller {
129 request_sender: Option<GenericSender<PipelineNamespaceRequest>>,
130 namespace_sender: GenericSender<PipelineNamespaceId>,
131 namespace_receiver: GenericReceiver<PipelineNamespaceId>,
132}
133
134impl Default for PipelineNamespaceInstaller {
135 fn default() -> Self {
136 let (namespace_sender, namespace_receiver) =
137 generic_channel::channel().expect("PipelineNamespaceInstaller channel failure");
138 Self {
139 request_sender: None,
140 namespace_sender,
141 namespace_receiver,
142 }
143 }
144}
145
146impl PipelineNamespaceInstaller {
147 pub fn set_sender(&mut self, sender: GenericSender<PipelineNamespaceRequest>) {
149 self.request_sender = Some(sender);
150 }
151
152 pub fn install_namespace(&self) {
154 match self.request_sender.as_ref() {
155 Some(sender) => {
156 let _ = sender.send(PipelineNamespaceRequest(self.namespace_sender.clone()));
157 let namespace_id = self
158 .namespace_receiver
159 .recv()
160 .expect("The constellation to make a pipeline namespace id available");
161 PipelineNamespace::install(namespace_id);
162 },
163 None => unreachable!("PipelineNamespaceInstaller should have a request_sender setup"),
164 }
165 }
166}
167
168static PIPELINE_NAMESPACE_INSTALLER: LazyLock<Arc<Mutex<PipelineNamespaceInstaller>>> =
176 LazyLock::new(|| Arc::new(Mutex::new(PipelineNamespaceInstaller::default())));
177
178#[derive(Clone, Copy)]
198pub struct PipelineNamespace {
199 id: PipelineNamespaceId,
200 index: u32,
201}
202
203impl PipelineNamespace {
204 pub fn install(namespace_id: PipelineNamespaceId) {
206 PIPELINE_NAMESPACE.with(|tls| {
207 assert!(tls.get().is_none());
208 tls.set(Some(PipelineNamespace {
209 id: namespace_id,
210 index: 0,
211 }));
212 });
213 }
214
215 pub fn set_installer_sender(sender: GenericSender<PipelineNamespaceRequest>) {
218 PIPELINE_NAMESPACE_INSTALLER.lock().set_sender(sender);
219 }
220
221 pub fn auto_install() {
224 PIPELINE_NAMESPACE_INSTALLER.lock().install_namespace();
232 }
233
234 fn next_index(&mut self) -> NonZeroU32 {
235 self.index += 1;
236 NonZeroU32::new(self.index).expect("pipeline id index wrapped!")
237 }
238
239 fn next_namespace_index<T>(&mut self) -> NamespaceIndex<T> {
240 NamespaceIndex {
241 namespace_id: self.id,
242 index: Index(self.next_index(), PhantomData),
243 }
244 }
245}
246
247thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = const { Cell::new(None) });
248
249#[derive(
250 Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
251)]
252pub struct PipelineNamespaceId(pub u32);
253
254pub const EMBEDDER_PIPELINE_NAMESPACE_ID: PipelineNamespaceId = PipelineNamespaceId(0);
255pub const CONSTELLATION_PIPELINE_NAMESPACE_ID: PipelineNamespaceId = PipelineNamespaceId(1);
256pub const FIRST_CONTENT_PIPELINE_NAMESPACE_ID: PipelineNamespaceId = PipelineNamespaceId(2);
259
260namespace_id! {PipelineId, PipelineIndex, "Pipeline"}
261
262size_of_test!(PipelineId, 8);
263size_of_test!(Option<PipelineId>, 8);
264
265impl PipelineId {
266 pub fn root_scroll_id(&self) -> webrender_api::ExternalScrollId {
267 ExternalScrollId(0, self.into())
268 }
269}
270
271impl From<PipelineId> for TreeId {
272 fn from(pipeline_id: PipelineId) -> TreeId {
280 const PIPELINE_IDS: Uuid = Uuid::from_u128(0x429419c0_3277_47eb_8d31_7573b97621ee);
281 let with_namespace_id =
282 Uuid::new_v5(&PIPELINE_IDS, &pipeline_id.namespace_id.0.to_be_bytes());
283 let with_index = Uuid::new_v5(&with_namespace_id, &pipeline_id.index.0.get().to_be_bytes());
284 TreeId(with_index)
285 }
286}
287
288impl From<PipelineId> for u64 {
289 fn from(pipeline_id: PipelineId) -> Self {
290 ((pipeline_id.namespace_id.0 as u64) << 32) + pipeline_id.index.0.get() as u64
291 }
292}
293
294#[cfg(test)]
295#[test]
296fn test_pipeline_id_to_accesskit_tree_id() {
297 let namespace_id = PipelineNamespaceId(1);
298 let index = Index::new(1).expect("Guaranteed by argument");
299 let pipeline_id = PipelineId {
300 namespace_id,
301 index,
302 };
303 assert_eq!(
304 TreeId::from(pipeline_id),
305 TreeId(Uuid::from_u128(0x879211fb_8799_5492_9a31_95a35c05a192))
306 );
307}
308
309impl From<WebRenderPipelineId> for PipelineId {
310 #[expect(unsafe_code)]
311 fn from(pipeline: WebRenderPipelineId) -> Self {
312 let WebRenderPipelineId(namespace_id, index) = pipeline;
313 unsafe {
314 PipelineId {
315 namespace_id: PipelineNamespaceId(namespace_id),
316 index: Index(NonZeroU32::new_unchecked(index), PhantomData),
317 }
318 }
319 }
320}
321
322impl From<PipelineId> for WebRenderPipelineId {
323 fn from(value: PipelineId) -> Self {
324 let PipelineNamespaceId(namespace_id) = value.namespace_id;
325 let Index(index, _) = value.index;
326 WebRenderPipelineId(namespace_id, index.get())
327 }
328}
329
330impl From<&PipelineId> for WebRenderPipelineId {
331 fn from(value: &PipelineId) -> Self {
332 (*value).into()
333 }
334}
335
336namespace_id! {BrowsingContextId, BrowsingContextIndex, "BrowsingContext"}
337
338size_of_test!(BrowsingContextId, 8);
339size_of_test!(Option<BrowsingContextId>, 8);
340
341impl BrowsingContextId {
342 pub fn from_string(str: &str) -> Option<BrowsingContextId> {
343 let re = Regex::new(r"^BrowsingContext\((\d+),(\d+)\)$").ok()?;
344 let caps = re.captures(str)?;
345 let namespace_id = caps.get(1)?.as_str().parse::<u32>().ok()?;
346 let index = caps.get(2)?.as_str().parse::<u32>().ok()?;
347
348 let result = BrowsingContextId {
349 namespace_id: PipelineNamespaceId(namespace_id),
350 index: Index::new(index).ok()?,
351 };
352 assert_eq!(result.to_string(), str.to_string());
353 Some(result)
354 }
355}
356
357#[derive(
358 Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
359)]
360pub struct WebViewId(PainterId, BrowsingContextId);
361
362size_of_test!(WebViewId, 12);
363size_of_test!(Option<WebViewId>, 12);
364
365impl fmt::Display for WebViewId {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 write!(f, "{}, TopLevel{}", self.0, self.1)
368 }
369}
370
371impl WebViewId {
372 pub fn new(painter_id: PainterId) -> WebViewId {
373 WebViewId(painter_id, BrowsingContextId::new())
374 }
375
376 pub fn mock_for_testing(browsing_context_id: BrowsingContextId) -> WebViewId {
377 WebViewId(TEST_PAINTER_ID, browsing_context_id)
378 }
379}
380
381impl From<WebViewId> for BrowsingContextId {
382 fn from(id: WebViewId) -> BrowsingContextId {
383 id.1
384 }
385}
386
387impl From<WebViewId> for PainterId {
388 fn from(id: WebViewId) -> PainterId {
389 id.0
390 }
391}
392
393impl PartialEq<WebViewId> for BrowsingContextId {
394 fn eq(&self, rhs: &WebViewId) -> bool {
395 self.eq(&rhs.1)
396 }
397}
398
399impl PartialEq<BrowsingContextId> for WebViewId {
400 fn eq(&self, rhs: &BrowsingContextId) -> bool {
401 self.1.eq(rhs)
402 }
403}
404
405namespace_id! {MessagePortId, MessagePortIndex, "MessagePort"}
406
407namespace_id! {MessagePortRouterId, MessagePortRouterIndex, "MessagePortRouter"}
408
409namespace_id! {BroadcastChannelRouterId, BroadcastChannelRouterIndex, "BroadcastChannelRouter"}
410
411namespace_id! {ServiceWorkerId, ServiceWorkerIndex, "ServiceWorker"}
412
413namespace_id! {ServiceWorkerRegistrationId, ServiceWorkerRegistrationIndex, "ServiceWorkerRegistration"}
414
415#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
417pub struct WorkerId(pub Uuid);
418
419impl fmt::Display for WorkerId {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 write!(f, "{}", self.0)
422 }
423}
424
425impl std::str::FromStr for WorkerId {
426 type Err = uuid::Error;
427
428 fn from_str(s: &str) -> Result<Self, Self::Err> {
429 Ok(Self(s.parse()?))
430 }
431}
432
433namespace_id! {BlobId, BlobIndex, "Blob"}
434
435namespace_id! {FileId, FileIndex, "File"}
436
437namespace_id! {FileListId, FileListIndex, "FileList"}
438
439namespace_id! {DomPointId, DomPointIndex, "DomPoint"}
440
441namespace_id! {DomRectId, DomRectIndex, "DomRect"}
442
443namespace_id! {DomQuadId, DomQuadIndex, "DomQuad"}
444
445namespace_id! {DomMatrixId, DomMatrixIndex, "DomMatrix"}
446
447namespace_id! {DomExceptionId, DomExceptionIndex, "DomException"}
448
449namespace_id! {QuotaExceededErrorId, QuotaExceededErrorIndex, "QuotaExceededError"}
450
451namespace_id! {HistoryStateId, HistoryStateIndex, "HistoryState"}
452
453namespace_id! {ImageBitmapId, ImageBitmapIndex, "ImageBitmap"}
454
455namespace_id! {OffscreenCanvasId, OffscreenCanvasIndex, "OffscreenCanvas"}
456
457namespace_id! {CookieStoreId, CookieStoreIndex, "CookieStore"}
458
459namespace_id! {ImageDataId, ImageDataIndex, "ImageData"}
460
461namespace_id! {CryptoKeyId, CryptoKeyIndex, "CryptoKey"}
462
463pub const TEST_NAMESPACE: PipelineNamespaceId = PipelineNamespaceId(1234);
465pub const TEST_PIPELINE_INDEX: Index<PipelineIndex> =
466 Index(NonZeroU32::new(5678).unwrap(), PhantomData);
467pub const TEST_PIPELINE_ID: PipelineId = PipelineId {
468 namespace_id: TEST_NAMESPACE,
469 index: TEST_PIPELINE_INDEX,
470};
471pub const TEST_BROWSING_CONTEXT_INDEX: Index<BrowsingContextIndex> =
472 Index(NonZeroU32::new(8765).unwrap(), PhantomData);
473pub const TEST_BROWSING_CONTEXT_ID: BrowsingContextId = BrowsingContextId {
474 namespace_id: TEST_NAMESPACE,
475 index: TEST_BROWSING_CONTEXT_INDEX,
476};
477
478pub const TEST_PAINTER_ID: PainterId = PainterId(9999);
479pub const TEST_WEBVIEW_ID: WebViewId = WebViewId(TEST_PAINTER_ID, TEST_BROWSING_CONTEXT_ID);
480pub const TEST_SCRIPT_EVENT_LOOP_ID: ScriptEventLoopId = ScriptEventLoopId(1234);
481
482#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
486pub struct ScrollTreeNodeId {
487 pub index: usize,
489}
490
491#[derive(MallocSizeOf)]
492pub struct AtomicOptionScrollTreeNodeId(AtomicUsize);
493
494impl AtomicOptionScrollTreeNodeId {
495 pub fn new(option_id: Option<ScrollTreeNodeId>) -> Self {
496 Self(AtomicUsize::new(Self::from_option(option_id)))
497 }
498
499 pub fn set(&self, option_id: Option<ScrollTreeNodeId>) {
500 self.0
501 .store(Self::from_option(option_id), Ordering::Relaxed);
502 }
503
504 fn from_option(option_id: Option<ScrollTreeNodeId>) -> usize {
505 if let Some(ScrollTreeNodeId { index }) = option_id {
506 debug_assert_ne!(index, usize::MAX);
507 index
508 } else {
509 usize::MAX
510 }
511 }
512
513 pub fn get(&self) -> Option<ScrollTreeNodeId> {
514 match self.0.load(Ordering::Relaxed) {
515 usize::MAX => None,
516 index => Some(ScrollTreeNodeId { index }),
517 }
518 }
519}
520
521static PAINTER_ID: AtomicU32 = AtomicU32::new(1);
522
523#[derive(
524 Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Hash, Eq, Serialize, Deserialize, MallocSizeOf,
525)]
526pub struct PainterId(u32);
527
528impl fmt::Display for PainterId {
529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530 write!(f, "PainterId: {}", self.0)
531 }
532}
533
534impl PainterId {
535 pub fn next() -> Self {
536 Self(PAINTER_ID.fetch_add(1, Ordering::Relaxed))
537 }
538}
539
540impl From<PainterId> for IdNamespace {
541 fn from(painter_id: PainterId) -> Self {
542 IdNamespace(painter_id.0)
543 }
544}
545
546impl From<IdNamespace> for PainterId {
547 fn from(id_namespace: IdNamespace) -> Self {
548 PainterId(id_namespace.0)
549 }
550}
551
552impl From<FontKey> for PainterId {
553 fn from(font_key: FontKey) -> Self {
554 font_key.0.into()
555 }
556}
557
558impl From<FontInstanceKey> for PainterId {
559 fn from(font_instance_key: FontInstanceKey) -> Self {
560 font_instance_key.0.into()
561 }
562}
563
564impl From<ImageKey> for PainterId {
565 fn from(image_key: ImageKey) -> Self {
566 image_key.0.into()
567 }
568}
569
570static SCRIPT_EVENT_LOOP_ID: AtomicU32 = AtomicU32::new(1);
571thread_local!(pub static INSTALLED_SCRIPT_EVENT_LOOP_ID: Cell<Option<ScriptEventLoopId>> =
572 const { Cell::new(None) });
573
574#[derive(
575 Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Hash, Eq, Serialize, Deserialize, MallocSizeOf,
576)]
577pub struct ScriptEventLoopId(u32);
578
579impl ScriptEventLoopId {
580 pub fn new() -> Self {
581 Self(SCRIPT_EVENT_LOOP_ID.fetch_add(1, Ordering::Relaxed))
582 }
583
584 pub fn install(id: Self) {
587 INSTALLED_SCRIPT_EVENT_LOOP_ID.with(|tls| tls.set(Some(id)))
588 }
589
590 pub fn installed() -> Option<Self> {
591 INSTALLED_SCRIPT_EVENT_LOOP_ID.with(|tls| tls.get())
592 }
593}
594
595impl fmt::Display for ScriptEventLoopId {
596 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597 write!(f, "{}", self.0)
598 }
599}
600
601#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
604pub struct LCPCandidateID(pub u64);
605
606#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
616pub struct ContainerTimingID(pub u64);