From c8d878795966f0b685385e6961e0d69df4268734 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sun, 16 Mar 2025 10:08:22 -0400 Subject: [PATCH] Move CustomTraceable to script_bindings. (#35988) * script: Move CustomTraceable to script_bindings. Signed-off-by: Josh Matthews * script: Move record binding support to script_bindings. Signed-off-by: Josh Matthews * Address clippy warnings. Signed-off-by: Josh Matthews --------- Signed-off-by: Josh Matthews --- Cargo.lock | 8 + components/script/dom/bindings/conversions.rs | 21 +- components/script/dom/bindings/import.rs | 2 +- components/script/dom/bindings/mod.rs | 1 - components/script/dom/bindings/trace.rs | 190 ------------- components/script/dom/servoparser/html.rs | 27 +- components/script/dom/servoparser/prefetch.rs | 26 +- components/script/dom/servoparser/xml.rs | 27 +- components/script/dom/testbinding.rs | 2 +- components/script_bindings/Cargo.toml | 10 +- .../script_bindings/codegen/CodegenRust.py | 4 +- components/script_bindings/conversions.rs | 25 +- components/script_bindings/lib.rs | 3 + .../bindings => script_bindings}/record.rs | 16 +- components/script_bindings/trace.rs | 255 ++++++++++++++++++ 15 files changed, 340 insertions(+), 277 deletions(-) rename components/{script/dom/bindings => script_bindings}/record.rs (94%) diff --git a/Cargo.lock b/Cargo.lock index 394e72a6b27..2be29aa3e59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6382,22 +6382,30 @@ name = "script_bindings" version = "0.0.1" dependencies = [ "bitflags 2.9.0", + "crossbeam-channel", "cssparser", "html5ever", + "indexmap", "jstraceable_derive", "libc", "log", "malloc_size_of_derive", "mozjs", "num-traits", + "parking_lot", "phf_codegen", "phf_shared", "regex", "serde_json", + "servo_arc", "servo_config", "servo_malloc_size_of", + "smallvec", "style", "stylo_atoms", + "tendril", + "webxr-api", + "xml5ever", ] [[package]] diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index f2359ecbd21..0df5a82c3b0 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -42,7 +42,7 @@ use js::glue::GetProxyReservedSlot; use js::jsapi::{Heap, IsWindowProxy, JS_IsExceptionPending, JSContext, JSObject}; use js::jsval::UndefinedValue; use js::rust::wrappers::{IsArrayObject, JS_GetProperty, JS_HasProperty}; -use js::rust::{HandleId, HandleObject, HandleValue, MutableHandleValue}; +use js::rust::{HandleObject, HandleValue, MutableHandleValue}; use num_traits::Float; pub(crate) use script_bindings::conversions::*; @@ -50,7 +50,6 @@ use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::DomRoot; -use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::{JSTraceable, RootedTraceableBox}; use crate::dom::bindings::utils::DOMClass; use crate::dom::filelist::FileList; @@ -122,24 +121,6 @@ where } } -/// Convert `id` to a `DOMString`. Returns `None` if `id` is not a string or -/// integer. -/// -/// Handling of invalid UTF-16 in strings depends on the relevant option. -pub(crate) unsafe fn jsid_to_string(cx: *mut JSContext, id: HandleId) -> Option { - let id_raw = *id; - if id_raw.is_string() { - let jsstr = std::ptr::NonNull::new(id_raw.to_string()).unwrap(); - return Some(jsstring_to_str(cx, jsstr)); - } - - if id_raw.is_int() { - return Some(id_raw.to_int().to_string().into()); - } - - None -} - pub(crate) use script_bindings::conversions::is_dom_proxy; /// Get a `*const libc::c_void` for the given DOM object, unless it is a DOM diff --git a/components/script/dom/bindings/import.rs b/components/script/dom/bindings/import.rs index 313ad17a3d8..39e21f4b349 100644 --- a/components/script/dom/bindings/import.rs +++ b/components/script/dom/bindings/import.rs @@ -99,6 +99,7 @@ pub(crate) mod module { JSCLASS_RESERVED_SLOTS_MASK, jsapi, typedarray, }; pub(crate) use script_bindings::constant::{ConstantSpec, ConstantVal}; + pub(crate) use script_bindings::record::Record; pub(crate) use servo_config::pref; pub(crate) use super::base::*; @@ -144,7 +145,6 @@ pub(crate) mod module { pub(crate) use crate::dom::bindings::proxyhandler::{ ensure_expando_object, get_expando_object, set_property_descriptor, }; - pub(crate) use crate::dom::bindings::record::Record; pub(crate) use crate::dom::bindings::reflector::{ DomObjectIteratorWrap, DomObjectWrap, Reflector, }; diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index e29a18a5917..9385df640c8 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -154,7 +154,6 @@ pub(crate) mod namespace; pub(crate) mod num; pub(crate) mod principals; pub(crate) mod proxyhandler; -pub(crate) mod record; pub(crate) mod refcounted; pub(crate) mod reflector; pub(crate) mod root; diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index b53eef8eab7..3e1e3cfdaec 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -29,7 +29,6 @@ //! The `unsafe_no_jsmanaged_fields!()` macro adds an empty implementation of //! `JSTraceable` to a datatype. -use std::cell::OnceCell; use std::collections::HashMap; use std::collections::hash_map::RandomState; use std::fmt::Display; @@ -37,8 +36,6 @@ use std::hash::{BuildHasher, Hash}; use std::mem; use std::ops::{Deref, DerefMut}; -use crossbeam_channel::Sender; -use indexmap::IndexMap; /// A trait to allow tracing (only) DOM objects. pub(crate) use js::gc::Traceable as JSTraceable; pub(crate) use js::gc::{RootableVec, RootedVec}; @@ -47,17 +44,7 @@ use js::jsapi::{GCTraceKindToAscii, Heap, JSScript, JSString, JSTracer, TraceKin use js::jsval::JSVal; use js::rust::{GCMethods, Handle}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use parking_lot::RwLock; pub(crate) use script_bindings::trace::*; -use servo_arc::Arc as ServoArc; -use smallvec::SmallVec; -use style::author_styles::AuthorStyles; -use style::stylesheet_set::{AuthorStylesheetSet, DocumentStylesheetSet}; -use tendril::TendrilSink; -use tendril::fmt::UTF8; -use tendril::stream::LossyDecoder; -#[cfg(feature = "webxr")] -use webxr_api::{Finger, Hand}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; @@ -69,48 +56,12 @@ use crate::script_runtime::StreamConsumer; use crate::script_thread::IncompleteParserContexts; use crate::task::TaskBox; -/// A trait to allow tracing only DOM sub-objects. -/// -/// # Safety -/// -/// This trait is unsafe; if it is implemented incorrectly, the GC may end up collecting objects -/// that are still reachable. -pub(crate) unsafe trait CustomTraceable { - /// Trace `self`. - /// - /// # Safety - /// - /// The `JSTracer` argument must point to a valid `JSTracer` in memory. In addition, - /// implementors of this method must ensure that all active objects are properly traced - /// or else the garbage collector may end up collecting objects that are still reachable. - unsafe fn trace(&self, trc: *mut JSTracer); -} - -unsafe impl CustomTraceable for Box { - #[inline] - unsafe fn trace(&self, trc: *mut JSTracer) { - (**self).trace(trc); - } -} - unsafe impl CustomTraceable for DomRefCell { unsafe fn trace(&self, trc: *mut JSTracer) { (*self).borrow().trace(trc) } } -unsafe impl CustomTraceable for OnceCell { - unsafe fn trace(&self, tracer: *mut JSTracer) { - if let Some(value) = self.get() { - value.trace(tracer) - } - } -} - -unsafe impl CustomTraceable for Sender { - unsafe fn trace(&self, _: *mut JSTracer) {} -} - /// Wrapper type for nop traceble /// /// SAFETY: Inner type must not impl JSTraceable @@ -298,59 +249,12 @@ pub(crate) fn trace_string(tracer: *mut JSTracer, description: &str, s: &Heap<*m } } -unsafe impl CustomTraceable for ServoArc { - unsafe fn trace(&self, trc: *mut JSTracer) { - (**self).trace(trc) - } -} - -unsafe impl CustomTraceable for RwLock { - unsafe fn trace(&self, trc: *mut JSTracer) { - self.read().trace(trc) - } -} - unsafe impl JSTraceable for DomRefCell { unsafe fn trace(&self, trc: *mut JSTracer) { (*self).borrow().trace(trc) } } -unsafe impl CustomTraceable for indexmap::IndexSet { - #[inline] - unsafe fn trace(&self, trc: *mut JSTracer) { - for e in self.iter() { - e.trace(trc); - } - } -} - -// XXXManishearth Check if the following three are optimized to no-ops -// if e.trace() is a no-op (e.g it is an unsafe_no_jsmanaged_fields type) -unsafe impl CustomTraceable for SmallVec<[T; 1]> { - #[inline] - unsafe fn trace(&self, trc: *mut JSTracer) { - for e in self.iter() { - e.trace(trc); - } - } -} - -unsafe impl CustomTraceable for IndexMap -where - K: Hash + Eq + JSTraceable, - V: JSTraceable, - S: BuildHasher, -{ - #[inline] - unsafe fn trace(&self, trc: *mut JSTracer) { - for (k, v) in self { - k.trace(trc); - v.trace(trc); - } - } -} - unsafe_no_jsmanaged_fields!(TrustedPromise); unsafe_no_jsmanaged_fields!(WindowProxyHandler); @@ -365,100 +269,6 @@ unsafe impl JSTraceable for Trusted { } } -unsafe impl CustomTraceable for DocumentStylesheetSet -where - S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, -{ - unsafe fn trace(&self, tracer: *mut JSTracer) { - for (s, _origin) in self.iter() { - s.trace(tracer) - } - } -} - -unsafe impl CustomTraceable for AuthorStylesheetSet -where - S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, -{ - unsafe fn trace(&self, tracer: *mut JSTracer) { - for s in self.iter() { - s.trace(tracer) - } - } -} - -unsafe impl CustomTraceable for AuthorStyles -where - S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, -{ - unsafe fn trace(&self, tracer: *mut JSTracer) { - self.stylesheets.trace(tracer) - } -} - -unsafe impl CustomTraceable for LossyDecoder -where - Sink: JSTraceable + TendrilSink, -{ - unsafe fn trace(&self, tracer: *mut JSTracer) { - self.inner_sink().trace(tracer); - } -} - -#[cfg(feature = "webxr")] -unsafe impl CustomTraceable for Hand -where - J: JSTraceable, -{ - #[inline] - unsafe fn trace(&self, trc: *mut JSTracer) { - // exhaustive match so we don't miss new fields - let Hand { - ref wrist, - ref thumb_metacarpal, - ref thumb_phalanx_proximal, - ref thumb_phalanx_distal, - ref thumb_phalanx_tip, - ref index, - ref middle, - ref ring, - ref little, - } = *self; - wrist.trace(trc); - thumb_metacarpal.trace(trc); - thumb_phalanx_proximal.trace(trc); - thumb_phalanx_distal.trace(trc); - thumb_phalanx_tip.trace(trc); - index.trace(trc); - middle.trace(trc); - ring.trace(trc); - little.trace(trc); - } -} - -#[cfg(feature = "webxr")] -unsafe impl CustomTraceable for Finger -where - J: JSTraceable, -{ - #[inline] - unsafe fn trace(&self, trc: *mut JSTracer) { - // exhaustive match so we don't miss new fields - let Finger { - ref metacarpal, - ref phalanx_proximal, - ref phalanx_intermediate, - ref phalanx_distal, - ref phalanx_tip, - } = *self; - metacarpal.trace(trc); - phalanx_proximal.trace(trc); - phalanx_intermediate.trace(trc); - phalanx_distal.trace(trc); - phalanx_tip.trace(trc); - } -} - /// Roots any JSTraceable thing /// /// If you have a valid DomObject, use DomRoot. diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs index 1a25d71058b..5a06008ff4b 100644 --- a/components/script/dom/servoparser/html.rs +++ b/components/script/dom/servoparser/html.rs @@ -12,14 +12,13 @@ use html5ever::buffer_queue::BufferQueue; use html5ever::serialize::TraversalScope::IncludeNode; use html5ever::serialize::{AttrRef, Serialize, Serializer, TraversalScope}; use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts, TokenizerResult}; -use html5ever::tree_builder::{Tracer as HtmlTracer, TreeBuilder, TreeBuilderOpts}; -use js::jsapi::JSTracer; +use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts}; +use script_bindings::trace::CustomTraceable; use servo_url::ServoUrl; use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods; use crate::dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId}; use crate::dom::bindings::root::{Dom, DomRoot}; -use crate::dom::bindings::trace::{CustomTraceable, JSTraceable}; use crate::dom::characterdata::CharacterData; use crate::dom::document::Document; use crate::dom::documentfragment::DocumentFragment; @@ -103,28 +102,6 @@ impl Tokenizer { } } -#[allow(unsafe_code)] -unsafe impl CustomTraceable for HtmlTokenizer, Sink>> { - unsafe fn trace(&self, trc: *mut JSTracer) { - struct Tracer(*mut JSTracer); - let tracer = Tracer(trc); - - impl HtmlTracer for Tracer { - type Handle = Dom; - #[cfg_attr(crown, allow(crown::unrooted_must_root))] - fn trace_handle(&self, node: &Dom) { - unsafe { - node.trace(self.0); - } - } - } - - let tree_builder = &self.sink; - tree_builder.trace_handles(&tracer); - tree_builder.sink.trace(trc); - } -} - fn start_element(node: &Element, serializer: &mut S) -> io::Result<()> { let name = QualName::new(None, node.namespace().clone(), node.local_name().clone()); let attrs = node diff --git a/components/script/dom/servoparser/prefetch.rs b/components/script/dom/servoparser/prefetch.rs index 81a6f6ce5b1..f2873406cd5 100644 --- a/components/script/dom/servoparser/prefetch.rs +++ b/components/script/dom/servoparser/prefetch.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::{Cell, RefCell}; +use std::ops::Deref; use base::id::{PipelineId, WebViewId}; use content_security_policy::Destination; @@ -30,13 +31,29 @@ use crate::script_module::ScriptFetchOptions; #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] pub(crate) struct Tokenizer { #[ignore_malloc_size_of = "Defined in html5ever"] - inner: HtmlTokenizer, + inner: TraceableTokenizer, +} + +struct TraceableTokenizer(HtmlTokenizer); + +impl Deref for TraceableTokenizer { + type Target = HtmlTokenizer; + fn deref(&self) -> &Self::Target { + &self.0 + } } #[allow(unsafe_code)] -unsafe impl CustomTraceable for HtmlTokenizer { +unsafe impl JSTraceable for TraceableTokenizer { unsafe fn trace(&self, trc: *mut JSTracer) { - self.sink.trace(trc) + CustomTraceable::trace(&self.0, trc) + } +} + +#[allow(unsafe_code)] +unsafe impl CustomTraceable for PrefetchSink { + unsafe fn trace(&self, trc: *mut JSTracer) { + ::trace(self, trc) } } @@ -58,7 +75,7 @@ impl Tokenizer { insecure_requests_policy: document.insecure_requests_policy(), }; let options = Default::default(); - let inner = HtmlTokenizer::new(sink, options); + let inner = TraceableTokenizer(HtmlTokenizer::new(sink, options)); Tokenizer { inner } } @@ -91,6 +108,7 @@ struct PrefetchSink { } /// The prefetch tokenizer produces trivial results +#[derive(Clone, Copy, JSTraceable)] struct PrefetchHandle; impl TokenSink for PrefetchSink { diff --git a/components/script/dom/servoparser/xml.rs b/components/script/dom/servoparser/xml.rs index 35c86d2ba4b..218fdfaece5 100644 --- a/components/script/dom/servoparser/xml.rs +++ b/components/script/dom/servoparser/xml.rs @@ -7,14 +7,13 @@ use std::cell::Cell; use html5ever::tokenizer::TokenizerResult; -use js::jsapi::JSTracer; +use script_bindings::trace::CustomTraceable; use servo_url::ServoUrl; use xml5ever::buffer_queue::BufferQueue; use xml5ever::tokenizer::XmlTokenizer; -use xml5ever::tree_builder::{Tracer as XmlTracer, XmlTreeBuilder}; +use xml5ever::tree_builder::XmlTreeBuilder; use crate::dom::bindings::root::{Dom, DomRoot}; -use crate::dom::bindings::trace::{CustomTraceable, JSTraceable}; use crate::dom::document::Document; use crate::dom::htmlscriptelement::HTMLScriptElement; use crate::dom::node::Node; @@ -59,25 +58,3 @@ impl Tokenizer { &self.inner.sink.sink.base_url } } - -#[allow(unsafe_code)] -unsafe impl CustomTraceable for XmlTokenizer, Sink>> { - unsafe fn trace(&self, trc: *mut JSTracer) { - struct Tracer(*mut JSTracer); - let tracer = Tracer(trc); - - impl XmlTracer for Tracer { - type Handle = Dom; - #[cfg_attr(crown, allow(crown::unrooted_must_root))] - fn trace_handle(&self, node: &Dom) { - unsafe { - node.trace(self.0); - } - } - } - - let tree_builder = &self.sink; - tree_builder.trace_handles(&tracer); - tree_builder.sink.trace(trc); - } -} diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs index 7dd61b98ed6..d03196de940 100644 --- a/components/script/dom/testbinding.rs +++ b/components/script/dom/testbinding.rs @@ -14,6 +14,7 @@ use js::jsapi::{Heap, JS_NewPlainObject, JSObject}; use js::jsval::JSVal; use js::rust::{CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleValue}; use js::typedarray::{self, Uint8ClampedArray}; +use script_bindings::record::Record; use script_traits::serializable::BlobImpl; use servo_config::prefs; @@ -35,7 +36,6 @@ use crate::dom::bindings::codegen::UnionTypes::{ }; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::num::Finite; -use crate::dom::bindings::record::Record; use crate::dom::bindings::refcounted::TrustedPromise; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto}; use crate::dom::bindings::root::DomRoot; diff --git a/components/script_bindings/Cargo.toml b/components/script_bindings/Cargo.toml index 3f022f74480..c911a0d4730 100644 --- a/components/script_bindings/Cargo.toml +++ b/components/script_bindings/Cargo.toml @@ -22,8 +22,10 @@ serde_json = { workspace = true } [dependencies] bitflags = { workspace = true } +crossbeam-channel = { workspace = true } cssparser = { workspace = true } html5ever = { workspace = true } +indexmap = { workspace = true } js = { workspace = true } jstraceable_derive = { path = "../jstraceable_derive" } libc = { workspace = true } @@ -31,15 +33,21 @@ log = { workspace = true } malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } num-traits = { workspace = true } +parking_lot = { workspace = true } regex = { workspace = true } +servo_arc = { workspace = true } +smallvec = { workspace = true } stylo_atoms = { workspace = true } servo_config = { path = "../config" } style = { workspace = true } +tendril = { version = "0.4.1", features = ["encoding_rs"] } +webxr-api = { workspace = true, optional = true } +xml5ever = { workspace = true } [features] bluetooth = [] webgpu = [] -webxr = [] +webxr = ["webxr-api"] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(crown)'] } diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py index e77709918f4..6b5c77c2c20 100644 --- a/components/script_bindings/codegen/CodegenRust.py +++ b/components/script_bindings/codegen/CodegenRust.py @@ -2287,7 +2287,7 @@ class CGImports(CGWrapper): extras += [descriptor.path, descriptor.bindingPath] parentName = descriptor.getParentName() elif t.isType() and t.isRecord(): - extras += ['crate::dom::bindings::record::Record'] + extras += ['script_bindings::record::Record'] elif isinstance(t, IDLPromiseType): extras += ['crate::dom::promise::Promise'] else: @@ -2643,7 +2643,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): 'crate::dom::bindings::import::base::*', 'crate::dom::bindings::codegen::DomTypes::DomTypes', 'crate::dom::bindings::conversions::windowproxy_from_handlevalue', - 'crate::dom::bindings::record::Record', + 'script_bindings::record::Record', 'crate::dom::types::*', 'crate::dom::windowproxy::WindowProxy', 'js::typedarray', diff --git a/components/script_bindings/conversions.rs b/components/script_bindings/conversions.rs index 98998e170da..6e0ce7adee0 100644 --- a/components/script_bindings/conversions.rs +++ b/components/script_bindings/conversions.rs @@ -18,8 +18,8 @@ use js::jsapi::{ }; use js::jsval::{ObjectValue, StringValue, UndefinedValue}; use js::rust::{ - HandleValue, MutableHandleValue, ToString, get_object_class, is_dom_class, is_dom_object, - maybe_wrap_value, + HandleId, HandleValue, MutableHandleValue, ToString, get_object_class, is_dom_class, + is_dom_object, maybe_wrap_value, }; use crate::inheritance::Castable; @@ -387,3 +387,24 @@ where } root_from_object(v.get().to_object(), cx) } + +/// Convert `id` to a `DOMString`. Returns `None` if `id` is not a string or +/// integer. +/// +/// Handling of invalid UTF-16 in strings depends on the relevant option. +/// +/// # Safety +/// - cx must point to a non-null, valid JSContext instance. +pub unsafe fn jsid_to_string(cx: *mut JSContext, id: HandleId) -> Option { + let id_raw = *id; + if id_raw.is_string() { + let jsstr = std::ptr::NonNull::new(id_raw.to_string()).unwrap(); + return Some(jsstring_to_str(cx, jsstr)); + } + + if id_raw.is_int() { + return Some(id_raw.to_int().to_string().into()); + } + + None +} diff --git a/components/script_bindings/lib.rs b/components/script_bindings/lib.rs index 8090144b73b..e857a2289f8 100644 --- a/components/script_bindings/lib.rs +++ b/components/script_bindings/lib.rs @@ -24,6 +24,7 @@ pub mod error; pub mod inheritance; pub mod iterable; pub mod like; +pub mod record; pub mod reflector; pub mod root; pub mod script_runtime; @@ -51,3 +52,5 @@ pub mod codegen { // Since they are used in derive macros, // it is useful that they are accessible at the root of the crate. pub(crate) use js::gc::Traceable as JSTraceable; + +pub(crate) use crate::trace::CustomTraceable; diff --git a/components/script/dom/bindings/record.rs b/components/script_bindings/record.rs similarity index 94% rename from components/script/dom/bindings/record.rs rename to components/script_bindings/record.rs index 5d092e6c34c..2668a84f42c 100644 --- a/components/script/dom/bindings/record.rs +++ b/components/script_bindings/record.rs @@ -17,14 +17,20 @@ use js::jsapi::{ JSITER_SYMBOLS, JSPROP_ENUMERATE, PropertyDescriptor, }; use js::jsval::{ObjectValue, UndefinedValue}; +use js::rooted; use js::rust::wrappers::{GetPropertyKeys, JS_DefineUCProperty2, JS_GetPropertyById, JS_IdToValue}; use js::rust::{HandleId, HandleValue, IdVector, MutableHandleValue}; -use crate::dom::bindings::conversions::jsid_to_string; -use crate::dom::bindings::str::{ByteString, DOMString, USVString}; +use crate::conversions::jsid_to_string; +use crate::str::{ByteString, DOMString, USVString}; -pub(crate) trait RecordKey: Eq + Hash + Sized { +pub trait RecordKey: Eq + Hash + Sized { fn to_utf16_vec(&self) -> Vec; + + /// Attempt to extract a key from a JS id. + /// # Safety + /// - cx must point to a non-null, valid JSContext. + #[allow(clippy::result_unit_err)] unsafe fn from_id(cx: *mut JSContext, id: HandleId) -> Result, ()>; } @@ -71,14 +77,14 @@ impl RecordKey for ByteString { /// The `Record` (open-ended dictionary) type. #[derive(Clone, JSTraceable)] -pub(crate) struct Record { +pub struct Record { #[custom_trace] map: IndexMap, } impl Record { /// Create an empty `Record`. - pub(crate) fn new() -> Self { + pub fn new() -> Self { Record { map: IndexMap::new(), } diff --git a/components/script_bindings/trace.rs b/components/script_bindings/trace.rs index 04819803d33..3248fbfc67c 100644 --- a/components/script_bindings/trace.rs +++ b/components/script_bindings/trace.rs @@ -2,9 +2,32 @@ * 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/. */ +use std::cell::OnceCell; +use std::hash::{BuildHasher, Hash}; +use std::marker::PhantomData; + +use crossbeam_channel::Sender; +use html5ever::interface::{Tracer as HtmlTracer, TreeSink}; +use html5ever::tokenizer::{TokenSink, Tokenizer}; +use html5ever::tree_builder::TreeBuilder; +use indexmap::IndexMap; use js::glue::CallObjectTracer; use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind}; +use parking_lot::RwLock; +use servo_arc::Arc as ServoArc; +use smallvec::SmallVec; +use style::author_styles::AuthorStyles; +use style::stylesheet_set::{AuthorStylesheetSet, DocumentStylesheetSet}; +use tendril::TendrilSink; +use tendril::fmt::UTF8; +use tendril::stream::LossyDecoder; +#[cfg(feature = "webxr")] +use webxr_api::{Finger, Hand}; +use xml5ever::interface::TreeSink as XmlTreeSink; +use xml5ever::tokenizer::XmlTokenizer; +use xml5ever::tree_builder::{Tracer as XmlTracer, XmlTreeBuilder}; +use crate::JSTraceable; use crate::error::Error; use crate::reflector::Reflector; use crate::str::{DOMString, USVString}; @@ -53,3 +76,235 @@ macro_rules! unsafe_no_jsmanaged_fields( unsafe_no_jsmanaged_fields!(DOMString); unsafe_no_jsmanaged_fields!(USVString); unsafe_no_jsmanaged_fields!(Error); + +/// A trait to allow tracing only DOM sub-objects. +/// +/// # Safety +/// +/// This trait is unsafe; if it is implemented incorrectly, the GC may end up collecting objects +/// that are still reachable. +pub unsafe trait CustomTraceable { + /// Trace `self`. + /// + /// # Safety + /// + /// The `JSTracer` argument must point to a valid `JSTracer` in memory. In addition, + /// implementors of this method must ensure that all active objects are properly traced + /// or else the garbage collector may end up collecting objects that are still reachable. + unsafe fn trace(&self, trc: *mut JSTracer); +} + +unsafe impl CustomTraceable for Box { + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + (**self).trace(trc); + } +} + +unsafe impl CustomTraceable for OnceCell { + unsafe fn trace(&self, tracer: *mut JSTracer) { + if let Some(value) = self.get() { + value.trace(tracer) + } + } +} + +unsafe impl CustomTraceable for Sender { + unsafe fn trace(&self, _: *mut JSTracer) {} +} + +unsafe impl CustomTraceable for ServoArc { + unsafe fn trace(&self, trc: *mut JSTracer) { + (**self).trace(trc) + } +} + +unsafe impl CustomTraceable for RwLock { + unsafe fn trace(&self, trc: *mut JSTracer) { + self.read().trace(trc) + } +} + +unsafe impl CustomTraceable for indexmap::IndexSet { + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + for e in self.iter() { + e.trace(trc); + } + } +} + +// XXXManishearth Check if the following three are optimized to no-ops +// if e.trace() is a no-op (e.g it is an unsafe_no_jsmanaged_fields type) +unsafe impl CustomTraceable for SmallVec<[T; 1]> { + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + for e in self.iter() { + e.trace(trc); + } + } +} + +unsafe impl CustomTraceable for IndexMap +where + K: Hash + Eq + JSTraceable, + V: JSTraceable, + S: BuildHasher, +{ + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + for (k, v) in self { + k.trace(trc); + v.trace(trc); + } + } +} + +unsafe impl CustomTraceable for DocumentStylesheetSet +where + S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, +{ + unsafe fn trace(&self, tracer: *mut JSTracer) { + for (s, _origin) in self.iter() { + s.trace(tracer) + } + } +} + +unsafe impl CustomTraceable for AuthorStylesheetSet +where + S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, +{ + unsafe fn trace(&self, tracer: *mut JSTracer) { + for s in self.iter() { + s.trace(tracer) + } + } +} + +unsafe impl CustomTraceable for AuthorStyles +where + S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, +{ + unsafe fn trace(&self, tracer: *mut JSTracer) { + self.stylesheets.trace(tracer) + } +} + +unsafe impl CustomTraceable for LossyDecoder +where + Sink: JSTraceable + TendrilSink, +{ + unsafe fn trace(&self, tracer: *mut JSTracer) { + self.inner_sink().trace(tracer); + } +} + +#[cfg(feature = "webxr")] +unsafe impl CustomTraceable for Hand +where + J: JSTraceable, +{ + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + // exhaustive match so we don't miss new fields + let Hand { + ref wrist, + ref thumb_metacarpal, + ref thumb_phalanx_proximal, + ref thumb_phalanx_distal, + ref thumb_phalanx_tip, + ref index, + ref middle, + ref ring, + ref little, + } = *self; + wrist.trace(trc); + thumb_metacarpal.trace(trc); + thumb_phalanx_proximal.trace(trc); + thumb_phalanx_distal.trace(trc); + thumb_phalanx_tip.trace(trc); + index.trace(trc); + middle.trace(trc); + ring.trace(trc); + little.trace(trc); + } +} + +#[cfg(feature = "webxr")] +unsafe impl CustomTraceable for Finger +where + J: JSTraceable, +{ + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + // exhaustive match so we don't miss new fields + let Finger { + ref metacarpal, + ref phalanx_proximal, + ref phalanx_intermediate, + ref phalanx_distal, + ref phalanx_tip, + } = *self; + metacarpal.trace(trc); + phalanx_proximal.trace(trc); + phalanx_intermediate.trace(trc); + phalanx_distal.trace(trc); + phalanx_tip.trace(trc); + } +} + +unsafe impl + JSTraceable> + CustomTraceable for TreeBuilder +{ + unsafe fn trace(&self, trc: *mut JSTracer) { + struct Tracer(*mut JSTracer, PhantomData); + let tracer = Tracer::(trc, PhantomData); + + impl HtmlTracer for Tracer { + type Handle = Handle; + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + fn trace_handle(&self, node: &Handle) { + unsafe { + node.trace(self.0); + } + } + } + + self.trace_handles(&tracer); + self.sink.trace(trc); + } +} + +#[allow(unsafe_code)] +unsafe impl + CustomTraceable> + CustomTraceable for Tokenizer +{ + unsafe fn trace(&self, trc: *mut JSTracer) { + self.sink.trace(trc); + } +} + +#[allow(unsafe_code)] +unsafe impl> + CustomTraceable for XmlTokenizer> +{ + unsafe fn trace(&self, trc: *mut JSTracer) { + struct Tracer(*mut JSTracer, PhantomData); + let tracer = Tracer(trc, PhantomData); + + impl XmlTracer for Tracer { + type Handle = Handle; + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + fn trace_handle(&self, node: &Handle) { + unsafe { + node.trace(self.0); + } + } + } + + let tree_builder = &self.sink; + tree_builder.trace_handles(&tracer); + tree_builder.sink.trace(trc); + } +}