mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Devtools: Support worker scripts in Debugger > Source
panel (#36562)
This patch adds support for listing `worker scripts` in `debugger > source` panel For example: ``` <!-- test.html --> <!doctype html><meta charset=utf-8> <script> setTimeout(() => { console.log("inline classic"); new Worker("worker.js"); const blob = new Blob([`console.log("blob worker");`], { type: "text/javascript" }); const blobURL = URL.createObjectURL(blob); new Worker(blobURL); }, 2000); </script> ``` ``` // worker.js console.log("external classic worker"); ``` ``` ./mach run --devtools=6080 http://127.0.0.1:3000/test.html ```  Another example: ``` ./mach run --devtools=6080 https://charming.daz.cat/ ```  - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes partially implement #36027 - [x] These changes require tests, but they are blocked on https://github.com/servo/servo/issues/36325 --------- Signed-off-by: atbrakhi <atbrakhi@igalia.com>
This commit is contained in:
parent
107fd25465
commit
b92542b756
6 changed files with 142 additions and 38 deletions
|
@ -20,8 +20,10 @@ use serde_json::{Map, Value};
|
||||||
|
|
||||||
use self::network_parent::{NetworkParentActor, NetworkParentActorMsg};
|
use self::network_parent::{NetworkParentActor, NetworkParentActorMsg};
|
||||||
use super::thread::ThreadActor;
|
use super::thread::ThreadActor;
|
||||||
|
use super::worker::WorkerMsg;
|
||||||
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
||||||
use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
|
use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
|
||||||
|
use crate::actors::root::RootActor;
|
||||||
use crate::actors::watcher::target_configuration::{
|
use crate::actors::watcher::target_configuration::{
|
||||||
TargetConfigurationActor, TargetConfigurationActorMsg,
|
TargetConfigurationActor, TargetConfigurationActorMsg,
|
||||||
};
|
};
|
||||||
|
@ -29,8 +31,8 @@ use crate::actors::watcher::thread_configuration::{
|
||||||
ThreadConfigurationActor, ThreadConfigurationActorMsg,
|
ThreadConfigurationActor, ThreadConfigurationActorMsg,
|
||||||
};
|
};
|
||||||
use crate::protocol::JsonPacketStream;
|
use crate::protocol::JsonPacketStream;
|
||||||
use crate::resource::ResourceAvailable;
|
use crate::resource::{ResourceAvailable, ResourceAvailableReply};
|
||||||
use crate::{EmptyReplyMsg, StreamId};
|
use crate::{EmptyReplyMsg, StreamId, WorkerActor};
|
||||||
|
|
||||||
pub mod network_parent;
|
pub mod network_parent;
|
||||||
pub mod target_configuration;
|
pub mod target_configuration;
|
||||||
|
@ -55,7 +57,7 @@ impl SessionContext {
|
||||||
supported_targets: HashMap::from([
|
supported_targets: HashMap::from([
|
||||||
("frame", true),
|
("frame", true),
|
||||||
("process", false),
|
("process", false),
|
||||||
("worker", false),
|
("worker", true),
|
||||||
("service_worker", false),
|
("service_worker", false),
|
||||||
("shared_worker", false),
|
("shared_worker", false),
|
||||||
]),
|
]),
|
||||||
|
@ -102,12 +104,19 @@ pub enum SessionContextType {
|
||||||
_All,
|
_All,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum TargetActorMsg {
|
||||||
|
BrowsingContext(BrowsingContextActorMsg),
|
||||||
|
Worker(WorkerMsg),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct WatchTargetsReply {
|
struct WatchTargetsReply {
|
||||||
from: String,
|
from: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
type_: String,
|
type_: String,
|
||||||
target: BrowsingContextActorMsg,
|
target: TargetActorMsg,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -212,16 +221,38 @@ impl Actor for WatcherActor {
|
||||||
_id: StreamId,
|
_id: StreamId,
|
||||||
) -> Result<ActorMessageStatus, ()> {
|
) -> Result<ActorMessageStatus, ()> {
|
||||||
let target = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
|
let target = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
|
||||||
|
let root = registry.find::<RootActor>("root");
|
||||||
Ok(match msg_type {
|
Ok(match msg_type {
|
||||||
"watchTargets" => {
|
"watchTargets" => {
|
||||||
let msg = WatchTargetsReply {
|
// As per logs we either get targetType as "frame" or "worker"
|
||||||
from: self.name(),
|
let target_type = msg
|
||||||
type_: "target-available-form".into(),
|
.get("targetType")
|
||||||
target: target.encodable(),
|
.and_then(Value::as_str)
|
||||||
};
|
.unwrap_or("frame"); // default to "frame"
|
||||||
let _ = stream.write_json_packet(&msg);
|
|
||||||
|
|
||||||
target.frame_update(stream);
|
if target_type == "frame" {
|
||||||
|
let msg = WatchTargetsReply {
|
||||||
|
from: self.name(),
|
||||||
|
type_: "target-available-form".into(),
|
||||||
|
target: TargetActorMsg::BrowsingContext(target.encodable()),
|
||||||
|
};
|
||||||
|
let _ = stream.write_json_packet(&msg);
|
||||||
|
|
||||||
|
target.frame_update(stream);
|
||||||
|
} else if target_type == "worker" {
|
||||||
|
for worker_name in &root.workers {
|
||||||
|
let worker = registry.find::<WorkerActor>(worker_name);
|
||||||
|
let worker_msg = WatchTargetsReply {
|
||||||
|
from: self.name(),
|
||||||
|
type_: "target-available-form".into(),
|
||||||
|
target: TargetActorMsg::Worker(worker.encodable()),
|
||||||
|
};
|
||||||
|
let _ = stream.write_json_packet(&worker_msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("Unexpected target_type: {}", target_type);
|
||||||
|
return Ok(ActorMessageStatus::Ignored);
|
||||||
|
}
|
||||||
|
|
||||||
// Messages that contain a `type` field are used to send event callbacks, but they
|
// Messages that contain a `type` field are used to send event callbacks, but they
|
||||||
// don't count as a reply. Since every message needs to be responded, we send an
|
// don't count as a reply. Since every message needs to be responded, we send an
|
||||||
|
@ -267,6 +298,22 @@ impl Actor for WatcherActor {
|
||||||
let thread_actor = registry.find::<ThreadActor>(&target.thread);
|
let thread_actor = registry.find::<ThreadActor>(&target.thread);
|
||||||
let sources = thread_actor.source_manager.sources();
|
let sources = thread_actor.source_manager.sources();
|
||||||
target.resources_available(sources.iter().collect(), "source".into());
|
target.resources_available(sources.iter().collect(), "source".into());
|
||||||
|
|
||||||
|
for worker_name in &root.workers {
|
||||||
|
let worker = registry.find::<WorkerActor>(worker_name);
|
||||||
|
let thread = registry.find::<ThreadActor>(&worker.thread);
|
||||||
|
let worker_sources = thread.source_manager.sources();
|
||||||
|
|
||||||
|
let msg = ResourceAvailableReply {
|
||||||
|
from: worker.name(),
|
||||||
|
type_: "resources-available-array".into(),
|
||||||
|
array: vec![(
|
||||||
|
"source".to_string(),
|
||||||
|
worker_sources.iter().cloned().collect(),
|
||||||
|
)],
|
||||||
|
};
|
||||||
|
let _ = stream.write_json_packet(&msg);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"console-message" | "error-message" => {},
|
"console-message" | "error-message" => {},
|
||||||
_ => warn!("resource {} not handled yet", resource),
|
_ => warn!("resource {} not handled yet", resource),
|
||||||
|
|
|
@ -17,7 +17,7 @@ use servo_url::ServoUrl;
|
||||||
use crate::StreamId;
|
use crate::StreamId;
|
||||||
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
||||||
use crate::protocol::JsonPacketStream;
|
use crate::protocol::JsonPacketStream;
|
||||||
use crate::resource::ResourceAvailable;
|
use crate::resource::{ResourceAvailable, ResourceAvailableReply};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -48,8 +48,10 @@ impl WorkerActor {
|
||||||
url: self.url.to_string(),
|
url: self.url.to_string(),
|
||||||
traits: WorkerTraits {
|
traits: WorkerTraits {
|
||||||
is_parent_intercept_enabled: false,
|
is_parent_intercept_enabled: false,
|
||||||
|
supports_top_level_target_flag: false,
|
||||||
},
|
},
|
||||||
type_: self.type_ as u32,
|
type_: self.type_ as u32,
|
||||||
|
target_type: "worker".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,6 +133,28 @@ impl Actor for WorkerActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WorkerActor {
|
||||||
|
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, resources)],
|
||||||
|
};
|
||||||
|
|
||||||
|
for stream in self.streams.borrow_mut().values_mut() {
|
||||||
|
let _ = stream.write_json_packet(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct DetachedReply {
|
struct DetachedReply {
|
||||||
from: String,
|
from: String,
|
||||||
|
@ -160,6 +184,7 @@ struct ConnectReply {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct WorkerTraits {
|
struct WorkerTraits {
|
||||||
is_parent_intercept_enabled: bool,
|
is_parent_intercept_enabled: bool,
|
||||||
|
supports_top_level_target_flag: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -173,4 +198,6 @@ pub(crate) struct WorkerMsg {
|
||||||
traits: WorkerTraits,
|
traits: WorkerTraits,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
type_: u32,
|
type_: u32,
|
||||||
|
#[serde(rename = "targetType")]
|
||||||
|
target_type: String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -510,35 +510,54 @@ impl DevtoolsInstance {
|
||||||
fn handle_script_source_info(&mut self, pipeline_id: PipelineId, source_info: SourceInfo) {
|
fn handle_script_source_info(&mut self, pipeline_id: PipelineId, source_info: SourceInfo) {
|
||||||
let mut actors = self.actors.lock().unwrap();
|
let mut actors = self.actors.lock().unwrap();
|
||||||
|
|
||||||
let browsing_context_id = match self.pipelines.get(&pipeline_id) {
|
if let Some(worker_id) = source_info.worker_id {
|
||||||
Some(id) => id,
|
let Some(worker_actor_name) = self.actor_workers.get(&worker_id) else {
|
||||||
None => return,
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let actor_name = match self.browsing_contexts.get(browsing_context_id) {
|
let thread_actor_name = actors.find::<WorkerActor>(worker_actor_name).thread.clone();
|
||||||
Some(name) => name,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let thread_actor_name = actors
|
let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
|
||||||
.find::<BrowsingContextActor>(actor_name)
|
thread_actor
|
||||||
.thread
|
.source_manager
|
||||||
.clone();
|
.add_source(source_info.url.clone());
|
||||||
|
|
||||||
let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
|
let source = SourceData {
|
||||||
thread_actor
|
actor: thread_actor_name.clone(),
|
||||||
.source_manager
|
url: source_info.url.to_string(),
|
||||||
.add_source(source_info.url.clone());
|
is_black_boxed: false,
|
||||||
|
};
|
||||||
|
|
||||||
let source = SourceData {
|
let worker_actor = actors.find::<WorkerActor>(worker_actor_name);
|
||||||
actor: thread_actor_name.clone(),
|
worker_actor.resource_available(source, "source".into());
|
||||||
url: source_info.url.to_string(),
|
} else {
|
||||||
is_black_boxed: false,
|
let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else {
|
||||||
};
|
return;
|
||||||
|
};
|
||||||
|
let Some(actor_name) = self.browsing_contexts.get(browsing_context_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
// Notify browsing context about the new source
|
let thread_actor_name = actors
|
||||||
let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
|
.find::<BrowsingContextActor>(actor_name)
|
||||||
browsing_context.resource_available(source, "source".into());
|
.thread
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
|
||||||
|
thread_actor
|
||||||
|
.source_manager
|
||||||
|
.add_source(source_info.url.clone());
|
||||||
|
|
||||||
|
let source = SourceData {
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1008,12 +1008,12 @@ impl HTMLScriptElement {
|
||||||
Ok(script) => script,
|
Ok(script) => script,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: we need to handle this for worker
|
|
||||||
if let Some(chan) = self.global().devtools_chan() {
|
if let Some(chan) = self.global().devtools_chan() {
|
||||||
let pipeline_id = self.global().pipeline_id();
|
let pipeline_id = self.global().pipeline_id();
|
||||||
let source_info = SourceInfo {
|
let source_info = SourceInfo {
|
||||||
url: script.url.clone(),
|
url: script.url.clone(),
|
||||||
external: script.external,
|
external: script.external,
|
||||||
|
worker_id: None,
|
||||||
};
|
};
|
||||||
let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
|
let _ = chan.send(ScriptToDevtoolsControlMsg::ScriptSourceLoaded(
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use constellation_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
|
use constellation_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
|
||||||
use crossbeam_channel::{Sender, unbounded};
|
use crossbeam_channel::{Sender, unbounded};
|
||||||
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg, WorkerId};
|
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg, SourceInfo, WorkerId};
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use js::jsapi::{Heap, JSObject};
|
use js::jsapi::{Heap, JSObject};
|
||||||
|
@ -213,6 +213,16 @@ impl WorkerMethods<crate::DomTypeHolder> for Worker {
|
||||||
devtools_sender.clone(),
|
devtools_sender.clone(),
|
||||||
page_info,
|
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,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -551,4 +551,5 @@ impl fmt::Display for ShadowRootMode {
|
||||||
pub struct SourceInfo {
|
pub struct SourceInfo {
|
||||||
pub url: ServoUrl,
|
pub url: ServoUrl,
|
||||||
pub external: bool,
|
pub external: bool,
|
||||||
|
pub worker_id: Option<WorkerId>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue