diff options
author | Josh Matthews <josh@joshmatthews.net> | 2025-03-16 10:08:22 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-16 14:08:22 +0000 |
commit | c8d878795966f0b685385e6961e0d69df4268734 (patch) | |
tree | 3560dee84d5ed70506b1de90910d361868b868dd | |
parent | d35da38a2fd6f093967e74f704612391b4988e69 (diff) | |
download | servo-c8d878795966f0b685385e6961e0d69df4268734.tar.gz servo-c8d878795966f0b685385e6961e0d69df4268734.zip |
Move CustomTraceable to script_bindings. (#35988)
* script: Move CustomTraceable to script_bindings.
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
* script: Move record binding support to script_bindings.
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
* Address clippy warnings.
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
---------
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
-rw-r--r-- | Cargo.lock | 8 | ||||
-rw-r--r-- | components/script/dom/bindings/conversions.rs | 21 | ||||
-rw-r--r-- | components/script/dom/bindings/import.rs | 2 | ||||
-rw-r--r-- | components/script/dom/bindings/mod.rs | 1 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 190 | ||||
-rw-r--r-- | components/script/dom/servoparser/html.rs | 27 | ||||
-rw-r--r-- | components/script/dom/servoparser/prefetch.rs | 26 | ||||
-rw-r--r-- | components/script/dom/servoparser/xml.rs | 27 | ||||
-rw-r--r-- | components/script/dom/testbinding.rs | 2 | ||||
-rw-r--r-- | components/script_bindings/Cargo.toml | 10 | ||||
-rw-r--r-- | components/script_bindings/codegen/CodegenRust.py | 4 | ||||
-rw-r--r-- | components/script_bindings/conversions.rs | 25 | ||||
-rw-r--r-- | components/script_bindings/lib.rs | 3 | ||||
-rw-r--r-- | components/script_bindings/record.rs (renamed from components/script/dom/bindings/record.rs) | 16 | ||||
-rw-r--r-- | components/script_bindings/trace.rs | 255 |
15 files changed, 340 insertions, 277 deletions
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<DOMString> { - 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<T: CustomTraceable> CustomTraceable for Box<T> { - #[inline] - unsafe fn trace(&self, trc: *mut JSTracer) { - (**self).trace(trc); - } -} - unsafe impl<T: CustomTraceable> CustomTraceable for DomRefCell<T> { unsafe fn trace(&self, trc: *mut JSTracer) { (*self).borrow().trace(trc) } } -unsafe impl<T: JSTraceable> CustomTraceable for OnceCell<T> { - unsafe fn trace(&self, tracer: *mut JSTracer) { - if let Some(value) = self.get() { - value.trace(tracer) - } - } -} - -unsafe impl<T> CustomTraceable for Sender<T> { - 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<T: JSTraceable> CustomTraceable for ServoArc<T> { - unsafe fn trace(&self, trc: *mut JSTracer) { - (**self).trace(trc) - } -} - -unsafe impl<T: JSTraceable> CustomTraceable for RwLock<T> { - unsafe fn trace(&self, trc: *mut JSTracer) { - self.read().trace(trc) - } -} - unsafe impl<T: JSTraceable> JSTraceable for DomRefCell<T> { unsafe fn trace(&self, trc: *mut JSTracer) { (*self).borrow().trace(trc) } } -unsafe impl<T: JSTraceable + Eq + Hash> CustomTraceable for indexmap::IndexSet<T> { - #[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<T: JSTraceable + 'static> CustomTraceable for SmallVec<[T; 1]> { - #[inline] - unsafe fn trace(&self, trc: *mut JSTracer) { - for e in self.iter() { - e.trace(trc); - } - } -} - -unsafe impl<K, V, S> CustomTraceable for IndexMap<K, V, S> -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<T: DomObject> JSTraceable for Trusted<T> { } } -unsafe impl<S> CustomTraceable for DocumentStylesheetSet<S> -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<S> CustomTraceable for AuthorStylesheetSet<S> -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<S> CustomTraceable for AuthorStyles<S> -where - S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, -{ - unsafe fn trace(&self, tracer: *mut JSTracer) { - self.stylesheets.trace(tracer) - } -} - -unsafe impl<Sink> CustomTraceable for LossyDecoder<Sink> -where - Sink: JSTraceable + TendrilSink<UTF8>, -{ - unsafe fn trace(&self, tracer: *mut JSTracer) { - self.inner_sink().trace(tracer); - } -} - -#[cfg(feature = "webxr")] -unsafe impl<J> CustomTraceable for Hand<J> -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<J> CustomTraceable for Finger<J> -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<TreeBuilder<Dom<Node>, Sink>> { - unsafe fn trace(&self, trc: *mut JSTracer) { - struct Tracer(*mut JSTracer); - let tracer = Tracer(trc); - - impl HtmlTracer for Tracer { - type Handle = Dom<Node>; - #[cfg_attr(crown, allow(crown::unrooted_must_root))] - fn trace_handle(&self, node: &Dom<Node>) { - unsafe { - node.trace(self.0); - } - } - } - - let tree_builder = &self.sink; - tree_builder.trace_handles(&tracer); - tree_builder.sink.trace(trc); - } -} - fn start_element<S: Serializer>(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<PrefetchSink>, + inner: TraceableTokenizer, +} + +struct TraceableTokenizer(HtmlTokenizer<PrefetchSink>); + +impl Deref for TraceableTokenizer { + type Target = HtmlTokenizer<PrefetchSink>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[allow(unsafe_code)] +unsafe impl JSTraceable for TraceableTokenizer { + unsafe fn trace(&self, trc: *mut JSTracer) { + CustomTraceable::trace(&self.0, trc) + } } #[allow(unsafe_code)] -unsafe impl CustomTraceable for HtmlTokenizer<PrefetchSink> { +unsafe impl CustomTraceable for PrefetchSink { unsafe fn trace(&self, trc: *mut JSTracer) { - self.sink.trace(trc) + <Self as JSTraceable>::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<XmlTreeBuilder<Dom<Node>, Sink>> { - unsafe fn trace(&self, trc: *mut JSTracer) { - struct Tracer(*mut JSTracer); - let tracer = Tracer(trc); - - impl XmlTracer for Tracer { - type Handle = Dom<Node>; - #[cfg_attr(crown, allow(crown::unrooted_must_root))] - fn trace_handle(&self, node: &Dom<Node>) { - 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<DOMString> { + 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 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<u16>; + + /// 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<ConversionResult<Self>, ()>; } @@ -71,14 +77,14 @@ impl RecordKey for ByteString { /// The `Record` (open-ended dictionary) type. #[derive(Clone, JSTraceable)] -pub(crate) struct Record<K: RecordKey, V> { +pub struct Record<K: RecordKey, V> { #[custom_trace] map: IndexMap<K, V>, } impl<K: RecordKey, V> Record<K, V> { /// 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<T: CustomTraceable> CustomTraceable for Box<T> { + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + (**self).trace(trc); + } +} + +unsafe impl<T: JSTraceable> CustomTraceable for OnceCell<T> { + unsafe fn trace(&self, tracer: *mut JSTracer) { + if let Some(value) = self.get() { + value.trace(tracer) + } + } +} + +unsafe impl<T> CustomTraceable for Sender<T> { + unsafe fn trace(&self, _: *mut JSTracer) {} +} + +unsafe impl<T: JSTraceable> CustomTraceable for ServoArc<T> { + unsafe fn trace(&self, trc: *mut JSTracer) { + (**self).trace(trc) + } +} + +unsafe impl<T: JSTraceable> CustomTraceable for RwLock<T> { + unsafe fn trace(&self, trc: *mut JSTracer) { + self.read().trace(trc) + } +} + +unsafe impl<T: JSTraceable + Eq + Hash> CustomTraceable for indexmap::IndexSet<T> { + #[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<T: JSTraceable + 'static> CustomTraceable for SmallVec<[T; 1]> { + #[inline] + unsafe fn trace(&self, trc: *mut JSTracer) { + for e in self.iter() { + e.trace(trc); + } + } +} + +unsafe impl<K, V, S> CustomTraceable for IndexMap<K, V, S> +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<S> CustomTraceable for DocumentStylesheetSet<S> +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<S> CustomTraceable for AuthorStylesheetSet<S> +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<S> CustomTraceable for AuthorStyles<S> +where + S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static, +{ + unsafe fn trace(&self, tracer: *mut JSTracer) { + self.stylesheets.trace(tracer) + } +} + +unsafe impl<Sink> CustomTraceable for LossyDecoder<Sink> +where + Sink: JSTraceable + TendrilSink<UTF8>, +{ + unsafe fn trace(&self, tracer: *mut JSTracer) { + self.inner_sink().trace(tracer); + } +} + +#[cfg(feature = "webxr")] +unsafe impl<J> CustomTraceable for Hand<J> +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<J> CustomTraceable for Finger<J> +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<Handle: JSTraceable + Clone, Sink: TreeSink<Handle = Handle> + JSTraceable> + CustomTraceable for TreeBuilder<Handle, Sink> +{ + unsafe fn trace(&self, trc: *mut JSTracer) { + struct Tracer<Handle>(*mut JSTracer, PhantomData<Handle>); + let tracer = Tracer::<Handle>(trc, PhantomData); + + impl<Handle: JSTraceable> HtmlTracer for Tracer<Handle> { + 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<Handle: JSTraceable + Clone, Sink: TokenSink<Handle = Handle> + CustomTraceable> + CustomTraceable for Tokenizer<Sink> +{ + unsafe fn trace(&self, trc: *mut JSTracer) { + self.sink.trace(trc); + } +} + +#[allow(unsafe_code)] +unsafe impl<Handle: JSTraceable + Clone, Sink: JSTraceable + XmlTreeSink<Handle = Handle>> + CustomTraceable for XmlTokenizer<XmlTreeBuilder<Handle, Sink>> +{ + unsafe fn trace(&self, trc: *mut JSTracer) { + struct Tracer<Handle>(*mut JSTracer, PhantomData<Handle>); + let tracer = Tracer(trc, PhantomData); + + impl<Handle: JSTraceable> XmlTracer for Tracer<Handle> { + 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); + } +} |