aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2025-03-16 10:08:22 -0400
committerGitHub <noreply@github.com>2025-03-16 14:08:22 +0000
commitc8d878795966f0b685385e6961e0d69df4268734 (patch)
tree3560dee84d5ed70506b1de90910d361868b868dd
parentd35da38a2fd6f093967e74f704612391b4988e69 (diff)
downloadservo-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.lock8
-rw-r--r--components/script/dom/bindings/conversions.rs21
-rw-r--r--components/script/dom/bindings/import.rs2
-rw-r--r--components/script/dom/bindings/mod.rs1
-rw-r--r--components/script/dom/bindings/trace.rs190
-rw-r--r--components/script/dom/servoparser/html.rs27
-rw-r--r--components/script/dom/servoparser/prefetch.rs26
-rw-r--r--components/script/dom/servoparser/xml.rs27
-rw-r--r--components/script/dom/testbinding.rs2
-rw-r--r--components/script_bindings/Cargo.toml10
-rw-r--r--components/script_bindings/codegen/CodegenRust.py4
-rw-r--r--components/script_bindings/conversions.rs25
-rw-r--r--components/script_bindings/lib.rs3
-rw-r--r--components/script_bindings/record.rs (renamed from components/script/dom/bindings/record.rs)16
-rw-r--r--components/script_bindings/trace.rs255
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);
+ }
+}