//! Interior-mutable storage for Stylo's per-element style slot. Adapted from Blitz's //! `blitz-dom/src/node/stylo_data.rs` (Apache-2.0): hand-directed scaffolding, not //! generated code. //! //! An `UnsafeCell` rather than a `RefCell`: the `ElementDataRef`/`ElementDataMut` //! wrappers Stylo hands back do not carry the borrow guard that would keep a //! `RefCell`'s runtime check honest across the call, so wrapping them in one would //! either leak the borrow count or need the same unsafe code one layer further in. //! Safety instead rests on the traversal invariant this project takes from Stylo //! (`roadmap.md` -> "DOM layer, shaped by Stylo"): a style traversal has exclusive //! access to the tree, and `ensure_init`/`clear` are only ever called from inside one. use std::cell::UnsafeCell; use std::fmt; use std::ops::{Deref, DerefMut}; use style::data::{ElementDataMut, ElementDataRef, ElementDataWrapper}; use style::selector_parser::RestyleDamage; pub struct StyloData { inner: UnsafeCell>, } impl Default for StyloData { fn default() -> Self { Self { inner: UnsafeCell::new(None) } } } impl fmt::Debug for StyloData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StyloData").finish_non_exhaustive() } } impl Deref for StyloData { type Target = Option; fn deref(&self) -> &Self::Target { // SAFETY: `&self` here borrows the `StyloData`, and every other access in // this file requires at least that same borrow, so this cannot alias a `&mut`. unsafe { &*self.inner.get() } } } impl DerefMut for StyloData { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.get_mut() } } impl StyloData { pub fn has_data(&self) -> bool { self.deref().is_some() } pub fn get(&self) -> Option> { self.deref().as_ref().map(|w| w.borrow()) } /// Get a mutable reference to the data. /// /// SAFETY: there must be no outstanding borrow of this container, or of anything /// borrowed from it via [`get`](Self::get), when this is called. pub unsafe fn get_mut_unchecked(&self) -> Option> { let opt = unsafe { &mut *self.inner.get() }; opt.as_mut().map(|w| w.borrow_mut()) } /// Initialize the element data if it is not already initialized, and return a /// mutable borrow either way. /// /// SAFETY: same as [`get_mut_unchecked`](Self::get_mut_unchecked). pub unsafe fn ensure_init(&self) -> ElementDataMut<'_> { if !self.has_data() { unsafe { *self.inner.get() = Some(ElementDataWrapper::default()) }; let mut data = unsafe { self.get_mut_unchecked() }.expect("just initialized"); data.damage = RestyleDamage::reconstruct(); data } else { unsafe { self.get_mut_unchecked() }.expect("has_data() just returned true") } } /// Clear the element data, returning to the uninitialized state. /// /// SAFETY: same as [`get_mut_unchecked`](Self::get_mut_unchecked). pub unsafe fn clear(&self) { unsafe { *self.inner.get() = None }; } }