script: introduce safe wrappers for js val conversions (#38004)

Introduce a safe wrapper trait for the unsafe `ToJSValConvertible`, and
use it in `script/dom` where the default `T` implementation works.

Part of https://github.com/servo/servo/issues/37951

---------

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
This commit is contained in:
Gregory Terzian 2025-07-15 08:57:15 +07:00 committed by GitHub
parent 9e2ee0029a
commit 027954dbad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 149 additions and 187 deletions

View file

@ -6,11 +6,11 @@ use std::ptr;
use html5ever::interface::QualName; use html5ever::interface::QualName;
use html5ever::{LocalName, local_name, ns}; use html5ever::{LocalName, local_name, ns};
use js::conversions::ToJSValConvertible;
use js::glue::{UnwrapObjectDynamic, UnwrapObjectStatic}; use js::glue::{UnwrapObjectDynamic, UnwrapObjectStatic};
use js::jsapi::{CallArgs, CurrentGlobalOrNull, JSAutoRealm, JSObject}; use js::jsapi::{CallArgs, CurrentGlobalOrNull, JSAutoRealm, JSObject};
use js::rust::wrappers::{JS_SetPrototype, JS_WrapObject}; use js::rust::wrappers::{JS_SetPrototype, JS_WrapObject};
use js::rust::{HandleObject, MutableHandleObject, MutableHandleValue}; use js::rust::{HandleObject, MutableHandleObject, MutableHandleValue};
use script_bindings::conversions::SafeToJSValConvertible;
use script_bindings::interface::get_desired_proto; use script_bindings::interface::get_desired_proto;
use super::utils::ProtoOrIfaceArray; use super::utils::ProtoOrIfaceArray;
@ -242,7 +242,7 @@ fn html_constructor(
JS_SetPrototype(*cx, element.handle(), prototype.handle()); JS_SetPrototype(*cx, element.handle(), prototype.handle());
result.to_jsval(*cx, MutableHandleValue::from_raw(call_args.rval())); result.safe_to_jsval(cx, MutableHandleValue::from_raw(call_args.rval()));
} }
Ok(()) Ok(())
} }

View file

@ -8,6 +8,7 @@ use std::ptr::NonNull;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject, Value}; use js::jsapi::{Heap, JSObject, Value};
use js::rust::HandleObject; use js::rust::HandleObject;
use script_bindings::conversions::SafeToJSValConvertible;
use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{ use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
CryptoKeyMethods, KeyType, KeyUsage, CryptoKeyMethods, KeyType, KeyUsage,
@ -16,7 +17,6 @@ use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::js::conversions::ToJSValConvertible;
use crate::script_runtime::{CanGc, JSContext}; use crate::script_runtime::{CanGc, JSContext};
/// The underlying cryptographic data this key represents /// The underlying cryptographic data this key represents
@ -135,15 +135,12 @@ impl CryptoKeyMethods<crate::DomTypeHolder> for CryptoKey {
NonNull::new(self.algorithm_object.get()).unwrap() NonNull::new(self.algorithm_object.get()).unwrap()
} }
#[allow(unsafe_code)]
/// <https://w3c.github.io/webcrypto/#dom-cryptokey-usages> /// <https://w3c.github.io/webcrypto/#dom-cryptokey-usages>
fn Usages(&self, cx: JSContext) -> NonNull<JSObject> { fn Usages(&self, cx: JSContext) -> NonNull<JSObject> {
unsafe {
rooted!(in(*cx) let mut usages: Value); rooted!(in(*cx) let mut usages: Value);
self.usages.to_jsval(*cx, usages.handle_mut()); self.usages.safe_to_jsval(cx, usages.handle_mut());
NonNull::new(usages.to_object()).unwrap() NonNull::new(usages.to_object()).unwrap()
} }
}
} }
impl Handle { impl Handle {

View file

@ -10,12 +10,12 @@ use std::{mem, ptr};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use html5ever::{LocalName, Namespace, Prefix, ns}; use html5ever::{LocalName, Namespace, Prefix, ns};
use js::conversions::ToJSValConvertible;
use js::glue::UnwrapObjectStatic; use js::glue::UnwrapObjectStatic;
use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSAutoRealm, JSObject}; use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSAutoRealm, JSObject};
use js::jsval::{BooleanValue, JSVal, NullValue, ObjectValue, UndefinedValue}; use js::jsval::{BooleanValue, JSVal, NullValue, ObjectValue, UndefinedValue};
use js::rust::wrappers::{Construct1, JS_GetProperty, SameValue}; use js::rust::wrappers::{Construct1, JS_GetProperty, SameValue};
use js::rust::{HandleObject, HandleValue, MutableHandleValue}; use js::rust::{HandleObject, HandleValue, MutableHandleValue};
use script_bindings::conversions::SafeToJSValConvertible;
use super::bindings::trace::HashMapTracedValues; use super::bindings::trace::HashMapTracedValues;
use crate::dom::bindings::callback::{CallbackContainer, ExceptionHandling}; use crate::dom::bindings::callback::{CallbackContainer, ExceptionHandling};
@ -539,24 +539,19 @@ impl CustomElementRegistryMethods<crate::DomTypeHolder> for CustomElementRegistr
// Step 16, 16.3 // Step 16, 16.3
let promise = self.when_defined.borrow_mut().remove(&name); let promise = self.when_defined.borrow_mut().remove(&name);
if let Some(promise) = promise { if let Some(promise) = promise {
unsafe {
rooted!(in(*cx) let mut constructor = UndefinedValue()); rooted!(in(*cx) let mut constructor = UndefinedValue());
definition definition
.constructor .constructor
.to_jsval(*cx, constructor.handle_mut()); .safe_to_jsval(cx, constructor.handle_mut());
promise.resolve_native(&constructor.get(), can_gc); promise.resolve_native(&constructor.get(), can_gc);
} }
}
Ok(()) Ok(())
} }
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-get> /// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-get>
#[allow(unsafe_code)]
fn Get(&self, cx: JSContext, name: DOMString, mut retval: MutableHandleValue) { fn Get(&self, cx: JSContext, name: DOMString, mut retval: MutableHandleValue) {
match self.definitions.borrow().get(&LocalName::from(&*name)) { match self.definitions.borrow().get(&LocalName::from(&*name)) {
Some(definition) => unsafe { Some(definition) => definition.constructor.safe_to_jsval(cx, retval),
definition.constructor.to_jsval(*cx, retval);
},
None => retval.set(UndefinedValue()), None => retval.set(UndefinedValue()),
} }
} }
@ -572,7 +567,6 @@ impl CustomElementRegistryMethods<crate::DomTypeHolder> for CustomElementRegistr
} }
/// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-whendefined> /// <https://html.spec.whatwg.org/multipage/#dom-customelementregistry-whendefined>
#[allow(unsafe_code)]
fn WhenDefined(&self, name: DOMString, comp: InRealm, can_gc: CanGc) -> Rc<Promise> { fn WhenDefined(&self, name: DOMString, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
let name = LocalName::from(&*name); let name = LocalName::from(&*name);
@ -592,17 +586,15 @@ impl CustomElementRegistryMethods<crate::DomTypeHolder> for CustomElementRegistr
// Step 2 // Step 2
if let Some(definition) = self.definitions.borrow().get(&LocalName::from(&*name)) { if let Some(definition) = self.definitions.borrow().get(&LocalName::from(&*name)) {
unsafe {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut constructor = UndefinedValue()); rooted!(in(*cx) let mut constructor = UndefinedValue());
definition definition
.constructor .constructor
.to_jsval(*cx, constructor.handle_mut()); .safe_to_jsval(cx, constructor.handle_mut());
let promise = Promise::new_in_current_realm(comp, can_gc); let promise = Promise::new_in_current_realm(comp, can_gc);
promise.resolve_native(&constructor.get(), can_gc); promise.resolve_native(&constructor.get(), can_gc);
return promise; return promise;
} }
}
// Steps 3, 4, 5 // Steps 3, 4, 5
let existing_promise = self.when_defined.borrow().get(&name).cloned(); let existing_promise = self.when_defined.borrow().get(&name).cloned();
@ -927,9 +919,7 @@ fn run_upgrade_constructor(
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
rooted!(in(*cx) let constructor_val = ObjectValue(constructor.callback())); rooted!(in(*cx) let constructor_val = ObjectValue(constructor.callback()));
rooted!(in(*cx) let mut element_val = UndefinedValue()); rooted!(in(*cx) let mut element_val = UndefinedValue());
unsafe { element.safe_to_jsval(cx, element_val.handle_mut());
element.to_jsval(*cx, element_val.handle_mut());
}
rooted!(in(*cx) let mut construct_result = ptr::null_mut::<JSObject>()); rooted!(in(*cx) let mut construct_result = ptr::null_mut::<JSObject>());
{ {
// Step 8.1. If definition's disable shadow is true and element's shadow root is non-null, // Step 8.1. If definition's disable shadow is true and element's shadow root is non-null,
@ -1128,7 +1118,6 @@ impl CustomElementReactionStack {
} }
/// <https://html.spec.whatwg.org/multipage/#enqueue-a-custom-element-callback-reaction> /// <https://html.spec.whatwg.org/multipage/#enqueue-a-custom-element-callback-reaction>
#[allow(unsafe_code)]
pub(crate) fn enqueue_callback_reaction( pub(crate) fn enqueue_callback_reaction(
&self, &self,
element: &Element, element: &Element,
@ -1174,30 +1163,22 @@ impl CustomElementReactionStack {
let local_name = DOMString::from(&*local_name); let local_name = DOMString::from(&*local_name);
rooted!(in(*cx) let mut name_value = UndefinedValue()); rooted!(in(*cx) let mut name_value = UndefinedValue());
unsafe { local_name.safe_to_jsval(cx, name_value.handle_mut());
local_name.to_jsval(*cx, name_value.handle_mut());
}
rooted!(in(*cx) let mut old_value = NullValue()); rooted!(in(*cx) let mut old_value = NullValue());
if let Some(old_val) = old_val { if let Some(old_val) = old_val {
unsafe { old_val.safe_to_jsval(cx, old_value.handle_mut());
old_val.to_jsval(*cx, old_value.handle_mut());
}
} }
rooted!(in(*cx) let mut value = NullValue()); rooted!(in(*cx) let mut value = NullValue());
if let Some(val) = val { if let Some(val) = val {
unsafe { val.safe_to_jsval(cx, value.handle_mut());
val.to_jsval(*cx, value.handle_mut());
}
} }
rooted!(in(*cx) let mut namespace_value = NullValue()); rooted!(in(*cx) let mut namespace_value = NullValue());
if namespace != ns!() { if namespace != ns!() {
let namespace = DOMString::from(&*namespace); let namespace = DOMString::from(&*namespace);
unsafe { namespace.safe_to_jsval(cx, namespace_value.handle_mut());
namespace.to_jsval(*cx, namespace_value.handle_mut());
}
} }
let args = vec![ let args = vec![

View file

@ -14,7 +14,6 @@ use http::StatusCode;
use http::header::{self, HeaderName, HeaderValue}; use http::header::{self, HeaderName, HeaderValue};
use ipc_channel::ipc; use ipc_channel::ipc;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use js::conversions::ToJSValConvertible;
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::rust::HandleObject; use js::rust::HandleObject;
use mime::{self, Mime}; use mime::{self, Mime};
@ -23,6 +22,7 @@ use net_traits::{
CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg, CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg,
FilteredMetadata, NetworkError, ResourceFetchTiming, ResourceTimingType, FilteredMetadata, NetworkError, ResourceFetchTiming, ResourceTimingType,
}; };
use script_bindings::conversions::SafeToJSValConvertible;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use stylo_atoms::Atom; use stylo_atoms::Atom;
@ -231,7 +231,6 @@ impl EventSourceContext {
} }
// https://html.spec.whatwg.org/multipage/#dispatchMessage // https://html.spec.whatwg.org/multipage/#dispatchMessage
#[allow(unsafe_code)]
fn dispatch_event(&mut self, can_gc: CanGc) { fn dispatch_event(&mut self, can_gc: CanGc) {
let event_source = self.event_source.root(); let event_source = self.event_source.root();
// Step 1 // Step 1
@ -258,10 +257,8 @@ impl EventSourceContext {
let event = { let event = {
let _ac = enter_realm(&*event_source); let _ac = enter_realm(&*event_source);
rooted!(in(*GlobalScope::get_cx()) let mut data = UndefinedValue()); rooted!(in(*GlobalScope::get_cx()) let mut data = UndefinedValue());
unsafe {
self.data self.data
.to_jsval(*GlobalScope::get_cx(), data.handle_mut()) .safe_to_jsval(GlobalScope::get_cx(), data.handle_mut());
};
MessageEvent::new( MessageEvent::new(
&event_source.global(), &event_source.global(),
type_, type_,

View file

@ -5,7 +5,6 @@
use std::ptr; use std::ptr;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
use js::jsapi::{ use js::jsapi::{
ESClass, GetBuiltinClass, IsArrayBufferObject, JS_DeleteUCProperty, ESClass, GetBuiltinClass, IsArrayBufferObject, JS_DeleteUCProperty,
JS_GetOwnUCPropertyDescriptor, JS_GetStringLength, JS_IsArrayBufferViewObject, JSObject, JS_GetOwnUCPropertyDescriptor, JS_GetStringLength, JS_IsArrayBufferViewObject, JSObject,
@ -20,6 +19,7 @@ use net_traits::indexeddb_thread::{
IndexedDBThreadMsg, SyncOperation, IndexedDBThreadMsg, SyncOperation,
}; };
use profile_traits::ipc; use profile_traits::ipc;
use script_bindings::conversions::SafeToJSValConvertible;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::IDBObjectStoreParameters; use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::IDBObjectStoreParameters;
@ -236,7 +236,7 @@ impl IDBObjectStore {
rooted!(in(*cx) let input_val = current_val.to_string()); rooted!(in(*cx) let input_val = current_val.to_string());
unsafe { unsafe {
let string_len = JS_GetStringLength(*input_val) as u64; let string_len = JS_GetStringLength(*input_val) as u64;
string_len.to_jsval(*cx, return_val); string_len.safe_to_jsval(cx, return_val);
} }
break; break;
} }

View file

@ -9,6 +9,7 @@ use js::rust::HandleValue;
use net_traits::IpcSend; use net_traits::IpcSend;
use net_traits::indexeddb_thread::{IndexedDBThreadMsg, SyncOperation}; use net_traits::indexeddb_thread::{IndexedDBThreadMsg, SyncOperation};
use profile_traits::ipc; use profile_traits::ipc;
use script_bindings::conversions::SafeToJSValConvertible;
use stylo_atoms::Atom; use stylo_atoms::Atom;
use crate::dom::bindings::codegen::Bindings::IDBOpenDBRequestBinding::IDBOpenDBRequestMethods; use crate::dom::bindings::codegen::Bindings::IDBOpenDBRequestBinding::IDBOpenDBRequestMethods;
@ -25,7 +26,6 @@ use crate::dom::idbdatabase::IDBDatabase;
use crate::dom::idbrequest::IDBRequest; use crate::dom::idbrequest::IDBRequest;
use crate::dom::idbtransaction::IDBTransaction; use crate::dom::idbtransaction::IDBTransaction;
use crate::dom::idbversionchangeevent::IDBVersionChangeEvent; use crate::dom::idbversionchangeevent::IDBVersionChangeEvent;
use crate::js::conversions::ToJSValConvertible;
use crate::realms::enter_realm; use crate::realms::enter_realm;
use crate::script_runtime::CanGc; use crate::script_runtime::CanGc;
@ -140,7 +140,6 @@ impl IDBOpenDBRequest {
reflect_dom_object(Box::new(IDBOpenDBRequest::new_inherited()), global, can_gc) reflect_dom_object(Box::new(IDBOpenDBRequest::new_inherited()), global, can_gc)
} }
#[allow(unsafe_code)]
// https://www.w3.org/TR/IndexedDB-2/#run-an-upgrade-transaction // https://www.w3.org/TR/IndexedDB-2/#run-an-upgrade-transaction
fn upgrade_db_version(&self, connection: &IDBDatabase, version: u64, can_gc: CanGc) { fn upgrade_db_version(&self, connection: &IDBDatabase, version: u64, can_gc: CanGc) {
let global = self.global(); let global = self.global();
@ -178,9 +177,7 @@ impl IDBOpenDBRequest {
// Step 8.1 // Step 8.1
let _ac = enter_realm(&*conn); let _ac = enter_realm(&*conn);
rooted!(in(*cx) let mut connection_val = UndefinedValue()); rooted!(in(*cx) let mut connection_val = UndefinedValue());
unsafe { conn.safe_to_jsval(cx, connection_val.handle_mut());
conn.to_jsval(*cx, connection_val.handle_mut());
}
this.idbrequest.set_result(connection_val.handle()); this.idbrequest.set_result(connection_val.handle());
// Step 8.2 // Step 8.2
@ -328,7 +325,6 @@ impl IDBOpenDBRequest {
.unwrap(); .unwrap();
} }
#[allow(unsafe_code)]
pub fn dispatch_success(&self, result: &IDBDatabase) { pub fn dispatch_success(&self, result: &IDBDatabase) {
let global = self.global(); let global = self.global();
let this = Trusted::new(self); let this = Trusted::new(self);
@ -344,9 +340,7 @@ impl IDBOpenDBRequest {
let _ac = enter_realm(&*result); let _ac = enter_realm(&*result);
rooted!(in(*cx) let mut result_val = UndefinedValue()); rooted!(in(*cx) let mut result_val = UndefinedValue());
unsafe { result.safe_to_jsval(cx, result_val.handle_mut());
result.to_jsval(*cx, result_val.handle_mut());
}
this.set_result(result_val.handle()); this.set_result(result_val.handle());
let event = Event::new( let event = Event::new(

View file

@ -16,7 +16,7 @@ use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::conversions::{ConversionResult, FromJSValConvertibleRc, ToJSValConvertible}; use js::conversions::{ConversionResult, FromJSValConvertibleRc};
use js::jsapi::{ use js::jsapi::{
AddRawValueRoot, CallArgs, GetFunctionNativeReserved, Heap, JS_ClearPendingException, AddRawValueRoot, CallArgs, GetFunctionNativeReserved, Heap, JS_ClearPendingException,
JS_GetFunctionObject, JS_NewFunction, JSAutoRealm, JSContext, JSObject, JS_GetFunctionObject, JS_NewFunction, JSAutoRealm, JSContext, JSObject,
@ -30,6 +30,7 @@ use js::rust::wrappers::{
ResolvePromise, SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState, ResolvePromise, SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState,
}; };
use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime}; use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime};
use script_bindings::conversions::SafeToJSValConvertible;
use crate::dom::bindings::conversions::root_from_object; use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, ErrorToJsval}; use crate::dom::bindings::error::{Error, ErrorToJsval};
@ -156,13 +157,13 @@ impl Promise {
pub(crate) fn new_resolved( pub(crate) fn new_resolved(
global: &GlobalScope, global: &GlobalScope,
cx: SafeJSContext, cx: SafeJSContext,
value: impl ToJSValConvertible, value: impl SafeToJSValConvertible,
_can_gc: CanGc, _can_gc: CanGc,
) -> Rc<Promise> { ) -> Rc<Promise> {
let _ac = JSAutoRealm::new(*cx, global.reflector().get_jsobject().get()); let _ac = JSAutoRealm::new(*cx, global.reflector().get_jsobject().get());
unsafe {
rooted!(in(*cx) let mut rval = UndefinedValue()); rooted!(in(*cx) let mut rval = UndefinedValue());
value.to_jsval(*cx, rval.handle_mut()); value.safe_to_jsval(cx, rval.handle_mut());
unsafe {
rooted!(in(*cx) let p = CallOriginalPromiseResolve(*cx, rval.handle())); rooted!(in(*cx) let p = CallOriginalPromiseResolve(*cx, rval.handle()));
assert!(!p.handle().is_null()); assert!(!p.handle().is_null());
Promise::new_with_js_promise(p.handle(), cx) Promise::new_with_js_promise(p.handle(), cx)
@ -174,30 +175,27 @@ impl Promise {
pub(crate) fn new_rejected( pub(crate) fn new_rejected(
global: &GlobalScope, global: &GlobalScope,
cx: SafeJSContext, cx: SafeJSContext,
value: impl ToJSValConvertible, value: impl SafeToJSValConvertible,
_can_gc: CanGc, _can_gc: CanGc,
) -> Rc<Promise> { ) -> Rc<Promise> {
let _ac = JSAutoRealm::new(*cx, global.reflector().get_jsobject().get()); let _ac = JSAutoRealm::new(*cx, global.reflector().get_jsobject().get());
unsafe {
rooted!(in(*cx) let mut rval = UndefinedValue()); rooted!(in(*cx) let mut rval = UndefinedValue());
value.to_jsval(*cx, rval.handle_mut()); value.safe_to_jsval(cx, rval.handle_mut());
unsafe {
rooted!(in(*cx) let p = CallOriginalPromiseReject(*cx, rval.handle())); rooted!(in(*cx) let p = CallOriginalPromiseReject(*cx, rval.handle()));
assert!(!p.handle().is_null()); assert!(!p.handle().is_null());
Promise::new_with_js_promise(p.handle(), cx) Promise::new_with_js_promise(p.handle(), cx)
} }
} }
#[allow(unsafe_code)]
pub(crate) fn resolve_native<T>(&self, val: &T, can_gc: CanGc) pub(crate) fn resolve_native<T>(&self, val: &T, can_gc: CanGc)
where where
T: ToJSValConvertible, T: SafeToJSValConvertible,
{ {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
let _ac = enter_realm(self); let _ac = enter_realm(self);
rooted!(in(*cx) let mut v = UndefinedValue()); rooted!(in(*cx) let mut v = UndefinedValue());
unsafe { val.safe_to_jsval(cx, v.handle_mut());
val.to_jsval(*cx, v.handle_mut());
}
self.resolve(cx, v.handle(), can_gc); self.resolve(cx, v.handle(), can_gc);
} }
@ -211,17 +209,14 @@ impl Promise {
} }
} }
#[allow(unsafe_code)]
pub(crate) fn reject_native<T>(&self, val: &T, can_gc: CanGc) pub(crate) fn reject_native<T>(&self, val: &T, can_gc: CanGc)
where where
T: ToJSValConvertible, T: SafeToJSValConvertible,
{ {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
let _ac = enter_realm(self); let _ac = enter_realm(self);
rooted!(in(*cx) let mut v = UndefinedValue()); rooted!(in(*cx) let mut v = UndefinedValue());
unsafe { val.safe_to_jsval(cx, v.handle_mut());
val.to_jsval(*cx, v.handle_mut());
}
self.reject(cx, v.handle(), can_gc); self.reject(cx, v.handle(), can_gc);
} }

View file

@ -12,7 +12,7 @@ use base::id::{MessagePortId, MessagePortIndex};
use constellation_traits::MessagePortImpl; use constellation_traits::MessagePortImpl;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use ipc_channel::ipc::IpcSharedMemory; use ipc_channel::ipc::IpcSharedMemory;
use js::conversions::ToJSValConvertible; use script_bindings::conversions::SafeToJSValConvertible;
use js::jsapi::{Heap, JSObject}; use js::jsapi::{Heap, JSObject};
use js::jsval::{JSVal, ObjectValue, UndefinedValue}; use js::jsval::{JSVal, ObjectValue, UndefinedValue};
use js::rust::{ use js::rust::{
@ -1595,7 +1595,6 @@ impl ReadableStream {
} }
/// <https://streams.spec.whatwg.org/#readable-stream-cancel> /// <https://streams.spec.whatwg.org/#readable-stream-cancel>
#[allow(unsafe_code)]
pub(crate) fn cancel( pub(crate) fn cancel(
&self, &self,
cx: SafeJSContext, cx: SafeJSContext,
@ -1613,13 +1612,11 @@ impl ReadableStream {
// If stream.[[state]] is "errored", return a promise rejected with stream.[[storedError]]. // If stream.[[state]] is "errored", return a promise rejected with stream.[[storedError]].
if self.is_errored() { if self.is_errored() {
let promise = Promise::new(global, can_gc); let promise = Promise::new(global, can_gc);
unsafe {
rooted!(in(*cx) let mut rval = UndefinedValue()); rooted!(in(*cx) let mut rval = UndefinedValue());
self.stored_error.to_jsval(*cx, rval.handle_mut()); self.stored_error.safe_to_jsval(cx, rval.handle_mut());
promise.reject_native(&rval.handle(), can_gc); promise.reject_native(&rval.handle(), can_gc);
return promise; return promise;
} }
}
// Perform ! ReadableStreamClose(stream). // Perform ! ReadableStreamClose(stream).
self.close(can_gc); self.close(can_gc);
@ -2322,7 +2319,6 @@ impl CrossRealmTransformReadable {
/// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable> /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
/// Add a handler for ports messageerror event with the following steps: /// Add a handler for ports messageerror event with the following steps:
#[allow(unsafe_code)]
pub(crate) fn handle_error( pub(crate) fn handle_error(
&self, &self,
cx: SafeJSContext, cx: SafeJSContext,
@ -2334,7 +2330,7 @@ impl CrossRealmTransformReadable {
// Let error be a new "DataCloneError" DOMException. // Let error be a new "DataCloneError" DOMException.
let error = DOMException::new(global, DOMErrorName::DataCloneError, can_gc); let error = DOMException::new(global, DOMErrorName::DataCloneError, can_gc);
rooted!(in(*cx) let mut rooted_error = UndefinedValue()); rooted!(in(*cx) let mut rooted_error = UndefinedValue());
unsafe { error.to_jsval(*cx, rooted_error.handle_mut()) }; error.safe_to_jsval(cx, rooted_error.handle_mut());
// Perform ! CrossRealmTransformSendError(port, error). // Perform ! CrossRealmTransformSendError(port, error).
port.cross_realm_transform_send_error(rooted_error.handle(), can_gc); port.cross_realm_transform_send_error(rooted_error.handle(), can_gc);

View file

@ -13,6 +13,7 @@ use js::jsval::{JSVal, UndefinedValue};
use js::rust::wrappers::JS_GetPendingException; use js::rust::wrappers::JS_GetPendingException;
use js::rust::{HandleObject, HandleValue as SafeHandleValue, HandleValue, MutableHandleValue}; use js::rust::{HandleObject, HandleValue as SafeHandleValue, HandleValue, MutableHandleValue};
use js::typedarray::Uint8; use js::typedarray::Uint8;
use script_bindings::conversions::SafeToJSValConvertible;
use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize; use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
use super::bindings::root::Dom; use super::bindings::root::Dom;
@ -30,7 +31,6 @@ use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
use crate::dom::readablestream::ReadableStream; use crate::dom::readablestream::ReadableStream;
use crate::dom::readablestreamdefaultreader::ReadRequest; use crate::dom::readablestreamdefaultreader::ReadRequest;
use crate::dom::underlyingsourcecontainer::{UnderlyingSourceContainer, UnderlyingSourceType}; use crate::dom::underlyingsourcecontainer::{UnderlyingSourceContainer, UnderlyingSourceType};
use crate::js::conversions::ToJSValConvertible;
use crate::realms::{InRealm, enter_realm}; use crate::realms::{InRealm, enter_realm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
@ -147,18 +147,15 @@ impl EnqueuedValue {
} }
} }
#[allow(unsafe_code)]
fn to_jsval(&self, cx: SafeJSContext, rval: MutableHandleValue, can_gc: CanGc) { fn to_jsval(&self, cx: SafeJSContext, rval: MutableHandleValue, can_gc: CanGc) {
match self { match self {
EnqueuedValue::Native(chunk) => { EnqueuedValue::Native(chunk) => {
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>()); rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<Uint8>(cx, chunk, array_buffer_ptr.handle_mut(), can_gc) create_buffer_source::<Uint8>(cx, chunk, array_buffer_ptr.handle_mut(), can_gc)
.expect("failed to create buffer source for native chunk."); .expect("failed to create buffer source for native chunk.");
unsafe { array_buffer_ptr.to_jsval(*cx, rval) }; array_buffer_ptr.safe_to_jsval(cx, rval);
},
EnqueuedValue::Js(value_with_size) => unsafe {
value_with_size.value.to_jsval(*cx, rval);
}, },
EnqueuedValue::Js(value_with_size) => value_with_size.value.safe_to_jsval(cx, rval),
EnqueuedValue::CloseSentinel => { EnqueuedValue::CloseSentinel => {
unreachable!("The close sentinel is never made available as a js val.") unreachable!("The close sentinel is never made available as a js val.")
}, },

View file

@ -7,11 +7,11 @@ use std::ptr;
use constellation_traits::BlobImpl; use constellation_traits::BlobImpl;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
use js::jsapi::{JSAutoRealm, JSObject}; use js::jsapi::{JSAutoRealm, JSObject};
use js::jsval::UndefinedValue; use js::jsval::UndefinedValue;
use js::rust::CustomAutoRooterGuard; use js::rust::CustomAutoRooterGuard;
use js::typedarray::{ArrayBuffer, ArrayBufferView, CreateWith}; use js::typedarray::{ArrayBuffer, ArrayBufferView, CreateWith};
use script_bindings::conversions::SafeToJSValConvertible;
use script_bindings::weakref::WeakRef; use script_bindings::weakref::WeakRef;
use servo_media::webrtc::{ use servo_media::webrtc::{
DataChannelId, DataChannelInit, DataChannelMessage, DataChannelState, WebRtcError, DataChannelId, DataChannelInit, DataChannelMessage, DataChannelState, WebRtcError,
@ -195,7 +195,6 @@ impl RTCDataChannel {
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) fn on_message(&self, channel_message: DataChannelMessage, can_gc: CanGc) { pub(crate) fn on_message(&self, channel_message: DataChannelMessage, can_gc: CanGc) {
unsafe {
let global = self.global(); let global = self.global();
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
let _ac = JSAutoRealm::new(*cx, self.reflector().get_jsobject().get()); let _ac = JSAutoRealm::new(*cx, self.reflector().get_jsobject().get());
@ -203,7 +202,7 @@ impl RTCDataChannel {
match channel_message { match channel_message {
DataChannelMessage::Text(text) => { DataChannelMessage::Text(text) => {
text.to_jsval(*cx, message.handle_mut()); text.safe_to_jsval(cx, message.handle_mut());
}, },
DataChannelMessage::Binary(data) => match &**self.binary_type.borrow() { DataChannelMessage::Binary(data) => match &**self.binary_type.borrow() {
"blob" => { "blob" => {
@ -212,10 +211,11 @@ impl RTCDataChannel {
BlobImpl::new_from_bytes(data, "".to_owned()), BlobImpl::new_from_bytes(data, "".to_owned()),
can_gc, can_gc,
); );
blob.to_jsval(*cx, message.handle_mut()); blob.safe_to_jsval(cx, message.handle_mut());
}, },
"arraybuffer" => { "arraybuffer" => {
rooted!(in(*cx) let mut array_buffer = ptr::null_mut::<JSObject>()); rooted!(in(*cx) let mut array_buffer = ptr::null_mut::<JSObject>());
unsafe {
assert!( assert!(
ArrayBuffer::create( ArrayBuffer::create(
*cx, *cx,
@ -223,9 +223,10 @@ impl RTCDataChannel {
array_buffer.handle_mut() array_buffer.handle_mut()
) )
.is_ok() .is_ok()
); )
};
(*array_buffer).to_jsval(*cx, message.handle_mut()); (*array_buffer).safe_to_jsval(cx, message.handle_mut());
}, },
_ => unreachable!(), _ => unreachable!(),
}, },
@ -241,7 +242,6 @@ impl RTCDataChannel {
can_gc, can_gc,
); );
} }
}
pub(crate) fn on_state_change(&self, state: DataChannelState, can_gc: CanGc) { pub(crate) fn on_state_change(&self, state: DataChannelState, can_gc: CanGc) {
if let DataChannelState::Closing = state { if let DataChannelState::Closing = state {

View file

@ -22,13 +22,13 @@ use net_traits::{
CoreResourceMsg, FetchChannels, MessageData, WebSocketDomAction, WebSocketNetworkEvent, CoreResourceMsg, FetchChannels, MessageData, WebSocketDomAction, WebSocketNetworkEvent,
}; };
use profile_traits::ipc as ProfiledIpc; use profile_traits::ipc as ProfiledIpc;
use script_bindings::conversions::SafeToJSValConvertible;
use servo_url::{ImmutableOrigin, ServoUrl}; use servo_url::{ImmutableOrigin, ServoUrl};
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
use crate::dom::bindings::codegen::Bindings::WebSocketBinding::{BinaryType, WebSocketMethods}; use crate::dom::bindings::codegen::Bindings::WebSocketBinding::{BinaryType, WebSocketMethods};
use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence; use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence;
use crate::dom::bindings::conversions::ToJSValConvertible;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::refcounted::Trusted;
@ -590,13 +590,11 @@ impl TaskOnce for MessageReceivedTask {
// Step 2-5. // Step 2-5.
let global = ws.global(); let global = ws.global();
// GlobalScope::get_cx() returns a valid `JSContext` pointer, so this is safe.
unsafe {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
let _ac = JSAutoRealm::new(*cx, ws.reflector().get_jsobject().get()); let _ac = JSAutoRealm::new(*cx, ws.reflector().get_jsobject().get());
rooted!(in(*cx) let mut message = UndefinedValue()); rooted!(in(*cx) let mut message = UndefinedValue());
match self.message { match self.message {
MessageData::Text(text) => text.to_jsval(*cx, message.handle_mut()), MessageData::Text(text) => text.safe_to_jsval(cx, message.handle_mut()),
MessageData::Binary(data) => match ws.binary_type.get() { MessageData::Binary(data) => match ws.binary_type.get() {
BinaryType::Blob => { BinaryType::Blob => {
let blob = Blob::new( let blob = Blob::new(
@ -604,10 +602,12 @@ impl TaskOnce for MessageReceivedTask {
BlobImpl::new_from_bytes(data, "".to_owned()), BlobImpl::new_from_bytes(data, "".to_owned()),
CanGc::note(), CanGc::note(),
); );
blob.to_jsval(*cx, message.handle_mut()); blob.safe_to_jsval(cx, message.handle_mut());
}, },
BinaryType::Arraybuffer => { BinaryType::Arraybuffer => {
rooted!(in(*cx) let mut array_buffer = ptr::null_mut::<JSObject>()); rooted!(in(*cx) let mut array_buffer = ptr::null_mut::<JSObject>());
// GlobalScope::get_cx() returns a valid `JSContext` pointer, so this is safe.
unsafe {
assert!( assert!(
ArrayBuffer::create( ArrayBuffer::create(
*cx, *cx,
@ -615,9 +615,10 @@ impl TaskOnce for MessageReceivedTask {
array_buffer.handle_mut() array_buffer.handle_mut()
) )
.is_ok() .is_ok()
); )
};
(*array_buffer).to_jsval(*cx, message.handle_mut()); (*array_buffer).safe_to_jsval(cx, message.handle_mut());
}, },
}, },
} }
@ -631,5 +632,4 @@ impl TaskOnce for MessageReceivedTask {
CanGc::note(), CanGc::note(),
); );
} }
}
} }

View file

@ -4,10 +4,10 @@
use dom_struct::dom_struct; use dom_struct::dom_struct;
use embedder_traits::GamepadSupportedHapticEffects; use embedder_traits::GamepadSupportedHapticEffects;
use js::conversions::ToJSValConvertible;
use js::jsapi::Heap; use js::jsapi::Heap;
use js::jsval::{JSVal, UndefinedValue}; use js::jsval::{JSVal, UndefinedValue};
use js::rust::MutableHandleValue; use js::rust::MutableHandleValue;
use script_bindings::conversions::SafeToJSValConvertible;
use webxr_api::{Handedness, InputFrame, InputId, InputSource, TargetRayMode}; use webxr_api::{Handedness, InputFrame, InputId, InputSource, TargetRayMode};
use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{ use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{
@ -73,7 +73,6 @@ impl XRInputSource {
} }
} }
#[allow(unsafe_code)]
pub(crate) fn new( pub(crate) fn new(
window: &Window, window: &Window,
session: &XRSession, session: &XRSession,
@ -88,11 +87,12 @@ impl XRInputSource {
let _ac = enter_realm(window); let _ac = enter_realm(window);
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
unsafe {
rooted!(in(*cx) let mut profiles = UndefinedValue()); rooted!(in(*cx) let mut profiles = UndefinedValue());
source.info.profiles.to_jsval(*cx, profiles.handle_mut()); source
.info
.profiles
.safe_to_jsval(cx, profiles.handle_mut());
source.profiles.set(profiles.get()); source.profiles.set(profiles.get());
}
source source
} }

View file

@ -4,10 +4,10 @@
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::RigidTransform3D; use euclid::RigidTransform3D;
use js::conversions::ToJSValConvertible;
use js::jsapi::Heap; use js::jsapi::Heap;
use js::jsval::{JSVal, UndefinedValue}; use js::jsval::{JSVal, UndefinedValue};
use js::rust::MutableHandleValue; use js::rust::MutableHandleValue;
use script_bindings::conversions::SafeToJSValConvertible;
use webxr_api::{Viewer, ViewerPose, Views}; use webxr_api::{Viewer, ViewerPose, Views};
use crate::dom::bindings::codegen::Bindings::XRViewBinding::XREye; use crate::dom::bindings::codegen::Bindings::XRViewBinding::XREye;
@ -38,7 +38,6 @@ impl XRViewerPose {
} }
} }
#[allow(unsafe_code)]
pub(crate) fn new( pub(crate) fn new(
window: &Window, window: &Window,
session: &XRSession, session: &XRSession,
@ -183,11 +182,9 @@ impl XRViewerPose {
); );
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
unsafe {
rooted!(in(*cx) let mut jsval = UndefinedValue()); rooted!(in(*cx) let mut jsval = UndefinedValue());
views.to_jsval(*cx, jsval.handle_mut()); views.safe_to_jsval(cx, jsval.handle_mut());
pose.views.set(jsval.get()); pose.views.set(jsval.get());
}
pose pose
} }

View file

@ -40,7 +40,6 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as
use euclid::{Point2D, Scale, Size2D, Vector2D}; use euclid::{Point2D, Scale, Size2D, Vector2D};
use fonts::FontContext; use fonts::FontContext;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use js::conversions::ToJSValConvertible;
use js::glue::DumpJSStack; use js::glue::DumpJSStack;
use js::jsapi::{ use js::jsapi::{
GCReason, Heap, JS_GC, JSAutoRealm, JSContext as RawJSContext, JSObject, JSPROP_ENUMERATE, GCReason, Heap, JS_GC, JSAutoRealm, JSContext as RawJSContext, JSObject, JSPROP_ENUMERATE,
@ -70,6 +69,7 @@ use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods; use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods;
use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods; use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods;
use script_bindings::conversions::SafeToJSValConvertible;
use script_bindings::interfaces::WindowHelpers; use script_bindings::interfaces::WindowHelpers;
use script_bindings::root::Root; use script_bindings::root::Root;
use script_traits::ScriptThreadMessage; use script_traits::ScriptThreadMessage;
@ -1796,12 +1796,9 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
} }
// https://dom.spec.whatwg.org/#dom-window-event // https://dom.spec.whatwg.org/#dom-window-event
#[allow(unsafe_code)]
fn Event(&self, cx: JSContext, rval: MutableHandleValue) { fn Event(&self, cx: JSContext, rval: MutableHandleValue) {
if let Some(ref event) = *self.current_event.borrow() { if let Some(ref event) = *self.current_event.borrow() {
unsafe { event.reflector().get_jsobject().safe_to_jsval(cx, rval);
event.reflector().get_jsobject().to_jsval(*cx, rval);
}
} }
} }

View file

@ -18,6 +18,7 @@ use js::rust::{
MutableHandleValue as SafeMutableHandleValue, MutableHandleValue as SafeMutableHandleValue,
}; };
use script_bindings::codegen::GenericBindings::MessagePortBinding::MessagePortMethods; use script_bindings::codegen::GenericBindings::MessagePortBinding::MessagePortMethods;
use script_bindings::conversions::SafeToJSValConvertible;
use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize; use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
@ -41,7 +42,6 @@ use crate::dom::writablestreamdefaultcontroller::{
UnderlyingSinkType, WritableStreamDefaultController, UnderlyingSinkType, WritableStreamDefaultController,
}; };
use crate::dom::writablestreamdefaultwriter::WritableStreamDefaultWriter; use crate::dom::writablestreamdefaultwriter::WritableStreamDefaultWriter;
use crate::js::conversions::ToJSValConvertible;
use crate::realms::{InRealm, enter_realm}; use crate::realms::{InRealm, enter_realm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
@ -1191,7 +1191,6 @@ impl CrossRealmTransformWritable {
/// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable> /// <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable>
/// Add a handler for ports messageerror event with the following steps: /// Add a handler for ports messageerror event with the following steps:
#[allow(unsafe_code)]
pub(crate) fn handle_error( pub(crate) fn handle_error(
&self, &self,
cx: SafeJSContext, cx: SafeJSContext,
@ -1203,7 +1202,7 @@ impl CrossRealmTransformWritable {
// Let error be a new "DataCloneError" DOMException. // Let error be a new "DataCloneError" DOMException.
let error = DOMException::new(global, DOMErrorName::DataCloneError, can_gc); let error = DOMException::new(global, DOMErrorName::DataCloneError, can_gc);
rooted!(in(*cx) let mut rooted_error = UndefinedValue()); rooted!(in(*cx) let mut rooted_error = UndefinedValue());
unsafe { error.to_jsval(*cx, rooted_error.handle_mut()) }; error.safe_to_jsval(cx, rooted_error.handle_mut());
// Perform ! CrossRealmTransformSendError(port, error). // Perform ! CrossRealmTransformSendError(port, error).
port.cross_realm_transform_send_error(rooted_error.handle(), can_gc); port.cross_realm_transform_send_error(rooted_error.handle(), can_gc);

View file

@ -31,10 +31,22 @@ use crate::inheritance::Castable;
use crate::num::Finite; use crate::num::Finite;
use crate::reflector::{DomObject, Reflector}; use crate::reflector::{DomObject, Reflector};
use crate::root::DomRoot; use crate::root::DomRoot;
use crate::script_runtime::JSContext as SafeJSContext;
use crate::str::{ByteString, DOMString, USVString}; use crate::str::{ByteString, DOMString, USVString};
use crate::trace::RootedTraceableBox; use crate::trace::RootedTraceableBox;
use crate::utils::{DOMClass, DOMJSClass}; use crate::utils::{DOMClass, DOMJSClass};
/// A safe wrapper for `ToJSValConvertible`.
pub trait SafeToJSValConvertible {
fn safe_to_jsval(&self, cx: SafeJSContext, rval: MutableHandleValue);
}
impl<T: ToJSValConvertible> SafeToJSValConvertible for T {
fn safe_to_jsval(&self, cx: SafeJSContext, rval: MutableHandleValue) {
unsafe { self.to_jsval(*cx, rval) };
}
}
/// A trait to check whether a given `JSObject` implements an IDL interface. /// A trait to check whether a given `JSObject` implements an IDL interface.
pub trait IDLInterface { pub trait IDLInterface {
/// Returns whether the given DOM class derives that interface. /// Returns whether the given DOM class derives that interface.