mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Devtools: initial Debugger > Sources panel (#36164)
This patch adds support for listing scripts in the Sources panel. Classic scripts, both external and inline, are implemented, but worker scripts and imported module scripts are not yet implemented. For example: ```html <!-- sources.html --> <!doctype html><meta charset=utf-8> <script src="classic.js"></script> <script> console.log("inline classic"); new Worker("worker.js"); </script> <script type="module"> import module from "./module.js"; console.log("inline module"); </script> <script src="https://servo.org/js/load-table.js"></script> ``` ```js // classic.js console.log("external classic"); ``` ```js // worker.js console.log("external classic worker"); ``` ```js // module.js export default 1; console.log("external module"); ```  --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes partially implement #36027 <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes require tests, but they are blocked on #36325 Signed-off-by: Delan Azabani <dazabani@igalia.com> Co-authored-by: atbrakhi <atbrakhi@igalia.com>
This commit is contained in:
parent
40655cc06c
commit
95eedb997a
6 changed files with 143 additions and 7 deletions
|
@ -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<T: Serialize>(&self, message: T, resource_type: String) {
|
||||
pub(crate) fn resource_available<T: Serialize>(&self, resource: T, resource_type: String) {
|
||||
self.resources_available(vec![resource], resource_type);
|
||||
}
|
||||
|
||||
pub(crate) fn resources_available<T: Serialize>(
|
||||
&self,
|
||||
resources: Vec<T>,
|
||||
resource_type: String,
|
||||
) {
|
||||
let msg = ResourceAvailableReply::<T> {
|
||||
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() {
|
||||
|
|
|
@ -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<Source>,
|
||||
}
|
||||
|
||||
#[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<BTreeSet<Source>>,
|
||||
}
|
||||
|
||||
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<BTreeSet<Source>> {
|
||||
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.
|
||||
// <https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#loading-script-sources>
|
||||
"sources" => {
|
||||
let msg = SourcesReply {
|
||||
from: self.name(),
|
||||
|
|
|
@ -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::<ThreadActor>(&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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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::<BrowsingContextActor>(actor_name)
|
||||
.thread
|
||||
.clone();
|
||||
|
||||
let thread_actor = actors.find_mut::<ThreadActor>(&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::<BrowsingContextActor>(actor_name);
|
||||
browsing_context.resource_available(source, "source".into());
|
||||
}
|
||||
}
|
||||
|
||||
fn allow_devtools_client(stream: &mut TcpStream, embedder: &EmbedderProxy, token: &str) -> bool {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue