/
githubmirror
/
servo
Обзор
Документация
Войти
/
githubmirror
/
servo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
components/script/dom/bindings/root.rs
285 строк
9 KB
Martin Robinson
script/layout: Add initial support for visible selections (#46698)
23 июл 2026, 12:21
Не верифицирован
23 июл 2026, 12:21
295e3cf
Код
Авторство
О чём код?
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! Smart pointers for the JS-managed DOM objects. //! //! The DOM is made up of DOM objects whose lifetime is entirely controlled by //! the whims of the SpiderMonkey garbage collector. The types in this module //! are designed to ensure that any interactions with said Rust types only //! occur on values that will remain alive the entire time. //! //! Here is a brief overview of the important types: //! //! - `Root<T>`: a stack-based rooted value. //! - `DomRoot<T>`: a stack-based reference to a rooted DOM object. //! - `Dom<T>`: a reference to a DOM object that can automatically be traced by //! the GC when encountered as a field of a Rust structure. //! //! `Dom<T>` does not allow access to their inner value without explicitly //! creating a stack-based root via the `root` method. This returns a `DomRoot<T>`, //! which causes the JS-owned value to be uncollectable for the duration of the //! `Root` object's lifetime. A reference to the object can then be obtained //! from the `Root` object. These references are not allowed to outlive their //! originating `DomRoot<T>`. //! use std::cell::OnceCell; use std::default::Default; use std::hash::{Hash, Hasher}; use std::mem; use js::jsapi::{Heap, JSObject, JSTracer, Value}; use js::rust::HandleValue; use layout_api::TrustedNodeAddress; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use script_bindings::assert::{assert_in_layout, assert_in_script}; pub(crate) use script_bindings::dom::*; use script_bindings::reflector::DomObject; pub(crate) use script_bindings::root::*; use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::trace::JSTraceable; use crate::dom::node::Node; /// An unrooted reference to a DOM object for use in layout. `Layout*Helpers` /// traits must be implemented on this. #[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)] #[repr(transparent)] pub struct LayoutDom<'dom, T> { value: &'dom T, } impl LayoutDom<'_, Node> { /// Create a new JS-owned value wrapped from an address known to be a /// `Node` pointer. pub(crate) unsafe fn from_trusted_node_address(inner: TrustedNodeAddress) -> Self { assert_in_layout(); let TrustedNodeAddress(addr) = inner; LayoutDom { value: unsafe { &*(addr as *const Node) }, } } } impl<'dom, T> LayoutDom<'dom, T> where T: 'dom + DomObject, { /// Returns a reference to the interior of this JS object. The fact /// that this is unsafe is what necessitates the layout wrappers. pub fn unsafe_get(self) -> &'dom T { assert_in_layout(); self.value } /// Transforms a slice of `Dom<T>` into a slice of `LayoutDom<T>`. // FIXME(nox): This should probably be done through a ToLayout trait. pub(crate) unsafe fn to_layout_slice(slice: &'dom [Dom<T>]) -> &'dom [LayoutDom<'dom, T>] { // This doesn't compile if Dom and LayoutDom don't have the same // representation. let _ = mem::transmute::<Dom<T>, LayoutDom<T>>; unsafe { &*(slice as *const [Dom<T>] as *const [LayoutDom<T>]) } } } impl<'dom, T> LayoutDom<'dom, T> where T: Castable, { /// Cast a DOM object root upwards to one of the interfaces it derives from. pub(crate) fn upcast<U>(&self) -> LayoutDom<'dom, U> where U: Castable, T: DerivedFrom<U>, { assert_in_layout(); LayoutDom { value: self.value.upcast::<U>(), } } /// Cast a DOM object downwards to one of the interfaces it might implement. pub(crate) fn downcast<U>(&self) -> Option<LayoutDom<'dom, U>> where U: DerivedFrom<T>, { assert_in_layout(); self.value.downcast::<U>().map(|value| LayoutDom { value }) } /// Returns whether this inner object is a U. pub(crate) fn is<U>(&self) -> bool where U: DerivedFrom<T>, { assert_in_layout(); self.value.is::<U>() } /// Get a reference to the internal value. /// /// ## SAFETY /// This function effectively circumvents all the safety provided by `LayoutDom` as it allows /// performing arbitrary (potentially mutating) operations on the value. Use with caution! pub(crate) unsafe fn as_ref(self) -> &'dom T { self.value } } impl<T> LayoutDom<'_, T> where T: DomObject, { /// Get the reflector. pub(crate) unsafe fn get_jsobject(&self) -> *mut JSObject { assert_in_layout(); self.value.reflector().get_jsobject().get() } } impl<T> Copy for LayoutDom<'_, T> {} impl<T> PartialEq for LayoutDom<'_, T> { fn eq(&self, other: &Self) -> bool { std::ptr::eq(self.value, other.value) } } impl<T> Eq for LayoutDom<'_, T> {} impl<T> Hash for LayoutDom<'_, T> { fn hash<H: Hasher>(&self, state: &mut H) { (self.value as *const T).hash(state) } } #[expect(clippy::non_canonical_clone_impl)] impl<T> Clone for LayoutDom<'_, T> { #[inline] fn clone(&self) -> Self { assert_in_layout(); *self } } pub(crate) trait ToLayout<'dom, T: DomObject> { /// Get a reference to the contents of this smart pointer as a [`LayoutDom`], /// for use during layout. Note that this should only be called in the course /// of layout. /// /// # Safety /// The return value holds a Rust reference to the underlying data, which should be /// safe as long as `unsafe` is not used to override the lifetime in some way. /// /// - The caller *must not* modify the underlying DOM object via non-layout handles. /// - The caller *must ensure* that garbage collection does not occur while the /// [`LayoutDom`] handle is alive. unsafe fn to_layout(&self) -> LayoutDom<'dom, T>; } impl<'dom, T: DomObject> ToLayout<'dom, T> for Dom<T> { unsafe fn to_layout(&self) -> LayoutDom<'dom, T> { assert_in_layout(); LayoutDom { value: unsafe { self.as_ptr().as_ref().unwrap() }, } } } impl<'dom, T: DomObject> ToLayout<'dom, T> for MutDom<T> { unsafe fn to_layout(&self) -> LayoutDom<'dom, T> { assert_in_layout(); unsafe { self.as_ref_unsafe().to_layout() } } } pub(crate) trait ToLayoutOptional<'dom, T: DomObject> { /// Retrieve a copy of the inner optional `Dom<T>` as `LayoutDom<T>`. /// For use by layout, which can't use safe types like Temporary. /// /// # Safety /// The return value holds a Rust reference to the underlying data, which should be /// safe as long as `unsafe` is not used to override the lifetime in some way. /// /// - The caller *must not* modify the underlying DOM object via non-layout handles. /// - The caller *must ensure* that garbage collection does not occur while the /// [`LayoutDom`] handle is alive. unsafe fn to_layout(&self) -> Option<LayoutDom<'dom, T>>; } impl<'dom, T: DomObject> ToLayoutOptional<'dom, T> for MutNullableDom<T> { unsafe fn to_layout(&self) -> Option<LayoutDom<'dom, T>> { assert_in_layout(); unsafe { self.as_ref_unsafe().map(|dom_ref| dom_ref.to_layout()) } } } /// A holder that allows to lazily initialize the value only once /// `Dom<T>`, using OnceCell /// Essentially a `OnceCell<Dom<T>>`. /// /// This should only be used as a field in other DOM objects; see warning /// on `Dom<T>`. #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] pub(crate) struct DomOnceCell<T: DomObject> { ptr: OnceCell<Dom<T>>, } impl<T> DomOnceCell<T> where T: DomObject, { /// Retrieve a copy of the current inner value. If it is `None`, it is /// initialized with the result of `cb` first. pub(crate) fn init_once<F>(&self, cb: F) -> &T where F: FnOnce() -> DomRoot<T>, { assert_in_script(); self.ptr.get_or_init(|| Dom::from_ref(&cb())) } } impl<T: DomObject> Default for DomOnceCell<T> { fn default() -> DomOnceCell<T> { assert_in_script(); DomOnceCell { ptr: OnceCell::new(), } } } impl<T: DomObject> MallocSizeOf for DomOnceCell<T> { fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { // See comment on MallocSizeOf for Dom<T>. 0 } } unsafe impl<T: DomObject> JSTraceable for DomOnceCell<T> { unsafe fn trace(&self, trc: *mut JSTracer) { if let Some(ptr) = self.ptr.get() { unsafe { ptr.trace(trc) }; } } } /// Converts a rooted `Heap<Value>` into a `HandleValue`. /// /// This is only safe if the `Heap` is rooted (e.g., held inside a `Dom`-managed struct), /// and the `#[must_root]` crown lint is active to enforce rooting at compile time. /// Avoids repeating unsafe `from_raw` calls at each usage site. pub trait AsHandleValue<'a> { fn as_handle_value(&'a self) -> HandleValue<'a>; } impl<'a> AsHandleValue<'a> for Heap<Value> { #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] fn as_handle_value(&'a self) -> HandleValue<'a> { // SAFETY: `self` is assumed to be rooted, and `handle()` ties // the lifetime to `&self`, which the compiler can enforce. unsafe { HandleValue::from_marked_location(self.ptr.get() as *const _) } } }