script: Refer to DOM interfaces with generic types in generated bindings. (#35457)

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2025-02-21 06:10:00 -05:00 committed by GitHub
parent 14db055d46
commit 1192ae32b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 627 additions and 167 deletions

View file

@ -397,14 +397,14 @@ pub(crate) unsafe fn call_html_constructor<T: DerivedFrom<Element> + DomObject>(
.is_ok()
}
pub(crate) unsafe fn call_default_constructor(
pub(crate) unsafe fn call_default_constructor<D: crate::DomTypes>(
cx: JSContext,
args: &CallArgs,
global: &GlobalScope,
global: &D::GlobalScope,
proto_id: PrototypeList::ID,
ctor_name: &str,
creator: unsafe fn(JSContext, HandleObject, *mut ProtoOrIfaceArray),
constructor: impl FnOnce(JSContext, &CallArgs, &GlobalScope, HandleObject) -> bool,
constructor: impl FnOnce(JSContext, &CallArgs, &D::GlobalScope, HandleObject) -> bool,
) -> bool {
if !args.is_constructing() {
throw_constructor_without_new(cx, ctor_name);

View file

@ -58,7 +58,6 @@ use crate::dom::htmlcollection::HTMLCollection;
use crate::dom::htmlformcontrolscollection::HTMLFormControlsCollection;
use crate::dom::htmloptionscollection::HTMLOptionsCollection;
use crate::dom::nodelist::NodeList;
use crate::dom::windowproxy::WindowProxy;
impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> {
#[inline]
@ -425,10 +424,10 @@ where
/// Get a `DomRoot<T>` for a WindowProxy accessible from a `HandleValue`.
/// Caller is responsible for throwing a JS exception if needed in case of error.
pub(crate) unsafe fn windowproxy_from_handlevalue(
pub(crate) unsafe fn windowproxy_from_handlevalue<D: crate::DomTypes>(
v: HandleValue,
_cx: *mut JSContext,
) -> Result<DomRoot<WindowProxy>, ()> {
) -> Result<DomRoot<D::WindowProxy>, ()> {
if !v.get().is_object() {
return Err(());
}
@ -438,6 +437,6 @@ pub(crate) unsafe fn windowproxy_from_handlevalue(
}
let mut value = UndefinedValue();
GetProxyReservedSlot(object, 0, &mut value);
let ptr = value.to_private() as *const WindowProxy;
let ptr = value.to_private() as *const D::WindowProxy;
Ok(DomRoot::from_ref(&*ptr))
}

View file

@ -26,7 +26,7 @@ pub(crate) mod base {
ChannelInterpretationValues,
};
pub(crate) use crate::dom::bindings::codegen::DomTypes::DomTypes;
pub(crate) use crate::dom::bindings::codegen::UnionTypes;
pub(crate) use crate::dom::bindings::codegen::{GenericUnionTypes, UnionTypes};
pub(crate) use crate::dom::bindings::conversions::{
root_from_handlevalue, ConversionBehavior, ConversionResult, FromJSValConvertible,
StringificationBehavior, ToJSValConvertible,
@ -35,14 +35,15 @@ pub(crate) mod base {
pub(crate) use crate::dom::bindings::error::{throw_dom_exception, Fallible};
pub(crate) use crate::dom::bindings::num::Finite;
pub(crate) use crate::dom::bindings::proxyhandler::CrossOriginProperties;
pub(crate) use crate::dom::bindings::reflector::{DomGlobal, DomObject};
pub(crate) use crate::dom::bindings::reflector::{DomGlobalGeneric, DomObject};
pub(crate) use crate::dom::bindings::root::DomRoot;
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::{
get_dictionary_property, set_dictionary_property, ThreadUnsafeOnceLock,
get_dictionary_property, set_dictionary_property, DomHelpers, ThreadUnsafeOnceLock,
};
pub(crate) use crate::dom::globalscope::GlobalScope;
pub(crate) use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers};
pub(crate) use crate::dom::promise::PromiseHelpers;
pub(crate) use crate::script_runtime::JSContext as SafeJSContext;
}

View file

@ -7,6 +7,7 @@
//! Implementation of `iterable<...>` and `iterable<..., ...>` WebIDL declarations.
use std::cell::Cell;
use std::marker::PhantomData;
use std::ptr;
use std::ptr::NonNull;
@ -15,18 +16,20 @@ use js::conversions::ToJSValConvertible;
use js::jsapi::{Heap, JSObject};
use js::jsval::UndefinedValue;
use js::rust::{HandleObject, HandleValue, MutableHandleObject};
use script_bindings::conversions::IDLInterface;
use script_bindings::utils::DOMClass;
use crate::dom::bindings::codegen::Bindings::IterableIteratorBinding::{
IterableKeyAndValueResult, IterableKeyOrValueResult,
};
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{
reflect_dom_object, DomGlobal, DomObjectIteratorWrap, DomObjectWrap, Reflector,
reflect_dom_object, DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector,
};
use crate::dom::bindings::root::{Dom, DomRoot, Root};
use crate::dom::bindings::trace::{JSTraceable, RootedTraceableBox};
use crate::dom::globalscope::GlobalScope;
use crate::dom::bindings::trace::{JSTraceable, NoTrace, RootedTraceableBox};
use crate::script_runtime::{CanGc, JSContext};
use crate::DomTypes;
/// The values that an iterator will iterate over.
#[derive(JSTraceable, MallocSizeOf)]
@ -55,14 +58,41 @@ pub(crate) trait Iterable {
/// An iterator over the iterable entries of a given DOM interface.
#[dom_struct]
pub(crate) struct IterableIterator<T: DomObjectIteratorWrap + JSTraceable + Iterable> {
pub(crate) struct IterableIterator<
D: DomTypes,
T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>,
> {
reflector: Reflector,
iterable: Dom<T>,
type_: IteratorType,
index: Cell<u32>,
_marker: NoTrace<PhantomData<D>>,
}
impl<T: DomObjectIteratorWrap + JSTraceable + Iterable> IterableIterator<T> {
impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable> IterableIterator<D, T> {
pub fn global(&self) -> DomRoot<D::GlobalScope> {
<Self as DomGlobalGeneric<D>>::global(self)
}
}
impl<
D: DomTypes,
T: DomObjectIteratorWrap<D>
+ JSTraceable
+ Iterable
+ DomGlobalGeneric<D>
+ IDLInterface
+ IteratorDerives,
> IDLInterface for IterableIterator<D, T>
{
fn derives(class: &'static DOMClass) -> bool {
<T as IteratorDerives>::derives(class)
}
}
impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
IterableIterator<D, T>
{
/// Create a new iterator instance for the provided iterable DOM interface.
pub(crate) fn new(iterable: &T, type_: IteratorType) -> DomRoot<Self> {
let iterator = Box::new(IterableIterator {
@ -70,6 +100,7 @@ impl<T: DomObjectIteratorWrap + JSTraceable + Iterable> IterableIterator<T> {
type_,
iterable: Dom::from_ref(iterable),
index: Cell::new(0),
_marker: NoTrace(PhantomData),
});
reflect_dom_object(iterator, &*iterable.global(), CanGc::note())
}
@ -119,16 +150,26 @@ impl<T: DomObjectIteratorWrap + JSTraceable + Iterable> IterableIterator<T> {
}
}
impl<T: DomObjectIteratorWrap + JSTraceable + Iterable> DomObjectWrap for IterableIterator<T> {
impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
DomObjectWrap<D> for IterableIterator<D, T>
{
const WRAP: unsafe fn(
JSContext,
&GlobalScope,
&D::GlobalScope,
Option<HandleObject>,
Box<Self>,
CanGc,
) -> Root<Dom<Self>> = T::ITER_WRAP;
}
/// A version of the [IDLInterface] trait that is specific to types that have
/// iterators defined for them. This allows the `script` crate to define the
/// derives check for the concrete interface type, while the [IteratableIterator]
/// type defined in this module can be parameterized over an unknown generic.
pub trait IteratorDerives {
fn derives(class: &'static DOMClass) -> bool;
}
fn dict_return(
cx: JSContext,
mut result: MutableHandleObject,

View file

@ -179,9 +179,16 @@ pub(crate) mod codegen {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/DomTypes.rs"));
}
#[allow(dead_code)]
pub(crate) mod Bindings {
pub(crate) mod GenericBindings {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/Bindings/mod.rs"));
}
#[allow(dead_code)]
pub(crate) mod Bindings {
include!(concat!(
env!("BINDINGS_OUT_DIR"),
"/ConcreteBindings/mod.rs"
));
}
pub(crate) mod InterfaceObjectMap {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/InterfaceObjectMap.rs"));
pub(crate) use script_bindings::codegen::Globals::Globals;
@ -206,6 +213,10 @@ pub(crate) mod codegen {
clippy::upper_case_acronyms,
clippy::enum_variant_names
)]
pub(crate) mod GenericUnionTypes {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/GenericUnionTypes.rs"));
}
#[allow(dead_code)]
pub(crate) mod UnionTypes {
include!(concat!(env!("BINDINGS_OUT_DIR"), "/UnionTypes.rs"));
}

View file

@ -10,59 +10,74 @@ use crate::dom::bindings::conversions::DerivedFrom;
use crate::dom::bindings::iterable::{Iterable, IterableIterator};
use crate::dom::bindings::root::{Dom, DomRoot, Root};
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::globalscope::GlobalScope;
use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers};
use crate::realms::AlreadyInRealm;
use crate::script_runtime::{CanGc, JSContext};
use crate::DomTypes;
/// Create the reflector for a new DOM object and yield ownership to the
/// reflector.
pub(crate) fn reflect_dom_object<T, U>(obj: Box<T>, global: &U, can_gc: CanGc) -> DomRoot<T>
pub(crate) fn reflect_dom_object<D, T, U>(obj: Box<T>, global: &U, can_gc: CanGc) -> DomRoot<T>
where
T: DomObject + DomObjectWrap,
U: DerivedFrom<GlobalScope>,
D: DomTypes,
T: DomObject + DomObjectWrap<D>,
U: DerivedFrom<D::GlobalScope>,
{
let global_scope = global.upcast();
unsafe { T::WRAP(GlobalScope::get_cx(), global_scope, None, obj, can_gc) }
unsafe { T::WRAP(D::GlobalScope::get_cx(), global_scope, None, obj, can_gc) }
}
pub(crate) fn reflect_dom_object_with_proto<T, U>(
pub(crate) fn reflect_dom_object_with_proto<D, T, U>(
obj: Box<T>,
global: &U,
proto: Option<HandleObject>,
can_gc: CanGc,
) -> DomRoot<T>
where
T: DomObject + DomObjectWrap,
U: DerivedFrom<GlobalScope>,
D: DomTypes,
T: DomObject + DomObjectWrap<D>,
U: DerivedFrom<D::GlobalScope>,
{
let global_scope = global.upcast();
unsafe { T::WRAP(GlobalScope::get_cx(), global_scope, proto, obj, can_gc) }
unsafe { T::WRAP(D::GlobalScope::get_cx(), global_scope, proto, obj, can_gc) }
}
pub trait DomGlobal: DomObject {
pub(crate) trait DomGlobalGeneric<D: DomTypes>: DomObject {
/// Returns the [`GlobalScope`] of the realm that the [`DomObject`] was created in. If this
/// object is a `Node`, this will be different from it's owning `Document` if adopted by. For
/// `Node`s it's almost always better to use `NodeTraits::owning_global`.
fn global(&self) -> DomRoot<GlobalScope>
fn global(&self) -> DomRoot<D::GlobalScope>
where
Self: Sized,
{
let realm = AlreadyInRealm::assert_for_cx(GlobalScope::get_cx());
GlobalScope::from_reflector(self, &realm)
let realm = AlreadyInRealm::assert_for_cx(D::GlobalScope::get_cx());
D::GlobalScope::from_reflector(self, &realm)
}
}
impl<T: DomObject> DomGlobal for T {}
impl<D: DomTypes, T: DomObject> DomGlobalGeneric<D> for T {}
pub(crate) trait DomGlobal {
fn global(&self) -> DomRoot<GlobalScope>;
}
impl<T: DomGlobalGeneric<crate::DomTypeHolder>> DomGlobal for T {
fn global(&self) -> DomRoot<GlobalScope> {
<Self as DomGlobalGeneric<crate::DomTypeHolder>>::global(self)
}
}
pub(crate) use script_bindings::reflector::{DomObject, MutDomObject, Reflector};
/// A trait to provide a function pointer to wrap function for DOM objects.
pub(crate) trait DomObjectWrap: Sized + DomObject {
pub(crate) trait DomObjectWrap<D: DomTypes>:
Sized + DomObject + DomGlobalGeneric<D>
{
/// Function pointer to the general wrap function type
#[allow(clippy::type_complexity)]
const WRAP: unsafe fn(
JSContext,
&GlobalScope,
&D::GlobalScope,
Option<HandleObject>,
Box<Self>,
CanGc,
@ -71,14 +86,16 @@ pub(crate) trait DomObjectWrap: Sized + DomObject {
/// A trait to provide a function pointer to wrap function for
/// DOM iterator interfaces.
pub(crate) trait DomObjectIteratorWrap: DomObjectWrap + JSTraceable + Iterable {
pub(crate) trait DomObjectIteratorWrap<D: DomTypes>:
DomObjectWrap<D> + JSTraceable + Iterable
{
/// Function pointer to the wrap function for `IterableIterator<T>`
#[allow(clippy::type_complexity)]
const ITER_WRAP: unsafe fn(
JSContext,
&GlobalScope,
&D::GlobalScope,
Option<HandleObject>,
Box<IterableIterator<Self>>,
Box<IterableIterator<D, Self>>,
CanGc,
) -> Root<Dom<IterableIterator<Self>>>;
) -> Root<Dom<IterableIterator<D, Self>>>;
}

View file

@ -37,15 +37,18 @@ use js::rust::{
use js::JS_CALLEE;
use crate::dom::bindings::codegen::InterfaceObjectMap;
use crate::dom::bindings::codegen::PrototypeList::PROTO_OR_IFACE_LENGTH;
use crate::dom::bindings::codegen::PrototypeList::{self, PROTO_OR_IFACE_LENGTH};
use crate::dom::bindings::constructor::call_html_constructor;
use crate::dom::bindings::conversions::{
jsstring_to_str, private_from_proto_check, PrototypeCheck,
jsstring_to_str, private_from_proto_check, DerivedFrom, PrototypeCheck,
};
use crate::dom::bindings::error::throw_invalid_this;
use crate::dom::bindings::error::{throw_dom_exception, throw_invalid_this, Error};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::trace_object;
use crate::dom::windowproxy::WindowProxyHandler;
use crate::script_runtime::JSContext as SafeJSContext;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
use crate::DomTypes;
/// 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).
@ -660,3 +663,40 @@ pub(crate) unsafe fn exception_to_promise(cx: *mut JSContext, rval: RawMutableHa
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);
unsafe fn call_html_constructor<T: DerivedFrom<D::Element> + DomObject>(
cx: SafeJSContext,
args: &CallArgs,
global: &D::GlobalScope,
proto_id: crate::dom::bindings::codegen::PrototypeList::ID,
creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray),
can_gc: CanGc,
) -> bool;
}
impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder {
fn throw_dom_exception(
cx: SafeJSContext,
global: &<crate::DomTypeHolder as DomTypes>::GlobalScope,
result: Error,
) {
throw_dom_exception(cx, global, result)
}
unsafe fn call_html_constructor<
T: DerivedFrom<<crate::DomTypeHolder as DomTypes>::Element> + DomObject,
>(
cx: SafeJSContext,
args: &CallArgs,
global: &<crate::DomTypeHolder as DomTypes>::GlobalScope,
proto_id: PrototypeList::ID,
creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray),
can_gc: CanGc,
) -> bool {
call_html_constructor::<T>(cx, args, global, proto_id, creator, can_gc)
}
}