aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2025-02-21 07:17:11 -0500
committerGitHub <noreply@github.com>2025-02-21 12:17:11 +0000
commita433b202595bb0e2208571b0680be396937e092f (patch)
tree85df5479edf76809570c93aa46c72cef69b51b8b /components/script
parent2b0d2ecc7378f653e4755fd7d948dcc15914ffca (diff)
downloadservo-a433b202595bb0e2208571b0680be396937e092f.tar.gz
servo-a433b202595bb0e2208571b0680be396937e092f.zip
script: Make callbacks generic over DOM interfaces. (#35459)
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/bindings/callback.rs82
-rw-r--r--components/script/dom/bindings/settings_stack.rs57
-rw-r--r--components/script/dom/bindings/utils.rs9
-rw-r--r--components/script/dom/document.rs11
-rw-r--r--components/script/dom/eventtarget.rs10
-rw-r--r--components/script/dom/globalscope.rs12
6 files changed, 113 insertions, 68 deletions
diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs
index a33cc49e8f0..bd73ec37d52 100644
--- a/components/script/dom/bindings/callback.rs
+++ b/components/script/dom/bindings/callback.rs
@@ -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);
diff --git a/components/script/dom/bindings/settings_stack.rs b/components/script/dom/bindings/settings_stack.rs
index 28e48ea4d93..1f566a59052 100644
--- a/components/script/dom/bindings/settings_stack.rs
+++ b/components/script/dom/bindings/settings_stack.rs
@@ -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);
diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs
index 631ba43c4d5..b3f3219cf1a 100644
--- a/components/script/dom/bindings/utils.rs
+++ b/components/script/dom/bindings/utils.rs
@@ -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
+ }
}
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 5307a6f29cd..e7a0e6813b8 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -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)
+ }
+}
diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs
index 0d879bca452..b66a54cb5a5 100644
--- a/components/script/dom/eventtarget.rs
+++ b/components/script/dom/eventtarget.rs
@@ -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,
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index b16e404ccbd..4cc27bea3f0 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -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)
+ }
}