This commit is contained in:
atbrakhi 2025-06-04 14:21:00 +05:30 committed by GitHub
commit 8cd17dac55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 142 additions and 41 deletions

View file

@ -4,10 +4,16 @@
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::StreamId;
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::protocol::JsonPacketStream;
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SourceData {
@ -15,6 +21,7 @@ pub(crate) struct SourceData {
/// URL of the script, or URL of the page for inline scripts.
pub url: String,
pub is_black_boxed: bool,
pub source_content: String,
}
#[derive(Serialize)]
@ -23,24 +30,39 @@ pub(crate) struct SourcesReply {
pub sources: Vec<SourceData>,
}
pub(crate) struct Source {
actor_name: String,
source_urls: RefCell<BTreeSet<SourceData>>,
pub(crate) struct SourceManager {
pub source_urls: RefCell<BTreeSet<SourceData>>,
}
impl Source {
pub fn new(actor_name: String) -> Self {
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SourceActor {
pub name: String,
pub content: String,
pub content_type: String,
}
#[derive(Serialize)]
struct SourceContentReply {
from: String,
#[serde(rename = "contentType")]
content_type: String,
source: String,
}
impl SourceManager {
pub fn new() -> Self {
Self {
actor_name,
source_urls: RefCell::new(BTreeSet::default()),
}
}
pub fn add_source(&self, url: ServoUrl) {
pub fn add_source(&self, url: ServoUrl, source_content: String, actor_name: String) {
self.source_urls.borrow_mut().insert(SourceData {
actor: self.actor_name.clone(),
actor: actor_name,
url: url.to_string(),
is_black_boxed: false,
source_content,
});
}
@ -48,3 +70,50 @@ impl Source {
self.source_urls.borrow()
}
}
impl SourceActor {
pub fn new(name: String, content: String, content_type: String) -> SourceActor {
SourceActor {
name,
content,
content_type,
}
}
pub fn new_source(actors: &mut ActorRegistry, content: String, content_type: String) -> String {
let source_actor_name = actors.new_name("source");
let source_actor = SourceActor::new(source_actor_name.clone(), content, content_type);
actors.register(Box::new(source_actor));
source_actor_name
}
}
impl Actor for SourceActor {
fn name(&self) -> String {
self.name.clone()
}
fn handle_message(
&self,
_registry: &ActorRegistry,
msg_type: &str,
_msg: &Map<String, Value>,
stream: &mut TcpStream,
_id: StreamId,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
"source" => {
let reply = SourceContentReply {
from: self.name(),
content_type: self.content_type.clone(),
source: self.content.clone(),
};
let _ = stream.write_json_packet(&reply);
ActorMessageStatus::Processed
},
_ => ActorMessageStatus::Ignored,
})
}
}

View file

@ -7,7 +7,7 @@ use std::net::TcpStream;
use serde::Serialize;
use serde_json::{Map, Value};
use super::source::{Source, SourcesReply};
use super::source::{SourceData, SourceManager, SourcesReply};
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::protocol::JsonPacketStream;
use crate::{EmptyReplyMsg, StreamId};
@ -52,14 +52,14 @@ struct ThreadInterruptedReply {
pub struct ThreadActor {
pub name: String,
pub source_manager: Source,
pub source_manager: SourceManager,
}
impl ThreadActor {
pub fn new(name: String) -> ThreadActor {
ThreadActor {
name: name.clone(),
source_manager: Source::new(name),
source_manager: SourceManager::new(),
}
}
}
@ -124,14 +124,16 @@ impl Actor for ThreadActor {
// 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 sources = self.source_manager.source_urls.borrow();
let sources_vec: Vec<SourceData> = sources.iter().cloned().collect();
let msg = SourcesReply {
from: self.name(),
sources: vec![], // TODO: Add sources for the debugger here
sources: sources_vec,
};
let _ = stream.write_json_packet(&msg);
ActorMessageStatus::Processed
},
_ => ActorMessageStatus::Ignored,
})
}

View file

@ -19,7 +19,6 @@ use std::net::{Shutdown, TcpListener, TcpStream};
use std::sync::{Arc, Mutex};
use std::thread;
use actors::source::SourceData;
use base::id::{BrowsingContextId, PipelineId, WebViewId};
use crossbeam_channel::{Receiver, Sender, unbounded};
use devtools_traits::{
@ -44,6 +43,7 @@ use crate::actors::performance::PerformanceActor;
use crate::actors::preference::PreferenceActor;
use crate::actors::process::ProcessActor;
use crate::actors::root::RootActor;
use crate::actors::source::{SourceActor, SourceData};
use crate::actors::thread::ThreadActor;
use crate::actors::worker::{WorkerActor, WorkerType};
use crate::id::IdMap;
@ -515,22 +515,31 @@ impl DevtoolsInstance {
fn handle_script_source_info(&mut self, pipeline_id: PipelineId, source_info: SourceInfo) {
let mut actors = self.actors.lock().unwrap();
let source_actor_name = SourceActor::new_source(
&mut actors,
source_info.content.clone(),
source_info.content_type.unwrap(),
);
if let Some(worker_id) = source_info.worker_id {
let Some(worker_actor_name) = self.actor_workers.get(&worker_id) else {
return;
};
let thread_actor_name = actors.find::<WorkerActor>(worker_actor_name).thread.clone();
let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
thread_actor
.source_manager
.add_source(source_info.url.clone());
thread_actor.source_manager.add_source(
source_info.url.clone(),
source_info.content.clone(),
source_actor_name.clone(),
);
let source = SourceData {
actor: thread_actor_name.clone(),
actor: source_actor_name,
url: source_info.url.to_string(),
is_black_boxed: false,
source_content: source_info.content,
};
let worker_actor = actors.find::<WorkerActor>(worker_actor_name);
@ -546,20 +555,24 @@ impl DevtoolsInstance {
return;
};
let thread_actor_name = actors
.find::<BrowsingContextActor>(actor_name)
.thread
.clone();
let thread_actor_name = {
let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
browsing_context.thread.clone()
};
let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
thread_actor
.source_manager
.add_source(source_info.url.clone());
thread_actor.source_manager.add_source(
source_info.url.clone(),
source_info.content.clone(),
source_actor_name.clone(),
);
let source = SourceData {
actor: thread_actor_name.clone(),
actor: source_actor_name,
url: source_info.url.to_string(),
is_black_boxed: false,
source_content: source_info.content,
};
// Notify browsing context about the new source

View file

@ -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;
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, SourceInfo};
use dom_struct::dom_struct;
use ipc_channel::ipc::IpcReceiver;
use ipc_channel::router::ROUTER;
@ -478,10 +478,24 @@ impl DedicatedWorkerGlobalScope {
},
Ok((metadata, bytes)) => (metadata, bytes),
};
scope.set_url(metadata.final_url);
scope.set_url(metadata.final_url.clone());
scope.set_csp_list(GlobalScope::parse_csp_list_from_metadata(&metadata.headers));
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::<WorkerGlobalScope>().get_worker_id()),
content: source.to_string(),
content_type: metadata.content_type.map(|c_type| c_type.0.to_string()),
};
let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
pipeline_id,
source_info,
));
}
unsafe {
// Handle interrupt requests

View file

@ -1001,7 +1001,6 @@ impl HTMLScriptElement {
}
// TODO: Step 3. Unblock rendering on el.
let mut script = match result {
// Step 4. If el's result is null, then fire an event named error at el, and return.
Err(e) => {
@ -1015,10 +1014,22 @@ impl HTMLScriptElement {
if let Some(chan) = self.global().devtools_chan() {
let pipeline_id = self.global().pipeline_id();
// TODO: https://github.com/servo/servo/issues/36874
let content = match &script.code {
SourceCode::Text(text) => text.to_string(),
SourceCode::Compiled(compiled) => compiled.original_text.to_string(),
};
// https://html.spec.whatwg.org/multipage/#scriptingLanguages
let content_type = Some("text/javascript".to_string());
let source_info = SourceInfo {
url: script.url.clone(),
external: script.external,
worker_id: None,
content,
content_type,
};
let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
pipeline_id,

View file

@ -8,7 +8,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use constellation_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
use crossbeam_channel::{Sender, unbounded};
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg, SourceInfo, WorkerId};
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg, WorkerId};
use dom_struct::dom_struct;
use ipc_channel::ipc;
use js::jsapi::{Heap, JSObject};
@ -224,16 +224,6 @@ impl WorkerMethods<crate::DomTypeHolder> for Worker {
devtools_sender.clone(),
page_info,
));
let source_info = SourceInfo {
url: worker_url.clone(),
external: true, // Worker scripts are always external.
worker_id: Some(worker_id),
};
let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
pipeline_id,
source_info,
));
}
}

View file

@ -563,4 +563,6 @@ pub struct SourceInfo {
pub url: ServoUrl,
pub external: bool,
pub worker_id: Option<WorkerId>,
pub content: String,
pub content_type: Option<String>,
}