#![allow(clippy::new_without_default)]
use std::cell::Cell;
use std::fmt;
use std::marker::PhantomData;
use std::num::NonZeroU32;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, LazyLock};
use accesskit::{TreeId, Uuid};
use malloc_size_of::MallocSizeOfOps;
use malloc_size_of_derive::MallocSizeOf;
use parking_lot::Mutex;
use regex::Regex;
use serde::{Deserialize, Serialize};
use webrender_api::{
ExternalScrollId, FontInstanceKey, FontKey, IdNamespace, ImageKey,
PipelineId as WebRenderPipelineId, SpatialTreeItemKey,
};
use crate::generic_channel::{self, GenericReceiver, GenericSender};
macro_rules! size_of_test {
($t: ty, $expected_size: expr) => {
const _: () = assert!(std::mem::size_of::<$t>() == $expected_size);
};
}
pub trait Indexable {
const DISPLAY_PREFIX: &'static str;
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Index<T>(pub NonZeroU32, pub PhantomData<T>);
#[derive(Debug)]
pub struct ZeroIndex;
impl<T> Index<T> {
pub fn new(value: u32) -> Result<Index<T>, ZeroIndex> {
Ok(Index(NonZeroU32::new(value).ok_or(ZeroIndex)?, PhantomData))
}
}
impl<T> malloc_size_of::MallocSizeOf for Index<T> {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
}
#[derive(
Clone, Copy, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct NamespaceIndex<T> {
pub namespace_id: PipelineNamespaceId,
pub index: Index<T>,
}
impl<T> fmt::Debug for NamespaceIndex<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let PipelineNamespaceId(namespace_id) = self.namespace_id;
let Index(index, _) = self.index;
write!(fmt, "({},{})", namespace_id, index.get())
}
}
impl<T: Indexable> fmt::Display for NamespaceIndex<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}{:?}", T::DISPLAY_PREFIX, self)
}
}
macro_rules! namespace_id {
($id_name:ident, $index_name:ident, $display_prefix:literal) => {
#[derive(
Clone,
Copy,
Debug,
Deserialize,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Serialize,
MallocSizeOf,
)]
pub struct $index_name;
impl Indexable for $index_name {
const DISPLAY_PREFIX: &'static str = $display_prefix;
}
pub type $id_name = NamespaceIndex<$index_name>;
impl $id_name {
pub fn new() -> $id_name {
PIPELINE_NAMESPACE.with(|tls| {
let mut namespace = tls.get().expect("No namespace set for this thread!");
let next_id = namespace.next_namespace_index();
tls.set(Some(namespace));
next_id
})
}
}
};
}
#[derive(Debug, Deserialize, Serialize)]
pub struct PipelineNamespaceRequest(pub GenericSender<PipelineNamespaceId>);
pub struct PipelineNamespaceInstaller {
request_sender: Option<GenericSender<PipelineNamespaceRequest>>,
namespace_sender: GenericSender<PipelineNamespaceId>,
namespace_receiver: GenericReceiver<PipelineNamespaceId>,
}
impl Default for PipelineNamespaceInstaller {
fn default() -> Self {
let (namespace_sender, namespace_receiver) =
generic_channel::channel().expect("PipelineNamespaceInstaller channel failure");
Self {
request_sender: None,
namespace_sender,
namespace_receiver,
}
}
}
impl PipelineNamespaceInstaller {
pub fn set_sender(&mut self, sender: GenericSender<PipelineNamespaceRequest>) {
self.request_sender = Some(sender);
}
pub fn install_namespace(&self) {
match self.request_sender.as_ref() {
Some(sender) => {
let _ = sender.send(PipelineNamespaceRequest(self.namespace_sender.clone()));
let namespace_id = self
.namespace_receiver
.recv()
.expect("The constellation to make a pipeline namespace id available");
PipelineNamespace::install(namespace_id);
},
None => unreachable!("PipelineNamespaceInstaller should have a request_sender setup"),
}
}
}
static PIPELINE_NAMESPACE_INSTALLER: LazyLock<Arc<Mutex<PipelineNamespaceInstaller>>> =
LazyLock::new(|| Arc::new(Mutex::new(PipelineNamespaceInstaller::default())));
#[derive(Clone, Copy)]
pub struct PipelineNamespace {
id: PipelineNamespaceId,
index: u32,
}
impl PipelineNamespace {
pub fn install(namespace_id: PipelineNamespaceId) {
PIPELINE_NAMESPACE.with(|tls| {
assert!(tls.get().is_none());
tls.set(Some(PipelineNamespace {
id: namespace_id,
index: 0,
}));
});
}
pub fn set_installer_sender(sender: GenericSender<PipelineNamespaceRequest>) {
PIPELINE_NAMESPACE_INSTALLER.lock().set_sender(sender);
}
pub fn auto_install() {
PIPELINE_NAMESPACE_INSTALLER.lock().install_namespace();
}
fn next_index(&mut self) -> NonZeroU32 {
self.index += 1;
NonZeroU32::new(self.index).expect("pipeline id index wrapped!")
}
fn next_namespace_index<T>(&mut self) -> NamespaceIndex<T> {
NamespaceIndex {
namespace_id: self.id,
index: Index(self.next_index(), PhantomData),
}
}
}
thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = const { Cell::new(None) });
#[derive(
Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct PipelineNamespaceId(pub u32);
pub const EMBEDDER_PIPELINE_NAMESPACE_ID: PipelineNamespaceId = PipelineNamespaceId(0);
pub const CONSTELLATION_PIPELINE_NAMESPACE_ID: PipelineNamespaceId = PipelineNamespaceId(1);
pub const FIRST_CONTENT_PIPELINE_NAMESPACE_ID: PipelineNamespaceId = PipelineNamespaceId(2);
namespace_id! {PipelineId, PipelineIndex, "Pipeline"}
size_of_test!(PipelineId, 8);
size_of_test!(Option<PipelineId>, 8);
impl PipelineId {
pub fn root_scroll_id(&self) -> webrender_api::ExternalScrollId {
ExternalScrollId(0, self.into())
}
}
impl From<PipelineId> for TreeId {
fn from(pipeline_id: PipelineId) -> TreeId {
const PIPELINE_IDS: Uuid = Uuid::from_u128(0x429419c0_3277_47eb_8d31_7573b97621ee);
let with_namespace_id =
Uuid::new_v5(&PIPELINE_IDS, &pipeline_id.namespace_id.0.to_be_bytes());
let with_index = Uuid::new_v5(&with_namespace_id, &pipeline_id.index.0.get().to_be_bytes());
TreeId(with_index)
}
}
impl From<PipelineId> for u64 {
fn from(pipeline_id: PipelineId) -> Self {
((pipeline_id.namespace_id.0 as u64) << 32) + pipeline_id.index.0.get() as u64
}
}
#[cfg(test)]
#[test]
fn test_pipeline_id_to_accesskit_tree_id() {
let namespace_id = PipelineNamespaceId(1);
let index = Index::new(1).expect("Guaranteed by argument");
let pipeline_id = PipelineId {
namespace_id,
index,
};
assert_eq!(
TreeId::from(pipeline_id),
TreeId(Uuid::from_u128(0x879211fb_8799_5492_9a31_95a35c05a192))
);
}
impl From<WebRenderPipelineId> for PipelineId {
#[expect(unsafe_code)]
fn from(pipeline: WebRenderPipelineId) -> Self {
let WebRenderPipelineId(namespace_id, index) = pipeline;
unsafe {
PipelineId {
namespace_id: PipelineNamespaceId(namespace_id),
index: Index(NonZeroU32::new_unchecked(index), PhantomData),
}
}
}
}
impl From<PipelineId> for WebRenderPipelineId {
fn from(value: PipelineId) -> Self {
let PipelineNamespaceId(namespace_id) = value.namespace_id;
let Index(index, _) = value.index;
WebRenderPipelineId(namespace_id, index.get())
}
}
impl From<&PipelineId> for WebRenderPipelineId {
fn from(value: &PipelineId) -> Self {
(*value).into()
}
}
namespace_id! {BrowsingContextId, BrowsingContextIndex, "BrowsingContext"}
size_of_test!(BrowsingContextId, 8);
size_of_test!(Option<BrowsingContextId>, 8);
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct BrowsingContextGroupId(pub u32);
impl fmt::Display for BrowsingContextGroupId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BrowsingContextGroup{:?}", self)
}
}
impl BrowsingContextId {
pub fn from_string(str: &str) -> Option<BrowsingContextId> {
let re = Regex::new(r"^BrowsingContext\((\d+),(\d+)\)$").ok()?;
let caps = re.captures(str)?;
let namespace_id = caps.get(1)?.as_str().parse::<u32>().ok()?;
let index = caps.get(2)?.as_str().parse::<u32>().ok()?;
let result = BrowsingContextId {
namespace_id: PipelineNamespaceId(namespace_id),
index: Index::new(index).ok()?,
};
assert_eq!(result.to_string(), str.to_string());
Some(result)
}
}
#[derive(
Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct WebViewId(PainterId, BrowsingContextId);
size_of_test!(WebViewId, 12);
size_of_test!(Option<WebViewId>, 12);
impl fmt::Display for WebViewId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}, TopLevel{}", self.0, self.1)
}
}
impl From<WebViewId> for SpatialTreeItemKey {
fn from(webview_id: WebViewId) -> Self {
Self::new(webview_id.1.index.0.get() as u64, 0)
}
}
impl WebViewId {
pub fn new(painter_id: PainterId) -> WebViewId {
WebViewId(painter_id, BrowsingContextId::new())
}
pub fn mock_for_testing(browsing_context_id: BrowsingContextId) -> WebViewId {
WebViewId(TEST_PAINTER_ID, browsing_context_id)
}
}
impl From<WebViewId> for BrowsingContextId {
fn from(id: WebViewId) -> BrowsingContextId {
id.1
}
}
impl From<WebViewId> for PainterId {
fn from(id: WebViewId) -> PainterId {
id.0
}
}
impl PartialEq<WebViewId> for BrowsingContextId {
fn eq(&self, rhs: &WebViewId) -> bool {
self.eq(&rhs.1)
}
}
impl PartialEq<BrowsingContextId> for WebViewId {
fn eq(&self, rhs: &BrowsingContextId) -> bool {
self.1.eq(rhs)
}
}
namespace_id! {MessagePortId, MessagePortIndex, "MessagePort"}
namespace_id! {MessagePortRouterId, MessagePortRouterIndex, "MessagePortRouter"}
namespace_id! {BroadcastChannelRouterId, BroadcastChannelRouterIndex, "BroadcastChannelRouter"}
namespace_id! {ServiceWorkerId, ServiceWorkerIndex, "ServiceWorker"}
namespace_id! {ServiceWorkerRegistrationId, ServiceWorkerRegistrationIndex, "ServiceWorkerRegistration"}
namespace_id! {BlobId, BlobIndex, "Blob"}
namespace_id! {FileId, FileIndex, "File"}
namespace_id! {FileListId, FileListIndex, "FileList"}
namespace_id! {DomPointId, DomPointIndex, "DomPoint"}
namespace_id! {DomRectId, DomRectIndex, "DomRect"}
namespace_id! {DomQuadId, DomQuadIndex, "DomQuad"}
namespace_id! {DomMatrixId, DomMatrixIndex, "DomMatrix"}
namespace_id! {DomExceptionId, DomExceptionIndex, "DomException"}
namespace_id! {QuotaExceededErrorId, QuotaExceededErrorIndex, "QuotaExceededError"}
namespace_id! {HistoryStateId, HistoryStateIndex, "HistoryState"}
namespace_id! {ImageBitmapId, ImageBitmapIndex, "ImageBitmap"}
namespace_id! {OffscreenCanvasId, OffscreenCanvasIndex, "OffscreenCanvas"}
namespace_id! {CookieStoreId, CookieStoreIndex, "CookieStore"}
namespace_id! {ImageDataId, ImageDataIndex, "ImageData"}
pub const TEST_NAMESPACE: PipelineNamespaceId = PipelineNamespaceId(1234);
pub const TEST_PIPELINE_INDEX: Index<PipelineIndex> =
Index(NonZeroU32::new(5678).unwrap(), PhantomData);
pub const TEST_PIPELINE_ID: PipelineId = PipelineId {
namespace_id: TEST_NAMESPACE,
index: TEST_PIPELINE_INDEX,
};
pub const TEST_BROWSING_CONTEXT_INDEX: Index<BrowsingContextIndex> =
Index(NonZeroU32::new(8765).unwrap(), PhantomData);
pub const TEST_BROWSING_CONTEXT_ID: BrowsingContextId = BrowsingContextId {
namespace_id: TEST_NAMESPACE,
index: TEST_BROWSING_CONTEXT_INDEX,
};
pub const TEST_PAINTER_ID: PainterId = PainterId(9999);
pub const TEST_WEBVIEW_ID: WebViewId = WebViewId(TEST_PAINTER_ID, TEST_BROWSING_CONTEXT_ID);
pub const TEST_SCRIPT_EVENT_LOOP_ID: ScriptEventLoopId = ScriptEventLoopId(1234);
#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
pub struct ScrollTreeNodeId {
pub index: usize,
}
static PAINTER_ID: AtomicU32 = AtomicU32::new(1);
#[derive(
Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Hash, Eq, Serialize, Deserialize, MallocSizeOf,
)]
pub struct PainterId(u32);
impl fmt::Display for PainterId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PainterId: {}", self.0)
}
}
impl PainterId {
pub fn next() -> Self {
Self(PAINTER_ID.fetch_add(1, Ordering::Relaxed))
}
}
impl From<PainterId> for IdNamespace {
fn from(painter_id: PainterId) -> Self {
IdNamespace(painter_id.0)
}
}
impl From<IdNamespace> for PainterId {
fn from(id_namespace: IdNamespace) -> Self {
PainterId(id_namespace.0)
}
}
impl From<FontKey> for PainterId {
fn from(font_key: FontKey) -> Self {
font_key.0.into()
}
}
impl From<FontInstanceKey> for PainterId {
fn from(font_instance_key: FontInstanceKey) -> Self {
font_instance_key.0.into()
}
}
impl From<ImageKey> for PainterId {
fn from(image_key: ImageKey) -> Self {
image_key.0.into()
}
}
static SCRIPT_EVENT_LOOP_ID: AtomicU32 = AtomicU32::new(1);
thread_local!(pub static INSTALLED_SCRIPT_EVENT_LOOP_ID: Cell<Option<ScriptEventLoopId>> =
const { Cell::new(None) });
#[derive(
Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Hash, Eq, Serialize, Deserialize, MallocSizeOf,
)]
pub struct ScriptEventLoopId(u32);
impl ScriptEventLoopId {
pub fn new() -> Self {
Self(SCRIPT_EVENT_LOOP_ID.fetch_add(1, Ordering::Relaxed))
}
pub fn install(id: Self) {
INSTALLED_SCRIPT_EVENT_LOOP_ID.with(|tls| tls.set(Some(id)))
}
pub fn installed() -> Option<Self> {
INSTALLED_SCRIPT_EVENT_LOOP_ID.with(|tls| tls.get())
}
}
impl fmt::Display for ScriptEventLoopId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}