Skip to content

Commit 3b388ed

Browse files
delanatbrakhi
authored andcommitted
Devtools: initial Debugger > Sources panel (servo#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"); ``` ![image](https://github.com/user-attachments/assets/2f1d8d7c-501f-4fe5-bd07-085c95e504f2) --- <!-- 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 servo#36027 <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes require tests, but they are blocked on servo#36325 Signed-off-by: Delan Azabani <[email protected]> Co-authored-by: atbrakhi <[email protected]>
1 parent c8f6ee4 commit 3b388ed

File tree

6 files changed

+143
-7
lines changed

6 files changed

+143
-7
lines changed

components/devtools/actors/browsing_context.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ use crate::id::{DevtoolsBrowserId, DevtoolsBrowsingContextId, DevtoolsOuterWindo
3333
use crate::protocol::JsonPacketStream;
3434
use crate::{EmptyReplyMsg, StreamId};
3535

36+
#[derive(Serialize)]
37+
struct ListWorkersReply {
38+
from: String,
39+
workers: Vec<()>,
40+
}
41+
3642
#[derive(Serialize)]
3743
struct FrameUpdateReply {
3844
from: String,
@@ -166,6 +172,14 @@ impl Actor for BrowsingContextActor {
166172
let _ = stream.write_json_packet(&msg);
167173
ActorMessageStatus::Processed
168174
},
175+
"listWorkers" => {
176+
let _ = stream.write_json_packet(&ListWorkersReply {
177+
from: self.name(),
178+
// TODO: Find out what needs to be listed here
179+
workers: vec![],
180+
});
181+
ActorMessageStatus::Processed
182+
},
169183
_ => ActorMessageStatus::Ignored,
170184
})
171185
}
@@ -344,11 +358,19 @@ impl BrowsingContextActor {
344358
});
345359
}
346360

347-
pub(crate) fn resource_available<T: Serialize>(&self, message: T, resource_type: String) {
361+
pub(crate) fn resource_available<T: Serialize>(&self, resource: T, resource_type: String) {
362+
self.resources_available(vec![resource], resource_type);
363+
}
364+
365+
pub(crate) fn resources_available<T: Serialize>(
366+
&self,
367+
resources: Vec<T>,
368+
resource_type: String,
369+
) {
348370
let msg = ResourceAvailableReply::<T> {
349371
from: self.name(),
350372
type_: "resources-available-array".into(),
351-
array: vec![(resource_type, vec![message])],
373+
array: vec![(resource_type, resources)],
352374
};
353375

354376
for stream in self.streams.borrow_mut().values_mut() {

components/devtools/actors/thread.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44

5+
use std::cell::{Ref, RefCell};
6+
use std::collections::BTreeSet;
57
use std::net::TcpStream;
68

79
use serde::Serialize;
810
use serde_json::{Map, Value};
11+
use servo_url::ServoUrl;
912

1013
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
1114
use crate::protocol::JsonPacketStream;
@@ -55,16 +58,38 @@ struct SourcesReply {
5558
sources: Vec<Source>,
5659
}
5760

58-
#[derive(Serialize)]
59-
enum Source {}
61+
#[derive(Eq, Ord, PartialEq, PartialOrd, Serialize)]
62+
#[serde(rename_all = "camelCase")]
63+
pub struct Source {
64+
pub actor: String,
65+
/// URL of the script, or URL of the page for inline scripts.
66+
pub url: String,
67+
pub is_black_boxed: bool,
68+
}
6069

6170
pub struct ThreadActor {
6271
name: String,
72+
source_urls: RefCell<BTreeSet<Source>>,
6373
}
6474

6575
impl ThreadActor {
6676
pub fn new(name: String) -> ThreadActor {
67-
ThreadActor { name }
77+
ThreadActor {
78+
name,
79+
source_urls: RefCell::new(BTreeSet::default()),
80+
}
81+
}
82+
83+
pub fn add_source(&self, url: ServoUrl) {
84+
self.source_urls.borrow_mut().insert(Source {
85+
actor: self.name.clone(),
86+
url: url.to_string(),
87+
is_black_boxed: false,
88+
});
89+
}
90+
91+
pub fn sources(&self) -> Ref<BTreeSet<Source>> {
92+
self.source_urls.borrow()
6893
}
6994
}
7095

@@ -125,6 +150,8 @@ impl Actor for ThreadActor {
125150
ActorMessageStatus::Processed
126151
},
127152

153+
// Client has attached to the thread and wants to load script sources.
154+
// <https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#loading-script-sources>
128155
"sources" => {
129156
let msg = SourcesReply {
130157
from: self.name(),

components/devtools/actors/watcher.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use serde::Serialize;
1919
use serde_json::{Map, Value};
2020

2121
use self::network_parent::{NetworkParentActor, NetworkParentActorMsg};
22+
use super::thread::ThreadActor;
2223
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
2324
use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
2425
use crate::actors::watcher::target_configuration::{
@@ -78,7 +79,7 @@ impl SessionContext {
7879
("network-event-stacktrace", false),
7980
("reflow", false),
8081
("stylesheet", false),
81-
("source", false),
82+
("source", true),
8283
("thread-state", false),
8384
("server-sent-event", false),
8485
("websocket", false),
@@ -133,6 +134,18 @@ struct GetThreadConfigurationActorReply {
133134
configuration: ThreadConfigurationActorMsg,
134135
}
135136

137+
#[derive(Serialize)]
138+
#[serde(rename_all = "camelCase")]
139+
struct GetBreakpointListActorReply {
140+
from: String,
141+
breakpoint_list: GetBreakpointListActorReplyInner,
142+
}
143+
144+
#[derive(Serialize)]
145+
struct GetBreakpointListActorReplyInner {
146+
actor: String,
147+
}
148+
136149
#[derive(Serialize)]
137150
#[serde(rename_all = "camelCase")]
138151
struct DocumentEvent {
@@ -249,6 +262,11 @@ impl Actor for WatcherActor {
249262
target.resource_available(event, "document-event".into());
250263
}
251264
},
265+
"source" => {
266+
let thread_actor = registry.find::<ThreadActor>(&target.thread);
267+
let sources = thread_actor.sources();
268+
target.resources_available(sources.iter().collect(), "source".into());
269+
},
252270
"console-message" | "error-message" => {},
253271
_ => warn!("resource {} not handled yet", resource),
254272
}
@@ -295,6 +313,15 @@ impl Actor for WatcherActor {
295313
let _ = stream.write_json_packet(&msg);
296314
ActorMessageStatus::Processed
297315
},
316+
"getBreakpointListActor" => {
317+
let _ = stream.write_json_packet(&GetBreakpointListActorReply {
318+
from: self.name(),
319+
breakpoint_list: GetBreakpointListActorReplyInner {
320+
actor: registry.new_name("breakpoint-list"),
321+
},
322+
});
323+
ActorMessageStatus::Processed
324+
},
298325
_ => ActorMessageStatus::Ignored,
299326
})
300327
}

components/devtools/lib.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ use std::net::{Shutdown, TcpListener, TcpStream};
1919
use std::sync::{Arc, Mutex};
2020
use std::thread;
2121

22+
use actors::thread::Source;
2223
use base::id::{BrowsingContextId, PipelineId, WebViewId};
2324
use crossbeam_channel::{Receiver, Sender, unbounded};
2425
use devtools_traits::{
2526
ChromeToDevtoolsControlMsg, ConsoleMessage, ConsoleMessageBuilder, DevtoolScriptControlMsg,
2627
DevtoolsControlMsg, DevtoolsPageInfo, LogLevel, NavigationState, NetworkEvent, PageError,
27-
ScriptToDevtoolsControlMsg, WorkerId,
28+
ScriptToDevtoolsControlMsg, SourceInfo, WorkerId,
2829
};
2930
use embedder_traits::{AllowOrDeny, EmbedderMsg, EmbedderProxy};
3031
use ipc_channel::ipc::{self, IpcSender};
@@ -247,6 +248,10 @@ impl DevtoolsInstance {
247248
console_message,
248249
worker_id,
249250
)) => self.handle_console_message(pipeline_id, worker_id, console_message),
251+
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
252+
pipeline_id,
253+
source_info,
254+
)) => self.handle_script_source_info(pipeline_id, source_info),
250255
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportPageError(
251256
pipeline_id,
252257
page_error,
@@ -498,6 +503,38 @@ impl DevtoolsInstance {
498503
},
499504
}
500505
}
506+
507+
fn handle_script_source_info(&mut self, pipeline_id: PipelineId, source_info: SourceInfo) {
508+
let mut actors = self.actors.lock().unwrap();
509+
510+
let browsing_context_id = match self.pipelines.get(&pipeline_id) {
511+
Some(id) => id,
512+
None => return,
513+
};
514+
515+
let actor_name = match self.browsing_contexts.get(browsing_context_id) {
516+
Some(name) => name,
517+
None => return,
518+
};
519+
520+
let thread_actor_name = actors
521+
.find::<BrowsingContextActor>(actor_name)
522+
.thread
523+
.clone();
524+
525+
let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
526+
thread_actor.add_source(source_info.url.clone());
527+
528+
let source = Source {
529+
actor: thread_actor_name.clone(),
530+
url: source_info.url.to_string(),
531+
is_black_boxed: false,
532+
};
533+
534+
// Notify browsing context about the new source
535+
let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
536+
browsing_context.resource_available(source, "source".into());
537+
}
501538
}
502539

503540
fn allow_devtools_client(stream: &mut TcpStream, embedder: &EmbedderProxy, token: &str) -> bool {

components/script/dom/htmlscriptelement.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex};
1313

1414
use base::id::{PipelineId, WebViewId};
1515
use content_security_policy as csp;
16+
use devtools_traits::{ScriptToDevtoolsControlMsg, SourceInfo};
1617
use dom_struct::dom_struct;
1718
use encoding_rs::Encoding;
1819
use html5ever::{LocalName, Prefix, local_name, namespace_url, ns};
@@ -988,6 +989,19 @@ impl HTMLScriptElement {
988989
Ok(script) => script,
989990
};
990991

992+
// TODO: we need to handle this for worker
993+
if let Some(chan) = self.global().devtools_chan() {
994+
let pipeline_id = self.global().pipeline_id();
995+
let source_info = SourceInfo {
996+
url: script.url.clone(),
997+
external: script.external,
998+
};
999+
let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
1000+
pipeline_id,
1001+
source_info,
1002+
));
1003+
}
1004+
9911005
if script.type_ == ScriptType::Classic {
9921006
unminify_js(&mut script);
9931007
self.substitute_with_local_script(&mut script);

components/shared/devtools/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ pub enum ScriptToDevtoolsControlMsg {
103103

104104
/// Report a page title change
105105
TitleChanged(PipelineId, String),
106+
107+
/// Get source information from script
108+
ScriptSourceLoaded(PipelineId, SourceInfo),
106109
}
107110

108111
/// Serialized JS return values
@@ -543,3 +546,9 @@ impl fmt::Display for ShadowRootMode {
543546
}
544547
}
545548
}
549+
550+
#[derive(Debug, Deserialize, Serialize)]
551+
pub struct SourceInfo {
552+
pub url: ServoUrl,
553+
pub external: bool,
554+
}

0 commit comments

Comments
 (0)