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

View file

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

View file

@ -4,10 +4,12 @@
//! Various utilities to glue JavaScript and the DOM implementation together. //! Various utilities to glue JavaScript and the DOM implementation together.
use std::cell::RefCell;
use std::ffi::CString; use std::ffi::CString;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::sync::OnceLock; use std::sync::OnceLock;
use std::thread::LocalKey;
use std::{ptr, slice, str}; use std::{ptr, slice, str};
use js::conversions::ToJSValConvertible; 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::error::{throw_dom_exception, throw_invalid_this, Error};
use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::settings_stack::{self, StackEntry};
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::trace_object; use crate::dom::bindings::trace::trace_object;
use crate::dom::windowproxy::WindowProxyHandler; use crate::dom::windowproxy::WindowProxyHandler;
@ -676,6 +679,8 @@ pub(crate) trait DomHelpers<D: DomTypes> {
creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray), creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray),
can_gc: CanGc, can_gc: CanGc,
) -> bool; ) -> bool;
fn settings_stack() -> &'static LocalKey<RefCell<Vec<StackEntry<D>>>>;
} }
impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder { impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder {
@ -699,4 +704,8 @@ impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder {
) -> bool { ) -> bool {
call_html_constructor::<T>(cx, args, global, proto_id, creator, can_gc) 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::TaskBox;
use crate::task_source::TaskSourceName; use crate::task_source::TaskSourceName;
use crate::timers::OneshotTimerCallback; use crate::timers::OneshotTimerCallback;
use crate::DomTypes;
/// The number of times we are allowed to see spurious `requestAnimationFrame()` calls before /// The number of times we are allowed to see spurious `requestAnimationFrame()` calls before
/// falling back to fake ones. /// falling back to fake ones.
@ -6214,3 +6215,13 @@ fn is_named_element_with_id_attribute(elem: &Element) -> bool {
// behaviour is actually implemented // behaviour is actually implemented
elem.is::<HTMLImageElement>() && elem.get_name().is_some_and(|name| !name.is_empty()) 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 { impl CommonEventHandler {
fn parent(&self) -> &CallbackFunction { fn parent(&self) -> &CallbackFunction<crate::DomTypeHolder> {
match *self { match *self {
CommonEventHandler::EventHandler(ref handler) => &handler.parent, CommonEventHandler::EventHandler(ref handler) => &handler.parent,
CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent, CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
@ -612,7 +612,7 @@ impl EventTarget {
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) fn set_event_handler_common<T: CallbackContainer>( pub(crate) fn set_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
&self, &self,
ty: &str, ty: &str,
listener: Option<Rc<T>>, listener: Option<Rc<T>>,
@ -628,7 +628,7 @@ impl EventTarget {
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) fn set_error_event_handler<T: CallbackContainer>( pub(crate) fn set_error_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
&self, &self,
ty: &str, ty: &str,
listener: Option<Rc<T>>, listener: Option<Rc<T>>,
@ -644,7 +644,7 @@ impl EventTarget {
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer>( pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
&self, &self,
ty: &str, ty: &str,
listener: Option<Rc<T>>, listener: Option<Rc<T>>,
@ -660,7 +660,7 @@ impl EventTarget {
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) fn get_event_handler_common<T: CallbackContainer>( pub(crate) fn get_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
&self, &self,
ty: &str, ty: &str,
can_gc: CanGc, can_gc: CanGc,

View file

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

View file

@ -228,9 +228,9 @@ class Descriptor(DescriptorProvider):
self.nativeType = typeName self.nativeType = typeName
pathDefault = 'crate::dom::types::%s' % typeName pathDefault = 'crate::dom::types::%s' % typeName
elif self.interface.isCallback(): 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 pathDefault = ty
self.returnType = "Rc<%s>" % ty self.returnType = "Rc<%s<D>>" % ty
self.argumentType = "???" self.argumentType = "???"
self.nativeType = ty self.nativeType = ty
else: else: