mirror of
https://github.com/servo/servo.git
synced 2025-09-30 00:29:14 +01:00
devtools: Show clients where they can set breakpoints (#37667)
devtools clients query source actors to determine where the user can set breakpoints in a source. there are two relevant requests here: `getBreakableLines` controls which line numbers can be clicked in the margin, and once a line number is clicked, `getBreakpointPositionsCompressed` controls where to show breakpoint buttons within that line. this patch handles those requests by querying the [SpiderMonkey Debugger API](https://firefox-source-docs.mozilla.org/js/Debugger/) for that information: - devtools sends its script thread a GetPossibleBreakpoints message for the source’s [`spidermonkey_id`](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Source.html#id) - the script thread fires a `getPossibleBreakpoints` event into its debugger global - the debugger script looks up the [root](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.html#onnewscript-script-global) [Debugger.Script](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html#getpossiblebreakpoints-query) for that source, calls [getPossibleBreakpoints()](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Script.html#getpossiblebreakpoints-query), and returns the result via DebuggerGlobalScope#getPossibleBreakpointsResult() - that method takes the pending result sender, and sends the result back to devtools - devtools massages the result into the format required by the request, and replies to the client as a result, users of the Firefox devtools client can now set breakpoints, though they don’t have any effect. Testing: this patch adds new devtools tests Fixes: part of #36027 <img width="1433" height="1328" alt="image" src="https://github.com/user-attachments/assets/f0cd31e0-742f-44d3-8c5d-ceedd9a2706d" /> --------- Signed-off-by: Delan Azabani <dazabani@igalia.com> Co-authored-by: atbrakhi <atbrakhi@igalia.com>
This commit is contained in:
parent
1995e22e19
commit
f5b631e270
14 changed files with 340 additions and 8 deletions
62
components/script/dom/debuggergetpossiblebreakpointsevent.rs
Normal file
62
components/script/dom/debuggergetpossiblebreakpointsevent.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
/* 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::fmt::Debug;
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::DebuggerGetPossibleBreakpointsEventBinding::DebuggerGetPossibleBreakpointsEventMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
|
||||
use crate::dom::bindings::reflector::reflect_dom_object;
|
||||
use crate::dom::bindings::root::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 DebuggerGetPossibleBreakpointsEvent {
|
||||
event: Event,
|
||||
spidermonkey_id: u32,
|
||||
}
|
||||
|
||||
impl DebuggerGetPossibleBreakpointsEvent {
|
||||
pub(crate) fn new(
|
||||
debugger_global: &GlobalScope,
|
||||
spidermonkey_id: u32,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<Self> {
|
||||
let result = Box::new(Self {
|
||||
event: Event::new_inherited(),
|
||||
spidermonkey_id,
|
||||
});
|
||||
let result = reflect_dom_object(result, debugger_global, can_gc);
|
||||
result
|
||||
.event
|
||||
.init_event("getPossibleBreakpoints".into(), false, false);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl DebuggerGetPossibleBreakpointsEventMethods<crate::DomTypeHolder>
|
||||
for DebuggerGetPossibleBreakpointsEvent
|
||||
{
|
||||
// check-tidy: no specs after this line
|
||||
fn SpidermonkeyId(&self) -> u32 {
|
||||
self.spidermonkey_id
|
||||
}
|
||||
|
||||
fn IsTrusted(&self) -> bool {
|
||||
self.event.IsTrusted()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DebuggerGetPossibleBreakpointsEvent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("DebuggerGetPossibleBreakpointsEvent")
|
||||
.field("spidermonkey_id", &self.spidermonkey_id)
|
||||
.finish()
|
||||
}
|
||||
}
|
|
@ -2,9 +2,11 @@
|
|||
* 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::cell::RefCell;
|
||||
|
||||
use base::id::{Index, PipelineId, PipelineNamespaceId};
|
||||
use constellation_traits::ScriptToConstellationChan;
|
||||
use devtools_traits::{ScriptToDevtoolsControlMsg, SourceInfo, WorkerId};
|
||||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, SourceInfo, WorkerId};
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::JavaScriptEvaluationError;
|
||||
use embedder_traits::resources::{self, Resource};
|
||||
|
@ -14,6 +16,7 @@ use js::rust::Runtime;
|
|||
use js::rust::wrappers::JS_DefineDebuggerObject;
|
||||
use net_traits::ResourceThreads;
|
||||
use profile_traits::{mem, time};
|
||||
use script_bindings::codegen::GenericBindings::DebuggerGetPossibleBreakpointsEventBinding::RecommendedBreakpointLocation;
|
||||
use script_bindings::codegen::GenericBindings::DebuggerGlobalScopeBinding::{
|
||||
DebuggerGlobalScopeMethods, NotifyNewSource,
|
||||
};
|
||||
|
@ -27,7 +30,7 @@ use crate::dom::bindings::inheritance::Castable;
|
|||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::utils::define_all_exposed_interfaces;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::types::{DebuggerAddDebuggeeEvent, Event};
|
||||
use crate::dom::types::{DebuggerAddDebuggeeEvent, DebuggerGetPossibleBreakpointsEvent, Event};
|
||||
#[cfg(feature = "testbinding")]
|
||||
#[cfg(feature = "webgpu")]
|
||||
use crate::dom::webgpu::identityhub::IdentityHub;
|
||||
|
@ -41,6 +44,11 @@ use crate::script_runtime::{CanGc, IntroductionType, JSContext};
|
|||
/// <https://firefox-source-docs.mozilla.org/js/Debugger/>
|
||||
pub(crate) struct DebuggerGlobalScope {
|
||||
global_scope: GlobalScope,
|
||||
#[no_trace]
|
||||
devtools_to_script_sender: IpcSender<DevtoolScriptControlMsg>,
|
||||
#[no_trace]
|
||||
get_possible_breakpoints_result_sender:
|
||||
RefCell<Option<IpcSender<Vec<devtools_traits::RecommendedBreakpointLocation>>>>,
|
||||
}
|
||||
|
||||
impl DebuggerGlobalScope {
|
||||
|
@ -55,7 +63,8 @@ impl DebuggerGlobalScope {
|
|||
pub(crate) fn new(
|
||||
runtime: &Runtime,
|
||||
debugger_pipeline_id: PipelineId,
|
||||
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||
script_to_devtools_sender: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||
devtools_to_script_sender: IpcSender<DevtoolScriptControlMsg>,
|
||||
mem_profiler_chan: mem::ProfilerChan,
|
||||
time_profiler_chan: time::ProfilerChan,
|
||||
script_to_constellation_chan: ScriptToConstellationChan,
|
||||
|
@ -66,7 +75,7 @@ impl DebuggerGlobalScope {
|
|||
let global = Box::new(Self {
|
||||
global_scope: GlobalScope::new_inherited(
|
||||
debugger_pipeline_id,
|
||||
devtools_chan,
|
||||
script_to_devtools_sender,
|
||||
mem_profiler_chan,
|
||||
time_profiler_chan,
|
||||
script_to_constellation_chan,
|
||||
|
@ -81,6 +90,8 @@ impl DebuggerGlobalScope {
|
|||
None,
|
||||
false,
|
||||
),
|
||||
devtools_to_script_sender,
|
||||
get_possible_breakpoints_result_sender: RefCell::new(None),
|
||||
});
|
||||
let global = unsafe {
|
||||
DebuggerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(
|
||||
|
@ -155,6 +166,28 @@ impl DebuggerGlobalScope {
|
|||
"Guaranteed by DebuggerAddDebuggeeEvent::new"
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn fire_get_possible_breakpoints(
|
||||
&self,
|
||||
can_gc: CanGc,
|
||||
spidermonkey_id: u32,
|
||||
result_sender: IpcSender<Vec<devtools_traits::RecommendedBreakpointLocation>>,
|
||||
) {
|
||||
assert!(
|
||||
self.get_possible_breakpoints_result_sender
|
||||
.replace(Some(result_sender))
|
||||
.is_none()
|
||||
);
|
||||
let event = DomRoot::upcast::<Event>(DebuggerGetPossibleBreakpointsEvent::new(
|
||||
self.upcast(),
|
||||
spidermonkey_id,
|
||||
can_gc,
|
||||
));
|
||||
assert!(
|
||||
DomRoot::upcast::<Event>(event).fire(self.upcast(), can_gc),
|
||||
"Guaranteed by DebuggerGetPossibleBreakpointsEvent::new"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl DebuggerGlobalScopeMethods<crate::DomTypeHolder> for DebuggerGlobalScope {
|
||||
|
@ -242,6 +275,7 @@ impl DebuggerGlobalScopeMethods<crate::DomTypeHolder> for DebuggerGlobalScope {
|
|||
spidermonkey_id: args.spidermonkeyId,
|
||||
};
|
||||
if let Err(error) = devtools_chan.send(ScriptToDevtoolsControlMsg::CreateSourceActor(
|
||||
self.devtools_to_script_sender.clone(),
|
||||
pipeline_id,
|
||||
source_info,
|
||||
)) {
|
||||
|
@ -251,4 +285,27 @@ impl DebuggerGlobalScopeMethods<crate::DomTypeHolder> for DebuggerGlobalScope {
|
|||
debug!("Not creating debuggee for script with no `introductionType`");
|
||||
}
|
||||
}
|
||||
|
||||
fn GetPossibleBreakpointsResult(
|
||||
&self,
|
||||
event: &DebuggerGetPossibleBreakpointsEvent,
|
||||
result: Vec<RecommendedBreakpointLocation>,
|
||||
) {
|
||||
info!("GetPossibleBreakpointsResult: {event:?} {result:?}");
|
||||
let sender = self
|
||||
.get_possible_breakpoints_result_sender
|
||||
.take()
|
||||
.expect("Guaranteed by Self::fire_get_possible_breakpoints()");
|
||||
let _ = sender.send(
|
||||
result
|
||||
.into_iter()
|
||||
.map(|entry| devtools_traits::RecommendedBreakpointLocation {
|
||||
offset: entry.offset,
|
||||
line_number: entry.lineNumber,
|
||||
column_number: entry.columnNumber,
|
||||
is_step_start: entry.isStepStart,
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -430,6 +430,9 @@ impl DedicatedWorkerGlobalScope {
|
|||
&runtime,
|
||||
pipeline_id,
|
||||
init.to_devtools_sender.clone(),
|
||||
init.from_devtools_sender
|
||||
.clone()
|
||||
.expect("Guaranteed by Worker::Constructor"),
|
||||
init.mem_profiler_chan.clone(),
|
||||
init.time_profiler_chan.clone(),
|
||||
init.script_to_constellation_chan.clone(),
|
||||
|
|
|
@ -290,6 +290,7 @@ pub(crate) mod datatransfer;
|
|||
pub(crate) mod datatransferitem;
|
||||
pub(crate) mod datatransferitemlist;
|
||||
pub(crate) mod debuggeradddebuggeeevent;
|
||||
pub(crate) mod debuggergetpossiblebreakpointsevent;
|
||||
pub(crate) mod debuggerglobalscope;
|
||||
pub(crate) mod dedicatedworkerglobalscope;
|
||||
pub(crate) mod defaultteereadrequest;
|
||||
|
|
|
@ -945,6 +945,7 @@ impl ScriptThread {
|
|||
&js_runtime.clone(),
|
||||
PipelineId::new(),
|
||||
senders.devtools_server_sender.clone(),
|
||||
senders.devtools_client_to_script_thread_sender.clone(),
|
||||
senders.memory_profiler_sender.clone(),
|
||||
senders.time_profiler_sender.clone(),
|
||||
script_to_constellation_chan,
|
||||
|
@ -2180,6 +2181,13 @@ impl ScriptThread {
|
|||
DevtoolScriptControlMsg::HighlightDomNode(id, node_id) => {
|
||||
devtools::handle_highlight_dom_node(&documents, id, node_id)
|
||||
},
|
||||
DevtoolScriptControlMsg::GetPossibleBreakpoints(spidermonkey_id, result_sender) => {
|
||||
self.debugger_global.fire_get_possible_breakpoints(
|
||||
can_gc,
|
||||
spidermonkey_id,
|
||||
result_sender,
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue