diff --git a/components/devtools/actors/source.rs b/components/devtools/actors/source.rs index ba659b2653f..ec48f8146df 100644 --- a/components/devtools/actors/source.rs +++ b/components/devtools/actors/source.rs @@ -53,6 +53,8 @@ pub struct SourceActor { pub content: Option, pub content_type: Option, + + pub spidermonkey_id: u32, } #[derive(Serialize)] @@ -103,6 +105,7 @@ impl SourceActor { url: ServoUrl, content: Option, content_type: Option, + spidermonkey_id: u32, ) -> SourceActor { SourceActor { name, @@ -110,6 +113,7 @@ impl SourceActor { content, content_type, is_black_boxed: false, + spidermonkey_id, } } @@ -119,10 +123,17 @@ impl SourceActor { url: ServoUrl, content: Option, content_type: Option, + spidermonkey_id: u32, ) -> &SourceActor { let source_actor_name = actors.new_name("source"); - let source_actor = SourceActor::new(source_actor_name.clone(), url, content, content_type); + let source_actor = SourceActor::new( + source_actor_name.clone(), + url, + content, + content_type, + spidermonkey_id, + ); actors.register(Box::new(source_actor)); actors.register_source_actor(pipeline_id, &source_actor_name); diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 22594c9ddd8..2552f798d2e 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -549,6 +549,7 @@ impl DevtoolsInstance { source_info.url, source_info.content, source_info.content_type, + source_info.spidermonkey_id, ); let source_actor_name = source_actor.name.clone(); let source_form = source_actor.source_form(); diff --git a/components/script/dom/debuggerglobalscope.rs b/components/script/dom/debuggerglobalscope.rs index d1e3a102d20..5bc160babbf 100644 --- a/components/script/dom/debuggerglobalscope.rs +++ b/components/script/dom/debuggerglobalscope.rs @@ -4,18 +4,22 @@ use std::sync::Arc; -use base::id::PipelineId; +use base::id::{Index, PipelineId, PipelineNamespaceId}; use constellation_traits::ScriptToConstellationChan; use crossbeam_channel::Sender; -use devtools_traits::ScriptToDevtoolsControlMsg; +use devtools_traits::{ScriptToDevtoolsControlMsg, SourceInfo}; use dom_struct::dom_struct; use embedder_traits::resources::{self, Resource}; use ipc_channel::ipc::IpcSender; -use js::jsval::UndefinedValue; +use js::gc::HandleValue; +use js::jsval::{UInt32Value, UndefinedValue}; use js::rust::Runtime; use js::rust::wrappers::JS_DefineDebuggerObject; use net_traits::ResourceThreads; use profile_traits::{mem, time}; +use script_bindings::codegen::GenericBindings::DebuggerGlobalScopeBinding::{ + DebuggerGlobalScopeMethods, NotifyNewSource, +}; use script_bindings::conversions::SafeToJSValConvertible; use script_bindings::realms::InRealm; use script_bindings::reflector::DomObject; @@ -102,6 +106,10 @@ impl DebuggerGlobalScope { GlobalScope::get_cx() } + pub(crate) fn as_global_scope(&self) -> &GlobalScope { + self.upcast::() + } + 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( @@ -120,29 +128,83 @@ impl DebuggerGlobalScope { } #[allow(unsafe_code)] - pub(crate) fn execute_with_global(&self, can_gc: CanGc, global: &GlobalScope) { + pub(crate) fn execute_new_global( + &self, + can_gc: CanGc, + global: &GlobalScope, + pipeline_id: PipelineId, + ) { let cx = Self::get_cx(); - rooted!(in(*cx) let mut property_value = UndefinedValue()); + rooted!(in(*cx) let pipeline_namespace_id = UInt32Value(pipeline_id.namespace_id.0)); + rooted!(in(*cx) let pipeline_index = UInt32Value(pipeline_id.index.0.get())); + rooted!(in(*cx) let mut debuggee = UndefinedValue()); let _realm = enter_realm(self); // Convert the debuggee global’s 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. - global - .reflector() - .safe_to_jsval(cx, property_value.handle_mut()); + global.reflector().safe_to_jsval(cx, debuggee.handle_mut()); // TODO: what invariants do we need to uphold for the unsafe call? - if let Err(()) = unsafe { - set_dictionary_property( - *cx, - self.global_scope.reflector().get_jsobject(), - "debuggee", - property_value.handle(), - ) - } { - warn!("Failed to set debuggee"); + if let Err(()) = (|| -> Result<(), ()> { + unsafe { + set_dictionary_property( + *cx, + self.global_scope.reflector().get_jsobject(), + "pipelineNamespaceId", + pipeline_namespace_id.handle(), + )?; + set_dictionary_property( + *cx, + self.global_scope.reflector().get_jsobject(), + "pipelineIndex", + pipeline_index.handle(), + )?; + set_dictionary_property( + *cx, + self.global_scope.reflector().get_jsobject(), + "debuggee", + debuggee.handle(), + ) + } + })() { + warn!("Failed to set properties"); return; } self.execute(can_gc); } } + +impl DebuggerGlobalScopeMethods for DebuggerGlobalScope { + // check-tidy: no specs after this line + fn NotifyNewSource(&self, args: &NotifyNewSource) { + info!( + "NotifyNewSource: ({},{}) {} {} {}", + args.pipelineId.namespaceId, + args.pipelineId.index, + args.spidermonkeyId, + args.url, + args.text + ); + if let Some(devtools_chan) = self.as_global_scope().devtools_chan() { + let pipeline_id = PipelineId { + namespace_id: PipelineNamespaceId(args.pipelineId.namespaceId), + index: Index::new(args.pipelineId.index) + .expect("`pipelineId.index` must not be zero"), + }; + let source_info = SourceInfo { + url: ServoUrl::parse(args.url.str()).expect("Failed to parse url"), + external: true, // TODO + worker_id: None, // TODO + content: Some(args.text.to_string()), + content_type: None, // TODO + spidermonkey_id: args.spidermonkeyId, + }; + devtools_chan + .send(ScriptToDevtoolsControlMsg::CreateSourceActor( + pipeline_id, + source_info, + )) + .expect("Failed to send to devtools server"); + } + } +} diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 30404d23757..c62699e09cc 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -9,7 +9,7 @@ use std::thread::{self, JoinHandle}; use base::id::{BrowsingContextId, PipelineId, WebViewId}; use constellation_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; use crossbeam_channel::{Receiver, Sender, unbounded}; -use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, SourceInfo}; +use devtools_traits::DevtoolScriptControlMsg; use dom_struct::dom_struct; use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader}; use ipc_channel::ipc::IpcReceiver; @@ -513,20 +513,6 @@ impl DedicatedWorkerGlobalScope { )); global_scope.set_https_state(metadata.https_state); let source = String::from_utf8_lossy(&bytes); - if let Some(chan) = global_scope.devtools_chan() { - let pipeline_id = global_scope.pipeline_id(); - let source_info = SourceInfo { - url: metadata.final_url, - external: true, // Worker scripts are always external. - worker_id: Some(global.upcast::().get_worker_id()), - content: Some(source.to_string()), - content_type: metadata.content_type.map(|c_type| c_type.0.to_string()), - }; - let _ = chan.send(ScriptToDevtoolsControlMsg::CreateSourceActor( - pipeline_id, - source_info, - )); - } unsafe { // Handle interrupt requests diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 7a028504109..548791a8149 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -1091,40 +1091,6 @@ impl HTMLScriptElement { Ok(script) => script, }; - if let Some(chan) = self.global().devtools_chan() { - let pipeline_id = self.global().pipeline_id(); - - let (url, content, content_type, is_external) = if script.external { - let content = match &script.code { - SourceCode::Text(text) => text.to_string(), - SourceCode::Compiled(compiled) => compiled.original_text.to_string(), - }; - - // content_type: https://html.spec.whatwg.org/multipage/#scriptingLanguages - (script.url.clone(), Some(content), "text/javascript", true) - } else { - // TODO: if needed, fetch the page again, in the same way as in the original request. - // Fetch it from cache, even if the original request was non-idempotent (e.g. POST). - // If we can’t fetch it from cache, we should probably give up, because with a real - // fetch, the server could return a different response. - - // TODO: handle cases where Content-Type is not text/html. - (doc.url(), None, "text/html", false) - }; - - let source_info = SourceInfo { - url, - external: is_external, - worker_id: None, - content, - content_type: Some(content_type.to_string()), - }; - let _ = chan.send(ScriptToDevtoolsControlMsg::CreateSourceActor( - pipeline_id, - source_info, - )); - } - if script.type_ == ScriptType::Classic { unminify_js(&mut script); self.substitute_with_local_script(&mut script); diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index f51ea3fc44a..73bdeea7a71 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -3430,8 +3430,9 @@ impl ScriptThread { incomplete.load_data.inherited_secure_context, incomplete.theme, ); + // TODO: call this for workers too self.debugger_global - .execute_with_global(can_gc, window.upcast()); + .execute_new_global(can_gc, window.upcast(), incomplete.pipeline_id); let _realm = enter_realm(&*window); diff --git a/components/script_bindings/webidls/DebuggerGlobalScope.webidl b/components/script_bindings/webidls/DebuggerGlobalScope.webidl index 4f85602a157..cd45fb4be35 100644 --- a/components/script_bindings/webidls/DebuggerGlobalScope.webidl +++ b/components/script_bindings/webidls/DebuggerGlobalScope.webidl @@ -6,4 +6,24 @@ // web pages. [Global=DebuggerGlobalScope, Exposed=DebuggerGlobalScope] interface DebuggerGlobalScope: GlobalScope { + undefined notifyNewSource(NotifyNewSource args); +}; + +// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-window-interface +dictionary NotifyNewSource { + required PipelineId pipelineId; + required unsigned long spidermonkeyId; + required DOMString url; + required DOMString text; + + // FIXME: error[E0599]: the method `trace` exists for reference `&Option>`, but + // its trait bounds were not satisfied + // Uint8Array binary; + + // TODO: contentType +}; + +dictionary PipelineId { + required unsigned long namespaceId; + required unsigned long index; }; diff --git a/components/shared/devtools/lib.rs b/components/shared/devtools/lib.rs index a1bc247ccbb..63523ebbff7 100644 --- a/components/shared/devtools/lib.rs +++ b/components/shared/devtools/lib.rs @@ -587,4 +587,5 @@ pub struct SourceInfo { pub worker_id: Option, pub content: Option, pub content_type: Option, + pub spidermonkey_id: u32, } diff --git a/resources/debugger.js b/resources/debugger.js index bae8988006d..a9556efbec5 100644 --- a/resources/debugger.js +++ b/resources/debugger.js @@ -1,14 +1,28 @@ if (!("dbg" in this)) { dbg = new Debugger; + debuggeesToPipelineIds = new Map; dbg.onNewGlobalObject = function(global) { - console.log("[debugger] onNewGlobalObject"); - console.log(this, global); + console.log("[debugger] onNewGlobalObject", this, global); }; - dbg.onNewScript = function(script, global) { - console.log("[debugger] onNewScript"); - console.log(this, script, global); + dbg.onNewScript = function(script, /* undefined; seems to be `script.global` now */ global) { + try { + console.log("[debugger] onNewScript url=", script.url, "source id=", script.source.id, "introductionType=", script.source.introductionType); + try { + console.log("[debugger] source binary=", typeof script.source.binary); + } catch (error) { + // Do nothing; the source is not wasm + } + notifyNewSource({ + pipelineId: debuggeesToPipelineIds.get(script.global), + spidermonkeyId: script.source.id, + url: script.source.url, + text: script.source.text, + }); + } catch (error) { + logError(error); + } }; } @@ -16,6 +30,13 @@ console.log("[debugger] Executing"); if ("debuggee" in this) { console.log("[debugger] Adding debuggee"); - dbg.addDebuggee(debuggee); - console.log("[debugger] getDebuggees().length =", dbg.getDebuggees().length); + const debuggerObject = dbg.addDebuggee(debuggee); + debuggeesToPipelineIds.set(debuggerObject, { + namespaceId: pipelineNamespaceId, + index: pipelineIndex, + }); +} + +function logError(error) { + console.log(`[debugger] ERROR at ${error.fileName}:${error.lineNumber}:${error.columnNumber}: ${error.name}: ${error.message}`); }