diff --git a/components/script/dom/debuggerevent.rs b/components/script/dom/debuggerevent.rs index ad3a4261df7..4c69f23cd43 100644 --- a/components/script/dom/debuggerevent.rs +++ b/components/script/dom/debuggerevent.rs @@ -8,13 +8,14 @@ use dom_struct::dom_struct; use js::jsapi::{JSObject, Value}; use script_bindings::conversions::SafeToJSValConvertible; use script_bindings::reflector::DomObject; +use script_bindings::str::DOMString; 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::dom::types::{GlobalScope, PipelineId}; use crate::script_runtime::CanGc; #[dom_struct] @@ -22,17 +23,23 @@ use crate::script_runtime::CanGc; pub(crate) struct DebuggerEvent { event: Event, global: Dom, + pipeline_id: Dom, + worker_id: Option, } impl DebuggerEvent { pub(crate) fn new( debugger_global: &GlobalScope, global: &GlobalScope, + pipeline_id: &PipelineId, + worker_id: Option, can_gc: CanGc, ) -> DomRoot { let result = Box::new(Self { event: Event::new_inherited(), global: Dom::from_ref(global), + pipeline_id: Dom::from_ref(pipeline_id), + worker_id, }); let result = reflect_dom_object(result, debugger_global, can_gc); result.event.init_event("addDebuggee".into(), false, false); @@ -53,6 +60,16 @@ impl DebuggerEventMethods for DebuggerEvent { NonNull::new(result.to_object()).unwrap() } + fn PipelineId( + &self, + ) -> DomRoot<::PipelineId> { + DomRoot::from_ref(&self.pipeline_id) + } + + fn GetWorkerId(&self) -> Option { + self.worker_id.clone() + } + fn IsTrusted(&self) -> bool { self.event.IsTrusted() } diff --git a/components/script/dom/debuggerglobalscope.rs b/components/script/dom/debuggerglobalscope.rs index 0429b7593db..1fcf20c7cd8 100644 --- a/components/script/dom/debuggerglobalscope.rs +++ b/components/script/dom/debuggerglobalscope.rs @@ -4,8 +4,7 @@ use base::id::PipelineId; use constellation_traits::ScriptToConstellationChan; -use crossbeam_channel::Sender; -use devtools_traits::ScriptToDevtoolsControlMsg; +use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use dom_struct::dom_struct; use embedder_traits::resources::{self, Resource}; use ipc_channel::ipc::IpcSender; @@ -22,14 +21,12 @@ 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; use crate::dom::types::{DebuggerEvent, Event}; #[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}; @@ -40,15 +37,20 @@ use crate::script_runtime::{CanGc, JSContext}; /// pub(crate) struct DebuggerGlobalScope { global_scope: GlobalScope, - script_chan: Sender, } impl DebuggerGlobalScope { /// Create a new heap-allocated `DebuggerGlobalScope`. + /// + /// `debugger_pipeline_id` is the pipeline id to use when creating the debugger’s [`GlobalScope`]: + /// - in normal script threads, it should be set to `PipelineId::new()`, because those threads can generate + /// pipeline ids, and they may contain debuggees from more than one pipeline + /// - in web worker threads, it should be set to the pipeline id of the page that created the thread, because + /// those threads can’t generate pipeline ids, and they only contain one debuggee from one pipeline #[allow(unsafe_code, clippy::too_many_arguments)] pub(crate) fn new( runtime: &Runtime, - script_chan: Sender, + debugger_pipeline_id: PipelineId, devtools_chan: Option>, mem_profiler_chan: mem::ProfilerChan, time_profiler_chan: time::ProfilerChan, @@ -59,7 +61,7 @@ impl DebuggerGlobalScope { ) -> DomRoot { let global = Box::new(Self { global_scope: GlobalScope::new_inherited( - PipelineId::new(), + debugger_pipeline_id, devtools_chan, mem_profiler_chan, time_profiler_chan, @@ -75,7 +77,6 @@ impl DebuggerGlobalScope { None, false, ), - script_chan, }); let global = unsafe { DebuggerGlobalScopeBinding::Wrap::( @@ -122,10 +123,24 @@ impl DebuggerGlobalScope { } } - pub(crate) fn fire_add_debuggee(&self, can_gc: CanGc, global: &GlobalScope) { + pub(crate) fn fire_add_debuggee( + &self, + can_gc: CanGc, + debuggee_global: &GlobalScope, + debuggee_pipeline_id: PipelineId, + debuggee_worker_id: Option, + ) { + let debuggee_pipeline_id = + crate::dom::pipelineid::PipelineId::new(self.upcast(), debuggee_pipeline_id, can_gc); + let event = DomRoot::upcast::(DebuggerEvent::new( + self.upcast(), + debuggee_global, + &debuggee_pipeline_id, + debuggee_worker_id.map(|id| id.to_string().into()), + can_gc, + )); assert!( - DomRoot::upcast::(DebuggerEvent::new(self.upcast(), global, can_gc)) - .fire(self.upcast(), can_gc), + DomRoot::upcast::(event).fire(self.upcast(), can_gc), "Guaranteed by DebuggerEvent::new" ); } diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 671da3c2c48..aceff6e03ea 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -51,6 +51,7 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::messageevent::MessageEvent; use crate::dom::reportingendpoint::ReportingEndpoint; +use crate::dom::types::DebuggerGlobalScope; #[cfg(feature = "webgpu")] use crate::dom::webgpu::identityhub::IdentityHub; use crate::dom::worker::{TrustedWorkerAddress, Worker}; @@ -427,6 +428,19 @@ impl DedicatedWorkerGlobalScope { }; Runtime::new_with_parent(Some(parent), Some(task_source)) }; + let debugger_global = DebuggerGlobalScope::new( + &runtime, + pipeline_id, + init.to_devtools_sender.clone(), + init.mem_profiler_chan.clone(), + init.time_profiler_chan.clone(), + init.script_to_constellation_chan.clone(), + init.resource_threads.clone(), + #[cfg(feature = "webgpu")] + gpu_id_hub.clone(), + CanGc::note(), + ); + debugger_global.execute(CanGc::note()); let context_for_interrupt = runtime.thread_safe_js_context(); let _ = context_sender.send(context_for_interrupt); @@ -453,6 +467,7 @@ impl DedicatedWorkerGlobalScope { } } + let worker_id = init.worker_id; let global = DedicatedWorkerGlobalScope::new( init, DOMString::from_string(worker_name), @@ -471,6 +486,12 @@ impl DedicatedWorkerGlobalScope { control_receiver, insecure_requests_policy, ); + debugger_global.fire_add_debuggee( + CanGc::note(), + global.upcast(), + pipeline_id, + Some(worker_id), + ); // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter // registration (#6631), so we instead use a random number and cross our fingers. let scope = global.upcast::(); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 630ad4f9fb2..ec9c1733b29 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -500,6 +500,7 @@ pub(crate) mod performancepainttiming; pub(crate) mod performanceresourcetiming; pub(crate) mod permissions; pub(crate) mod permissionstatus; +pub(crate) mod pipelineid; pub(crate) mod plugin; pub(crate) mod pluginarray; #[allow(dead_code)] diff --git a/components/script/dom/pipelineid.rs b/components/script/dom/pipelineid.rs new file mode 100644 index 00000000000..3a8482d175e --- /dev/null +++ b/components/script/dom/pipelineid.rs @@ -0,0 +1,46 @@ +/* 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 dom_struct::dom_struct; + +use crate::dom::bindings::codegen::Bindings::DebuggerEventBinding::PipelineIdMethods; +use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::globalscope::GlobalScope; +use crate::script_runtime::CanGc; + +#[dom_struct] +pub(crate) struct PipelineId { + reflector_: Reflector, + #[no_trace] + inner: base::id::PipelineId, +} + +impl PipelineId { + pub(crate) fn new( + global: &GlobalScope, + pipeline_id: base::id::PipelineId, + can_gc: CanGc, + ) -> DomRoot { + reflect_dom_object( + Box::new(Self { + reflector_: Reflector::new(), + inner: pipeline_id, + }), + global, + can_gc, + ) + } +} + +impl PipelineIdMethods for PipelineId { + // check-tidy: no specs after this line + fn NamespaceId(&self) -> u32 { + self.inner.namespace_id.0 + } + + fn Index(&self) -> u32 { + self.inner.index.0.get() + } +} diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 59bf9c585c8..a35d96c3280 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -950,7 +950,7 @@ impl ScriptThread { }; let debugger_global = DebuggerGlobalScope::new( &js_runtime.clone(), - senders.self_sender.clone(), + PipelineId::new(), senders.devtools_server_sender.clone(), senders.memory_profiler_sender.clone(), senders.time_profiler_sender.clone(), @@ -3446,8 +3446,12 @@ impl ScriptThread { incomplete.load_data.inherited_secure_context, incomplete.theme, ); - self.debugger_global - .fire_add_debuggee(can_gc, window.upcast()); + self.debugger_global.fire_add_debuggee( + can_gc, + window.upcast(), + incomplete.pipeline_id, + None, + ); let _realm = enter_realm(&*window); diff --git a/components/script_bindings/webidls/DebuggerEvent.webidl b/components/script_bindings/webidls/DebuggerEvent.webidl index a0682fc3165..089d87fd6c0 100644 --- a/components/script_bindings/webidls/DebuggerEvent.webidl +++ b/components/script_bindings/webidls/DebuggerEvent.webidl @@ -7,4 +7,12 @@ [Exposed=DebuggerGlobalScope] interface DebuggerEvent : Event { readonly attribute object global; + readonly attribute PipelineId pipelineId; + readonly attribute DOMString? workerId; +}; + +[Exposed=DebuggerGlobalScope] +interface PipelineId { + readonly attribute unsigned long namespaceId; + readonly attribute unsigned long index; }; diff --git a/components/shared/devtools/lib.rs b/components/shared/devtools/lib.rs index 696d3a8b4c5..3a044a8f4ae 100644 --- a/components/shared/devtools/lib.rs +++ b/components/shared/devtools/lib.rs @@ -12,7 +12,9 @@ use core::fmt; use std::collections::HashMap; +use std::fmt::Display; use std::net::TcpStream; +use std::str::FromStr; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use base::cross_process_instant::CrossProcessInstant; @@ -488,6 +490,18 @@ impl StartedTimelineMarker { } #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] pub struct WorkerId(pub Uuid); +impl Display for WorkerId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} +impl FromStr for WorkerId { + type Err = uuid::Error; + + fn from_str(s: &str) -> Result { + Ok(Self(s.parse()?)) + } +} #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] diff --git a/resources/debugger.js b/resources/debugger.js index e299c1f66ec..2239783b914 100644 --- a/resources/debugger.js +++ b/resources/debugger.js @@ -1,14 +1,42 @@ -if (!("dbg" in this)) { - dbg = new Debugger; - - dbg.onNewGlobalObject = function(global) { - }; - - dbg.onNewScript = function(script, global) { - }; +if ("dbg" in this) { + throw new Error("Debugger script must not run more than once!"); } +const dbg = new Debugger; +const debuggeesToPipelineIds = new Map; +const debuggeesToWorkerIds = new Map; + +dbg.onNewGlobalObject = function(global) { +}; + +dbg.onNewScript = function(script, /* undefined; seems to be `script.global` now */ global) { + try { + // TODO: notify script system about new source + /* notifyNewSource */({ + pipelineId: debuggeesToPipelineIds.get(script.global), + workerId: debuggeesToWorkerIds.get(script.global), + spidermonkeyId: script.source.id, + url: script.source.url, + urlOverride: script.source.displayURL, + text: script.source.text, + introductionType: script.source.introductionType ?? null, + }); + } catch (error) { + logError(error); + } +}; + addEventListener("addDebuggee", event => { - const {global} = event; + const {global, pipelineId: {namespaceId, index}, workerId} = event; dbg.addDebuggee(global); + const debuggerObject = dbg.addDebuggee(global); + debuggeesToPipelineIds.set(debuggerObject, { + namespaceId, + index, + }); + debuggeesToWorkerIds.set(debuggerObject, workerId); }); + +function logError(error) { + console.log(`[debugger] ERROR at ${error.fileName}:${error.lineNumber}:${error.columnNumber}: ${error.name}: ${error.message}`); +}