script: Make callbacks generic over DOM interfaces. (#35459)

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2025-02-21 07:17:11 -05:00 committed by GitHub
parent 2b0d2ecc73
commit a433b20259
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 141 additions and 97 deletions

View file

@ -20,12 +20,13 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::Wind
use crate::dom::bindings::error::{report_pending_exception, Error, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::settings_stack::{AutoEntryScript, AutoIncumbentScript};
use crate::dom::bindings::settings_stack::{GenericAutoEntryScript, GenericAutoIncumbentScript};
use crate::dom::bindings::utils::AsCCharPtrPtr;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use crate::dom::document::DocumentHelpers;
use crate::dom::globalscope::GlobalScopeHelpers;
use crate::realms::{enter_realm, InRealm};
use crate::script_runtime::{CanGc, JSContext};
use crate::DomTypes;
/// The exception handling used for a call.
#[derive(Clone, Copy, PartialEq)]
@ -40,7 +41,7 @@ pub(crate) enum ExceptionHandling {
/// callback interface types.
#[derive(JSTraceable)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct CallbackObject {
pub(crate) struct CallbackObject<D: DomTypes> {
/// The underlying `JSObject`.
callback: Heap<*mut JSObject>,
permanent_js_root: Heap<JSVal>,
@ -56,18 +57,18 @@ pub(crate) struct CallbackObject {
///
/// ["callback context"]: https://heycam.github.io/webidl/#dfn-callback-context
/// [sometimes]: https://github.com/whatwg/html/issues/2248
incumbent: Option<Dom<GlobalScope>>,
incumbent: Option<Dom<D::GlobalScope>>,
}
impl CallbackObject {
impl<D: DomTypes> CallbackObject<D> {
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
// These are used by the bindings and do not need `default()` functions.
#[allow(clippy::new_without_default)]
fn new() -> CallbackObject {
CallbackObject {
fn new() -> Self {
Self {
callback: Heap::default(),
permanent_js_root: Heap::default(),
incumbent: GlobalScope::incumbent().map(|i| Dom::from_ref(&*i)),
incumbent: D::GlobalScope::incumbent().map(|i| Dom::from_ref(&*i)),
}
}
@ -87,7 +88,7 @@ impl CallbackObject {
}
}
impl Drop for CallbackObject {
impl<D: DomTypes> Drop for CallbackObject<D> {
#[allow(unsafe_code)]
fn drop(&mut self) {
unsafe {
@ -98,19 +99,19 @@ impl Drop for CallbackObject {
}
}
impl PartialEq for CallbackObject {
fn eq(&self, other: &CallbackObject) -> bool {
impl<D: DomTypes> PartialEq for CallbackObject<D> {
fn eq(&self, other: &CallbackObject<D>) -> bool {
self.callback.get() == other.callback.get()
}
}
/// A trait to be implemented by concrete IDL callback function and
/// callback interface types.
pub(crate) trait CallbackContainer {
pub(crate) trait CallbackContainer<D: DomTypes> {
/// Create a new CallbackContainer object for the given `JSObject`.
unsafe fn new(cx: JSContext, callback: *mut JSObject) -> Rc<Self>;
/// Returns the underlying `CallbackObject`.
fn callback_holder(&self) -> &CallbackObject;
fn callback_holder(&self) -> &CallbackObject<D>;
/// Returns the underlying `JSObject`.
fn callback(&self) -> *mut JSObject {
self.callback_holder().get()
@ -119,7 +120,7 @@ pub(crate) trait CallbackContainer {
/// incumbent global when calling the callback.
///
/// ["callback context"]: https://heycam.github.io/webidl/#dfn-callback-context
fn incumbent(&self) -> Option<&GlobalScope> {
fn incumbent(&self) -> Option<&D::GlobalScope> {
self.callback_holder().incumbent.as_deref()
}
}
@ -127,23 +128,23 @@ pub(crate) trait CallbackContainer {
/// A common base class for representing IDL callback function types.
#[derive(JSTraceable, PartialEq)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct CallbackFunction {
object: CallbackObject,
pub(crate) struct CallbackFunction<D: DomTypes> {
object: CallbackObject<D>,
}
impl CallbackFunction {
impl<D: DomTypes> CallbackFunction<D> {
/// Create a new `CallbackFunction` for this object.
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
// These are used by the bindings and do not need `default()` functions.
#[allow(clippy::new_without_default)]
pub(crate) fn new() -> CallbackFunction {
CallbackFunction {
pub(crate) fn new() -> Self {
Self {
object: CallbackObject::new(),
}
}
/// Returns the underlying `CallbackObject`.
pub(crate) fn callback_holder(&self) -> &CallbackObject {
pub(crate) fn callback_holder(&self) -> &CallbackObject<D> {
&self.object
}
@ -157,22 +158,22 @@ impl CallbackFunction {
/// A common base class for representing IDL callback interface types.
#[derive(JSTraceable, PartialEq)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct CallbackInterface {
object: CallbackObject,
pub(crate) struct CallbackInterface<D: DomTypes> {
object: CallbackObject<D>,
}
impl CallbackInterface {
impl<D: DomTypes> CallbackInterface<D> {
/// Create a new CallbackInterface object for the given `JSObject`.
// These are used by the bindings and do not need `default()` functions.
#[allow(clippy::new_without_default)]
pub(crate) fn new() -> CallbackInterface {
CallbackInterface {
pub(crate) fn new() -> Self {
Self {
object: CallbackObject::new(),
}
}
/// Returns the underlying `CallbackObject`.
pub(crate) fn callback_holder(&self) -> &CallbackObject {
pub(crate) fn callback_holder(&self) -> &CallbackObject<D> {
&self.object
}
@ -227,10 +228,10 @@ pub(crate) fn wrap_call_this_value<T: ThisReflector>(
/// A class that performs whatever setup we need to safely make a call while
/// this class is on the stack. After `new` returns, the call is safe to make.
pub(crate) struct CallSetup {
pub(crate) struct CallSetup<D: DomTypes> {
/// The global for reporting exceptions. This is the global object of the
/// (possibly wrapped) callback object.
exception_global: DomRoot<GlobalScope>,
exception_global: DomRoot<D::GlobalScope>,
/// The `JSContext` used for the call.
cx: JSContext,
/// The realm we were in before the call.
@ -239,27 +240,24 @@ pub(crate) struct CallSetup {
handling: ExceptionHandling,
/// <https://heycam.github.io/webidl/#es-invoking-callback-functions>
/// steps 8 and 18.2.
entry_script: Option<AutoEntryScript>,
entry_script: Option<GenericAutoEntryScript<D>>,
/// <https://heycam.github.io/webidl/#es-invoking-callback-functions>
/// steps 9 and 18.1.
incumbent_script: Option<AutoIncumbentScript>,
incumbent_script: Option<GenericAutoIncumbentScript<D>>,
}
impl CallSetup {
impl<D: DomTypes> CallSetup<D> {
/// Performs the setup needed to make a call.
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new<T: CallbackContainer>(
callback: &T,
handling: ExceptionHandling,
) -> CallSetup {
let global = unsafe { GlobalScope::from_object(callback.callback()) };
if let Some(window) = global.downcast::<Window>() {
pub(crate) fn new<T: CallbackContainer<D>>(callback: &T, handling: ExceptionHandling) -> Self {
let global = unsafe { D::GlobalScope::from_object(callback.callback()) };
if let Some(window) = global.downcast::<D::Window>() {
window.Document().ensure_safe_to_run_script_or_layout();
}
let cx = GlobalScope::get_cx();
let cx = D::GlobalScope::get_cx();
let aes = AutoEntryScript::new(&global);
let ais = callback.incumbent().map(AutoIncumbentScript::new);
let aes = GenericAutoEntryScript::<D>::new(&global);
let ais = callback.incumbent().map(GenericAutoIncumbentScript::new);
CallSetup {
exception_global: global,
cx,
@ -276,7 +274,7 @@ impl CallSetup {
}
}
impl Drop for CallSetup {
impl<D: DomTypes> Drop for CallSetup<D> {
fn drop(&mut self) {
unsafe {
LeaveRealm(*self.cx, self.old_realm);

View file

@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::marker::PhantomData;
use std::thread;
use js::jsapi::{GetScriptedCallerGlobal, HideScriptedCaller, JSTracer, UnhideScriptedCaller};
@ -10,10 +11,14 @@ use js::rust::Runtime;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::trace::JSTraceable;
use crate::dom::globalscope::GlobalScope;
use crate::dom::bindings::utils::DomHelpers;
use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers};
use crate::script_runtime::CanGc;
use crate::DomTypes;
thread_local!(static STACK: RefCell<Vec<StackEntry>> = const { RefCell::new(Vec::new()) });
thread_local!(pub(super) static STACK: RefCell<Vec<StackEntry<crate::DomTypeHolder>>> = const {
RefCell::new(Vec::new())
});
#[derive(Debug, Eq, JSTraceable, PartialEq)]
enum StackEntryKind {
@ -23,8 +28,8 @@ enum StackEntryKind {
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
#[derive(JSTraceable)]
struct StackEntry {
global: Dom<GlobalScope>,
pub(crate) struct StackEntry<D: DomTypes> {
global: Dom<D::GlobalScope>,
kind: StackEntryKind,
}
@ -39,25 +44,28 @@ pub(crate) fn is_execution_stack_empty() -> bool {
STACK.with(|stack| stack.borrow().is_empty())
}
pub(crate) type AutoEntryScript = GenericAutoEntryScript<crate::DomTypeHolder>;
/// RAII struct that pushes and pops entries from the script settings stack.
pub(crate) struct AutoEntryScript {
global: DomRoot<GlobalScope>,
pub(crate) struct GenericAutoEntryScript<D: DomTypes> {
global: DomRoot<D::GlobalScope>,
#[cfg(feature = "tracing")]
#[allow(dead_code)]
span: tracing::span::EnteredSpan,
}
impl AutoEntryScript {
impl<D: DomTypes> GenericAutoEntryScript<D> {
/// <https://html.spec.whatwg.org/multipage/#prepare-to-run-script>
pub(crate) fn new(global: &GlobalScope) -> Self {
STACK.with(|stack| {
pub(crate) fn new(global: &D::GlobalScope) -> Self {
let settings_stack = <D as DomHelpers<D>>::settings_stack();
settings_stack.with(|stack| {
trace!("Prepare to run script with {:p}", global);
let mut stack = stack.borrow_mut();
stack.push(StackEntry {
global: Dom::from_ref(global),
kind: StackEntryKind::Entry,
});
AutoEntryScript {
Self {
global: DomRoot::from_ref(global),
#[cfg(feature = "tracing")]
span: tracing::info_span!(
@ -71,14 +79,15 @@ impl AutoEntryScript {
}
}
impl Drop for AutoEntryScript {
impl<D: DomTypes> Drop for GenericAutoEntryScript<D> {
/// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-script>
fn drop(&mut self) {
STACK.with(|stack| {
let settings_stack = <D as DomHelpers<D>>::settings_stack();
settings_stack.with(|stack| {
let mut stack = stack.borrow_mut();
let entry = stack.pop().unwrap();
assert_eq!(
&*entry.global as *const GlobalScope, &*self.global as *const GlobalScope,
&*entry.global as *const D::GlobalScope, &*self.global as *const D::GlobalScope,
"Dropped AutoEntryScript out of order."
);
assert_eq!(entry.kind, StackEntryKind::Entry);
@ -109,20 +118,24 @@ pub(crate) fn entry_global() -> DomRoot<GlobalScope> {
}
/// RAII struct that pushes and pops entries from the script settings stack.
pub(crate) struct AutoIncumbentScript {
pub(crate) struct GenericAutoIncumbentScript<D: DomTypes> {
global: usize,
_marker: PhantomData<D>,
}
impl AutoIncumbentScript {
pub(crate) type AutoIncumbentScript = GenericAutoIncumbentScript<crate::DomTypeHolder>;
impl<D: DomTypes> GenericAutoIncumbentScript<D> {
/// <https://html.spec.whatwg.org/multipage/#prepare-to-run-a-callback>
pub(crate) fn new(global: &GlobalScope) -> Self {
pub(crate) fn new(global: &D::GlobalScope) -> Self {
// Step 2-3.
unsafe {
let cx =
Runtime::get().expect("Creating a new incumbent script after runtime shutdown");
HideScriptedCaller(cx.as_ptr());
}
STACK.with(|stack| {
let settings_stack = <D as DomHelpers<D>>::settings_stack();
settings_stack.with(|stack| {
trace!("Prepare to run a callback with {:p}", global);
// Step 1.
let mut stack = stack.borrow_mut();
@ -130,23 +143,25 @@ impl AutoIncumbentScript {
global: Dom::from_ref(global),
kind: StackEntryKind::Incumbent,
});
AutoIncumbentScript {
Self {
global: global as *const _ as usize,
_marker: PhantomData,
}
})
}
}
impl Drop for AutoIncumbentScript {
impl<D: DomTypes> Drop for GenericAutoIncumbentScript<D> {
/// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-a-callback>
fn drop(&mut self) {
STACK.with(|stack| {
let settings_stack = <D as DomHelpers<D>>::settings_stack();
settings_stack.with(|stack| {
// Step 4.
let mut stack = stack.borrow_mut();
let entry = stack.pop().unwrap();
// Step 3.
assert_eq!(
&*entry.global as *const GlobalScope as usize, self.global,
&*entry.global as *const D::GlobalScope as usize, self.global,
"Dropped AutoIncumbentScript out of order."
);
assert_eq!(entry.kind, StackEntryKind::Incumbent);

View file

@ -4,10 +4,12 @@
//! 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 js::conversions::ToJSValConvertible;
@ -44,6 +46,7 @@ use crate::dom::bindings::conversions::{
};
use crate::dom::bindings::error::{throw_dom_exception, throw_invalid_this, Error};
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;
@ -676,6 +679,8 @@ pub(crate) trait DomHelpers<D: DomTypes> {
creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray),
can_gc: CanGc,
) -> bool;
fn settings_stack() -> &'static LocalKey<RefCell<Vec<StackEntry<D>>>>;
}
impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder {
@ -699,4 +704,8 @@ impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder {
) -> bool {
call_html_constructor::<T>(cx, args, global, proto_id, creator, can_gc)
}
fn settings_stack() -> &'static LocalKey<RefCell<Vec<StackEntry<crate::DomTypeHolder>>>> {
&settings_stack::STACK
}
}

View file

@ -205,6 +205,7 @@ use crate::stylesheet_set::StylesheetSetRef;
use crate::task::TaskBox;
use crate::task_source::TaskSourceName;
use crate::timers::OneshotTimerCallback;
use crate::DomTypes;
/// The number of times we are allowed to see spurious `requestAnimationFrame()` calls before
/// falling back to fake ones.
@ -6214,3 +6215,13 @@ fn is_named_element_with_id_attribute(elem: &Element) -> bool {
// behaviour is actually implemented
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 {
fn ensure_safe_to_run_script_or_layout(&self) {
Document::ensure_safe_to_run_script_or_layout(self)
}
}

View file

@ -77,7 +77,7 @@ pub(crate) enum CommonEventHandler {
}
impl CommonEventHandler {
fn parent(&self) -> &CallbackFunction {
fn parent(&self) -> &CallbackFunction<crate::DomTypeHolder> {
match *self {
CommonEventHandler::EventHandler(ref handler) => &handler.parent,
CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
@ -612,7 +612,7 @@ impl EventTarget {
}
#[allow(unsafe_code)]
pub(crate) fn set_event_handler_common<T: CallbackContainer>(
pub(crate) fn set_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
&self,
ty: &str,
listener: Option<Rc<T>>,
@ -628,7 +628,7 @@ impl EventTarget {
}
#[allow(unsafe_code)]
pub(crate) fn set_error_event_handler<T: CallbackContainer>(
pub(crate) fn set_error_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
&self,
ty: &str,
listener: Option<Rc<T>>,
@ -644,7 +644,7 @@ impl EventTarget {
}
#[allow(unsafe_code)]
pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer>(
pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
&self,
ty: &str,
listener: Option<Rc<T>>,
@ -660,7 +660,7 @@ impl EventTarget {
}
#[allow(unsafe_code)]
pub(crate) fn get_event_handler_common<T: CallbackContainer>(
pub(crate) fn get_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
&self,
ty: &str,
can_gc: CanGc,

View file

@ -3348,6 +3348,10 @@ pub(crate) trait GlobalScopeHelpers<D: crate::DomTypes> {
) -> DomRoot<D::GlobalScope>;
fn origin(&self) -> &MutableOrigin;
fn incumbent() -> Option<DomRoot<D::GlobalScope>>;
fn perform_a_microtask_checkpoint(&self, can_gc: CanGc);
}
#[allow(unsafe_code)]
@ -3375,4 +3379,12 @@ impl GlobalScopeHelpers<crate::DomTypeHolder> for GlobalScope {
fn origin(&self) -> &MutableOrigin {
GlobalScope::origin(self)
}
fn incumbent() -> Option<DomRoot<Self>> {
GlobalScope::incumbent()
}
fn perform_a_microtask_checkpoint(&self, can_gc: CanGc) {
GlobalScope::perform_a_microtask_checkpoint(self, can_gc)
}
}

View file

@ -96,6 +96,7 @@ DOMInterfaces = {
},
'Document': {
'additionalTraits': ["crate::dom::document::DocumentHelpers<Self>"],
'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'],
},

View file

@ -75,7 +75,7 @@ def isDomInterface(t, logging=False):
if isinstance(t, IDLInterface):
return True
if t.isCallback():
return False
return True
return t.isInterface() and (t.isGeckoInterface() or (t.isSpiderMonkeyInterface() and not t.isBufferSource()))
@ -866,7 +866,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if descriptor.interface.isCallback():
name = descriptor.nativeType
declType = CGWrapper(CGGeneric(name), pre="Rc<", post=">")
declType = CGWrapper(CGGeneric(f"{name}<D>"), pre="Rc<", post=">")
template = f"{name}::new(cx, ${{val}}.get().to_object())"
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=">")
@ -1078,7 +1078,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
callback = type.unroll().callback
declType = CGGeneric(callback.identifier.name)
declType = CGGeneric(f"{callback.identifier.name}<D>")
finalDeclType = CGTemplatedType("Rc", declType)
conversion = CGCallbackTempRoot(declType.define())
@ -1508,7 +1508,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
return result
if returnType.isCallback():
callback = returnType.unroll().callback
result = CGGeneric(f'Rc<{getModuleFromObject(callback)}::{callback.identifier.name}>')
result = CGGeneric(f'Rc<{getModuleFromObject(callback)}::{callback.identifier.name}<D>>')
if returnType.nullable():
result = CGWrapper(result, pre="Option<", post=">")
return result
@ -2611,7 +2611,7 @@ class CGGeneric(CGThing):
class CGCallbackTempRoot(CGGeneric):
def __init__(self, name):
CGGeneric.__init__(self, f"{name}::new(cx, ${{val}}.get().to_object())")
CGGeneric.__init__(self, f"{name.replace('<D>', '::<D>')}::new(cx, ${{val}}.get().to_object())")
def getAllTypes(descriptors, dictionaries, callbacks, typedefs):
@ -5100,7 +5100,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
typeName = f"typedarray::Heap{name}"
elif type.isCallback():
name = type.name
typeName = name
typeName = f"{name}<D>"
else:
raise TypeError(f"Can't handle {type} in unions yet")
@ -5571,8 +5571,9 @@ class ClassConstructor(ClassItem):
body += '\n'
body = f' {{\n{body}}}'
name = cgClass.getNameString().replace(': DomTypes', '')
return f"""
pub(crate) unsafe fn {self.getDecorators(True)}new({args}) -> Rc<{cgClass.getNameString()}>{body}
pub(crate) unsafe fn {self.getDecorators(True)}new({args}) -> Rc<{name}>{body}
"""
def define(self, cgClass):
@ -5710,7 +5711,7 @@ class CGClass(CGThing):
result = f"{result}{memberString}"
result += f'{self.indent}}}\n\n'
result += f'impl {self.name} {{\n'
result += f'impl{specialization} {self.name}{specialization.replace(": DomTypes", "")} {{\n'
order = [(self.constructors + disallowedCopyConstructors, '\n'),
(self.destructors, '\n'), (self.methods, '\n)')]
@ -7561,7 +7562,7 @@ class CGConcreteBindingRoot(CGThing):
cgthings += [CGGeneric(
f"pub(crate) type {c.identifier.name} = "
f"{originalBinding}::{c.identifier.name};"
f"{originalBinding}::{c.identifier.name}<crate::DomTypeHolder>;"
) for c in mainCallbacks]
cgthings += [CGGeneric(f"pub(crate) use {originalBinding} as GenericBindings;")]
@ -7595,7 +7596,9 @@ pub(crate) fn GetConstructorObject(
for c in callbackDescriptors:
ifaceName = c.interface.identifier.name
cgthings += [CGGeneric(f"pub(crate) type {ifaceName} = {originalBinding}::{ifaceName};")]
cgthings += [CGGeneric(
f"pub(crate) type {ifaceName} = {originalBinding}::{ifaceName}<crate::DomTypeHolder>;"
)]
# And make sure we have the right number of newlines at the end
curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
@ -7889,6 +7892,7 @@ class CGCallback(CGClass):
bases=[ClassBase(baseName)],
constructors=self.getConstructors(),
methods=realMethods,
templateSpecialization=['D: DomTypes'],
decorators="#[derive(JSTraceable, PartialEq)]\n"
"#[cfg_attr(crown, allow(crown::unrooted_must_root))]\n"
"#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]")
@ -7906,11 +7910,6 @@ class CGCallback(CGClass):
def getMethodImpls(self, method):
assert method.needThisHandling
args = list(method.args)
# Callbacks are not generic over DomTypes yet, so we need to manually
# re-specialize any use of generics within these generated methods.
for arg in args:
arg.argType = arg.argType.replace('D::', '').replace('<D>', '<crate::DomTypeHolder>')
method.returnType = method.returnType.replace('D::', '')
# Strip out the JSContext*/JSObject* args
# that got added.
assert args[0].name == "cx" and args[0].argType == "SafeJSContext"
@ -7936,7 +7935,7 @@ class CGCallback(CGClass):
args.insert(0, Argument(None, "&self"))
argsWithoutThis.insert(0, Argument(None, "&self"))
setupCall = "let s = CallSetup::new(self, aExceptionHandling);\n"
setupCall = "let s = CallSetup::<D>::new(self, aExceptionHandling);\n"
bodyWithThis = (
f"{setupCall}rooted!(in(*s.get_context()) let mut thisValue: JSVal);\n"
@ -7948,15 +7947,14 @@ class CGCallback(CGClass):
bodyWithoutThis = (
f"{setupCall}\n"
f"unsafe {{ self.{method.name}({', '.join(argnamesWithoutThis)}) }}")
method.body = method.body.replace('D::', '').replace('<D as DomHelpers<D>>::', '')
return [ClassMethod(f'{method.name}_', method.returnType, args,
bodyInHeader=True,
templateArgs=["T: ThisReflector"],
body=bodyWithThis.replace('D::', ''),
body=bodyWithThis,
visibility='pub'),
ClassMethod(f'{method.name}__', method.returnType, argsWithoutThis,
bodyInHeader=True,
body=bodyWithoutThis.replace('D::', ''),
body=bodyWithoutThis,
visibility='pub'),
method]
@ -7976,7 +7974,7 @@ def callbackSetterName(attr, descriptor):
class CGCallbackFunction(CGCallback):
def __init__(self, callback, descriptorProvider):
CGCallback.__init__(self, callback, descriptorProvider,
"CallbackFunction",
"CallbackFunction<D>",
methods=[CallCallback(callback, descriptorProvider)])
def getConstructors(self):
@ -7985,23 +7983,23 @@ class CGCallbackFunction(CGCallback):
class CGCallbackFunctionImpl(CGGeneric):
def __init__(self, callback):
type = callback.identifier.name
type = f"{callback.identifier.name}<D>"
impl = (f"""
impl CallbackContainer for {type} {{
impl<D: DomTypes> CallbackContainer<D> for {type} {{
unsafe fn new(cx: SafeJSContext, callback: *mut JSObject) -> Rc<{type}> {{
{type}::new(cx, callback)
{type.replace('<D>', '')}::new(cx, callback)
}}
fn callback_holder(&self) -> &CallbackObject {{
fn callback_holder(&self) -> &CallbackObject<D> {{
self.parent.callback_holder()
}}
}}
impl ToJSValConvertible for {type} {{
impl<D: DomTypes> ToJSValConvertible for {type} {{
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {{
self.callback().to_jsval(cx, rval);
}}
}}\
}}
""")
CGGeneric.__init__(self, impl)
@ -8016,7 +8014,7 @@ class CGCallbackInterface(CGCallback):
methods = [CallbackOperation(m, sig, descriptor) for m in methods
for sig in m.signatures()]
assert not iface.isJSImplemented() or not iface.ctor()
CGCallback.__init__(self, iface, descriptor, "CallbackInterface", methods)
CGCallback.__init__(self, iface, descriptor, "CallbackInterface<D>", methods)
class FakeMember():

View file

@ -228,9 +228,9 @@ class Descriptor(DescriptorProvider):
self.nativeType = typeName
pathDefault = 'crate::dom::types::%s' % typeName
elif self.interface.isCallback():
ty = 'crate::dom::bindings::codegen::Bindings::%sBinding::%s' % (ifaceName, ifaceName)
ty = 'crate::dom::bindings::codegen::GenericBindings::%sBinding::%s' % (ifaceName, ifaceName)
pathDefault = ty
self.returnType = "Rc<%s>" % ty
self.returnType = "Rc<%s<D>>" % ty
self.argumentType = "???"
self.nativeType = ty
else: