diff --git a/components/script/dom/debuggerglobalscope.rs b/components/script/dom/debuggerglobalscope.rs
new file mode 100644
index 00000000000..0c69b8106d5
--- /dev/null
+++ b/components/script/dom/debuggerglobalscope.rs
@@ -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.
+///
+///
+pub(crate) struct DebuggerGlobalScope {
+ global_scope: GlobalScope,
+ script_chan: Sender,
+}
+
+impl DebuggerGlobalScope {
+ /// Create a new heap-allocated `DebuggerGlobalScope`.
+ #[allow(unsafe_code, clippy::too_many_arguments)]
+ pub(crate) fn new(
+ runtime: &Runtime,
+ script_chan: Sender,
+ devtools_chan: Option>,
+ 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,
+ can_gc: CanGc,
+ ) -> DomRoot {
+ 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::(
+ 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);
+ }
+ }
+}
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index 01ff74c7c49..b2ab674e29d 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -114,7 +114,7 @@ use crate::dom::reportingobserver::ReportingObserver;
use crate::dom::serviceworker::ServiceWorker;
use crate::dom::serviceworkerregistration::ServiceWorkerRegistration;
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
-use crate::dom::types::MessageEvent;
+use crate::dom::types::{DebuggerGlobalScope, MessageEvent};
use crate::dom::underlyingsourcecontainer::UnderlyingSourceType;
#[cfg(feature = "webgpu")]
use crate::dom::webgpu::gpudevice::GPUDevice;
@@ -2530,6 +2530,9 @@ impl GlobalScope {
// https://drafts.css-houdini.org/worklets/#script-settings-for-worklets
return worklet.base_url();
}
+ if let Some(_debugger_global) = self.downcast::() {
+ return self.creation_url.clone();
+ }
unreachable!();
}
@@ -2545,6 +2548,9 @@ impl GlobalScope {
// TODO: is this the right URL to return?
return worklet.base_url();
}
+ if let Some(_debugger_global) = self.downcast::() {
+ return self.creation_url.clone();
+ }
unreachable!();
}
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 20f26e19655..b98cb689b11 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 debuggerglobalscope;
pub(crate) mod dedicatedworkerglobalscope;
pub(crate) mod defaultteereadrequest;
pub(crate) mod defaultteeunderlyingsource;
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 1d4b8bd304c..f291b23c89d 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -133,6 +133,7 @@ use crate::dom::htmlslotelement::HTMLSlotElement;
use crate::dom::mutationobserver::MutationObserver;
use crate::dom::node::{Node, NodeTraits, ShadowIncluding};
use crate::dom::servoparser::{ParserContext, ServoParser};
+use crate::dom::types::DebuggerGlobalScope;
#[cfg(feature = "webgpu")]
use crate::dom::webgpu::identityhub::IdentityHub;
use crate::dom::window::Window;
@@ -348,6 +349,8 @@ pub struct ScriptThread {
/// itself is managing animations the the timer fired triggering a [`ScriptThread`]-based
/// animation tick.
has_pending_animation_tick: Arc,
+
+ debugger_global: Dom,
}
struct BHMExitSignal {
@@ -928,6 +931,30 @@ impl ScriptThread {
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 {
documents: DomRefCell::new(DocumentCollection::default()),
last_render_opportunity_time: Default::default(),
@@ -942,8 +969,8 @@ impl ScriptThread {
background_hang_monitor,
closing,
timer_scheduler: Default::default(),
- microtask_queue: runtime.microtask_queue.clone(),
- js_runtime: Rc::new(runtime),
+ microtask_queue,
+ js_runtime,
topmost_mouse_over_target: MutNullableDom::new(Default::default()),
closed_pipelines: DomRefCell::new(HashSet::new()),
mutation_observer_microtask_queued: Default::default(),
@@ -967,12 +994,13 @@ impl ScriptThread {
pipeline_to_node_ids: Default::default(),
is_user_interacting: Cell::new(false),
#[cfg(feature = "webgpu")]
- gpu_id_hub: Arc::new(IdentityHub::default()),
+ gpu_id_hub,
inherited_secure_context: state.inherited_secure_context,
layout_factory,
relative_mouse_down_point: Cell::new(Point2D::zero()),
scheduled_script_thread_animation_timer: Default::default(),
has_pending_animation_tick: Arc::new(AtomicBool::new(false)),
+ debugger_global: debugger_global.as_traced(),
}
}
diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf
index 87486fcab45..2a4aaa82439 100644
--- a/components/script_bindings/codegen/Bindings.conf
+++ b/components/script_bindings/codegen/Bindings.conf
@@ -166,6 +166,10 @@ DOMInterfaces = {
'canGc': ['IndexedGetter', 'Add', 'Add_']
},
+'DebuggerGlobalScope': {
+ 'useSystemCompartment': True,
+},
+
'Document': {
'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'],
diff --git a/components/script_bindings/webidls/DebuggerGlobalScope.webidl b/components/script_bindings/webidls/DebuggerGlobalScope.webidl
new file mode 100644
index 00000000000..4f85602a157
--- /dev/null
+++ b/components/script_bindings/webidls/DebuggerGlobalScope.webidl
@@ -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 {
+};
diff --git a/components/script_bindings/webidls/EventTarget.webidl b/components/script_bindings/webidls/EventTarget.webidl
index a8a63cc2b32..411fb962d32 100644
--- a/components/script_bindings/webidls/EventTarget.webidl
+++ b/components/script_bindings/webidls/EventTarget.webidl
@@ -5,7 +5,7 @@
* https://dom.spec.whatwg.org/#interface-eventtarget
*/
-[Exposed=(Window,Worker,Worklet,DissimilarOriginWindow)]
+[Exposed=(Window,Worker,Worklet,DissimilarOriginWindow,DebuggerGlobalScope)]
interface EventTarget {
[Throws] constructor();
undefined addEventListener(
diff --git a/components/script_bindings/webidls/GlobalScope.webidl b/components/script_bindings/webidls/GlobalScope.webidl
index 57206d13e6a..dfa44738781 100644
--- a/components/script_bindings/webidls/GlobalScope.webidl
+++ b/components/script_bindings/webidls/GlobalScope.webidl
@@ -5,6 +5,6 @@
// This interface is entirely internal to Servo, and should not be accessible to
// web pages.
-[Exposed=(Window,Worker,Worklet,DissimilarOriginWindow),
+[Exposed=(Window,Worker,Worklet,DissimilarOriginWindow,DebuggerGlobalScope),
Inline]
interface GlobalScope : EventTarget {};
diff --git a/components/shared/embedder/resources.rs b/components/shared/embedder/resources.rs
index 28464a95d1a..7b7a1b61ae0 100644
--- a/components/shared/embedder/resources.rs
+++ b/components/shared/embedder/resources.rs
@@ -109,6 +109,8 @@ pub enum Resource {
DirectoryListingHTML,
/// A HTML page that is used for the about:memory url.
AboutMemoryHTML,
+ /// RPC script for the Debugger API on behalf of devtools.
+ DebuggerJS,
}
impl Resource {
@@ -123,6 +125,7 @@ impl Resource {
Resource::CrashHTML => "crash.html",
Resource::DirectoryListingHTML => "directory-listing.html",
Resource::AboutMemoryHTML => "about-memory.html",
+ Resource::DebuggerJS => "debugger.js",
}
}
}
@@ -167,6 +170,7 @@ fn resources_for_tests() -> Box {
Resource::AboutMemoryHTML => {
&include_bytes!("../../../resources/about-memory.html")[..]
},
+ Resource::DebuggerJS => &include_bytes!("../../../resources/debugger.js")[..],
}
.to_owned()
}
diff --git a/ports/servoshell/egl/android/resources.rs b/ports/servoshell/egl/android/resources.rs
index 2de3790060f..f8d6accc39a 100644
--- a/ports/servoshell/egl/android/resources.rs
+++ b/ports/servoshell/egl/android/resources.rs
@@ -33,6 +33,7 @@ impl ResourceReaderMethods for ResourceReaderInstance {
Resource::AboutMemoryHTML => {
&include_bytes!("../../../../resources/about-memory.html")[..]
},
+ Resource::DebuggerJS => &include_bytes!("../../../../resources/debugger.js")[..],
})
}
diff --git a/resources/debugger.js b/resources/debugger.js
new file mode 100644
index 00000000000..6d1e4b19bc8
--- /dev/null
+++ b/resources/debugger.js
@@ -0,0 +1,3 @@
+if (!("dbg" in this)) {
+ dbg = new Debugger;
+}