mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
script: Create a debugger script for the SpiderMonkey Debugger API (#38331)
to use the [SpiderMonkey Debugger API](https://firefox-source-docs.mozilla.org/js/Debugger/), we need to call it from an internal debugger script that we will supply. this script must run in the same runtime as the debuggee(s), but in a separate [compartment](https://udn.realityripple.com/docs/Mozilla/Projects/SpiderMonkey/Compartments) ([more details](https://hacks.mozilla.org/2020/03/future-proofing-firefoxs-javascript-debugger-implementation/)). this patch defines a new DebuggerGlobalScope type and a new debugger script resource. when creating each script thread, we create a debugger global, load the debugger script from resources/debugger.js, and run that script in the global to initialise the Debugger API. subsequent patches will use the debugger script as an RPC mechanism for the Debugger API. 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:
parent
8e27fd48cc
commit
c09e117bfe
11 changed files with 184 additions and 6 deletions
122
components/script/dom/debuggerglobalscope.rs
Normal file
122
components/script/dom/debuggerglobalscope.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/* 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 base::id::PipelineId;
|
||||||
|
use constellation_traits::ScriptToConstellationChan;
|
||||||
|
use crossbeam_channel::Sender;
|
||||||
|
use devtools_traits::ScriptToDevtoolsControlMsg;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use embedder_traits::resources::{self, Resource};
|
||||||
|
use ipc_channel::ipc::IpcSender;
|
||||||
|
use js::jsval::UndefinedValue;
|
||||||
|
use js::rust::Runtime;
|
||||||
|
use js::rust::wrappers::JS_DefineDebuggerObject;
|
||||||
|
use net_traits::ResourceThreads;
|
||||||
|
use profile_traits::{mem, time};
|
||||||
|
use script_bindings::realms::InRealm;
|
||||||
|
use script_bindings::reflector::DomObject;
|
||||||
|
use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
|
||||||
|
|
||||||
|
use crate::dom::bindings::codegen::Bindings::DebuggerGlobalScopeBinding;
|
||||||
|
use crate::dom::bindings::error::report_pending_exception;
|
||||||
|
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::globalscope::GlobalScope;
|
||||||
|
#[cfg(feature = "testbinding")]
|
||||||
|
#[cfg(feature = "webgpu")]
|
||||||
|
use crate::dom::webgpu::identityhub::IdentityHub;
|
||||||
|
use crate::messaging::MainThreadScriptMsg;
|
||||||
|
use crate::realms::enter_realm;
|
||||||
|
use crate::script_module::ScriptFetchOptions;
|
||||||
|
use crate::script_runtime::{CanGc, JSContext};
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
/// Global scope for interacting with the devtools Debugger API.
|
||||||
|
///
|
||||||
|
/// <https://firefox-source-docs.mozilla.org/js/Debugger/>
|
||||||
|
pub(crate) struct DebuggerGlobalScope {
|
||||||
|
global_scope: GlobalScope,
|
||||||
|
script_chan: Sender<MainThreadScriptMsg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DebuggerGlobalScope {
|
||||||
|
/// Create a new heap-allocated `DebuggerGlobalScope`.
|
||||||
|
#[allow(unsafe_code, clippy::too_many_arguments)]
|
||||||
|
pub(crate) fn new(
|
||||||
|
runtime: &Runtime,
|
||||||
|
script_chan: Sender<MainThreadScriptMsg>,
|
||||||
|
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||||
|
mem_profiler_chan: mem::ProfilerChan,
|
||||||
|
time_profiler_chan: time::ProfilerChan,
|
||||||
|
script_to_constellation_chan: ScriptToConstellationChan,
|
||||||
|
resource_threads: ResourceThreads,
|
||||||
|
#[cfg(feature = "webgpu")] gpu_id_hub: std::sync::Arc<IdentityHub>,
|
||||||
|
can_gc: CanGc,
|
||||||
|
) -> DomRoot<Self> {
|
||||||
|
let global = Box::new(Self {
|
||||||
|
global_scope: GlobalScope::new_inherited(
|
||||||
|
PipelineId::new(),
|
||||||
|
devtools_chan,
|
||||||
|
mem_profiler_chan,
|
||||||
|
time_profiler_chan,
|
||||||
|
script_to_constellation_chan,
|
||||||
|
resource_threads,
|
||||||
|
MutableOrigin::new(ImmutableOrigin::new_opaque()),
|
||||||
|
ServoUrl::parse_with_base(None, "about:internal/debugger")
|
||||||
|
.expect("Guaranteed by argument"),
|
||||||
|
None,
|
||||||
|
Default::default(),
|
||||||
|
#[cfg(feature = "webgpu")]
|
||||||
|
gpu_id_hub,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
script_chan,
|
||||||
|
});
|
||||||
|
let global = unsafe {
|
||||||
|
DebuggerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(
|
||||||
|
JSContext::from_ptr(runtime.cx()),
|
||||||
|
global,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let realm = enter_realm(&*global);
|
||||||
|
define_all_exposed_interfaces(global.upcast(), InRealm::entered(&realm), can_gc);
|
||||||
|
assert!(unsafe {
|
||||||
|
// Invariants: `cx` must be a non-null, valid JSContext pointer,
|
||||||
|
// and `obj` must be a handle to a JS global object.
|
||||||
|
JS_DefineDebuggerObject(
|
||||||
|
*Self::get_cx(),
|
||||||
|
global.global_scope.reflector().get_jsobject(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
global
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the JS context.
|
||||||
|
pub(crate) fn get_cx() -> JSContext {
|
||||||
|
GlobalScope::get_cx()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate_js(&self, script: &str, can_gc: CanGc) -> bool {
|
||||||
|
rooted!(in (*Self::get_cx()) let mut rval = UndefinedValue());
|
||||||
|
self.global_scope.evaluate_js_on_global_with_result(
|
||||||
|
script,
|
||||||
|
rval.handle_mut(),
|
||||||
|
ScriptFetchOptions::default_classic_script(&self.global_scope),
|
||||||
|
self.global_scope.api_base_url(),
|
||||||
|
can_gc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn execute(&self, can_gc: CanGc) {
|
||||||
|
if !self.evaluate_js(&resources::read_string(Resource::DebuggerJS), can_gc) {
|
||||||
|
let ar = enter_realm(self);
|
||||||
|
report_pending_exception(Self::get_cx(), true, InRealm::Entered(&ar), can_gc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -114,7 +114,7 @@ use crate::dom::reportingobserver::ReportingObserver;
|
||||||
use crate::dom::serviceworker::ServiceWorker;
|
use crate::dom::serviceworker::ServiceWorker;
|
||||||
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
|
||||||
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
|
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
|
||||||
use crate::dom::types::MessageEvent;
|
use crate::dom::types::{DebuggerGlobalScope, MessageEvent};
|
||||||
use crate::dom::underlyingsourcecontainer::UnderlyingSourceType;
|
use crate::dom::underlyingsourcecontainer::UnderlyingSourceType;
|
||||||
#[cfg(feature = "webgpu")]
|
#[cfg(feature = "webgpu")]
|
||||||
use crate::dom::webgpu::gpudevice::GPUDevice;
|
use crate::dom::webgpu::gpudevice::GPUDevice;
|
||||||
|
@ -2530,6 +2530,9 @@ impl GlobalScope {
|
||||||
// https://drafts.css-houdini.org/worklets/#script-settings-for-worklets
|
// https://drafts.css-houdini.org/worklets/#script-settings-for-worklets
|
||||||
return worklet.base_url();
|
return worklet.base_url();
|
||||||
}
|
}
|
||||||
|
if let Some(_debugger_global) = self.downcast::<DebuggerGlobalScope>() {
|
||||||
|
return self.creation_url.clone();
|
||||||
|
}
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2545,6 +2548,9 @@ impl GlobalScope {
|
||||||
// TODO: is this the right URL to return?
|
// TODO: is this the right URL to return?
|
||||||
return worklet.base_url();
|
return worklet.base_url();
|
||||||
}
|
}
|
||||||
|
if let Some(_debugger_global) = self.downcast::<DebuggerGlobalScope>() {
|
||||||
|
return self.creation_url.clone();
|
||||||
|
}
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -289,6 +289,7 @@ pub(crate) mod customevent;
|
||||||
pub(crate) mod datatransfer;
|
pub(crate) mod datatransfer;
|
||||||
pub(crate) mod datatransferitem;
|
pub(crate) mod datatransferitem;
|
||||||
pub(crate) mod datatransferitemlist;
|
pub(crate) mod datatransferitemlist;
|
||||||
|
pub(crate) mod debuggerglobalscope;
|
||||||
pub(crate) mod dedicatedworkerglobalscope;
|
pub(crate) mod dedicatedworkerglobalscope;
|
||||||
pub(crate) mod defaultteereadrequest;
|
pub(crate) mod defaultteereadrequest;
|
||||||
pub(crate) mod defaultteeunderlyingsource;
|
pub(crate) mod defaultteeunderlyingsource;
|
||||||
|
|
|
@ -133,6 +133,7 @@ use crate::dom::htmlslotelement::HTMLSlotElement;
|
||||||
use crate::dom::mutationobserver::MutationObserver;
|
use crate::dom::mutationobserver::MutationObserver;
|
||||||
use crate::dom::node::{Node, NodeTraits, ShadowIncluding};
|
use crate::dom::node::{Node, NodeTraits, ShadowIncluding};
|
||||||
use crate::dom::servoparser::{ParserContext, ServoParser};
|
use crate::dom::servoparser::{ParserContext, ServoParser};
|
||||||
|
use crate::dom::types::DebuggerGlobalScope;
|
||||||
#[cfg(feature = "webgpu")]
|
#[cfg(feature = "webgpu")]
|
||||||
use crate::dom::webgpu::identityhub::IdentityHub;
|
use crate::dom::webgpu::identityhub::IdentityHub;
|
||||||
use crate::dom::window::Window;
|
use crate::dom::window::Window;
|
||||||
|
@ -348,6 +349,8 @@ pub struct ScriptThread {
|
||||||
/// itself is managing animations the the timer fired triggering a [`ScriptThread`]-based
|
/// itself is managing animations the the timer fired triggering a [`ScriptThread`]-based
|
||||||
/// animation tick.
|
/// animation tick.
|
||||||
has_pending_animation_tick: Arc<AtomicBool>,
|
has_pending_animation_tick: Arc<AtomicBool>,
|
||||||
|
|
||||||
|
debugger_global: Dom<DebuggerGlobalScope>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BHMExitSignal {
|
struct BHMExitSignal {
|
||||||
|
@ -928,6 +931,30 @@ impl ScriptThread {
|
||||||
content_process_shutdown_sender: state.content_process_shutdown_sender,
|
content_process_shutdown_sender: state.content_process_shutdown_sender,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let microtask_queue = runtime.microtask_queue.clone();
|
||||||
|
let js_runtime = Rc::new(runtime);
|
||||||
|
#[cfg(feature = "webgpu")]
|
||||||
|
let gpu_id_hub = Arc::new(IdentityHub::default());
|
||||||
|
|
||||||
|
let pipeline_id = PipelineId::new();
|
||||||
|
let script_to_constellation_chan = ScriptToConstellationChan {
|
||||||
|
sender: senders.pipeline_to_constellation_sender.clone(),
|
||||||
|
pipeline_id,
|
||||||
|
};
|
||||||
|
let debugger_global = DebuggerGlobalScope::new(
|
||||||
|
&js_runtime.clone(),
|
||||||
|
senders.self_sender.clone(),
|
||||||
|
senders.devtools_server_sender.clone(),
|
||||||
|
senders.memory_profiler_sender.clone(),
|
||||||
|
senders.time_profiler_sender.clone(),
|
||||||
|
script_to_constellation_chan,
|
||||||
|
state.resource_threads.clone(),
|
||||||
|
#[cfg(feature = "webgpu")]
|
||||||
|
gpu_id_hub.clone(),
|
||||||
|
CanGc::note(),
|
||||||
|
);
|
||||||
|
debugger_global.execute(CanGc::note());
|
||||||
|
|
||||||
ScriptThread {
|
ScriptThread {
|
||||||
documents: DomRefCell::new(DocumentCollection::default()),
|
documents: DomRefCell::new(DocumentCollection::default()),
|
||||||
last_render_opportunity_time: Default::default(),
|
last_render_opportunity_time: Default::default(),
|
||||||
|
@ -942,8 +969,8 @@ impl ScriptThread {
|
||||||
background_hang_monitor,
|
background_hang_monitor,
|
||||||
closing,
|
closing,
|
||||||
timer_scheduler: Default::default(),
|
timer_scheduler: Default::default(),
|
||||||
microtask_queue: runtime.microtask_queue.clone(),
|
microtask_queue,
|
||||||
js_runtime: Rc::new(runtime),
|
js_runtime,
|
||||||
topmost_mouse_over_target: MutNullableDom::new(Default::default()),
|
topmost_mouse_over_target: MutNullableDom::new(Default::default()),
|
||||||
closed_pipelines: DomRefCell::new(HashSet::new()),
|
closed_pipelines: DomRefCell::new(HashSet::new()),
|
||||||
mutation_observer_microtask_queued: Default::default(),
|
mutation_observer_microtask_queued: Default::default(),
|
||||||
|
@ -967,12 +994,13 @@ impl ScriptThread {
|
||||||
pipeline_to_node_ids: Default::default(),
|
pipeline_to_node_ids: Default::default(),
|
||||||
is_user_interacting: Cell::new(false),
|
is_user_interacting: Cell::new(false),
|
||||||
#[cfg(feature = "webgpu")]
|
#[cfg(feature = "webgpu")]
|
||||||
gpu_id_hub: Arc::new(IdentityHub::default()),
|
gpu_id_hub,
|
||||||
inherited_secure_context: state.inherited_secure_context,
|
inherited_secure_context: state.inherited_secure_context,
|
||||||
layout_factory,
|
layout_factory,
|
||||||
relative_mouse_down_point: Cell::new(Point2D::zero()),
|
relative_mouse_down_point: Cell::new(Point2D::zero()),
|
||||||
scheduled_script_thread_animation_timer: Default::default(),
|
scheduled_script_thread_animation_timer: Default::default(),
|
||||||
has_pending_animation_tick: Arc::new(AtomicBool::new(false)),
|
has_pending_animation_tick: Arc::new(AtomicBool::new(false)),
|
||||||
|
debugger_global: debugger_global.as_traced(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,10 @@ DOMInterfaces = {
|
||||||
'canGc': ['IndexedGetter', 'Add', 'Add_']
|
'canGc': ['IndexedGetter', 'Add', 'Add_']
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'DebuggerGlobalScope': {
|
||||||
|
'useSystemCompartment': True,
|
||||||
|
},
|
||||||
|
|
||||||
'Document': {
|
'Document': {
|
||||||
'additionalTraits': ["crate::interfaces::DocumentHelpers"],
|
'additionalTraits': ["crate::interfaces::DocumentHelpers"],
|
||||||
'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', 'GetScrollingElement', 'ExitFullscreen', 'CreateExpression', 'CreateNSResolver', 'Evaluate', 'StyleSheets', 'Implementation', 'GetElementsByTagName', 'GetElementsByTagNameNS', 'GetElementsByClassName', 'AdoptNode', 'CreateNodeIterator', 'SetBody', 'GetElementsByName', 'Images', 'Embeds', 'Plugins', 'Links', 'Forms', 'Scripts', 'Anchors', 'Applets', 'Children', 'GetSelection', 'NamedGetter', 'AdoptedStyleSheets'],
|
'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', 'GetScrollingElement', 'ExitFullscreen', 'CreateExpression', 'CreateNSResolver', 'Evaluate', 'StyleSheets', 'Implementation', 'GetElementsByTagName', 'GetElementsByTagNameNS', 'GetElementsByClassName', 'AdoptNode', 'CreateNodeIterator', 'SetBody', 'GetElementsByName', 'Images', 'Embeds', 'Plugins', 'Links', 'Forms', 'Scripts', 'Anchors', 'Applets', 'Children', 'GetSelection', 'NamedGetter', 'AdoptedStyleSheets'],
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/* 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.
|
||||||
|
[Global=DebuggerGlobalScope, Exposed=DebuggerGlobalScope]
|
||||||
|
interface DebuggerGlobalScope: GlobalScope {
|
||||||
|
};
|
|
@ -5,7 +5,7 @@
|
||||||
* https://dom.spec.whatwg.org/#interface-eventtarget
|
* https://dom.spec.whatwg.org/#interface-eventtarget
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[Exposed=(Window,Worker,Worklet,DissimilarOriginWindow)]
|
[Exposed=(Window,Worker,Worklet,DissimilarOriginWindow,DebuggerGlobalScope)]
|
||||||
interface EventTarget {
|
interface EventTarget {
|
||||||
[Throws] constructor();
|
[Throws] constructor();
|
||||||
undefined addEventListener(
|
undefined addEventListener(
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
// This interface is entirely internal to Servo, and should not be accessible to
|
// This interface is entirely internal to Servo, and should not be accessible to
|
||||||
// web pages.
|
// web pages.
|
||||||
|
|
||||||
[Exposed=(Window,Worker,Worklet,DissimilarOriginWindow),
|
[Exposed=(Window,Worker,Worklet,DissimilarOriginWindow,DebuggerGlobalScope),
|
||||||
Inline]
|
Inline]
|
||||||
interface GlobalScope : EventTarget {};
|
interface GlobalScope : EventTarget {};
|
||||||
|
|
|
@ -109,6 +109,8 @@ pub enum Resource {
|
||||||
DirectoryListingHTML,
|
DirectoryListingHTML,
|
||||||
/// A HTML page that is used for the about:memory url.
|
/// A HTML page that is used for the about:memory url.
|
||||||
AboutMemoryHTML,
|
AboutMemoryHTML,
|
||||||
|
/// RPC script for the Debugger API on behalf of devtools.
|
||||||
|
DebuggerJS,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resource {
|
impl Resource {
|
||||||
|
@ -123,6 +125,7 @@ impl Resource {
|
||||||
Resource::CrashHTML => "crash.html",
|
Resource::CrashHTML => "crash.html",
|
||||||
Resource::DirectoryListingHTML => "directory-listing.html",
|
Resource::DirectoryListingHTML => "directory-listing.html",
|
||||||
Resource::AboutMemoryHTML => "about-memory.html",
|
Resource::AboutMemoryHTML => "about-memory.html",
|
||||||
|
Resource::DebuggerJS => "debugger.js",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,6 +170,7 @@ fn resources_for_tests() -> Box<dyn ResourceReaderMethods + Sync + Send> {
|
||||||
Resource::AboutMemoryHTML => {
|
Resource::AboutMemoryHTML => {
|
||||||
&include_bytes!("../../../resources/about-memory.html")[..]
|
&include_bytes!("../../../resources/about-memory.html")[..]
|
||||||
},
|
},
|
||||||
|
Resource::DebuggerJS => &include_bytes!("../../../resources/debugger.js")[..],
|
||||||
}
|
}
|
||||||
.to_owned()
|
.to_owned()
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ impl ResourceReaderMethods for ResourceReaderInstance {
|
||||||
Resource::AboutMemoryHTML => {
|
Resource::AboutMemoryHTML => {
|
||||||
&include_bytes!("../../../../resources/about-memory.html")[..]
|
&include_bytes!("../../../../resources/about-memory.html")[..]
|
||||||
},
|
},
|
||||||
|
Resource::DebuggerJS => &include_bytes!("../../../../resources/debugger.js")[..],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
3
resources/debugger.js
Normal file
3
resources/debugger.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
if (!("dbg" in this)) {
|
||||||
|
dbg = new Debugger;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue