diff --git a/components/devtools/actors/source.rs b/components/devtools/actors/source.rs index 487f34cef3d..eeab2d68746 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)] @@ -91,6 +93,7 @@ impl SourceActor { url: ServoUrl, content: Option, content_type: Option, + spidermonkey_id: u32, ) -> SourceActor { SourceActor { name, @@ -98,6 +101,7 @@ impl SourceActor { content, content_type, is_black_boxed: false, + spidermonkey_id, } } @@ -107,10 +111,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 03cba49a516..6986d9736fd 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -552,6 +552,7 @@ impl DevtoolsInstance { source_info.url, source_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/debuggerevent.rs b/components/script/dom/debuggerevent.rs index 18b2bdf33dc..a38862216ab 100644 --- a/components/script/dom/debuggerevent.rs +++ b/components/script/dom/debuggerevent.rs @@ -15,7 +15,7 @@ use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventM use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::event::Event; -use crate::dom::types::GlobalScope; +use crate::dom::types::{GlobalScope, PipelineId}; use crate::script_runtime::CanGc; #[dom_struct] @@ -23,20 +23,24 @@ use crate::script_runtime::CanGc; pub(crate) struct DebuggerEvent { event: Event, global: MutNullableDom, + pipeline_id: MutNullableDom, } impl DebuggerEvent { pub(crate) fn new( debugger_global: &GlobalScope, global: &GlobalScope, + pipeline_id: &PipelineId, can_gc: CanGc, ) -> DomRoot { let result = Box::new(Self { event: Event::new_inherited(), global: Default::default(), + pipeline_id: Default::default(), }); // TODO: make these fields not optional somehow? result.global.set(Some(global)); + result.pipeline_id.set(Some(pipeline_id)); result.event.init_event("addDebuggee".into(), false, false); @@ -58,6 +62,12 @@ impl DebuggerEventMethods for DebuggerEvent { NonNull::new(result.to_object()).unwrap() } + fn PipelineId( + &self, + ) -> DomRoot<::PipelineId> { + self.pipeline_id.get().expect("Guaranteed by new") + } + fn IsTrusted(&self) -> bool { self.event.IsTrusted() } diff --git a/components/script/dom/debuggerglobalscope.rs b/components/script/dom/debuggerglobalscope.rs index ab55fa0f914..65721a188fb 100644 --- a/components/script/dom/debuggerglobalscope.rs +++ b/components/script/dom/debuggerglobalscope.rs @@ -2,18 +2,22 @@ * 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 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::realms::InRealm; use script_bindings::reflector::DomObject; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; @@ -104,6 +108,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( @@ -123,8 +131,20 @@ impl DebuggerGlobalScope { } #[allow(unsafe_code)] - pub(crate) fn fire_add_debuggee(&self, can_gc: CanGc, global: &GlobalScope) { - let event = DomRoot::upcast::(DebuggerEvent::new(self.upcast(), global, can_gc)); + pub(crate) fn fire_add_debuggee( + &self, + can_gc: CanGc, + global: &GlobalScope, + pipeline_id: PipelineId, + ) { + let pipeline_id = + crate::dom::pipelineid::PipelineId::new(self.upcast(), pipeline_id, can_gc); + let event = DomRoot::upcast::(DebuggerEvent::new( + self.upcast(), + global, + &pipeline_id, + can_gc, + )); assert_eq!( event.fire(self.upcast(), can_gc), EventStatus::NotCanceled, @@ -132,3 +152,38 @@ impl DebuggerGlobalScope { ); } } + +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/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..30746320048 --- /dev/null +++ b/components/script/dom/pipelineid.rs @@ -0,0 +1,52 @@ +/* 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::cell::Cell; + +use dom_struct::dom_struct; +use js::rust::HandleObject; + +use crate::dom::bindings::codegen::Bindings::DebuggerEventBinding::PipelineIdMethods; +use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::reflector::{ + Reflector, reflect_dom_object, reflect_dom_object_with_proto, +}; +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 bb16e35b2bf..2eb4f4a1afe 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -3449,8 +3449,9 @@ impl ScriptThread { incomplete.load_data.inherited_secure_context, incomplete.theme, ); + // TODO: call this for workers too self.debugger_global - .fire_add_debuggee(can_gc, window.upcast()); + .fire_add_debuggee(can_gc, window.upcast(), incomplete.pipeline_id); let _realm = enter_realm(&*window); diff --git a/components/script_bindings/webidls/DebuggerEvent.webidl b/components/script_bindings/webidls/DebuggerEvent.webidl index a0682fc3165..f5d2a9039a5 100644 --- a/components/script_bindings/webidls/DebuggerEvent.webidl +++ b/components/script_bindings/webidls/DebuggerEvent.webidl @@ -7,4 +7,11 @@ [Exposed=DebuggerGlobalScope] interface DebuggerEvent : Event { readonly attribute object global; + readonly attribute PipelineId pipelineId; +}; + +[Exposed=DebuggerGlobalScope] +interface PipelineId { + readonly attribute unsigned long namespaceId; + readonly attribute unsigned long index; }; diff --git a/components/script_bindings/webidls/DebuggerGlobalScope.webidl b/components/script_bindings/webidls/DebuggerGlobalScope.webidl index 4f85602a157..545e68dbe62 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 PipelineIdInit 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 PipelineIdInit { + 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 d0c6198bfd2..4e36466bbfb 100644 --- a/resources/debugger.js +++ b/resources/debugger.js @@ -1,20 +1,38 @@ 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); + } }; } addEventListener("addDebuggee", event => { - const {global} = event; + const {global, pipelineId: {namespaceId, index}} = event; console.log("[debugger] Adding debuggee"); dbg.addDebuggee(global); - console.log("[debugger] getDebuggees().length =", dbg.getDebuggees().length); + const debuggerObject = dbg.addDebuggee(global); + debuggeesToPipelineIds.set(debuggerObject, { + namespaceId, + index, + }); });