aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/bindings/callback.rs2
-rw-r--r--components/script/dom/bindings/conversions.rs101
-rw-r--r--components/script/dom/bindings/error.rs18
-rw-r--r--components/script/dom/bindings/guard.rs19
-rw-r--r--components/script/dom/bindings/import.rs11
-rw-r--r--components/script/dom/bindings/interface.rs45
-rw-r--r--components/script/dom/bindings/mod.rs4
-rw-r--r--components/script/dom/bindings/namespace.rs5
-rw-r--r--components/script/dom/bindings/utils.rs490
-rw-r--r--components/script/dom/document.rs8
-rw-r--r--components/script/dom/globalscope.rs6
-rw-r--r--components/script/dom/readablestream.rs25
-rw-r--r--components/script/dom/servointernals.rs18
-rw-r--r--components/script/dom/testbinding.rs10
-rw-r--r--components/script/dom/webgl2renderingcontext.rs7
-rw-r--r--components/script/dom/windowproxy.rs6
-rw-r--r--components/script/webdriver_handlers.rs2
-rw-r--r--components/script_bindings/codegen/Bindings.conf5
-rw-r--r--components/script_bindings/codegen/CodegenRust.py45
-rw-r--r--components/script_bindings/conversions.rs86
-rw-r--r--components/script_bindings/error.rs23
-rw-r--r--components/script_bindings/finalize.rs (renamed from components/script/dom/bindings/finalize.rs)34
-rw-r--r--components/script_bindings/interfaces.rs24
-rw-r--r--components/script_bindings/lib.rs4
-rw-r--r--components/script_bindings/lock.rs37
-rw-r--r--components/script_bindings/num.rs (renamed from components/script/dom/bindings/num.rs)6
-rw-r--r--components/script_bindings/utils.rs482
-rw-r--r--components/script_bindings/webidls/ServoInternals.webidl4
28 files changed, 836 insertions, 691 deletions
diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs
index b5e32dc97a5..7a28451763f 100644
--- a/components/script/dom/bindings/callback.rs
+++ b/components/script/dom/bindings/callback.rs
@@ -15,6 +15,7 @@ use js::jsapi::{
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
use js::rust::wrappers::{JS_GetProperty, JS_WrapObject};
use js::rust::{MutableHandleValue, Runtime};
+use script_bindings::interfaces::DocumentHelpers;
use crate::DomTypes;
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
@@ -23,7 +24,6 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::settings_stack::{GenericAutoEntryScript, GenericAutoIncumbentScript};
use crate::dom::bindings::utils::AsCCharPtrPtr;
-use crate::dom::document::DocumentHelpers;
use crate::dom::globalscope::GlobalScopeHelpers;
use crate::realms::{InRealm, enter_realm};
use crate::script_runtime::{CanGc, JSContext};
diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs
index 0df5a82c3b0..0ea59e6ee83 100644
--- a/components/script/dom/bindings/conversions.rs
+++ b/components/script/dom/bindings/conversions.rs
@@ -37,60 +37,17 @@ use std::ffi;
pub(crate) use js::conversions::{
ConversionBehavior, ConversionResult, FromJSValConvertible, ToJSValConvertible,
};
-use js::error::throw_type_error;
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::{HandleObject, HandleValue, MutableHandleValue};
-use num_traits::Float;
pub(crate) use script_bindings::conversions::*;
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::trace::{JSTraceable, RootedTraceableBox};
-use crate::dom::bindings::utils::DOMClass;
-use crate::dom::filelist::FileList;
-use crate::dom::htmlcollection::HTMLCollection;
-use crate::dom::htmlformcontrolscollection::HTMLFormControlsCollection;
-use crate::dom::htmloptionscollection::HTMLOptionsCollection;
-use crate::dom::nodelist::NodeList;
-
-impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> {
- #[inline]
- unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
- let value = **self;
- value.to_jsval(cx, rval);
- }
-}
-
-impl<T: Float + FromJSValConvertible<Config = ()>> FromJSValConvertible for Finite<T> {
- type Config = ();
-
- unsafe fn from_jsval(
- cx: *mut JSContext,
- value: HandleValue,
- option: (),
- ) -> Result<ConversionResult<Finite<T>>, ()> {
- let result = match FromJSValConvertible::from_jsval(cx, value, option)? {
- ConversionResult::Success(v) => v,
- ConversionResult::Failure(error) => {
- // FIXME(emilio): Why throwing instead of propagating the error?
- throw_type_error(cx, &error);
- return Err(());
- },
- };
- match Finite::new(result) {
- Some(v) => Ok(ConversionResult::Success(v)),
- None => {
- throw_type_error(cx, "this argument is not a finite floating-point value");
- Err(())
- },
- }
- }
-}
impl<T: ToJSValConvertible + JSTraceable> ToJSValConvertible for RootedTraceableBox<T> {
#[inline]
@@ -123,35 +80,6 @@ where
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
-/// wrapper, and checking if the object is of the correct type.
-///
-/// Returns Err(()) if `obj` is a wrapper or if the object is not an object
-/// for a DOM object of the given type (as defined by the proto_id and proto_depth).
-#[inline]
-unsafe fn private_from_proto_check_static(
- obj: *mut JSObject,
- proto_check: fn(&'static DOMClass) -> bool,
-) -> Result<*const libc::c_void, ()> {
- let dom_class = get_dom_class(obj).map_err(|_| ())?;
- if proto_check(dom_class) {
- trace!("good prototype");
- Ok(private_from_object(obj))
- } else {
- trace!("bad prototype");
- Err(())
- }
-}
-
-/// Get a `*const T` for a DOM object accessible from a `JSObject`, where the DOM object
-/// is guaranteed not to be a wrapper.
-pub(crate) fn native_from_object_static<T>(obj: *mut JSObject) -> Result<*const T, ()>
-where
- T: DomObject + IDLInterface,
-{
- unsafe { private_from_proto_check_static(obj, T::derives).map(|ptr| ptr as *const T) }
-}
-
/// Get a `DomRoot<T>` for the given DOM object, unwrapping any wrapper
/// around it first, and checking if the object is of the correct type.
///
@@ -162,19 +90,7 @@ pub(crate) fn root_from_object_static<T>(obj: *mut JSObject) -> Result<DomRoot<T
where
T: DomObject + IDLInterface,
{
- native_from_object_static(obj).map(|ptr| unsafe { DomRoot::from_ref(&*ptr) })
-}
-
-/// Get a `*const T` for a DOM object accessible from a `HandleValue`.
-/// Caller is responsible for throwing a JS exception if needed in case of error.
-pub(crate) fn native_from_handlevalue<T>(v: HandleValue, cx: *mut JSContext) -> Result<*const T, ()>
-where
- T: DomObject + IDLInterface,
-{
- if !v.get().is_object() {
- return Err(());
- }
- unsafe { native_from_object(v.get().to_object(), cx) }
+ unsafe { native_from_object_static(obj).map(|ptr| DomRoot::from_ref(&*ptr)) }
}
/// Get a `DomRoot<T>` for a DOM object accessible from a `HandleObject`.
@@ -191,7 +107,10 @@ where
/// Returns whether `value` is an array-like object (Array, FileList,
/// HTMLCollection, HTMLFormControlsCollection, HTMLOptionsCollection,
/// NodeList).
-pub(crate) unsafe fn is_array_like(cx: *mut JSContext, value: HandleValue) -> bool {
+pub(crate) unsafe fn is_array_like<D: crate::DomTypes>(
+ cx: *mut JSContext,
+ value: HandleValue,
+) -> bool {
let mut is_array = false;
assert!(IsArrayObject(cx, value, &mut is_array));
if is_array {
@@ -203,19 +122,19 @@ pub(crate) unsafe fn is_array_like(cx: *mut JSContext, value: HandleValue) -> bo
_ => return false,
};
- if root_from_object::<FileList>(object, cx).is_ok() {
+ if root_from_object::<D::FileList>(object, cx).is_ok() {
return true;
}
- if root_from_object::<HTMLCollection>(object, cx).is_ok() {
+ if root_from_object::<D::HTMLCollection>(object, cx).is_ok() {
return true;
}
- if root_from_object::<HTMLFormControlsCollection>(object, cx).is_ok() {
+ if root_from_object::<D::HTMLFormControlsCollection>(object, cx).is_ok() {
return true;
}
- if root_from_object::<HTMLOptionsCollection>(object, cx).is_ok() {
+ if root_from_object::<D::HTMLOptionsCollection>(object, cx).is_ok() {
return true;
}
- if root_from_object::<NodeList>(object, cx).is_ok() {
+ if root_from_object::<D::NodeList>(object, cx).is_ok() {
return true;
}
diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs
index 13bdd97cad1..b9d51e77fcf 100644
--- a/components/script/dom/bindings/error.rs
+++ b/components/script/dom/bindings/error.rs
@@ -20,7 +20,6 @@ pub(crate) use script_bindings::error::*;
#[cfg(feature = "js_backtrace")]
use crate::dom::bindings::cell::DomRefCell;
-use crate::dom::bindings::codegen::PrototypeList::proto_id_to_name;
use crate::dom::bindings::conversions::{
ConversionResult, FromJSValConvertible, ToJSValConvertible, root_from_object,
};
@@ -257,23 +256,6 @@ pub(crate) fn report_pending_exception(
}
}
-/// Throw an exception to signal that a `JSObject` can not be converted to a
-/// given DOM type.
-pub(crate) fn throw_invalid_this(cx: SafeJSContext, proto_id: u16) {
- debug_assert!(unsafe { !JS_IsExceptionPending(*cx) });
- let error = format!(
- "\"this\" object does not implement interface {}.",
- proto_id_to_name(proto_id)
- );
- unsafe { throw_type_error(*cx, &error) };
-}
-
-pub(crate) fn throw_constructor_without_new(cx: SafeJSContext, name: &str) {
- debug_assert!(unsafe { !JS_IsExceptionPending(*cx) });
- let error = format!("{} constructor: 'new' is required", name);
- unsafe { throw_type_error(*cx, &error) };
-}
-
pub(crate) trait ErrorToJsval {
fn to_jsval(
self,
diff --git a/components/script/dom/bindings/guard.rs b/components/script/dom/bindings/guard.rs
index fb2b2ebc807..16b5187abef 100644
--- a/components/script/dom/bindings/guard.rs
+++ b/components/script/dom/bindings/guard.rs
@@ -5,11 +5,12 @@
//! Machinery to conditionally expose things.
use js::rust::HandleObject;
+use script_bindings::codegen::Globals::Globals;
use servo_config::prefs::get;
-use crate::dom::bindings::codegen::InterfaceObjectMap;
+use crate::DomTypes;
use crate::dom::bindings::interface::is_exposed_in;
-use crate::dom::globalscope::GlobalScope;
+use crate::dom::globalscope::GlobalScopeHelpers;
use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_runtime::JSContext;
@@ -28,7 +29,7 @@ impl<T: Clone + Copy> Guard<T> {
/// Expose the value if the conditions are satisfied.
///
/// The passed handle is the object on which the value may be exposed.
- pub(crate) fn expose(
+ pub(crate) fn expose<D: DomTypes>(
&self,
cx: JSContext,
obj: HandleObject,
@@ -45,7 +46,7 @@ impl<T: Clone + Copy> Guard<T> {
exposed_on_global |= is_exposed_in(global, *globals);
true
},
- _ => c.is_satisfied(cx, obj, global),
+ _ => c.is_satisfied::<D>(cx, obj, global),
});
if conditions_satisfied && exposed_on_global {
@@ -64,21 +65,21 @@ pub(crate) enum Condition {
/// The condition is satisfied if the preference is set.
Pref(&'static str),
// The condition is satisfied if the interface is exposed in the global.
- Exposed(InterfaceObjectMap::Globals),
+ Exposed(Globals),
SecureContext(),
/// The condition is always satisfied.
Satisfied,
}
-fn is_secure_context(cx: JSContext) -> bool {
+fn is_secure_context<D: DomTypes>(cx: JSContext) -> bool {
unsafe {
let in_realm_proof = AlreadyInRealm::assert_for_cx(JSContext::from_ptr(*cx));
- GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)).is_secure_context()
+ D::GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)).is_secure_context()
}
}
impl Condition {
- pub(crate) fn is_satisfied(
+ pub(crate) fn is_satisfied<D: DomTypes>(
&self,
cx: JSContext,
obj: HandleObject,
@@ -88,7 +89,7 @@ impl Condition {
Condition::Pref(name) => get().get_value(name).try_into().unwrap_or(false),
Condition::Func(f) => f(cx, obj),
Condition::Exposed(globals) => is_exposed_in(global, globals),
- Condition::SecureContext() => is_secure_context(cx),
+ Condition::SecureContext() => is_secure_context::<D>(cx),
Condition::Satisfied => true,
}
}
diff --git a/components/script/dom/bindings/import.rs b/components/script/dom/bindings/import.rs
index 33511666d17..fb893af33f4 100644
--- a/components/script/dom/bindings/import.rs
+++ b/components/script/dom/bindings/import.rs
@@ -16,6 +16,7 @@ pub(crate) mod base {
pub(crate) use js::panic::maybe_resume_unwind;
pub(crate) use js::rust::wrappers::{Call, JS_WrapValue};
pub(crate) use js::rust::{HandleObject, HandleValue, MutableHandleObject, MutableHandleValue};
+ pub(crate) use script_bindings::lock::ThreadUnsafeOnceLock;
pub(crate) use crate::dom::bindings::callback::{
CallSetup, CallbackContainer, CallbackFunction, CallbackInterface, CallbackObject,
@@ -40,7 +41,7 @@ pub(crate) mod base {
pub(crate) use crate::dom::bindings::str::{ByteString, DOMString, USVString};
pub(crate) use crate::dom::bindings::trace::RootedTraceableBox;
pub(crate) use crate::dom::bindings::utils::{
- DomHelpers, ThreadUnsafeOnceLock, get_dictionary_property, set_dictionary_property,
+ DomHelpers, get_dictionary_property, set_dictionary_property,
};
pub(crate) use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers};
pub(crate) use crate::dom::promise::PromiseHelpers;
@@ -99,7 +100,12 @@ pub(crate) mod module {
JS_CALLEE, JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL,
JSCLASS_RESERVED_SLOTS_MASK, jsapi, typedarray,
};
+ pub(crate) use script_bindings::codegen::Globals::Globals;
pub(crate) use script_bindings::constant::{ConstantSpec, ConstantVal};
+ pub(crate) use script_bindings::finalize::{
+ finalize_common, finalize_global, finalize_weak_referenceable,
+ };
+ pub(crate) use script_bindings::interfaces::*;
pub(crate) use script_bindings::record::Record;
pub(crate) use servo_config::pref;
@@ -124,9 +130,6 @@ pub(crate) mod module {
pub(crate) use crate::dom::bindings::error::{
Error, ErrorResult, throw_constructor_without_new,
};
- pub(crate) use crate::dom::bindings::finalize::{
- finalize_common, finalize_global, finalize_weak_referenceable,
- };
pub(crate) use crate::dom::bindings::guard::{Condition, Guard};
pub(crate) use crate::dom::bindings::inheritance::Castable;
pub(crate) use crate::dom::bindings::interface::{
diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs
index 56c50ef2b72..835d2f7f0be 100644
--- a/components/script/dom/bindings/interface.rs
+++ b/components/script/dom/bindings/interface.rs
@@ -35,6 +35,7 @@ use js::rust::{
use script_bindings::constant::{ConstantSpec, define_constants};
use servo_url::MutableOrigin;
+use crate::DomTypes;
use crate::dom::bindings::codegen::InterfaceObjectMap::Globals;
use crate::dom::bindings::codegen::PrototypeList;
use crate::dom::bindings::conversions::{DOM_OBJECT_SLOT, get_dom_class};
@@ -43,8 +44,6 @@ use crate::dom::bindings::principals::ServoJSPrincipals;
use crate::dom::bindings::utils::{
DOM_PROTOTYPE_SLOT, DOMJSClass, JSCLASS_DOM_GLOBAL, ProtoOrIfaceArray, get_proto_or_iface_array,
};
-use crate::dom::globalscope::GlobalScope;
-use crate::realms::{AlreadyInRealm, InRealm};
use crate::script_runtime::JSContext as SafeJSContext;
/// The class of a non-callback interface object.
@@ -213,7 +212,7 @@ fn select_compartment(cx: SafeJSContext, options: &mut RealmOptions) {
}
/// Create and define the interface object of a callback interface.
-pub(crate) fn create_callback_interface_object(
+pub(crate) fn create_callback_interface_object<D: DomTypes>(
cx: SafeJSContext,
global: HandleObject,
constants: &[Guard<&[ConstantSpec]>],
@@ -225,14 +224,14 @@ pub(crate) fn create_callback_interface_object(
rval.set(JS_NewObject(*cx, ptr::null()));
}
assert!(!rval.is_null());
- define_guarded_constants(cx, rval.handle(), constants, global);
+ define_guarded_constants::<D>(cx, rval.handle(), constants, global);
define_name(cx, rval.handle(), name);
define_on_global_object(cx, global, name, rval.handle());
}
/// Create the interface prototype object of a non-callback interface.
#[allow(clippy::too_many_arguments)]
-pub(crate) fn create_interface_prototype_object(
+pub(crate) fn create_interface_prototype_object<D: DomTypes>(
cx: SafeJSContext,
global: HandleObject,
proto: HandleObject,
@@ -243,7 +242,7 @@ pub(crate) fn create_interface_prototype_object(
unscopable_names: &[&CStr],
mut rval: MutableHandleObject,
) {
- create_object(
+ create_object::<D>(
cx,
global,
proto,
@@ -277,7 +276,7 @@ pub(crate) fn create_interface_prototype_object(
/// Create and define the interface object of a non-callback interface.
#[allow(clippy::too_many_arguments)]
-pub(crate) fn create_noncallback_interface_object(
+pub(crate) fn create_noncallback_interface_object<D: DomTypes>(
cx: SafeJSContext,
global: HandleObject,
proto: HandleObject,
@@ -291,7 +290,7 @@ pub(crate) fn create_noncallback_interface_object(
legacy_window_alias_names: &[&CStr],
mut rval: MutableHandleObject,
) {
- create_object(
+ create_object::<D>(
cx,
global,
proto,
@@ -350,7 +349,7 @@ pub(crate) fn create_named_constructors(
/// Create a new object with a unique type.
#[allow(clippy::too_many_arguments)]
-pub(crate) fn create_object(
+pub(crate) fn create_object<D: DomTypes>(
cx: SafeJSContext,
global: HandleObject,
proto: HandleObject,
@@ -364,34 +363,34 @@ pub(crate) fn create_object(
rval.set(JS_NewObjectWithGivenProto(*cx, class, proto));
}
assert!(!rval.is_null());
- define_guarded_methods(cx, rval.handle(), methods, global);
- define_guarded_properties(cx, rval.handle(), properties, global);
- define_guarded_constants(cx, rval.handle(), constants, global);
+ define_guarded_methods::<D>(cx, rval.handle(), methods, global);
+ define_guarded_properties::<D>(cx, rval.handle(), properties, global);
+ define_guarded_constants::<D>(cx, rval.handle(), constants, global);
}
/// Conditionally define constants on an object.
-pub(crate) fn define_guarded_constants(
+pub(crate) fn define_guarded_constants<D: DomTypes>(
cx: SafeJSContext,
obj: HandleObject,
constants: &[Guard<&[ConstantSpec]>],
global: HandleObject,
) {
for guard in constants {
- if let Some(specs) = guard.expose(cx, obj, global) {
+ if let Some(specs) = guard.expose::<D>(cx, obj, global) {
define_constants(cx, obj, specs);
}
}
}
/// Conditionally define methods on an object.
-pub(crate) fn define_guarded_methods(
+pub(crate) fn define_guarded_methods<D: DomTypes>(
cx: SafeJSContext,
obj: HandleObject,
methods: &[Guard<&'static [JSFunctionSpec]>],
global: HandleObject,
) {
for guard in methods {
- if let Some(specs) = guard.expose(cx, obj, global) {
+ if let Some(specs) = guard.expose::<D>(cx, obj, global) {
unsafe {
define_methods(*cx, obj, specs).unwrap();
}
@@ -400,14 +399,14 @@ pub(crate) fn define_guarded_methods(
}
/// Conditionally define properties on an object.
-pub(crate) fn define_guarded_properties(
+pub(crate) fn define_guarded_properties<D: DomTypes>(
cx: SafeJSContext,
obj: HandleObject,
properties: &[Guard<&'static [JSPropertySpec]>],
global: HandleObject,
) {
for guard in properties {
- if let Some(specs) = guard.expose(cx, obj, global) {
+ if let Some(specs) = guard.expose::<D>(cx, obj, global) {
unsafe {
define_properties(*cx, obj, specs).unwrap();
}
@@ -425,16 +424,6 @@ pub(crate) fn is_exposed_in(object: HandleObject, globals: Globals) -> bool {
}
}
-/// The navigator.servo api is only exposed to about: pages except about:blank
-pub(crate) fn is_servo_internal(cx: SafeJSContext, _object: HandleObject) -> bool {
- unsafe {
- let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
- let global_scope = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
- let url = global_scope.get_url();
- url.scheme() == "about" && url.as_str() != "about:blank"
- }
-}
-
/// Define a property with a given name on the global object. Should be called
/// through the resolve hook.
pub(crate) fn define_on_global_object(
diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs
index 9385df640c8..833915c0b98 100644
--- a/components/script/dom/bindings/mod.rs
+++ b/components/script/dom/bindings/mod.rs
@@ -141,7 +141,6 @@ pub(crate) mod cell;
pub(crate) mod constructor;
pub(crate) mod conversions;
pub(crate) mod error;
-pub(crate) mod finalize;
pub(crate) mod frozenarray;
pub(crate) mod function;
pub(crate) mod guard;
@@ -151,7 +150,6 @@ pub(crate) mod interface;
pub(crate) mod iterable;
pub(crate) mod like;
pub(crate) mod namespace;
-pub(crate) mod num;
pub(crate) mod principals;
pub(crate) mod proxyhandler;
pub(crate) mod refcounted;
@@ -167,6 +165,8 @@ pub(crate) mod utils;
pub(crate) mod weakref;
pub(crate) mod xmlname;
+pub(crate) use script_bindings::num;
+
/// Generated JS-Rust bindings.
#[allow(missing_docs, non_snake_case)]
pub(crate) mod codegen {
diff --git a/components/script/dom/bindings/namespace.rs b/components/script/dom/bindings/namespace.rs
index 46079a787c9..ad0a5801519 100644
--- a/components/script/dom/bindings/namespace.rs
+++ b/components/script/dom/bindings/namespace.rs
@@ -11,6 +11,7 @@ use js::jsapi::{JSClass, JSFunctionSpec};
use js::rust::{HandleObject, MutableHandleObject};
use script_bindings::constant::ConstantSpec;
+use crate::DomTypes;
use crate::dom::bindings::guard::Guard;
use crate::dom::bindings::interface::{create_object, define_on_global_object};
use crate::script_runtime::JSContext;
@@ -37,7 +38,7 @@ impl NamespaceObjectClass {
/// Create a new namespace object.
#[allow(clippy::too_many_arguments)]
-pub(crate) fn create_namespace_object(
+pub(crate) fn create_namespace_object<D: DomTypes>(
cx: JSContext,
global: HandleObject,
proto: HandleObject,
@@ -47,7 +48,7 @@ pub(crate) fn create_namespace_object(
name: &CStr,
mut rval: MutableHandleObject,
) {
- create_object(
+ create_object::<D>(
cx,
global,
proto,
diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs
index cf89f8379cd..f97c7188f32 100644
--- a/components/script/dom/bindings/utils.rs
+++ b/components/script/dom/bindings/utils.rs
@@ -5,86 +5,31 @@
//! Various utilities to glue JavaScript and the DOM implementation together.
use std::cell::RefCell;
-use std::ffi::CString;
use std::os::raw::c_char;
-use std::ptr::NonNull;
-use std::sync::OnceLock;
use std::thread::LocalKey;
-use std::{ptr, slice, str};
+use std::{ptr, slice};
-use js::JS_CALLEE;
use js::conversions::ToJSValConvertible;
-use js::glue::{
- CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper, JS_GetReservedSlot,
- RUST_FUNCTION_VALUE_TO_JITINFO, UnwrapObjectDynamic, UnwrapObjectStatic,
-};
+use js::glue::{IsWrapper, UnwrapObjectDynamic, UnwrapObjectStatic};
use js::jsapi::{
- AtomToLinearString, CallArgs, DOMCallbacks, ExceptionStackBehavior, GetLinearStringCharAt,
- GetLinearStringLength, GetNonCCWObjectGlobal, HandleId as RawHandleId,
- HandleObject as RawHandleObject, Heap, JS_ClearPendingException,
+ CallArgs, DOMCallbacks, HandleId as RawHandleId, HandleObject as RawHandleObject, Heap,
JS_DeprecatedStringHasLatin1Chars, JS_EnumerateStandardClasses, JS_FreezeObject,
- JS_GetLatin1StringCharsAndLength, JS_IsExceptionPending, JS_IsGlobalObject,
- JS_ResolveStandardClass, JSAtom, JSContext, JSJitInfo, JSObject, JSTracer,
- MutableHandleIdVector as RawMutableHandleIdVector, MutableHandleValue as RawMutableHandleValue,
- ObjectOpResult, StringIsArrayIndex,
-};
-use js::jsval::{JSVal, UndefinedValue};
-use js::rust::wrappers::{
- CallOriginalPromiseReject, JS_DeletePropertyById, JS_ForwardGetPropertyTo,
- JS_GetPendingException, JS_GetProperty, JS_GetPrototype, JS_HasProperty, JS_HasPropertyById,
- JS_SetPendingException, JS_SetProperty,
-};
-use js::rust::{
- GCMethods, Handle, HandleId, HandleObject, HandleValue, MutableHandleValue, ToString,
- get_object_class, is_dom_class,
+ JS_GetLatin1StringCharsAndLength, JS_IsGlobalObject, JS_ResolveStandardClass, JSContext,
+ JSObject, JSTracer, MutableHandleIdVector as RawMutableHandleIdVector,
};
+use js::rust::{Handle, HandleObject, MutableHandleValue, get_object_class, is_dom_class};
use crate::DomTypes;
-use crate::dom::bindings::codegen::InterfaceObjectMap;
-use crate::dom::bindings::codegen::PrototypeList::{self, PROTO_OR_IFACE_LENGTH};
+use crate::dom::bindings::codegen::{InterfaceObjectMap, PrototypeList};
use crate::dom::bindings::constructor::call_html_constructor;
-use crate::dom::bindings::conversions::{
- DerivedFrom, PrototypeCheck, jsstring_to_str, private_from_proto_check,
-};
-use crate::dom::bindings::error::{Error, throw_dom_exception, throw_invalid_this};
+use crate::dom::bindings::conversions::DerivedFrom;
+use crate::dom::bindings::error::{Error, throw_dom_exception};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::settings_stack::{self, StackEntry};
-use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::trace_object;
use crate::dom::windowproxy::WindowProxyHandler;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
-/// A OnceLock wrapping a type that is not considered threadsafe by the Rust compiler, but
-/// will be used in a threadsafe manner (it will not be mutated, after being initialized).
-///
-/// This is needed to allow using JS API types (which usually involve raw pointers) in static initializers,
-/// when Servo guarantees through the use of OnceLock that only one thread will ever initialize
-/// the value.
-pub(crate) struct ThreadUnsafeOnceLock<T>(OnceLock<T>);
-
-impl<T> ThreadUnsafeOnceLock<T> {
- pub(crate) const fn new() -> Self {
- Self(OnceLock::new())
- }
-
- /// Initialize the value inside this lock. Panics if the lock has been previously initialized.
- pub(crate) fn set(&self, val: T) {
- assert!(self.0.set(val).is_ok());
- }
-
- /// Get a reference to the value inside this lock. Panics if the lock has not been initialized.
- ///
- /// SAFETY:
- /// The caller must ensure that it does not mutate value contained inside this lock
- /// (using interior mutability).
- pub(crate) unsafe fn get(&self) -> &T {
- self.0.get().unwrap()
- }
-}
-
-unsafe impl<T> Sync for ThreadUnsafeOnceLock<T> {}
-unsafe impl<T> Send for ThreadUnsafeOnceLock<T> {}
-
#[derive(JSTraceable, MallocSizeOf)]
/// Static data associated with a global object.
pub(crate) struct GlobalStaticData {
@@ -102,21 +47,7 @@ impl GlobalStaticData {
}
}
-/// The index of the slot where the object holder of that interface's
-/// unforgeable members are defined.
-pub(crate) const DOM_PROTO_UNFORGEABLE_HOLDER_SLOT: u32 = 0;
-
-/// The index of the slot that contains a reference to the ProtoOrIfaceArray.
-// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
-pub(crate) const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT;
-
-/// The flag set on the `JSClass`es for DOM global objects.
-// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
-// LSetDOMProperty. Those constants need to be changed accordingly if this value
-// changes.
-pub(crate) const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1;
-
-pub(crate) use script_bindings::utils::{DOMClass, DOMJSClass};
+pub(crate) use script_bindings::utils::*;
/// Returns a JSVal representing the frozen JavaScript array
pub(crate) fn to_frozen_array<T: ToJSValConvertible>(
@@ -131,132 +62,6 @@ pub(crate) fn to_frozen_array<T: ToJSValConvertible>(
unsafe { JS_FreezeObject(*cx, RawHandleObject::from(obj.handle())) };
}
-/// Returns the ProtoOrIfaceArray for the given global object.
-/// Fails if `global` is not a DOM global object.
-pub(crate) fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {
- unsafe {
- assert_ne!(((*get_object_class(global)).flags & JSCLASS_DOM_GLOBAL), 0);
- let mut slot = UndefinedValue();
- JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT, &mut slot);
- slot.to_private() as *mut ProtoOrIfaceArray
- }
-}
-
-/// An array of *mut JSObject of size PROTO_OR_IFACE_LENGTH.
-pub(crate) type ProtoOrIfaceArray = [*mut JSObject; PROTO_OR_IFACE_LENGTH];
-
-/// Gets the property `id` on `proxy`'s prototype. If it exists, `*found` is
-/// set to true and `*vp` to the value, otherwise `*found` is set to false.
-///
-/// Returns false on JSAPI failure.
-pub(crate) unsafe fn get_property_on_prototype(
- cx: *mut JSContext,
- proxy: HandleObject,
- receiver: HandleValue,
- id: HandleId,
- found: *mut bool,
- vp: MutableHandleValue,
-) -> bool {
- rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
- if !JS_GetPrototype(cx, proxy, proto.handle_mut()) || proto.is_null() {
- *found = false;
- return true;
- }
- let mut has_property = false;
- if !JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) {
- return false;
- }
- *found = has_property;
- if !has_property {
- return true;
- }
-
- JS_ForwardGetPropertyTo(cx, proto.handle(), id, receiver, vp)
-}
-
-/// Get an array index from the given `jsid`. Returns `None` if the given
-/// `jsid` is not an integer.
-pub(crate) unsafe fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option<u32> {
- let raw_id = *id;
- if raw_id.is_int() {
- return Some(raw_id.to_int() as u32);
- }
-
- if raw_id.is_void() || !raw_id.is_string() {
- return None;
- }
-
- let atom = raw_id.to_string() as *mut JSAtom;
- let s = AtomToLinearString(atom);
- if GetLinearStringLength(s) == 0 {
- return None;
- }
-
- let chars = [GetLinearStringCharAt(s, 0)];
- let first_char = char::decode_utf16(chars.iter().cloned())
- .next()
- .map_or('\0', |r| r.unwrap_or('\0'));
- if first_char.is_ascii_lowercase() {
- return None;
- }
-
- let mut i = 0;
- if StringIsArrayIndex(s, &mut i) {
- Some(i)
- } else {
- None
- }
-
- /*let s = jsstr_to_string(cx, RUST_JSID_TO_STRING(raw_id));
- if s.len() == 0 {
- return None;
- }
-
- let first = s.chars().next().unwrap();
- if first.is_ascii_lowercase() {
- return None;
- }
-
- let mut i: u32 = 0;
- let is_array = if s.is_ascii() {
- let chars = s.as_bytes();
- StringIsArrayIndex1(chars.as_ptr() as *const _, chars.len() as u32, &mut i)
- } else {
- let chars = s.encode_utf16().collect::<Vec<u16>>();
- let slice = chars.as_slice();
- StringIsArrayIndex2(slice.as_ptr(), chars.len() as u32, &mut i)
- };
-
- if is_array {
- Some(i)
- } else {
- None
- }*/
-}
-
-/// Find the enum equivelent of a string given by `v` in `pairs`.
-/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
-/// `Ok((None, value))` if there was no matching string.
-pub(crate) unsafe fn find_enum_value<'a, T>(
- cx: *mut JSContext,
- v: HandleValue,
- pairs: &'a [(&'static str, T)],
-) -> Result<(Option<&'a T>, DOMString), ()> {
- match ptr::NonNull::new(ToString(cx, v)) {
- Some(jsstr) => {
- let search = jsstring_to_str(cx, jsstr);
- Ok((
- pairs
- .iter()
- .find(|&&(key, _)| search == *key)
- .map(|(_, ev)| ev),
- search,
- ))
- },
- None => Err(()),
- }
-}
-
/// Returns wether `obj` is a platform object using dynamic unwrap
/// <https://heycam.github.io/webidl/#dfn-platform-object>
#[allow(dead_code)]
@@ -295,104 +100,6 @@ fn is_platform_object(
}
}
-/// Get the property with name `property` from `object`.
-/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
-/// `Ok(false)` if there was no property with the given name.
-pub(crate) fn get_dictionary_property(
- cx: *mut JSContext,
- object: HandleObject,
- property: &str,
- rval: MutableHandleValue,
- _can_gc: CanGc,
-) -> Result<bool, ()> {
- fn has_property(
- cx: *mut JSContext,
- object: HandleObject,
- property: &CString,
- found: &mut bool,
- ) -> bool {
- unsafe { JS_HasProperty(cx, object, property.as_ptr(), found) }
- }
- fn get_property(
- cx: *mut JSContext,
- object: HandleObject,
- property: &CString,
- value: MutableHandleValue,
- ) -> bool {
- unsafe { JS_GetProperty(cx, object, property.as_ptr(), value) }
- }
-
- let property = CString::new(property).unwrap();
- if object.get().is_null() {
- return Ok(false);
- }
-
- let mut found = false;
- if !has_property(cx, object, &property, &mut found) {
- return Err(());
- }
-
- if !found {
- return Ok(false);
- }
-
- if !get_property(cx, object, &property, rval) {
- return Err(());
- }
-
- Ok(true)
-}
-
-/// Set the property with name `property` from `object`.
-/// Returns `Err(())` on JSAPI failure, or null object,
-/// and Ok(()) otherwise
-pub(crate) fn set_dictionary_property(
- cx: *mut JSContext,
- object: HandleObject,
- property: &str,
- value: HandleValue,
-) -> Result<(), ()> {
- if object.get().is_null() {
- return Err(());
- }
-
- let property = CString::new(property).unwrap();
- unsafe {
- if !JS_SetProperty(cx, object, property.as_ptr(), value) {
- return Err(());
- }
- }
-
- Ok(())
-}
-
-/// Returns whether `proxy` has a property `id` on its prototype.
-pub(crate) unsafe fn has_property_on_prototype(
- cx: *mut JSContext,
- proxy: HandleObject,
- id: HandleId,
- found: &mut bool,
-) -> bool {
- rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
- if !JS_GetPrototype(cx, proxy, proto.handle_mut()) {
- return false;
- }
- assert!(!proto.is_null());
- JS_HasPropertyById(cx, proto.handle(), id, found)
-}
-
-/// Drop the resources held by reserved slots of a global object
-pub(crate) unsafe fn finalize_global(obj: *mut JSObject) {
- let protolist = get_proto_or_iface_array(obj);
- let list = (*protolist).as_mut_ptr();
- for idx in 0..PROTO_OR_IFACE_LENGTH as isize {
- let entry = list.offset(idx);
- let value = *entry;
- <*mut JSObject>::post_barrier(entry, value, ptr::null_mut());
- }
- let _: Box<ProtoOrIfaceArray> = Box::from_raw(protolist);
-}
-
/// Trace the resources held by reserved slots of a global object
pub(crate) unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) {
let array = get_proto_or_iface_array(obj);
@@ -462,141 +169,6 @@ pub(crate) unsafe extern "C" fn resolve_global(
true
}
-/// Deletes the property `id` from `object`.
-pub(crate) unsafe fn delete_property_by_id(
- cx: *mut JSContext,
- object: HandleObject,
- id: HandleId,
- bp: *mut ObjectOpResult,
-) -> bool {
- JS_DeletePropertyById(cx, object, id, bp)
-}
-
-unsafe fn generic_call<const EXCEPTION_TO_REJECTION: bool>(
- cx: *mut JSContext,
- argc: libc::c_uint,
- vp: *mut JSVal,
- is_lenient: bool,
- call: unsafe extern "C" fn(
- *const JSJitInfo,
- *mut JSContext,
- RawHandleObject,
- *mut libc::c_void,
- u32,
- *mut JSVal,
- ) -> bool,
- can_gc: CanGc,
-) -> bool {
- let args = CallArgs::from_vp(vp, argc);
-
- let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
- let proto_id = (*info).__bindgen_anon_2.protoID;
- let cx = SafeJSContext::from_ptr(cx);
-
- let thisobj = args.thisv();
- if !thisobj.get().is_null_or_undefined() && !thisobj.get().is_object() {
- throw_invalid_this(cx, proto_id);
- return if EXCEPTION_TO_REJECTION {
- exception_to_promise(*cx, args.rval(), can_gc)
- } else {
- false
- };
- }
-
- rooted!(in(*cx) let obj = if thisobj.get().is_object() {
- thisobj.get().to_object()
- } else {
- GetNonCCWObjectGlobal(JS_CALLEE(*cx, vp).to_object_or_null())
- });
- let depth = (*info).__bindgen_anon_3.depth as usize;
- let proto_check = PrototypeCheck::Depth { depth, proto_id };
- let this = match private_from_proto_check(obj.get(), *cx, proto_check) {
- Ok(val) => val,
- Err(()) => {
- if is_lenient {
- debug_assert!(!JS_IsExceptionPending(*cx));
- *vp = UndefinedValue();
- return true;
- } else {
- throw_invalid_this(cx, proto_id);
- return if EXCEPTION_TO_REJECTION {
- exception_to_promise(*cx, args.rval(), can_gc)
- } else {
- false
- };
- }
- },
- };
- call(
- info,
- *cx,
- obj.handle().into(),
- this as *mut libc::c_void,
- argc,
- vp,
- )
-}
-
-/// Generic method of IDL interface.
-pub(crate) unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>(
- cx: *mut JSContext,
- argc: libc::c_uint,
- vp: *mut JSVal,
-) -> bool {
- generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, false, CallJitMethodOp, CanGc::note())
-}
-
-/// Generic getter of IDL interface.
-pub(crate) unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>(
- cx: *mut JSContext,
- argc: libc::c_uint,
- vp: *mut JSVal,
-) -> bool {
- generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, false, CallJitGetterOp, CanGc::note())
-}
-
-/// Generic lenient getter of IDL interface.
-pub(crate) unsafe extern "C" fn generic_lenient_getter<const EXCEPTION_TO_REJECTION: bool>(
- cx: *mut JSContext,
- argc: libc::c_uint,
- vp: *mut JSVal,
-) -> bool {
- generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, true, CallJitGetterOp, CanGc::note())
-}
-
-unsafe extern "C" fn call_setter(
- info: *const JSJitInfo,
- cx: *mut JSContext,
- handle: RawHandleObject,
- this: *mut libc::c_void,
- argc: u32,
- vp: *mut JSVal,
-) -> bool {
- if !CallJitSetterOp(info, cx, handle, this, argc, vp) {
- return false;
- }
- *vp = UndefinedValue();
- true
-}
-
-/// Generic setter of IDL interface.
-pub(crate) unsafe extern "C" fn generic_setter(
- cx: *mut JSContext,
- argc: libc::c_uint,
- vp: *mut JSVal,
-) -> bool {
- generic_call::<false>(cx, argc, vp, false, call_setter, CanGc::note())
-}
-
-/// Generic lenient setter of IDL interface.
-pub(crate) unsafe extern "C" fn generic_lenient_setter(
- cx: *mut JSContext,
- argc: libc::c_uint,
- vp: *mut JSVal,
-) -> bool {
- generic_call::<false>(cx, argc, vp, true, call_setter, CanGc::note())
-}
-
unsafe extern "C" fn instance_class_has_proto_at_depth(
clasp: *const js::jsapi::JSClass,
proto_id: u32,
@@ -633,48 +205,6 @@ impl AsCCharPtrPtr for [u8] {
}
}
-/// <https://searchfox.org/mozilla-central/rev/7279a1df13a819be254fd4649e07c4ff93e4bd45/dom/bindings/BindingUtils.cpp#3300>
-pub(crate) unsafe extern "C" fn generic_static_promise_method(
- cx: *mut JSContext,
- argc: libc::c_uint,
- vp: *mut JSVal,
-) -> bool {
- let args = CallArgs::from_vp(vp, argc);
-
- let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
- assert!(!info.is_null());
- // TODO: we need safe wrappers for this in mozjs!
- //assert_eq!((*info)._bitfield_1, JSJitInfo_OpType::StaticMethod as u8)
- let static_fn = (*info).__bindgen_anon_1.staticMethod.unwrap();
- if static_fn(cx, argc, vp) {
- return true;
- }
- exception_to_promise(cx, args.rval(), CanGc::note())
-}
-
-/// Coverts exception to promise rejection
-///
-/// <https://searchfox.org/mozilla-central/rev/b220e40ff2ee3d10ce68e07d8a8a577d5558e2a2/dom/bindings/BindingUtils.cpp#3315>
-pub(crate) unsafe fn exception_to_promise(
- cx: *mut JSContext,
- rval: RawMutableHandleValue,
- _can_gc: CanGc,
-) -> bool {
- rooted!(in(cx) let mut exception = UndefinedValue());
- if !JS_GetPendingException(cx, exception.handle_mut()) {
- return false;
- }
- JS_ClearPendingException(cx);
- if let Some(promise) = NonNull::new(CallOriginalPromiseReject(cx, exception.handle())) {
- promise.to_jsval(cx, MutableHandleValue::from_raw(rval));
- true
- } else {
- // We just give up. Put the exception back.
- JS_SetPendingException(cx, exception.handle(), ExceptionStackBehavior::Capture);
- false
- }
-}
-
/// Operations that must be invoked from the generated bindings.
pub(crate) trait DomHelpers<D: DomTypes> {
fn throw_dom_exception(cx: SafeJSContext, global: &D::GlobalScope, result: Error);
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index f27bd303648..4945892ef60 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -51,6 +51,7 @@ use num_traits::ToPrimitive;
use percent_encoding::percent_decode;
use profile_traits::ipc as profile_ipc;
use profile_traits::time::TimerMetadataFrameType;
+use script_bindings::interfaces::DocumentHelpers;
use script_layout_interface::{PendingRestyle, TrustedNodeAddress};
use script_traits::{
AnimationState, ConstellationInputEvent, DocumentActivity, ProgressiveWebMetricType, ScriptMsg,
@@ -78,7 +79,6 @@ use super::bindings::codegen::Bindings::XPathEvaluatorBinding::XPathEvaluatorMet
use super::canvasrenderingcontext2d::CanvasRenderingContext2D;
use super::clipboardevent::ClipboardEventType;
use super::performancepainttiming::PerformancePaintTiming;
-use crate::DomTypes;
use crate::animation_timeline::AnimationTimeline;
use crate::animations::Animations;
use crate::canvas_context::CanvasContext as _;
@@ -6398,11 +6398,7 @@ fn is_named_element_with_id_attribute(elem: &Element) -> bool {
elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty())
}
-pub(crate) trait DocumentHelpers<D: DomTypes> {
- fn ensure_safe_to_run_script_or_layout(&self);
-}
-
-impl DocumentHelpers<crate::DomTypeHolder> for Document {
+impl DocumentHelpers for Document {
fn ensure_safe_to_run_script_or_layout(&self) {
Document::ensure_safe_to_run_script_or_layout(self)
}
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index c4625895f3a..cb700d7d4c2 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -3348,6 +3348,8 @@ pub(crate) trait GlobalScopeHelpers<D: crate::DomTypes> {
fn perform_a_microtask_checkpoint(&self, can_gc: CanGc);
fn get_url(&self) -> ServoUrl;
+
+ fn is_secure_context(&self) -> bool;
}
#[allow(unsafe_code)]
@@ -3387,4 +3389,8 @@ impl GlobalScopeHelpers<crate::DomTypeHolder> for GlobalScope {
fn get_url(&self) -> ServoUrl {
self.get_url()
}
+
+ fn is_secure_context(&self) -> bool {
+ self.is_secure_context()
+ }
}
diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs
index f8b23072e66..b5048a4644d 100644
--- a/components/script/dom/readablestream.rs
+++ b/components/script/dom/readablestream.rs
@@ -155,6 +155,7 @@ impl Callback for PipeTo {
/// - the current state.
/// - the type of `result`.
/// - the state of a stored promise(in some cases).
+ #[allow(unsafe_code)]
fn callback(&self, cx: SafeJSContext, result: SafeHandleValue, realm: InRealm, can_gc: CanGc) {
let global = self.reader.global();
@@ -197,14 +198,16 @@ impl Callback for PipeTo {
} else {
rooted!(in(*cx) let object = result.to_object());
rooted!(in(*cx) let mut done = UndefinedValue());
- get_dictionary_property(
- *cx,
- object.handle(),
- "done",
- done.handle_mut(),
- can_gc,
- )
- .unwrap()
+ unsafe {
+ get_dictionary_property(
+ *cx,
+ object.handle(),
+ "done",
+ done.handle_mut(),
+ can_gc,
+ )
+ .unwrap()
+ }
}
};
// If any chunks have been read but not yet written, write them to dest.
@@ -342,6 +345,7 @@ impl PipeTo {
/// Try to write a chunk using the jsval, and returns wether it succeeded
// It will fail if it is the last `done` chunk, or if it is not a chunk at all.
+ #[allow(unsafe_code)]
fn write_chunk(
&self,
cx: SafeJSContext,
@@ -352,9 +356,10 @@ impl PipeTo {
if chunk.is_object() {
rooted!(in(*cx) let object = chunk.to_object());
rooted!(in(*cx) let mut bytes = UndefinedValue());
- let has_value =
+ let has_value = unsafe {
get_dictionary_property(*cx, object.handle(), "value", bytes.handle_mut(), can_gc)
- .expect("Chunk should have a value.");
+ .expect("Chunk should have a value.")
+ };
if !bytes.is_undefined() && has_value {
// Write the chunk.
let write_promise = self.writer.write(cx, global, bytes.handle(), can_gc);
diff --git a/components/script/dom/servointernals.rs b/components/script/dom/servointernals.rs
index 295bd838443..58d749d8227 100644
--- a/components/script/dom/servointernals.rs
+++ b/components/script/dom/servointernals.rs
@@ -5,7 +5,10 @@
use std::rc::Rc;
use dom_struct::dom_struct;
+use js::rust::HandleObject;
use profile_traits::mem::MemoryReportResult;
+use script_bindings::interfaces::ServoInternalsHelpers;
+use script_bindings::script_runtime::JSContext;
use script_traits::ScriptMsg;
use crate::dom::bindings::codegen::Bindings::ServoInternalsBinding::ServoInternalsMethods;
@@ -14,7 +17,7 @@ use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
-use crate::realms::InRealm;
+use crate::realms::{AlreadyInRealm, InRealm};
use crate::routed_promise::{RoutedPromiseListener, route_promise};
use crate::script_runtime::CanGc;
@@ -57,3 +60,16 @@ impl RoutedPromiseListener<MemoryReportResult> for ServoInternals {
promise.resolve_native(&response.content, can_gc);
}
}
+
+impl ServoInternalsHelpers for ServoInternals {
+ /// The navigator.servo api is only exposed to about: pages except about:blank
+ #[allow(unsafe_code)]
+ fn is_servo_internal(cx: JSContext, _global: HandleObject) -> bool {
+ unsafe {
+ let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
+ let global_scope = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
+ let url = global_scope.get_url();
+ url.scheme() == "about" && url.as_str() != "about:blank"
+ }
+ }
+}
diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs
index 4095ba1319a..e54fea515a6 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::interfaces::TestBindingHelpers;
use script_bindings::record::Record;
use script_traits::serializable::BlobImpl;
use servo_config::prefs;
@@ -1171,3 +1172,12 @@ impl TestBindingCallback {
.resolve_native(&self.value, CanGc::note());
}
}
+
+impl TestBindingHelpers for TestBinding {
+ fn condition_satisfied(cx: SafeJSContext, global: HandleObject) -> bool {
+ Self::condition_satisfied(cx, global)
+ }
+ fn condition_unsatisfied(cx: SafeJSContext, global: HandleObject) -> bool {
+ Self::condition_unsatisfied(cx, global)
+ }
+}
diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs
index d64a7f70376..bb6ffa11849 100644
--- a/components/script/dom/webgl2renderingcontext.rs
+++ b/components/script/dom/webgl2renderingcontext.rs
@@ -21,6 +21,7 @@ use js::jsapi::{JSObject, Type};
use js::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, ObjectValue, UInt32Value};
use js::rust::{CustomAutoRooterGuard, HandleObject, MutableHandleValue};
use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array};
+use script_bindings::interfaces::WebGL2RenderingContextHelpers;
use script_layout_interface::HTMLCanvasDataSource;
use servo_config::pref;
use url::Host;
@@ -4709,3 +4710,9 @@ impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, WebGL2RenderingContex
unsafe { (*this.base.to_layout().unsafe_get()).layout_handle() }
}
}
+
+impl WebGL2RenderingContextHelpers for WebGL2RenderingContext {
+ fn is_webgl2_enabled(cx: JSContext, global: HandleObject) -> bool {
+ Self::is_webgl2_enabled(cx, global)
+ }
+}
diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs
index d008c76f905..812499b3785 100644
--- a/components/script/dom/windowproxy.rs
+++ b/components/script/dom/windowproxy.rs
@@ -852,7 +852,7 @@ unsafe fn GetSubframeWindowProxy(
proxy: RawHandleObject,
id: RawHandleId,
) -> Option<(DomRoot<WindowProxy>, u32)> {
- let index = get_array_index_from_id(cx, Handle::from_raw(id));
+ let index = get_array_index_from_id(Handle::from_raw(id));
if let Some(index) = index {
let mut slot = UndefinedValue();
GetProxyPrivate(*proxy, &mut slot);
@@ -934,7 +934,7 @@ unsafe extern "C" fn defineProperty(
desc: RawHandle<PropertyDescriptor>,
res: *mut ObjectOpResult,
) -> bool {
- if get_array_index_from_id(cx, Handle::from_raw(id)).is_some() {
+ if get_array_index_from_id(Handle::from_raw(id)).is_some() {
// Spec says to Reject whether this is a supported index or not,
// since we have no indexed setter or indexed creator. That means
// throwing in strict mode (FIXME: Bug 828137), doing nothing in
@@ -1003,7 +1003,7 @@ unsafe extern "C" fn set(
receiver: RawHandleValue,
res: *mut ObjectOpResult,
) -> bool {
- if get_array_index_from_id(cx, Handle::from_raw(id)).is_some() {
+ if get_array_index_from_id(Handle::from_raw(id)).is_some() {
// Reject (which means throw if and only if strict) the set.
(*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t;
return true;
diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs
index a0ce6f1ca4a..9427784342e 100644
--- a/components/script/webdriver_handlers.rs
+++ b/components/script/webdriver_handlers.rs
@@ -224,7 +224,7 @@ pub(crate) unsafe fn jsval_to_webdriver(
});
let _ac = JSAutoRealm::new(cx, *object);
- if is_array_like(cx, val) || is_arguments_object(cx, val) {
+ if is_array_like::<crate::DomTypeHolder>(cx, val) || is_arguments_object(cx, val) {
let mut result: Vec<WebDriverJSValue> = Vec::new();
let length = match get_property::<u32>(
diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf
index 949b6360537..3f356d5dff0 100644
--- a/components/script_bindings/codegen/Bindings.conf
+++ b/components/script_bindings/codegen/Bindings.conf
@@ -137,7 +137,7 @@ DOMInterfaces = {
},
'Document': {
- 'additionalTraits': ["crate::dom::document::DocumentHelpers<Self>"],
+ 'additionalTraits': ["script_bindings::interfaces::DocumentHelpers"],
'canGc': ['Close', 'CreateElement', 'CreateElementNS', 'ImportNode', 'SetTitle', 'Write', 'Writeln', 'CreateEvent', 'CreateRange', 'Open', 'Open_', 'CreateComment', 'CreateAttribute', 'CreateAttributeNS', 'CreateDocumentFragment', 'CreateTextNode', 'CreateCDATASection', 'CreateProcessingInstruction', 'Prepend', 'Append', 'ReplaceChildren', 'SetBgColor', 'SetFgColor', 'Fonts', 'ElementFromPoint', 'ElementsFromPoint', 'ExitFullscreen', 'CreateExpression', 'CreateNSResolver', 'Evaluate'],
},
@@ -527,6 +527,7 @@ DOMInterfaces = {
'ServoInternals': {
'inRealms': ['ReportMemory'],
'canGc': ['ReportMemory'],
+ 'additionalTraits': ['script_bindings::interfaces::ServoInternalsHelpers'],
},
'ShadowRoot': {
@@ -550,6 +551,7 @@ DOMInterfaces = {
'TestBinding': {
'inRealms': ['PromiseAttribute', 'PromiseNativeHandler'],
'canGc': ['InterfaceAttribute', 'GetInterfaceAttributeNullable', 'ReceiveInterface', 'ReceiveInterfaceSequence', 'ReceiveNullableInterface', 'PromiseAttribute', 'PromiseNativeHandler', 'PromiseResolveNative', 'PromiseRejectNative', 'PromiseRejectWithTypeError'],
+ 'additionalTraits': ['script_bindings::interfaces::TestBindingHelpers'],
},
'TestWorklet': {
@@ -580,6 +582,7 @@ DOMInterfaces = {
'WebGL2RenderingContext': {
'canGc': ['MakeXRCompatible'],
+ 'additionalTraits': ['script_bindings::interfaces::WebGL2RenderingContextHelpers'],
},
'Window': {
diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py
index 1f18ab2c894..783590aad70 100644
--- a/components/script_bindings/codegen/CodegenRust.py
+++ b/components/script_bindings/codegen/CodegenRust.py
@@ -498,7 +498,7 @@ class CGMethodCall(CGThing):
# XXXbz Now we're supposed to check for distinguishingArg being
# an array or a platform object that supports indexed
# properties... skip that last for now. It's a bit of a pain.
- pickFirstSignature(f"{distinguishingArg}.get().is_object() && is_array_like(*cx, {distinguishingArg})",
+ pickFirstSignature(f"{distinguishingArg}.get().is_object() && is_array_like::<D>(*cx, {distinguishingArg})",
lambda s:
(s[1][distinguishingIndex].type.isSequence()
or s[1][distinguishingIndex].type.isObject()))
@@ -1565,10 +1565,10 @@ def MemberCondition(pref, func, exposed, secure):
if pref:
conditions.append(f'Condition::Pref("{pref}")')
if func:
- conditions.append(f'Condition::Func({func})')
+ conditions.append(f'Condition::Func(D::{func})')
if exposed:
conditions.extend([
- f"Condition::Exposed(InterfaceObjectMap::Globals::{camel_to_upper_snake(i)})" for i in exposed
+ f"Condition::Exposed(Globals::{camel_to_upper_snake(i)})" for i in exposed
])
if len(conditions) == 0:
conditions.append("Condition::Satisfied")
@@ -2369,7 +2369,7 @@ DOMClass {{
depth: {descriptor.prototypeDepth},
type_id: {DOMClassTypeId(descriptor)},
malloc_size_of: {mallocSizeOf} as unsafe fn(&mut _, _) -> _,
- global: InterfaceObjectMap::Globals::{globals_},
+ global: Globals::{globals_},
}}"""
@@ -2962,14 +2962,15 @@ class CGConstructorEnabled(CGAbstractMethod):
CGAbstractMethod.__init__(self, descriptor,
'ConstructorEnabled', 'bool',
[Argument("SafeJSContext", "aCx"),
- Argument("HandleObject", "aObj")])
+ Argument("HandleObject", "aObj")],
+ templateArgs=['D: DomTypes'])
def definition_body(self):
conditions = []
iface = self.descriptor.interface
bits = " | ".join(sorted(
- f"InterfaceObjectMap::Globals::{camel_to_upper_snake(i)}" for i in iface.exposureSet
+ f"Globals::{camel_to_upper_snake(i)}" for i in iface.exposureSet
))
conditions.append(f"is_exposed_in(aObj, {bits})")
@@ -2981,14 +2982,14 @@ class CGConstructorEnabled(CGAbstractMethod):
func = iface.getExtendedAttribute("Func")
if func:
assert isinstance(func, list) and len(func) == 1
- conditions.append(f"{func[0]}(aCx, aObj)")
+ conditions.append(f"D::{func[0]}(aCx, aObj)")
secure = iface.getExtendedAttribute("SecureContext")
if secure:
conditions.append("""
unsafe {
let in_realm_proof = AlreadyInRealm::assert_for_cx(aCx);
- GlobalScope::from_context(*aCx, InRealm::Already(&in_realm_proof)).is_secure_context()
+ D::GlobalScope::from_context(*aCx, InRealm::Already(&in_realm_proof)).is_secure_context()
}
""")
@@ -3004,8 +3005,8 @@ def InitLegacyUnforgeablePropertiesOnHolder(descriptor, properties):
"""
unforgeables = []
- defineLegacyUnforgeableAttrs = "define_guarded_properties(cx, unforgeable_holder.handle(), %s, global);"
- defineLegacyUnforgeableMethods = "define_guarded_methods(cx, unforgeable_holder.handle(), %s, global);"
+ defineLegacyUnforgeableAttrs = "define_guarded_properties::<D>(cx, unforgeable_holder.handle(), %s, global);"
+ defineLegacyUnforgeableMethods = "define_guarded_methods::<D>(cx, unforgeable_holder.handle(), %s, global);"
unforgeableMembers = [
(defineLegacyUnforgeableAttrs, properties.unforgeable_attrs),
@@ -3175,7 +3176,7 @@ class CGWrapGlobalMethod(CGAbstractMethod):
("define_guarded_methods", self.properties.methods),
("define_guarded_constants", self.properties.consts)
]
- members = [f"{function}(cx, obj.handle(), {array.variableName()}.get(), obj.handle());"
+ members = [f"{function}::<D>(cx, obj.handle(), {array.variableName()}.get(), obj.handle());"
for (function, array) in pairs if array.length() > 0]
membersStr = "\n".join(members)
@@ -3413,7 +3414,7 @@ let global = incumbent_global.reflector().get_jsobject();\n"""
"""
let conditions = ${conditions};
let is_satisfied = conditions.iter().any(|c|
- c.is_satisfied(
+ c.is_satisfied::<D>(
SafeJSContext::from_ptr(cx),
HandleObject::from_raw(obj),
global));
@@ -3469,7 +3470,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
rooted!(in(*cx) let proto = {proto});
assert!(!proto.is_null());
rooted!(in(*cx) let mut namespace = ptr::null_mut::<JSObject>());
-create_namespace_object(cx, global, proto.handle(), &NAMESPACE_OBJECT_CLASS,
+create_namespace_object::<D>(cx, global, proto.handle(), &NAMESPACE_OBJECT_CLASS,
{methods}, {constants}, {str_to_cstr(name)}, namespace.handle_mut());
assert!(!namespace.is_null());
assert!((*cache)[PrototypeList::Constructor::{id} as usize].is_null());
@@ -3483,7 +3484,7 @@ assert!((*cache)[PrototypeList::Constructor::{id} as usize].is_null());
cName = str_to_cstr(name)
return CGGeneric(f"""
rooted!(in(*cx) let mut interface = ptr::null_mut::<JSObject>());
-create_callback_interface_object(cx, global, sConstants.get(), {cName}, interface.handle_mut());
+create_callback_interface_object::<D>(cx, global, sConstants.get(), {cName}, interface.handle_mut());
assert!(!interface.is_null());
assert!((*cache)[PrototypeList::Constructor::{name} as usize].is_null());
(*cache)[PrototypeList::Constructor::{name} as usize] = interface.get();
@@ -3544,7 +3545,7 @@ assert!(!prototype_proto.is_null());"""))
code.append(CGGeneric(f"""
rooted!(in(*cx) let mut prototype = ptr::null_mut::<JSObject>());
-create_interface_prototype_object(cx,
+create_interface_prototype_object::<D>(cx,
global,
prototype_proto.handle(),
&PrototypeClass,
@@ -3579,7 +3580,7 @@ assert!((*cache)[PrototypeList::ID::{proto_properties['id']} as usize].is_null()
assert!(!interface_proto.is_null());
rooted!(in(*cx) let mut interface = ptr::null_mut::<JSObject>());
-create_noncallback_interface_object(cx,
+create_noncallback_interface_object::<D>(cx,
global,
interface_proto.handle(),
INTERFACE_OBJECT_CLASS.get(),
@@ -3870,7 +3871,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
return CGGeneric(
"define_dom_interface"
f"(cx, global, ProtoOrIfaceIndex::{self.variant}({self.id}),"
- "CreateInterfaceObjects::<D>, ConstructorEnabled)"
+ "CreateInterfaceObjects::<D>, ConstructorEnabled::<D>)"
)
@@ -5927,7 +5928,7 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
""")
if indexedGetter:
- get += "let index = get_array_index_from_id(*cx, Handle::from_raw(id));\n"
+ get += "let index = get_array_index_from_id(Handle::from_raw(id));\n"
attrs = "JSPROP_ENUMERATE"
if self.descriptor.operations['IndexedSetter'] is None:
@@ -6042,7 +6043,7 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
indexedSetter = self.descriptor.operations['IndexedSetter']
if indexedSetter:
- set += ("let index = get_array_index_from_id(*cx, Handle::from_raw(id));\n"
+ set += ("let index = get_array_index_from_id(Handle::from_raw(id));\n"
"if let Some(index) = index {\n"
" let this = UnwrapProxy::<D>(proxy);\n"
" let this = &*this;\n"
@@ -6050,7 +6051,7 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
" return (*opresult).succeed();\n"
"}\n")
elif self.descriptor.operations['IndexedGetter']:
- set += ("if get_array_index_from_id(*cx, Handle::from_raw(id)).is_some() {\n"
+ set += ("if get_array_index_from_id(Handle::from_raw(id)).is_some() {\n"
" return (*opresult).failNoIndexedSetter();\n"
"}\n")
@@ -6265,7 +6266,7 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
""")
if indexedGetter:
- indexed += ("let index = get_array_index_from_id(*cx, Handle::from_raw(id));\n"
+ indexed += ("let index = get_array_index_from_id(Handle::from_raw(id));\n"
"if let Some(index) = index {\n"
" let this = UnwrapProxy::<D>(proxy);\n"
" let this = &*this;\n"
@@ -6357,7 +6358,7 @@ if !expando.is_null() {
indexedGetter = self.descriptor.operations['IndexedGetter']
if indexedGetter:
- getIndexedOrExpando = ("let index = get_array_index_from_id(*cx, id_lt);\n"
+ getIndexedOrExpando = ("let index = get_array_index_from_id(id_lt);\n"
"if let Some(index) = index {\n"
" let this = UnwrapProxy::<D>(proxy);\n"
" let this = &*this;\n"
diff --git a/components/script_bindings/conversions.rs b/components/script_bindings/conversions.rs
index 6e0ce7adee0..847f83fc606 100644
--- a/components/script_bindings/conversions.rs
+++ b/components/script_bindings/conversions.rs
@@ -21,8 +21,10 @@ use js::rust::{
HandleId, HandleValue, MutableHandleValue, ToString, get_object_class, is_dom_class,
is_dom_object, maybe_wrap_value,
};
+use num_traits::Float;
use crate::inheritance::Castable;
+use crate::num::Finite;
use crate::reflector::{DomObject, Reflector};
use crate::root::DomRoot;
use crate::str::{ByteString, DOMString, USVString};
@@ -408,3 +410,87 @@ pub unsafe fn jsid_to_string(cx: *mut JSContext, id: HandleId) -> Option<DOMStri
None
}
+
+impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
+ let value = **self;
+ value.to_jsval(cx, rval);
+ }
+}
+
+impl<T: Float + FromJSValConvertible<Config = ()>> FromJSValConvertible for Finite<T> {
+ type Config = ();
+
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ value: HandleValue,
+ option: (),
+ ) -> Result<ConversionResult<Finite<T>>, ()> {
+ let result = match FromJSValConvertible::from_jsval(cx, value, option)? {
+ ConversionResult::Success(v) => v,
+ ConversionResult::Failure(error) => {
+ // FIXME(emilio): Why throwing instead of propagating the error?
+ throw_type_error(cx, &error);
+ return Err(());
+ },
+ };
+ match Finite::new(result) {
+ Some(v) => Ok(ConversionResult::Success(v)),
+ None => {
+ throw_type_error(cx, "this argument is not a finite floating-point value");
+ Err(())
+ },
+ }
+ }
+}
+
+/// Get a `*const libc::c_void` for the given DOM object, unless it is a DOM
+/// wrapper, and checking if the object is of the correct type.
+///
+/// Returns Err(()) if `obj` is a wrapper or if the object is not an object
+/// for a DOM object of the given type (as defined by the proto_id and proto_depth).
+#[inline]
+#[allow(clippy::result_unit_err)]
+unsafe fn private_from_proto_check_static(
+ obj: *mut JSObject,
+ proto_check: fn(&'static DOMClass) -> bool,
+) -> Result<*const libc::c_void, ()> {
+ let dom_class = get_dom_class(obj).map_err(|_| ())?;
+ if proto_check(dom_class) {
+ trace!("good prototype");
+ Ok(private_from_object(obj))
+ } else {
+ trace!("bad prototype");
+ Err(())
+ }
+}
+
+/// Get a `*const T` for a DOM object accessible from a `JSObject`, where the DOM object
+/// is guaranteed not to be a wrapper.
+///
+/// # Safety
+/// `obj` must point to a valid, non-null JSObject.
+#[allow(clippy::result_unit_err)]
+pub unsafe fn native_from_object_static<T>(obj: *mut JSObject) -> Result<*const T, ()>
+where
+ T: DomObject + IDLInterface,
+{
+ private_from_proto_check_static(obj, T::derives).map(|ptr| ptr as *const T)
+}
+
+/// Get a `*const T` for a DOM object accessible from a `HandleValue`.
+/// Caller is responsible for throwing a JS exception if needed in case of error.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+#[allow(clippy::result_unit_err)]
+pub unsafe fn native_from_handlevalue<T>(v: HandleValue, cx: *mut JSContext) -> Result<*const T, ()>
+where
+ T: DomObject + IDLInterface,
+{
+ if !v.get().is_object() {
+ return Err(());
+ }
+ native_from_object(v.get().to_object(), cx)
+}
diff --git a/components/script_bindings/error.rs b/components/script_bindings/error.rs
index bd8a5b0cb95..031eb41ee22 100644
--- a/components/script_bindings/error.rs
+++ b/components/script_bindings/error.rs
@@ -2,6 +2,12 @@
* 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 js::error::throw_type_error;
+use js::jsapi::JS_IsExceptionPending;
+
+use crate::codegen::PrototypeList::proto_id_to_name;
+use crate::script_runtime::JSContext as SafeJSContext;
+
/// DOM exceptions that can be thrown by a native DOM method.
#[derive(Clone, Debug, MallocSizeOf)]
pub enum Error {
@@ -69,3 +75,20 @@ pub type Fallible<T> = Result<T, Error>;
/// The return type for IDL operations that can throw DOM exceptions and
/// return `()`.
pub type ErrorResult = Fallible<()>;
+
+/// Throw an exception to signal that a `JSObject` can not be converted to a
+/// given DOM type.
+pub fn throw_invalid_this(cx: SafeJSContext, proto_id: u16) {
+ debug_assert!(unsafe { !JS_IsExceptionPending(*cx) });
+ let error = format!(
+ "\"this\" object does not implement interface {}.",
+ proto_id_to_name(proto_id)
+ );
+ unsafe { throw_type_error(*cx, &error) };
+}
+
+pub fn throw_constructor_without_new(cx: SafeJSContext, name: &str) {
+ debug_assert!(unsafe { !JS_IsExceptionPending(*cx) });
+ let error = format!("{} constructor: 'new' is required", name);
+ unsafe { throw_type_error(*cx, &error) };
+}
diff --git a/components/script/dom/bindings/finalize.rs b/components/script_bindings/finalize.rs
index 03d4950e1c7..fa1079b5624 100644
--- a/components/script/dom/bindings/finalize.rs
+++ b/components/script_bindings/finalize.rs
@@ -5,16 +5,32 @@
//! Generic finalizer implementations for DOM binding implementations.
use std::any::type_name;
-use std::mem;
+use std::{mem, ptr};
use js::glue::JS_GetReservedSlot;
use js::jsapi::JSObject;
use js::jsval::UndefinedValue;
+use js::rust::GCMethods;
-use crate::dom::bindings::utils::finalize_global as do_finalize_global;
-use crate::dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable};
+use crate::codegen::PrototypeList::PROTO_OR_IFACE_LENGTH;
+use crate::utils::{ProtoOrIfaceArray, get_proto_or_iface_array};
+use crate::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable};
-pub(crate) unsafe fn finalize_common<T>(this: *const T) {
+/// Drop the resources held by reserved slots of a global object
+unsafe fn do_finalize_global(obj: *mut JSObject) {
+ let protolist = get_proto_or_iface_array(obj);
+ let list = (*protolist).as_mut_ptr();
+ for idx in 0..PROTO_OR_IFACE_LENGTH as isize {
+ let entry = list.offset(idx);
+ let value = *entry;
+ <*mut JSObject>::post_barrier(entry, value, ptr::null_mut());
+ }
+ let _: Box<ProtoOrIfaceArray> = Box::from_raw(protolist);
+}
+
+/// # Safety
+/// `this` must point to a valid, non-null instance of T.
+pub unsafe fn finalize_common<T>(this: *const T) {
if !this.is_null() {
// The pointer can be null if the object is the unforgeable holder of that interface.
let _ = Box::from_raw(this as *mut T);
@@ -22,12 +38,18 @@ pub(crate) unsafe fn finalize_common<T>(this: *const T) {
debug!("{} finalize: {:p}", type_name::<T>(), this);
}
-pub(crate) unsafe fn finalize_global<T>(obj: *mut JSObject, this: *const T) {
+/// # Safety
+/// `obj` must point to a valid, non-null JS object.
+/// `this` must point to a valid, non-null instance of T.
+pub unsafe fn finalize_global<T>(obj: *mut JSObject, this: *const T) {
do_finalize_global(obj);
finalize_common::<T>(this);
}
-pub(crate) unsafe fn finalize_weak_referenceable<T: WeakReferenceable>(
+/// # Safety
+/// `obj` must point to a valid, non-null JS object.
+/// `this` must point to a valid, non-null instance of T.
+pub unsafe fn finalize_weak_referenceable<T: WeakReferenceable>(
obj: *mut JSObject,
this: *const T,
) {
diff --git a/components/script_bindings/interfaces.rs b/components/script_bindings/interfaces.rs
new file mode 100644
index 00000000000..2e7f6017e75
--- /dev/null
+++ b/components/script_bindings/interfaces.rs
@@ -0,0 +1,24 @@
+/* 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/. */
+
+use js::rust::HandleObject;
+
+use crate::script_runtime::JSContext;
+
+pub trait DocumentHelpers {
+ fn ensure_safe_to_run_script_or_layout(&self);
+}
+
+pub trait ServoInternalsHelpers {
+ fn is_servo_internal(cx: JSContext, global: HandleObject) -> bool;
+}
+
+pub trait TestBindingHelpers {
+ fn condition_satisfied(cx: JSContext, global: HandleObject) -> bool;
+ fn condition_unsatisfied(cx: JSContext, global: HandleObject) -> bool;
+}
+
+pub trait WebGL2RenderingContextHelpers {
+ fn is_webgl2_enabled(cx: JSContext, global: HandleObject) -> bool;
+}
diff --git a/components/script_bindings/lib.rs b/components/script_bindings/lib.rs
index e857a2289f8..17dc827b872 100644
--- a/components/script_bindings/lib.rs
+++ b/components/script_bindings/lib.rs
@@ -21,9 +21,13 @@ pub mod callback;
pub mod constant;
pub mod conversions;
pub mod error;
+pub mod finalize;
pub mod inheritance;
+pub mod interfaces;
pub mod iterable;
pub mod like;
+pub mod lock;
+pub mod num;
pub mod record;
pub mod reflector;
pub mod root;
diff --git a/components/script_bindings/lock.rs b/components/script_bindings/lock.rs
new file mode 100644
index 00000000000..e8cde2e4087
--- /dev/null
+++ b/components/script_bindings/lock.rs
@@ -0,0 +1,37 @@
+/* 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/. */
+
+use std::sync::OnceLock;
+
+/// A OnceLock wrapping a type that is not considered threadsafe by the Rust compiler, but
+/// will be used in a threadsafe manner (it will not be mutated, after being initialized).
+///
+/// This is needed to allow using JS API types (which usually involve raw pointers) in static initializers,
+/// when Servo guarantees through the use of OnceLock that only one thread will ever initialize
+/// the value.
+pub struct ThreadUnsafeOnceLock<T>(OnceLock<T>);
+
+impl<T> ThreadUnsafeOnceLock<T> {
+ #[allow(clippy::new_without_default)]
+ pub const fn new() -> Self {
+ Self(OnceLock::new())
+ }
+
+ /// Initialize the value inside this lock. Panics if the lock has been previously initialized.
+ pub fn set(&self, val: T) {
+ assert!(self.0.set(val).is_ok());
+ }
+
+ /// Get a reference to the value inside this lock. Panics if the lock has not been initialized.
+ ///
+ /// # Safety
+ /// The caller must ensure that it does not mutate value contained inside this lock
+ /// (using interior mutability).
+ pub unsafe fn get(&self) -> &T {
+ self.0.get().unwrap()
+ }
+}
+
+unsafe impl<T> Sync for ThreadUnsafeOnceLock<T> {}
+unsafe impl<T> Send for ThreadUnsafeOnceLock<T> {}
diff --git a/components/script/dom/bindings/num.rs b/components/script_bindings/num.rs
index 59b89a29e3e..58b12f6cb7f 100644
--- a/components/script/dom/bindings/num.rs
+++ b/components/script_bindings/num.rs
@@ -12,11 +12,11 @@ use num_traits::Float;
/// Encapsulates the IDL restricted float type.
#[derive(Clone, Copy, Eq, JSTraceable, PartialEq)]
-pub(crate) struct Finite<T: Float>(T);
+pub struct Finite<T: Float>(T);
impl<T: Float> Finite<T> {
/// Create a new `Finite<T: Float>` safely.
- pub(crate) fn new(value: T) -> Option<Finite<T>> {
+ pub fn new(value: T) -> Option<Finite<T>> {
if value.is_finite() {
Some(Finite(value))
} else {
@@ -26,7 +26,7 @@ impl<T: Float> Finite<T> {
/// Create a new `Finite<T: Float>`.
#[inline]
- pub(crate) fn wrap(value: T) -> Finite<T> {
+ pub fn wrap(value: T) -> Finite<T> {
assert!(
value.is_finite(),
"Finite<T> doesn't encapsulate unrestricted value."
diff --git a/components/script_bindings/utils.rs b/components/script_bindings/utils.rs
index fd307fd5ab3..2317f80dbfa 100644
--- a/components/script_bindings/utils.rs
+++ b/components/script_bindings/utils.rs
@@ -2,13 +2,40 @@
* 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::ffi::CString;
use std::os::raw::c_void;
+use std::ptr::{self, NonNull};
+use js::conversions::ToJSValConvertible;
+use js::glue::{
+ CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, JS_GetReservedSlot,
+ RUST_FUNCTION_VALUE_TO_JITINFO,
+};
+use js::jsapi::{
+ AtomToLinearString, CallArgs, ExceptionStackBehavior, GetLinearStringCharAt,
+ GetLinearStringLength, GetNonCCWObjectGlobal, HandleObject as RawHandleObject,
+ JS_ClearPendingException, JS_IsExceptionPending, JSAtom, JSContext, JSJitInfo, JSObject,
+ MutableHandleValue as RawMutableHandleValue, ObjectOpResult, StringIsArrayIndex,
+};
+use js::jsval::{JSVal, UndefinedValue};
+use js::rust::wrappers::{
+ CallOriginalPromiseReject, JS_DeletePropertyById, JS_ForwardGetPropertyTo,
+ JS_GetPendingException, JS_GetProperty, JS_GetPrototype, JS_HasProperty, JS_HasPropertyById,
+ JS_SetPendingException, JS_SetProperty,
+};
+use js::rust::{
+ HandleId, HandleObject, HandleValue, MutableHandleValue, ToString, get_object_class,
+};
+use js::{JS_CALLEE, rooted};
use malloc_size_of::MallocSizeOfOps;
use crate::codegen::Globals::Globals;
use crate::codegen::InheritTypes::TopTypeId;
-use crate::codegen::PrototypeList::{self, MAX_PROTO_CHAIN_LENGTH};
+use crate::codegen::PrototypeList::{self, MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH};
+use crate::conversions::{PrototypeCheck, jsstring_to_str, private_from_proto_check};
+use crate::error::throw_invalid_this;
+use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
+use crate::str::DOMString;
/// The struct that holds inheritance information for DOM object reflectors.
#[derive(Clone, Copy)]
@@ -46,3 +73,456 @@ impl Clone for DOMJSClass {
}
}
unsafe impl Sync for DOMJSClass {}
+
+/// The index of the slot where the object holder of that interface's
+/// unforgeable members are defined.
+pub const DOM_PROTO_UNFORGEABLE_HOLDER_SLOT: u32 = 0;
+
+/// The index of the slot that contains a reference to the ProtoOrIfaceArray.
+// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
+pub const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT;
+
+/// The flag set on the `JSClass`es for DOM global objects.
+// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
+// LSetDOMProperty. Those constants need to be changed accordingly if this value
+// changes.
+pub const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1;
+
+/// Returns the ProtoOrIfaceArray for the given global object.
+/// Fails if `global` is not a DOM global object.
+///
+/// # Safety
+/// `global` must point to a valid, non-null JS object.
+pub unsafe fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {
+ assert_ne!(((*get_object_class(global)).flags & JSCLASS_DOM_GLOBAL), 0);
+ let mut slot = UndefinedValue();
+ JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT, &mut slot);
+ slot.to_private() as *mut ProtoOrIfaceArray
+}
+
+/// An array of *mut JSObject of size PROTO_OR_IFACE_LENGTH.
+pub type ProtoOrIfaceArray = [*mut JSObject; PROTO_OR_IFACE_LENGTH];
+
+/// Gets the property `id` on `proxy`'s prototype. If it exists, `*found` is
+/// set to true and `*vp` to the value, otherwise `*found` is set to false.
+///
+/// Returns false on JSAPI failure.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+/// `found` must point to a valid, non-null bool.
+pub unsafe fn get_property_on_prototype(
+ cx: *mut JSContext,
+ proxy: HandleObject,
+ receiver: HandleValue,
+ id: HandleId,
+ found: *mut bool,
+ vp: MutableHandleValue,
+) -> bool {
+ rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
+ if !JS_GetPrototype(cx, proxy, proto.handle_mut()) || proto.is_null() {
+ *found = false;
+ return true;
+ }
+ let mut has_property = false;
+ if !JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) {
+ return false;
+ }
+ *found = has_property;
+ if !has_property {
+ return true;
+ }
+
+ JS_ForwardGetPropertyTo(cx, proto.handle(), id, receiver, vp)
+}
+
+/// Get an array index from the given `jsid`. Returns `None` if the given
+/// `jsid` is not an integer.
+pub fn get_array_index_from_id(id: HandleId) -> Option<u32> {
+ let raw_id = *id;
+ if raw_id.is_int() {
+ return Some(raw_id.to_int() as u32);
+ }
+
+ if raw_id.is_void() || !raw_id.is_string() {
+ return None;
+ }
+
+ unsafe {
+ let atom = raw_id.to_string() as *mut JSAtom;
+ let s = AtomToLinearString(atom);
+ if GetLinearStringLength(s) == 0 {
+ return None;
+ }
+
+ let chars = [GetLinearStringCharAt(s, 0)];
+ let first_char = char::decode_utf16(chars.iter().cloned())
+ .next()
+ .map_or('\0', |r| r.unwrap_or('\0'));
+ if first_char.is_ascii_lowercase() {
+ return None;
+ }
+
+ let mut i = 0;
+ if StringIsArrayIndex(s, &mut i) {
+ Some(i)
+ } else {
+ None
+ }
+ }
+
+ /*let s = jsstr_to_string(cx, RUST_JSID_TO_STRING(raw_id));
+ if s.len() == 0 {
+ return None;
+ }
+
+ let first = s.chars().next().unwrap();
+ if first.is_ascii_lowercase() {
+ return None;
+ }
+
+ let mut i: u32 = 0;
+ let is_array = if s.is_ascii() {
+ let chars = s.as_bytes();
+ StringIsArrayIndex1(chars.as_ptr() as *const _, chars.len() as u32, &mut i)
+ } else {
+ let chars = s.encode_utf16().collect::<Vec<u16>>();
+ let slice = chars.as_slice();
+ StringIsArrayIndex2(slice.as_ptr(), chars.len() as u32, &mut i)
+ };
+
+ if is_array {
+ Some(i)
+ } else {
+ None
+ }*/
+}
+
+/// Find the enum equivelent of a string given by `v` in `pairs`.
+/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
+/// `Ok((None, value))` if there was no matching string.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+#[allow(clippy::result_unit_err)]
+pub unsafe fn find_enum_value<'a, T>(
+ cx: *mut JSContext,
+ v: HandleValue,
+ pairs: &'a [(&'static str, T)],
+) -> Result<(Option<&'a T>, DOMString), ()> {
+ match ptr::NonNull::new(ToString(cx, v)) {
+ Some(jsstr) => {
+ let search = jsstring_to_str(cx, jsstr);
+ Ok((
+ pairs
+ .iter()
+ .find(|&&(key, _)| search == *key)
+ .map(|(_, ev)| ev),
+ search,
+ ))
+ },
+ None => Err(()),
+ }
+}
+
+/// Get the property with name `property` from `object`.
+/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
+/// `Ok(false)` if there was no property with the given name.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+#[allow(clippy::result_unit_err)]
+pub unsafe fn get_dictionary_property(
+ cx: *mut JSContext,
+ object: HandleObject,
+ property: &str,
+ rval: MutableHandleValue,
+ _can_gc: CanGc,
+) -> Result<bool, ()> {
+ unsafe fn has_property(
+ cx: *mut JSContext,
+ object: HandleObject,
+ property: &CString,
+ found: &mut bool,
+ ) -> bool {
+ JS_HasProperty(cx, object, property.as_ptr(), found)
+ }
+ unsafe fn get_property(
+ cx: *mut JSContext,
+ object: HandleObject,
+ property: &CString,
+ value: MutableHandleValue,
+ ) -> bool {
+ JS_GetProperty(cx, object, property.as_ptr(), value)
+ }
+
+ let property = CString::new(property).unwrap();
+ if object.get().is_null() {
+ return Ok(false);
+ }
+
+ let mut found = false;
+ if !has_property(cx, object, &property, &mut found) {
+ return Err(());
+ }
+
+ if !found {
+ return Ok(false);
+ }
+
+ if !get_property(cx, object, &property, rval) {
+ return Err(());
+ }
+
+ Ok(true)
+}
+
+/// Set the property with name `property` from `object`.
+/// Returns `Err(())` on JSAPI failure, or null object,
+/// and Ok(()) otherwise
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+#[allow(clippy::result_unit_err)]
+pub unsafe fn set_dictionary_property(
+ cx: *mut JSContext,
+ object: HandleObject,
+ property: &str,
+ value: HandleValue,
+) -> Result<(), ()> {
+ if object.get().is_null() {
+ return Err(());
+ }
+
+ let property = CString::new(property).unwrap();
+ if !JS_SetProperty(cx, object, property.as_ptr(), value) {
+ return Err(());
+ }
+
+ Ok(())
+}
+
+/// Returns whether `proxy` has a property `id` on its prototype.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+pub unsafe fn has_property_on_prototype(
+ cx: *mut JSContext,
+ proxy: HandleObject,
+ id: HandleId,
+ found: &mut bool,
+) -> bool {
+ rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
+ if !JS_GetPrototype(cx, proxy, proto.handle_mut()) {
+ return false;
+ }
+ assert!(!proto.is_null());
+ JS_HasPropertyById(cx, proto.handle(), id, found)
+}
+
+/// Deletes the property `id` from `object`.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+pub unsafe fn delete_property_by_id(
+ cx: *mut JSContext,
+ object: HandleObject,
+ id: HandleId,
+ bp: *mut ObjectOpResult,
+) -> bool {
+ JS_DeletePropertyById(cx, object, id, bp)
+}
+
+unsafe fn generic_call<const EXCEPTION_TO_REJECTION: bool>(
+ cx: *mut JSContext,
+ argc: libc::c_uint,
+ vp: *mut JSVal,
+ is_lenient: bool,
+ call: unsafe extern "C" fn(
+ *const JSJitInfo,
+ *mut JSContext,
+ RawHandleObject,
+ *mut libc::c_void,
+ u32,
+ *mut JSVal,
+ ) -> bool,
+ can_gc: CanGc,
+) -> bool {
+ let args = CallArgs::from_vp(vp, argc);
+
+ let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
+ let proto_id = (*info).__bindgen_anon_2.protoID;
+ let cx = SafeJSContext::from_ptr(cx);
+
+ let thisobj = args.thisv();
+ if !thisobj.get().is_null_or_undefined() && !thisobj.get().is_object() {
+ throw_invalid_this(cx, proto_id);
+ return if EXCEPTION_TO_REJECTION {
+ exception_to_promise(*cx, args.rval(), can_gc)
+ } else {
+ false
+ };
+ }
+
+ rooted!(in(*cx) let obj = if thisobj.get().is_object() {
+ thisobj.get().to_object()
+ } else {
+ GetNonCCWObjectGlobal(JS_CALLEE(*cx, vp).to_object_or_null())
+ });
+ let depth = (*info).__bindgen_anon_3.depth as usize;
+ let proto_check = PrototypeCheck::Depth { depth, proto_id };
+ let this = match private_from_proto_check(obj.get(), *cx, proto_check) {
+ Ok(val) => val,
+ Err(()) => {
+ if is_lenient {
+ debug_assert!(!JS_IsExceptionPending(*cx));
+ *vp = UndefinedValue();
+ return true;
+ } else {
+ throw_invalid_this(cx, proto_id);
+ return if EXCEPTION_TO_REJECTION {
+ exception_to_promise(*cx, args.rval(), can_gc)
+ } else {
+ false
+ };
+ }
+ },
+ };
+ call(
+ info,
+ *cx,
+ obj.handle().into(),
+ this as *mut libc::c_void,
+ argc,
+ vp,
+ )
+}
+
+/// Generic method of IDL interface.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+/// `vp` must point to a VALID, non-null JSVal.
+pub unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>(
+ cx: *mut JSContext,
+ argc: libc::c_uint,
+ vp: *mut JSVal,
+) -> bool {
+ generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, false, CallJitMethodOp, CanGc::note())
+}
+
+/// Generic getter of IDL interface.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+/// `vp` must point to a VALID, non-null JSVal.
+pub unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>(
+ cx: *mut JSContext,
+ argc: libc::c_uint,
+ vp: *mut JSVal,
+) -> bool {
+ generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, false, CallJitGetterOp, CanGc::note())
+}
+
+/// Generic lenient getter of IDL interface.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+/// `vp` must point to a VALID, non-null JSVal.
+pub unsafe extern "C" fn generic_lenient_getter<const EXCEPTION_TO_REJECTION: bool>(
+ cx: *mut JSContext,
+ argc: libc::c_uint,
+ vp: *mut JSVal,
+) -> bool {
+ generic_call::<EXCEPTION_TO_REJECTION>(cx, argc, vp, true, CallJitGetterOp, CanGc::note())
+}
+
+unsafe extern "C" fn call_setter(
+ info: *const JSJitInfo,
+ cx: *mut JSContext,
+ handle: RawHandleObject,
+ this: *mut libc::c_void,
+ argc: u32,
+ vp: *mut JSVal,
+) -> bool {
+ if !CallJitSetterOp(info, cx, handle, this, argc, vp) {
+ return false;
+ }
+ *vp = UndefinedValue();
+ true
+}
+
+/// Generic setter of IDL interface.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+/// `vp` must point to a VALID, non-null JSVal.
+pub unsafe extern "C" fn generic_setter(
+ cx: *mut JSContext,
+ argc: libc::c_uint,
+ vp: *mut JSVal,
+) -> bool {
+ generic_call::<false>(cx, argc, vp, false, call_setter, CanGc::note())
+}
+
+/// Generic lenient setter of IDL interface.
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+/// `vp` must point to a VALID, non-null JSVal.
+pub unsafe extern "C" fn generic_lenient_setter(
+ cx: *mut JSContext,
+ argc: libc::c_uint,
+ vp: *mut JSVal,
+) -> bool {
+ generic_call::<false>(cx, argc, vp, true, call_setter, CanGc::note())
+}
+
+/// <https://searchfox.org/mozilla-central/rev/7279a1df13a819be254fd4649e07c4ff93e4bd45/dom/bindings/BindingUtils.cpp#3300>
+/// # Safety
+///
+/// `cx` must point to a valid, non-null JSContext.
+/// `vp` must point to a VALID, non-null JSVal.
+pub unsafe extern "C" fn generic_static_promise_method(
+ cx: *mut JSContext,
+ argc: libc::c_uint,
+ vp: *mut JSVal,
+) -> bool {
+ let args = CallArgs::from_vp(vp, argc);
+
+ let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
+ assert!(!info.is_null());
+ // TODO: we need safe wrappers for this in mozjs!
+ //assert_eq!((*info)._bitfield_1, JSJitInfo_OpType::StaticMethod as u8)
+ let static_fn = (*info).__bindgen_anon_1.staticMethod.unwrap();
+ if static_fn(cx, argc, vp) {
+ return true;
+ }
+ exception_to_promise(cx, args.rval(), CanGc::note())
+}
+
+/// Coverts exception to promise rejection
+///
+/// <https://searchfox.org/mozilla-central/rev/b220e40ff2ee3d10ce68e07d8a8a577d5558e2a2/dom/bindings/BindingUtils.cpp#3315>
+///
+/// # Safety
+/// `cx` must point to a valid, non-null JSContext.
+pub unsafe fn exception_to_promise(
+ cx: *mut JSContext,
+ rval: RawMutableHandleValue,
+ _can_gc: CanGc,
+) -> bool {
+ rooted!(in(cx) let mut exception = UndefinedValue());
+ if !JS_GetPendingException(cx, exception.handle_mut()) {
+ return false;
+ }
+ JS_ClearPendingException(cx);
+ if let Some(promise) = NonNull::new(CallOriginalPromiseReject(cx, exception.handle())) {
+ promise.to_jsval(cx, MutableHandleValue::from_raw(rval));
+ true
+ } else {
+ // We just give up. Put the exception back.
+ JS_SetPendingException(cx, exception.handle(), ExceptionStackBehavior::Capture);
+ false
+ }
+}
diff --git a/components/script_bindings/webidls/ServoInternals.webidl b/components/script_bindings/webidls/ServoInternals.webidl
index af3dc7b35e6..609d49180e4 100644
--- a/components/script_bindings/webidls/ServoInternals.webidl
+++ b/components/script_bindings/webidls/ServoInternals.webidl
@@ -8,12 +8,12 @@
// This interface is entirely internal to Servo, and should not be accessible to
// web pages.
[Exposed=Window,
-Func="dom::bindings::interface::is_servo_internal"]
+Func="ServoInternals::is_servo_internal"]
interface ServoInternals {
Promise<object> reportMemory();
};
partial interface Navigator {
- [Func="dom::bindings::interface::is_servo_internal"]
+ [Func="ServoInternals::is_servo_internal"]
readonly attribute ServoInternals servo;
};