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>
This commit is contained in:
Josh Matthews 2025-03-16 10:08:22 -04:00 committed by GitHub
parent d35da38a2f
commit c8d8787959
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 340 additions and 277 deletions

View file

@ -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

View file

@ -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,
};

View file

@ -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;

View file

@ -1,203 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! The `Record` (open-ended dictionary) type.
use std::cmp::Eq;
use std::hash::Hash;
use std::marker::Sized;
use std::ops::Deref;
use indexmap::IndexMap;
use js::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible};
use js::jsapi::glue::JS_GetOwnPropertyDescriptorById;
use js::jsapi::{
HandleId as RawHandleId, JS_NewPlainObject, JSContext, JSITER_HIDDEN, JSITER_OWNONLY,
JSITER_SYMBOLS, JSPROP_ENUMERATE, PropertyDescriptor,
};
use js::jsval::{ObjectValue, UndefinedValue};
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};
pub(crate) trait RecordKey: Eq + Hash + Sized {
fn to_utf16_vec(&self) -> Vec<u16>;
unsafe fn from_id(cx: *mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()>;
}
impl RecordKey for DOMString {
fn to_utf16_vec(&self) -> Vec<u16> {
self.encode_utf16().collect::<Vec<_>>()
}
unsafe fn from_id(cx: *mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()> {
match jsid_to_string(cx, id) {
Some(s) => Ok(ConversionResult::Success(s)),
None => Ok(ConversionResult::Failure("Failed to get DOMString".into())),
}
}
}
impl RecordKey for USVString {
fn to_utf16_vec(&self) -> Vec<u16> {
self.0.encode_utf16().collect::<Vec<_>>()
}
unsafe fn from_id(cx: *mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()> {
rooted!(in(cx) let mut jsid_value = UndefinedValue());
let raw_id: RawHandleId = id.into();
JS_IdToValue(cx, *raw_id.ptr, jsid_value.handle_mut());
USVString::from_jsval(cx, jsid_value.handle(), ())
}
}
impl RecordKey for ByteString {
fn to_utf16_vec(&self) -> Vec<u16> {
self.iter().map(|&x| x as u16).collect::<Vec<u16>>()
}
unsafe fn from_id(cx: *mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()> {
rooted!(in(cx) let mut jsid_value = UndefinedValue());
let raw_id: RawHandleId = id.into();
JS_IdToValue(cx, *raw_id.ptr, jsid_value.handle_mut());
ByteString::from_jsval(cx, jsid_value.handle(), ())
}
}
/// The `Record` (open-ended dictionary) type.
#[derive(Clone, JSTraceable)]
pub(crate) 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 {
Record {
map: IndexMap::new(),
}
}
}
impl<K: RecordKey, V> Deref for Record<K, V> {
type Target = IndexMap<K, V>;
fn deref(&self) -> &IndexMap<K, V> {
&self.map
}
}
impl<K, V, C> FromJSValConvertible for Record<K, V>
where
K: RecordKey,
V: FromJSValConvertible<Config = C>,
C: Clone,
{
type Config = C;
unsafe fn from_jsval(
cx: *mut JSContext,
value: HandleValue,
config: C,
) -> Result<ConversionResult<Self>, ()> {
if !value.is_object() {
return Ok(ConversionResult::Failure(
"Record value was not an object".into(),
));
}
rooted!(in(cx) let object = value.to_object());
let mut ids = IdVector::new(cx);
if !GetPropertyKeys(
cx,
object.handle(),
JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
ids.handle_mut(),
) {
return Err(());
}
let mut map = IndexMap::new();
for id in &*ids {
rooted!(in(cx) let id = *id);
rooted!(in(cx) let mut desc = PropertyDescriptor::default());
let mut is_none = false;
if !JS_GetOwnPropertyDescriptorById(
cx,
object.handle().into(),
id.handle().into(),
desc.handle_mut().into(),
&mut is_none,
) {
return Err(());
}
if !desc.enumerable_() {
continue;
}
let key = match K::from_id(cx, id.handle())? {
ConversionResult::Success(key) => key,
ConversionResult::Failure(message) => {
return Ok(ConversionResult::Failure(message));
},
};
rooted!(in(cx) let mut property = UndefinedValue());
if !JS_GetPropertyById(cx, object.handle(), id.handle(), property.handle_mut()) {
return Err(());
}
let property = match V::from_jsval(cx, property.handle(), config.clone())? {
ConversionResult::Success(property) => property,
ConversionResult::Failure(message) => {
return Ok(ConversionResult::Failure(message));
},
};
map.insert(key, property);
}
Ok(ConversionResult::Success(Record { map }))
}
}
impl<K, V> ToJSValConvertible for Record<K, V>
where
K: RecordKey,
V: ToJSValConvertible,
{
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
rooted!(in(cx) let js_object = JS_NewPlainObject(cx));
assert!(!js_object.handle().is_null());
rooted!(in(cx) let mut js_value = UndefinedValue());
for (key, value) in &self.map {
let key = key.to_utf16_vec();
value.to_jsval(cx, js_value.handle_mut());
assert!(JS_DefineUCProperty2(
cx,
js_object.handle(),
key.as_ptr(),
key.len(),
js_value.handle(),
JSPROP_ENUMERATE as u32
));
}
rval.set(ObjectValue(js_object.handle().get()));
}
}
impl<K: RecordKey, V> Default for Record<K, V> {
fn default() -> Self {
Self::new()
}
}

View file

@ -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.

View file

@ -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

View file

@ -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 CustomTraceable for HtmlTokenizer<PrefetchSink> {
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) {
<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 {

View file

@ -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);
}
}

View file

@ -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;