From 92a9d24a1325eb18b0f200e07642642eb08185fb Mon Sep 17 00:00:00 2001 From: shuppy Date: Tue, 5 Aug 2025 13:48:53 +0800 Subject: [PATCH] script: Add new Window globals as debuggees (#38333) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to debug the scripts in a page with the [SpiderMonkey Debugger API](https://firefox-source-docs.mozilla.org/js/Debugger/), we need to pass the page’s global object to [Debugger.prototype.**addDebuggee()**](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#adddebuggee-global). we could pick up the global via the [onNewGlobalObject() hook](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewglobalobject-global), but if our script system passes the global to the debugger script instead, we can later add details like the PipelineId that will help servo identify debuggees that the API is notifying us about (#38334). this patch plumbs new Window globals from script into addDebuggee() via the debugger script. to call into the debugger script with structured input, we create a new DOM event type, DebuggerEvent, that the debugger script listens for as the “addDebuggee” event. Testing: no testable effects yet, but will be used in #37667 Fixes: part of #36027 --------- Signed-off-by: Delan Azabani Co-authored-by: atbrakhi --- components/script/dom/debuggerevent.rs | 59 +++++++++++++++++++ components/script/dom/debuggerglobalscope.rs | 11 ++++ components/script/dom/event.rs | 2 +- components/script/dom/mod.rs | 1 + components/script/script_thread.rs | 2 + .../webidls/DebuggerEvent.webidl | 10 ++++ resources/debugger.js | 17 ++++++ 7 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 components/script/dom/debuggerevent.rs create mode 100644 components/script_bindings/webidls/DebuggerEvent.webidl diff --git a/components/script/dom/debuggerevent.rs b/components/script/dom/debuggerevent.rs new file mode 100644 index 00000000000..ad3a4261df7 --- /dev/null +++ b/components/script/dom/debuggerevent.rs @@ -0,0 +1,59 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::ptr::NonNull; + +use dom_struct::dom_struct; +use js::jsapi::{JSObject, Value}; +use script_bindings::conversions::SafeToJSValConvertible; +use script_bindings::reflector::DomObject; + +use crate::dom::bindings::codegen::Bindings::DebuggerEventBinding::DebuggerEventMethods; +use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::event::Event; +use crate::dom::types::GlobalScope; +use crate::script_runtime::CanGc; + +#[dom_struct] +/// Event for Rust → JS calls in [`crate::dom::DebuggerGlobalScope`]. +pub(crate) struct DebuggerEvent { + event: Event, + global: Dom, +} + +impl DebuggerEvent { + pub(crate) fn new( + debugger_global: &GlobalScope, + global: &GlobalScope, + can_gc: CanGc, + ) -> DomRoot { + let result = Box::new(Self { + event: Event::new_inherited(), + global: Dom::from_ref(global), + }); + let result = reflect_dom_object(result, debugger_global, can_gc); + result.event.init_event("addDebuggee".into(), false, false); + + result + } +} + +impl DebuggerEventMethods for DebuggerEvent { + // check-tidy: no specs after this line + fn Global(&self, cx: script_bindings::script_runtime::JSContext) -> NonNull { + // Convert the debuggee global’s reflector to a Value, wrapping it from its originating realm (debuggee realm) + // into the active realm (debugger realm) so that it can be passed across compartments. + rooted!(in(*cx) let mut result: Value); + self.global + .reflector() + .safe_to_jsval(cx, result.handle_mut()); + NonNull::new(result.to_object()).unwrap() + } + + fn IsTrusted(&self) -> bool { + self.event.IsTrusted() + } +} diff --git a/components/script/dom/debuggerglobalscope.rs b/components/script/dom/debuggerglobalscope.rs index 0c69b8106d5..c928e1dd89d 100644 --- a/components/script/dom/debuggerglobalscope.rs +++ b/components/script/dom/debuggerglobalscope.rs @@ -24,7 +24,9 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::trace::CustomTraceable; use crate::dom::bindings::utils::define_all_exposed_interfaces; +use crate::dom::event::EventStatus; use crate::dom::globalscope::GlobalScope; +use crate::dom::types::{DebuggerEvent, Event}; #[cfg(feature = "testbinding")] #[cfg(feature = "webgpu")] use crate::dom::webgpu::identityhub::IdentityHub; @@ -119,4 +121,13 @@ impl DebuggerGlobalScope { report_pending_exception(Self::get_cx(), true, InRealm::Entered(&ar), can_gc); } } + + pub(crate) fn fire_add_debuggee(&self, can_gc: CanGc, global: &GlobalScope) { + assert_eq!( + DomRoot::upcast::(DebuggerEvent::new(self.upcast(), global, can_gc)) + .fire(self.upcast(), can_gc), + EventStatus::NotCanceled, + "Guaranteed by DebuggerEvent::new" + ); + } } diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index 345038a08da..6153866ce95 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -1105,7 +1105,7 @@ pub(crate) enum EventDefault { Handled, } -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] pub(crate) enum EventStatus { Canceled, NotCanceled, diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 57e16ad4941..630ad4f9fb2 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -289,6 +289,7 @@ pub(crate) mod customevent; pub(crate) mod datatransfer; pub(crate) mod datatransferitem; pub(crate) mod datatransferitemlist; +pub(crate) mod debuggerevent; pub(crate) mod debuggerglobalscope; pub(crate) mod dedicatedworkerglobalscope; pub(crate) mod defaultteereadrequest; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 7805505288b..cd115f5d3d6 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -3435,6 +3435,8 @@ impl ScriptThread { incomplete.load_data.inherited_secure_context, incomplete.theme, ); + self.debugger_global + .fire_add_debuggee(can_gc, window.upcast()); let _realm = enter_realm(&*window); diff --git a/components/script_bindings/webidls/DebuggerEvent.webidl b/components/script_bindings/webidls/DebuggerEvent.webidl new file mode 100644 index 00000000000..a0682fc3165 --- /dev/null +++ b/components/script_bindings/webidls/DebuggerEvent.webidl @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// This interface is entirely internal to Servo, and should not be accessible to +// web pages. +[Exposed=DebuggerGlobalScope] +interface DebuggerEvent : Event { + readonly attribute object global; +}; diff --git a/resources/debugger.js b/resources/debugger.js index 6d1e4b19bc8..d0c6198bfd2 100644 --- a/resources/debugger.js +++ b/resources/debugger.js @@ -1,3 +1,20 @@ if (!("dbg" in this)) { dbg = new Debugger; + + dbg.onNewGlobalObject = function(global) { + console.log("[debugger] onNewGlobalObject"); + console.log(this, global); + }; + + dbg.onNewScript = function(script, global) { + console.log("[debugger] onNewScript"); + console.log(this, script, global); + }; } + +addEventListener("addDebuggee", event => { + const {global} = event; + console.log("[debugger] Adding debuggee"); + dbg.addDebuggee(global); + console.log("[debugger] getDebuggees().length =", dbg.getDebuggees().length); +});