script: Add new Window globals as debuggees (#38333)

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 <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
This commit is contained in:
shuppy 2025-08-05 13:48:53 +08:00 committed by GitHub
parent 77ff351cde
commit 92a9d24a13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 101 additions and 1 deletions

View file

@ -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<GlobalScope>,
}
impl DebuggerEvent {
pub(crate) fn new(
debugger_global: &GlobalScope,
global: &GlobalScope,
can_gc: CanGc,
) -> DomRoot<Self> {
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<crate::DomTypeHolder> for DebuggerEvent {
// check-tidy: no specs after this line
fn Global(&self, cx: script_bindings::script_runtime::JSContext) -> NonNull<JSObject> {
// Convert the debuggee globals 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()
}
}

View file

@ -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::<Event>(DebuggerEvent::new(self.upcast(), global, can_gc))
.fire(self.upcast(), can_gc),
EventStatus::NotCanceled,
"Guaranteed by DebuggerEvent::new"
);
}
}

View file

@ -1105,7 +1105,7 @@ pub(crate) enum EventDefault {
Handled,
}
#[derive(PartialEq)]
#[derive(Debug, PartialEq)]
pub(crate) enum EventStatus {
Canceled,
NotCanceled,

View file

@ -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;

View file

@ -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);

View file

@ -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;
};