diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index 9a037c11428..c4ead7272bd 100644 --- a/components/devtools/actors/browsing_context.rs +++ b/components/devtools/actors/browsing_context.rs @@ -33,6 +33,12 @@ use crate::id::{DevtoolsBrowserId, DevtoolsBrowsingContextId, DevtoolsOuterWindo use crate::protocol::JsonPacketStream; use crate::{EmptyReplyMsg, StreamId}; +#[derive(Serialize)] +struct ListWorkersReply { + from: String, + workers: Vec<()>, +} + #[derive(Serialize)] struct FrameUpdateReply { from: String, @@ -166,6 +172,14 @@ impl Actor for BrowsingContextActor { let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, + "listWorkers" => { + let _ = stream.write_json_packet(&ListWorkersReply { + from: self.name(), + // TODO: Find out what needs to be listed here + workers: vec![], + }); + ActorMessageStatus::Processed + }, _ => ActorMessageStatus::Ignored, }) } @@ -344,11 +358,19 @@ impl BrowsingContextActor { }); } - pub(crate) fn resource_available(&self, message: T, resource_type: String) { + pub(crate) fn resource_available(&self, resource: T, resource_type: String) { + self.resources_available(vec![resource], resource_type); + } + + pub(crate) fn resources_available( + &self, + resources: Vec, + resource_type: String, + ) { let msg = ResourceAvailableReply:: { from: self.name(), type_: "resources-available-array".into(), - array: vec![(resource_type, vec![message])], + array: vec![(resource_type, resources)], }; for stream in self.streams.borrow_mut().values_mut() { diff --git a/components/devtools/actors/thread.rs b/components/devtools/actors/thread.rs index 6e272cd3d28..85ff2b732eb 100644 --- a/components/devtools/actors/thread.rs +++ b/components/devtools/actors/thread.rs @@ -2,10 +2,13 @@ * 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::{Ref, RefCell}; +use std::collections::BTreeSet; use std::net::TcpStream; use serde::Serialize; use serde_json::{Map, Value}; +use servo_url::ServoUrl; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; @@ -55,16 +58,38 @@ struct SourcesReply { sources: Vec, } -#[derive(Serialize)] -enum Source {} +#[derive(Eq, Ord, PartialEq, PartialOrd, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Source { + pub actor: String, + /// URL of the script, or URL of the page for inline scripts. + pub url: String, + pub is_black_boxed: bool, +} pub struct ThreadActor { name: String, + source_urls: RefCell>, } impl ThreadActor { pub fn new(name: String) -> ThreadActor { - ThreadActor { name } + ThreadActor { + name, + source_urls: RefCell::new(BTreeSet::default()), + } + } + + pub fn add_source(&self, url: ServoUrl) { + self.source_urls.borrow_mut().insert(Source { + actor: self.name.clone(), + url: url.to_string(), + is_black_boxed: false, + }); + } + + pub fn sources(&self) -> Ref> { + self.source_urls.borrow() } } @@ -125,6 +150,8 @@ impl Actor for ThreadActor { ActorMessageStatus::Processed }, + // Client has attached to the thread and wants to load script sources. + // "sources" => { let msg = SourcesReply { from: self.name(), diff --git a/components/devtools/actors/watcher.rs b/components/devtools/actors/watcher.rs index fab071faa2a..77f82c1023a 100644 --- a/components/devtools/actors/watcher.rs +++ b/components/devtools/actors/watcher.rs @@ -19,6 +19,7 @@ use serde::Serialize; use serde_json::{Map, Value}; use self::network_parent::{NetworkParentActor, NetworkParentActorMsg}; +use super::thread::ThreadActor; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg}; use crate::actors::watcher::target_configuration::{ @@ -78,7 +79,7 @@ impl SessionContext { ("network-event-stacktrace", false), ("reflow", false), ("stylesheet", false), - ("source", false), + ("source", true), ("thread-state", false), ("server-sent-event", false), ("websocket", false), @@ -133,6 +134,18 @@ struct GetThreadConfigurationActorReply { configuration: ThreadConfigurationActorMsg, } +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct GetBreakpointListActorReply { + from: String, + breakpoint_list: GetBreakpointListActorReplyInner, +} + +#[derive(Serialize)] +struct GetBreakpointListActorReplyInner { + actor: String, +} + #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct DocumentEvent { @@ -249,6 +262,11 @@ impl Actor for WatcherActor { target.resource_available(event, "document-event".into()); } }, + "source" => { + let thread_actor = registry.find::(&target.thread); + let sources = thread_actor.sources(); + target.resources_available(sources.iter().collect(), "source".into()); + }, "console-message" | "error-message" => {}, _ => warn!("resource {} not handled yet", resource), } @@ -295,6 +313,15 @@ impl Actor for WatcherActor { let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, + "getBreakpointListActor" => { + let _ = stream.write_json_packet(&GetBreakpointListActorReply { + from: self.name(), + breakpoint_list: GetBreakpointListActorReplyInner { + actor: registry.new_name("breakpoint-list"), + }, + }); + ActorMessageStatus::Processed + }, _ => ActorMessageStatus::Ignored, }) } diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 4fc92457b04..43d2f6de880 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -19,12 +19,13 @@ use std::net::{Shutdown, TcpListener, TcpStream}; use std::sync::{Arc, Mutex}; use std::thread; +use actors::thread::Source; use base::id::{BrowsingContextId, PipelineId, WebViewId}; use crossbeam_channel::{Receiver, Sender, unbounded}; use devtools_traits::{ ChromeToDevtoolsControlMsg, ConsoleMessage, ConsoleMessageBuilder, DevtoolScriptControlMsg, DevtoolsControlMsg, DevtoolsPageInfo, LogLevel, NavigationState, NetworkEvent, PageError, - ScriptToDevtoolsControlMsg, WorkerId, + ScriptToDevtoolsControlMsg, SourceInfo, WorkerId, }; use embedder_traits::{AllowOrDeny, EmbedderMsg, EmbedderProxy}; use ipc_channel::ipc::{self, IpcSender}; @@ -247,6 +248,10 @@ impl DevtoolsInstance { console_message, worker_id, )) => self.handle_console_message(pipeline_id, worker_id, console_message), + DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ScriptSourceLoaded( + pipeline_id, + source_info, + )) => self.handle_script_source_info(pipeline_id, source_info), DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportPageError( pipeline_id, page_error, @@ -498,6 +503,38 @@ impl DevtoolsInstance { }, } } + + fn handle_script_source_info(&mut self, pipeline_id: PipelineId, source_info: SourceInfo) { + let mut actors = self.actors.lock().unwrap(); + + let browsing_context_id = match self.pipelines.get(&pipeline_id) { + Some(id) => id, + None => return, + }; + + let actor_name = match self.browsing_contexts.get(browsing_context_id) { + Some(name) => name, + None => return, + }; + + let thread_actor_name = actors + .find::(actor_name) + .thread + .clone(); + + let thread_actor = actors.find_mut::(&thread_actor_name); + thread_actor.add_source(source_info.url.clone()); + + let source = Source { + actor: thread_actor_name.clone(), + url: source_info.url.to_string(), + is_black_boxed: false, + }; + + // Notify browsing context about the new source + let browsing_context = actors.find::(actor_name); + browsing_context.resource_available(source, "source".into()); + } } fn allow_devtools_client(stream: &mut TcpStream, embedder: &EmbedderProxy, token: &str) -> bool { diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index ed7c6e6da30..71e3d4ed72b 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex}; use base::id::{PipelineId, WebViewId}; use content_security_policy as csp; +use devtools_traits::{ScriptToDevtoolsControlMsg, SourceInfo}; use dom_struct::dom_struct; use encoding_rs::Encoding; use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; @@ -988,6 +989,19 @@ impl HTMLScriptElement { Ok(script) => script, }; + // TODO: we need to handle this for worker + if let Some(chan) = self.global().devtools_chan() { + let pipeline_id = self.global().pipeline_id(); + let source_info = SourceInfo { + url: script.url.clone(), + external: script.external, + }; + let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded( + pipeline_id, + source_info, + )); + } + if script.type_ == ScriptType::Classic { unminify_js(&mut script); self.substitute_with_local_script(&mut script); diff --git a/components/shared/devtools/lib.rs b/components/shared/devtools/lib.rs index cd934f5d5ff..59857dc0d00 100644 --- a/components/shared/devtools/lib.rs +++ b/components/shared/devtools/lib.rs @@ -103,6 +103,9 @@ pub enum ScriptToDevtoolsControlMsg { /// Report a page title change TitleChanged(PipelineId, String), + + /// Get source information from script + ScriptSourceLoaded(PipelineId, SourceInfo), } /// Serialized JS return values @@ -543,3 +546,9 @@ impl fmt::Display for ShadowRootMode { } } } + +#[derive(Debug, Deserialize, Serialize)] +pub struct SourceInfo { + pub url: ServoUrl, + pub external: bool, +}