mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
DevTools: Implement watcher actor (#32509)
* feat: base for watcher * feat: some more watcher tests * feat: implement getWatcher * refactor: clean up getWatcher * feat: implement watchTargets * feat: implement watchResources * feat: very messy watchTargets fix * refactor: clean browsing context * feat: target configuration * refactor: start cleanup * refactor: more doc coments * refactor: clean browsing context
This commit is contained in:
parent
26c585a0c5
commit
5eb8813448
9 changed files with 680 additions and 244 deletions
|
@ -2,7 +2,7 @@
|
|||
* 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/. */
|
||||
|
||||
//! Liberally derived from the [Firefox JS implementation](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webbrowser.js).
|
||||
//! Liberally derived from the [Firefox JS implementation](https://searchfox.org/mozilla-central/source/devtools/server/actors/webbrowser.js).
|
||||
//! Connection point for remote devtools that wish to investigate a particular Browsing Context's contents.
|
||||
//! Supports dynamic attaching and detaching which control notifications of navigation, etc.
|
||||
|
||||
|
@ -18,6 +18,7 @@ use serde::Serialize;
|
|||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
||||
use crate::actors::configuration::{TargetConfigurationActor, ThreadConfigurationActor};
|
||||
use crate::actors::emulation::EmulationActor;
|
||||
use crate::actors::inspector::InspectorActor;
|
||||
use crate::actors::performance::PerformanceActor;
|
||||
|
@ -26,118 +27,126 @@ use crate::actors::stylesheets::StyleSheetsActor;
|
|||
use crate::actors::tab::TabDescriptorActor;
|
||||
use crate::actors::thread::ThreadActor;
|
||||
use crate::actors::timeline::TimelineActor;
|
||||
use crate::actors::watcher::{SessionContext, SessionContextType, WatcherActor};
|
||||
use crate::protocol::JsonPacketStream;
|
||||
use crate::StreamId;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct BrowsingContextTraits {
|
||||
isBrowsingContext: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AttachedTraits {
|
||||
reconfigure: bool,
|
||||
frames: bool,
|
||||
logInPage: bool,
|
||||
canRewind: bool,
|
||||
watchpoints: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct BrowsingContextAttachedReply {
|
||||
struct FrameUpdateReply {
|
||||
from: String,
|
||||
#[serde(rename = "type")]
|
||||
type_: String,
|
||||
threadActor: String,
|
||||
cacheDisabled: bool,
|
||||
javascriptEnabled: bool,
|
||||
traits: AttachedTraits,
|
||||
frames: Vec<FrameUpdateMsg>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct BrowsingContextDetachedReply {
|
||||
from: String,
|
||||
#[serde(rename = "type")]
|
||||
type_: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ReconfigureReply {
|
||||
from: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ListFramesReply {
|
||||
from: String,
|
||||
frames: Vec<FrameMsg>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct FrameMsg {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct FrameUpdateMsg {
|
||||
id: u32,
|
||||
is_top_level: bool,
|
||||
url: String,
|
||||
title: String,
|
||||
parentID: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ListWorkersReply {
|
||||
struct ResourceAvailableReply {
|
||||
from: String,
|
||||
workers: Vec<WorkerMsg>,
|
||||
#[serde(rename = "type")]
|
||||
type_: String,
|
||||
resources: Vec<ResourceAvailableMsg>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct WorkerMsg {
|
||||
id: u32,
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ResourceAvailableMsg {
|
||||
#[serde(rename = "hasNativeConsoleAPI")]
|
||||
has_native_console_api: Option<bool>,
|
||||
name: String,
|
||||
#[serde(rename = "newURI")]
|
||||
new_uri: Option<String>,
|
||||
resource_type: String,
|
||||
time: u64,
|
||||
title: Option<String>,
|
||||
url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct TabNavigated {
|
||||
from: String,
|
||||
#[serde(rename = "type")]
|
||||
type_: String,
|
||||
url: String,
|
||||
title: Option<String>,
|
||||
#[serde(rename = "nativeConsoleAPI")]
|
||||
native_console_api: bool,
|
||||
state: String,
|
||||
is_frame_switching: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct BrowsingContextTraits {
|
||||
is_browsing_context: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BrowsingContextActorMsg {
|
||||
actor: String,
|
||||
title: String,
|
||||
url: String,
|
||||
outerWindowID: u32,
|
||||
browsingContextId: u32,
|
||||
consoleActor: String,
|
||||
/*emulationActor: String,
|
||||
inspectorActor: String,
|
||||
timelineActor: String,
|
||||
profilerActor: String,
|
||||
performanceActor: String,
|
||||
styleSheetsActor: String,*/
|
||||
#[serde(rename = "outerWindowID")]
|
||||
outer_window_id: u32,
|
||||
#[serde(rename = "browsingContextID")]
|
||||
browsing_context_id: u32,
|
||||
is_top_level_target: bool,
|
||||
console_actor: String,
|
||||
thread_actor: String,
|
||||
traits: BrowsingContextTraits,
|
||||
// Part of the official protocol, but not yet implemented.
|
||||
/*storageActor: String,
|
||||
memoryActor: String,
|
||||
framerateActor: String,
|
||||
reflowActor: String,
|
||||
cssPropertiesActor: String,
|
||||
animationsActor: String,
|
||||
webExtensionInspectedWindowActor: String,
|
||||
accessibilityActor: String,
|
||||
screenshotActor: String,
|
||||
changesActor: String,
|
||||
webSocketActor: String,
|
||||
manifestActor: String,*/
|
||||
// emulation_actor: String,
|
||||
// inspector_actor: String,
|
||||
// timeline_actor: String,
|
||||
// profiler_actor: String,
|
||||
// performance_actor: String,
|
||||
// style_sheets_actor: String,
|
||||
// storage_actor: String,
|
||||
// memory_actor: String,
|
||||
// framerate_actor: String,
|
||||
// reflow_actor: String,
|
||||
// css_properties_actor: String,
|
||||
// animations_actor: String,
|
||||
// web_extension_inspected_window_actor: String,
|
||||
// accessibility_actor: String,
|
||||
// screenshot_actor: String,
|
||||
// changes_actor: String,
|
||||
// web_socket_actor: String,
|
||||
// manifest_actor: String,
|
||||
}
|
||||
|
||||
/// The browsing context actor encompasses all of the other supporting actors when debugging a web
|
||||
/// view. To this extent, it contains a watcher actor that helps when communicating with the host,
|
||||
/// as well as resource actors that each perform one debugging function.
|
||||
pub(crate) struct BrowsingContextActor {
|
||||
pub name: String,
|
||||
pub title: RefCell<String>,
|
||||
pub url: RefCell<String>,
|
||||
pub active_pipeline: Cell<PipelineId>,
|
||||
pub browsing_context_id: BrowsingContextId,
|
||||
pub console: String,
|
||||
pub _emulation: String,
|
||||
pub _inspector: String,
|
||||
pub _timeline: String,
|
||||
pub _profiler: String,
|
||||
pub _performance: String,
|
||||
pub _styleSheets: String,
|
||||
pub _profiler: String,
|
||||
pub _style_sheets: String,
|
||||
pub target_configuration: String,
|
||||
pub thread_configuration: String,
|
||||
pub thread: String,
|
||||
pub _timeline: String,
|
||||
pub _tab: String,
|
||||
pub streams: RefCell<HashMap<StreamId, TcpStream>>,
|
||||
pub browsing_context_id: BrowsingContextId,
|
||||
pub active_pipeline: Cell<PipelineId>,
|
||||
pub script_chan: IpcSender<DevtoolScriptControlMsg>,
|
||||
pub streams: RefCell<HashMap<StreamId, TcpStream>>,
|
||||
pub watcher: String,
|
||||
}
|
||||
|
||||
impl Actor for BrowsingContextActor {
|
||||
|
@ -149,89 +158,11 @@ impl Actor for BrowsingContextActor {
|
|||
&self,
|
||||
_registry: &ActorRegistry,
|
||||
msg_type: &str,
|
||||
msg: &Map<String, Value>,
|
||||
stream: &mut TcpStream,
|
||||
id: StreamId,
|
||||
_msg: &Map<String, Value>,
|
||||
_stream: &mut TcpStream,
|
||||
_id: StreamId,
|
||||
) -> Result<ActorMessageStatus, ()> {
|
||||
Ok(match msg_type {
|
||||
"reconfigure" => {
|
||||
if let Some(options) = msg.get("options").and_then(|o| o.as_object()) {
|
||||
if let Some(val) = options.get("performReload") {
|
||||
if val.as_bool().unwrap_or(false) {
|
||||
let _ = self
|
||||
.script_chan
|
||||
.send(DevtoolScriptControlMsg::Reload(self.active_pipeline.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = stream.write_json_packet(&ReconfigureReply { from: self.name() });
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
|
||||
// https://docs.firefox-dev.tools/backend/protocol.html#listing-browser-tabs
|
||||
// (see "To attach to a _targetActor_")
|
||||
"attach" => {
|
||||
let msg = BrowsingContextAttachedReply {
|
||||
from: self.name(),
|
||||
type_: "tabAttached".to_owned(),
|
||||
threadActor: self.thread.clone(),
|
||||
cacheDisabled: false,
|
||||
javascriptEnabled: true,
|
||||
traits: AttachedTraits {
|
||||
reconfigure: false,
|
||||
frames: true,
|
||||
logInPage: false,
|
||||
canRewind: false,
|
||||
watchpoints: false,
|
||||
},
|
||||
};
|
||||
|
||||
if stream.write_json_packet(&msg).is_err() {
|
||||
return Ok(ActorMessageStatus::Processed);
|
||||
}
|
||||
self.streams
|
||||
.borrow_mut()
|
||||
.insert(id, stream.try_clone().unwrap());
|
||||
self.script_chan
|
||||
.send(WantsLiveNotifications(self.active_pipeline.get(), true))
|
||||
.unwrap();
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
|
||||
"detach" => {
|
||||
let msg = BrowsingContextDetachedReply {
|
||||
from: self.name(),
|
||||
type_: "detached".to_owned(),
|
||||
};
|
||||
let _ = stream.write_json_packet(&msg);
|
||||
self.cleanup(id);
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
|
||||
"listFrames" => {
|
||||
let msg = ListFramesReply {
|
||||
from: self.name(),
|
||||
frames: vec![FrameMsg {
|
||||
//FIXME: shouldn't ignore pipeline namespace field
|
||||
id: self.active_pipeline.get().index.0.get(),
|
||||
parentID: 0,
|
||||
url: self.url.borrow().clone(),
|
||||
title: self.title.borrow().clone(),
|
||||
}],
|
||||
};
|
||||
let _ = stream.write_json_packet(&msg);
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
|
||||
"listWorkers" => {
|
||||
let msg = ListWorkersReply {
|
||||
from: self.name(),
|
||||
workers: vec![],
|
||||
};
|
||||
let _ = stream.write_json_packet(&msg);
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
|
||||
_ => ActorMessageStatus::Ignored,
|
||||
})
|
||||
}
|
||||
|
@ -255,9 +186,10 @@ impl BrowsingContextActor {
|
|||
script_sender: IpcSender<DevtoolScriptControlMsg>,
|
||||
actors: &mut ActorRegistry,
|
||||
) -> BrowsingContextActor {
|
||||
let emulation = EmulationActor::new(actors.new_name("emulation"));
|
||||
|
||||
let name = actors.new_name("target");
|
||||
let DevtoolsPageInfo { title, url } = page_info;
|
||||
|
||||
let emulation = EmulationActor::new(actors.new_name("emulation"));
|
||||
|
||||
let inspector = InspectorActor {
|
||||
name: actors.new_name("inspector"),
|
||||
|
@ -268,48 +200,66 @@ impl BrowsingContextActor {
|
|||
browsing_context: name.clone(),
|
||||
};
|
||||
|
||||
let timeline =
|
||||
TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender.clone());
|
||||
let performance = PerformanceActor::new(actors.new_name("performance"));
|
||||
|
||||
let profiler = ProfilerActor::new(actors.new_name("profiler"));
|
||||
let performance = PerformanceActor::new(actors.new_name("performance"));
|
||||
|
||||
// the strange switch between styleSheets and stylesheets is due
|
||||
// to an inconsistency in devtools. See Bug #1498893 in bugzilla
|
||||
let styleSheets = StyleSheetsActor::new(actors.new_name("stylesheets"));
|
||||
let thread = ThreadActor::new(actors.new_name("context"));
|
||||
|
||||
let DevtoolsPageInfo { title, url } = page_info;
|
||||
let style_sheets = StyleSheetsActor::new(actors.new_name("stylesheets"));
|
||||
|
||||
let tabdesc = TabDescriptorActor::new(actors, name.clone());
|
||||
|
||||
let target_configuration =
|
||||
TargetConfigurationActor::new(actors.new_name("target-configuration"));
|
||||
|
||||
let thread_configuration =
|
||||
ThreadConfigurationActor::new(actors.new_name("thread-configuration"));
|
||||
|
||||
let thread = ThreadActor::new(actors.new_name("context"));
|
||||
|
||||
let timeline =
|
||||
TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender.clone());
|
||||
|
||||
let watcher = WatcherActor::new(
|
||||
actors.new_name("watcher"),
|
||||
name.clone(),
|
||||
SessionContext::new(SessionContextType::BrowserElement),
|
||||
);
|
||||
|
||||
let target = BrowsingContextActor {
|
||||
name,
|
||||
script_chan: script_sender,
|
||||
title: RefCell::new(title),
|
||||
url: RefCell::new(url.into_string()),
|
||||
active_pipeline: Cell::new(pipeline),
|
||||
browsing_context_id: id,
|
||||
console,
|
||||
_emulation: emulation.name(),
|
||||
_inspector: inspector.name(),
|
||||
_timeline: timeline.name(),
|
||||
_profiler: profiler.name(),
|
||||
_performance: performance.name(),
|
||||
_styleSheets: styleSheets.name(),
|
||||
_tab: tabdesc.name(),
|
||||
thread: thread.name(),
|
||||
_profiler: profiler.name(),
|
||||
streams: RefCell::new(HashMap::new()),
|
||||
browsing_context_id: id,
|
||||
active_pipeline: Cell::new(pipeline),
|
||||
_style_sheets: style_sheets.name(),
|
||||
_tab: tabdesc.name(),
|
||||
target_configuration: target_configuration.name(),
|
||||
thread_configuration: thread_configuration.name(),
|
||||
thread: thread.name(),
|
||||
_timeline: timeline.name(),
|
||||
watcher: watcher.name(),
|
||||
};
|
||||
|
||||
actors.register(Box::new(emulation));
|
||||
actors.register(Box::new(inspector));
|
||||
actors.register(Box::new(timeline));
|
||||
actors.register(Box::new(profiler));
|
||||
actors.register(Box::new(performance));
|
||||
actors.register(Box::new(styleSheets));
|
||||
actors.register(Box::new(thread));
|
||||
actors.register(Box::new(profiler));
|
||||
actors.register(Box::new(style_sheets));
|
||||
actors.register(Box::new(tabdesc));
|
||||
actors.register(Box::new(target_configuration));
|
||||
actors.register(Box::new(thread_configuration));
|
||||
actors.register(Box::new(thread));
|
||||
actors.register(Box::new(timeline));
|
||||
actors.register(Box::new(watcher));
|
||||
|
||||
target
|
||||
}
|
||||
|
@ -318,21 +268,23 @@ impl BrowsingContextActor {
|
|||
BrowsingContextActorMsg {
|
||||
actor: self.name(),
|
||||
traits: BrowsingContextTraits {
|
||||
isBrowsingContext: true,
|
||||
is_browsing_context: true,
|
||||
},
|
||||
title: self.title.borrow().clone(),
|
||||
url: self.url.borrow().clone(),
|
||||
//FIXME: shouldn't ignore pipeline namespace field
|
||||
browsingContextId: self.browsing_context_id.index.0.get(),
|
||||
browsing_context_id: self.browsing_context_id.index.0.get(),
|
||||
//FIXME: shouldn't ignore pipeline namespace field
|
||||
outerWindowID: self.active_pipeline.get().index.0.get(),
|
||||
consoleActor: self.console.clone(),
|
||||
/*emulationActor: self.emulation.clone(),
|
||||
inspectorActor: self.inspector.clone(),
|
||||
timelineActor: self.timeline.clone(),
|
||||
profilerActor: self.profiler.clone(),
|
||||
performanceActor: self.performance.clone(),
|
||||
styleSheetsActor: self.styleSheets.clone(),*/
|
||||
outer_window_id: self.active_pipeline.get().index.0.get(),
|
||||
is_top_level_target: true,
|
||||
console_actor: self.console.clone(),
|
||||
thread_actor: self.thread.clone(),
|
||||
// emulation_actor: self.emulation.clone(),
|
||||
// inspector_actor: self.inspector.clone(),
|
||||
// performance_actor: self.performance.clone(),
|
||||
// profiler_actor: self.profiler.clone(),
|
||||
// style_sheets_actor: self.style_sheets.clone(),
|
||||
// timeline_actor: self.timeline.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,10 +308,11 @@ impl BrowsingContextActor {
|
|||
type_: "tabNavigated".to_owned(),
|
||||
url: url.as_str().to_owned(),
|
||||
title,
|
||||
nativeConsoleAPI: true,
|
||||
native_console_api: true,
|
||||
state: state.to_owned(),
|
||||
isFrameSwitching: false,
|
||||
is_frame_switching: false,
|
||||
};
|
||||
|
||||
for stream in self.streams.borrow_mut().values_mut() {
|
||||
let _ = stream.write_json_packet(&msg);
|
||||
}
|
||||
|
@ -371,16 +324,40 @@ impl BrowsingContextActor {
|
|||
}
|
||||
*self.title.borrow_mut() = title;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct TabNavigated {
|
||||
from: String,
|
||||
#[serde(rename = "type")]
|
||||
type_: String,
|
||||
url: String,
|
||||
title: Option<String>,
|
||||
nativeConsoleAPI: bool,
|
||||
state: String,
|
||||
isFrameSwitching: bool,
|
||||
pub(crate) fn frame_update(&self, stream: &mut TcpStream) {
|
||||
let _ = stream.write_json_packet(&FrameUpdateReply {
|
||||
from: self.name(),
|
||||
type_: "frameUpdate".into(),
|
||||
frames: vec![FrameUpdateMsg {
|
||||
id: self.browsing_context_id.index.0.get(),
|
||||
is_top_level: true,
|
||||
title: self.title.borrow().clone(),
|
||||
url: self.url.borrow().clone(),
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn document_event(&self, stream: &mut TcpStream) {
|
||||
// TODO: This is a hacky way of sending the 3 messages
|
||||
// Figure out if there needs work to be done here, ensure the page is loaded
|
||||
for (i, &name) in ["dom-loading", "dom-interactive", "dom-complete"]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let _ = stream.write_json_packet(&ResourceAvailableReply {
|
||||
from: self.name(),
|
||||
type_: "resource-available-form".into(),
|
||||
resources: vec![ResourceAvailableMsg {
|
||||
has_native_console_api: None,
|
||||
name: name.into(),
|
||||
new_uri: None,
|
||||
resource_type: "document-event".into(),
|
||||
time: i as u64,
|
||||
title: Some(self.title.borrow().clone()),
|
||||
url: Some(self.url.borrow().clone()),
|
||||
}],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
153
components/devtools/actors/configuration.rs
Normal file
153
components/devtools/actors/configuration.rs
Normal file
|
@ -0,0 +1,153 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
//! Liberally derived from <https://searchfox.org/mozilla-central/source/devtools/server/actors/target-configuration.js>
|
||||
//! and <https://searchfox.org/mozilla-central/source/devtools/server/actors/thread-configuration.js>
|
||||
//! These actors manage the configuration flags that the devtools host can apply to the targets and threads.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::net::TcpStream;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
||||
use crate::protocol::JsonPacketStream;
|
||||
use crate::{EmptyReplyMsg, StreamId};
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TargetConfigurationTraits {
|
||||
supported_options: HashMap<&'static str, bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct TargetConfigurationActorMsg {
|
||||
actor: String,
|
||||
configuration: HashMap<&'static str, bool>,
|
||||
traits: TargetConfigurationTraits,
|
||||
}
|
||||
|
||||
pub struct TargetConfigurationActor {
|
||||
name: String,
|
||||
configuration: HashMap<&'static str, bool>,
|
||||
supported_options: HashMap<&'static str, bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ThreadConfigurationActorMsg {
|
||||
actor: String,
|
||||
}
|
||||
|
||||
pub struct ThreadConfigurationActor {
|
||||
name: String,
|
||||
_configuration: HashMap<&'static str, bool>,
|
||||
}
|
||||
|
||||
impl Actor for TargetConfigurationActor {
|
||||
fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
/// The target configuration actor can handle the following messages:
|
||||
///
|
||||
/// - `updateConfiguration`: Receives new configuration flags from the devtools host.
|
||||
fn handle_message(
|
||||
&self,
|
||||
_registry: &ActorRegistry,
|
||||
msg_type: &str,
|
||||
_msg: &Map<String, Value>,
|
||||
stream: &mut TcpStream,
|
||||
_id: StreamId,
|
||||
) -> Result<ActorMessageStatus, ()> {
|
||||
Ok(match msg_type {
|
||||
"updateConfiguration" => {
|
||||
// TODO: Actually update configuration
|
||||
let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() });
|
||||
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
_ => ActorMessageStatus::Ignored,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetConfigurationActor {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
configuration: HashMap::new(),
|
||||
supported_options: HashMap::from([
|
||||
("cacheDisabled", false),
|
||||
("colorSchemeSimulation", false),
|
||||
("customFormatters", false),
|
||||
("customUserAgent", false),
|
||||
("javascriptEnabled", false),
|
||||
("overrideDPPX", false),
|
||||
("printSimulationEnabled", false),
|
||||
("rdmPaneMaxTouchPoints", false),
|
||||
("rdmPaneOrientation", false),
|
||||
("recordAllocations", false),
|
||||
("reloadOnTouchSimulationToggle", false),
|
||||
("restoreFocus", false),
|
||||
("serviceWorkersTestingEnabled", false),
|
||||
("setTabOffline", false),
|
||||
("touchEventsOverride", false),
|
||||
("tracerOptions", false),
|
||||
("useSimpleHighlightersForReducedMotion", false),
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encodable(&self) -> TargetConfigurationActorMsg {
|
||||
TargetConfigurationActorMsg {
|
||||
actor: self.name(),
|
||||
configuration: self.configuration.clone(),
|
||||
traits: TargetConfigurationTraits {
|
||||
supported_options: self.supported_options.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for ThreadConfigurationActor {
|
||||
fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
/// The thread configuration actor can handle the following messages:
|
||||
///
|
||||
/// - `updateConfiguration`: Receives new configuration flags from the devtools host.
|
||||
fn handle_message(
|
||||
&self,
|
||||
_registry: &ActorRegistry,
|
||||
msg_type: &str,
|
||||
_msg: &Map<String, Value>,
|
||||
stream: &mut TcpStream,
|
||||
_id: StreamId,
|
||||
) -> Result<ActorMessageStatus, ()> {
|
||||
Ok(match msg_type {
|
||||
"updateConfiguration" => {
|
||||
// TODO: Actually update configuration
|
||||
let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() });
|
||||
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
_ => ActorMessageStatus::Ignored,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadConfigurationActor {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
_configuration: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encodable(&self) -> ThreadConfigurationActorMsg {
|
||||
ThreadConfigurationActorMsg { actor: self.name() }
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ impl Actor for DeviceActor {
|
|||
apptype: "servo".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
appbuildid: BUILD_ID.to_string(),
|
||||
platformversion: "124.0".to_string(),
|
||||
platformversion: "125.0".to_string(),
|
||||
brandName: "Servo".to_string(),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -2,12 +2,16 @@
|
|||
* 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/. */
|
||||
|
||||
//! Liberally derived from the [Firefox JS implementation]
|
||||
//! (https://searchfox.org/mozilla-central/source/devtools/server/actors/descriptors/process.js)
|
||||
|
||||
use std::net::TcpStream;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
||||
use crate::actors::root::DescriptorTraits;
|
||||
use crate::protocol::JsonPacketStream;
|
||||
use crate::StreamId;
|
||||
|
||||
|
@ -17,14 +21,18 @@ struct ListWorkersReply {
|
|||
workers: Vec<u32>, // TODO: use proper JSON structure.
|
||||
}
|
||||
|
||||
pub struct ProcessActor {
|
||||
name: String,
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProcessActorMsg {
|
||||
actor: String,
|
||||
id: u32,
|
||||
is_parent: bool,
|
||||
is_windowless_parent: bool,
|
||||
traits: DescriptorTraits,
|
||||
}
|
||||
|
||||
impl ProcessActor {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
pub struct ProcessActor {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Actor for ProcessActor {
|
||||
|
@ -32,6 +40,9 @@ impl Actor for ProcessActor {
|
|||
self.name.clone()
|
||||
}
|
||||
|
||||
/// The process actor can handle the following messages:
|
||||
///
|
||||
/// - `listWorkers`: Returns a list of web workers, not supported yet.
|
||||
fn handle_message(
|
||||
&self,
|
||||
_registry: &ActorRegistry,
|
||||
|
@ -54,3 +65,19 @@ impl Actor for ProcessActor {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ProcessActor {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
pub fn encodable(&self) -> ProcessActorMsg {
|
||||
ProcessActorMsg {
|
||||
actor: self.name(),
|
||||
id: 0,
|
||||
is_parent: true,
|
||||
is_windowless_parent: false,
|
||||
traits: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,18 +2,20 @@
|
|||
* 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/. */
|
||||
|
||||
//! Liberally derived from the [Firefox JS implementation]
|
||||
//! (https://searchfox.org/mozilla-central/source/devtools/server/actors/root.js).
|
||||
//! Connection point for all new remote devtools interactions, providing lists of know actors
|
||||
//! that perform more specific actions (targets, addons, browser chrome, etc.)
|
||||
|
||||
use std::net::TcpStream;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
/// Liberally derived from the [Firefox JS implementation]
|
||||
/// (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/root.js).
|
||||
/// Connection point for all new remote devtools interactions, providing lists of know actors
|
||||
/// that perform more specific actions (targets, addons, browser chrome, etc.)
|
||||
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
||||
use crate::actors::device::DeviceActor;
|
||||
use crate::actors::performance::PerformanceActor;
|
||||
use crate::actors::process::{ProcessActor, ProcessActorMsg};
|
||||
use crate::actors::tab::{TabDescriptorActor, TabDescriptorActorMsg};
|
||||
use crate::actors::worker::{WorkerActor, WorkerMsg};
|
||||
use crate::protocol::{ActorDescription, JsonPacketStream};
|
||||
|
@ -95,7 +97,7 @@ pub struct Types {
|
|||
#[derive(Serialize)]
|
||||
struct ListProcessesResponse {
|
||||
from: String,
|
||||
processes: Vec<ProcessForm>,
|
||||
processes: Vec<ProcessActorMsg>,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize)]
|
||||
|
@ -105,21 +107,11 @@ pub struct DescriptorTraits {
|
|||
pub(crate) supports_reload_descriptor: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ProcessForm {
|
||||
actor: String,
|
||||
id: u32,
|
||||
is_parent: bool,
|
||||
is_windowless_parent: bool,
|
||||
traits: DescriptorTraits,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GetProcessResponse {
|
||||
from: String,
|
||||
process_descriptor: ProcessForm,
|
||||
process_descriptor: ProcessActorMsg,
|
||||
}
|
||||
|
||||
pub struct RootActor {
|
||||
|
@ -155,30 +147,21 @@ impl Actor for RootActor {
|
|||
},
|
||||
|
||||
"listProcesses" => {
|
||||
let process = registry.find::<ProcessActor>(&self.process).encodable();
|
||||
let reply = ListProcessesResponse {
|
||||
from: self.name(),
|
||||
processes: vec![ProcessForm {
|
||||
actor: self.process.clone(),
|
||||
id: 0,
|
||||
is_parent: true,
|
||||
is_windowless_parent: false,
|
||||
traits: Default::default(),
|
||||
}],
|
||||
processes: vec![process],
|
||||
};
|
||||
let _ = stream.write_json_packet(&reply);
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
|
||||
// TODO: Unexpected message getTarget for process (when inspecting)
|
||||
"getProcess" => {
|
||||
let process = registry.find::<ProcessActor>(&self.process).encodable();
|
||||
let reply = GetProcessResponse {
|
||||
from: self.name(),
|
||||
process_descriptor: ProcessForm {
|
||||
actor: self.process.clone(),
|
||||
id: 0,
|
||||
is_parent: true,
|
||||
is_windowless_parent: false,
|
||||
traits: Default::default(),
|
||||
},
|
||||
process_descriptor: process,
|
||||
};
|
||||
let _ = stream.write_json_packet(&reply);
|
||||
ActorMessageStatus::Processed
|
||||
|
@ -196,7 +179,6 @@ impl Actor for RootActor {
|
|||
ActorMessageStatus::Processed
|
||||
},
|
||||
|
||||
// https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#listing-browser-tabs
|
||||
"listTabs" => {
|
||||
let actor = ListTabsReply {
|
||||
from: "root".to_owned(),
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
* 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/. */
|
||||
|
||||
//! Liberally derived from the [Firefox JS implementation]
|
||||
//! (https://searchfox.org/mozilla-central/source/devtools/server/actors/descriptors/tab.js)
|
||||
//! Descriptor actor that represents a web view. It can link a tab to the corresponding watcher
|
||||
//! actor to enable inspection.
|
||||
|
||||
use std::net::TcpStream;
|
||||
|
||||
use serde::Serialize;
|
||||
|
@ -10,17 +15,19 @@ use serde_json::{Map, Value};
|
|||
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
||||
use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
|
||||
use crate::actors::root::{DescriptorTraits, RootActor};
|
||||
use crate::actors::watcher::{WatcherActor, WatcherActorMsg};
|
||||
use crate::protocol::JsonPacketStream;
|
||||
use crate::StreamId;
|
||||
|
||||
// https://searchfox.org/mozilla-central/source/devtools/server/actors/descriptors/tab.js
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TabDescriptorActorMsg {
|
||||
actor: String,
|
||||
browser_id: u32,
|
||||
#[serde(rename = "browsingContextID")]
|
||||
browsing_context_id: u32,
|
||||
is_zombie_tab: bool,
|
||||
#[serde(rename = "outerWindowID")]
|
||||
outer_window_id: u32,
|
||||
selected: bool,
|
||||
title: String,
|
||||
|
@ -46,6 +53,13 @@ struct GetFaviconReply {
|
|||
favicon: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct GetWatcherReply {
|
||||
from: String,
|
||||
#[serde(flatten)]
|
||||
watcher: WatcherActorMsg,
|
||||
}
|
||||
|
||||
pub struct TabDescriptorActor {
|
||||
name: String,
|
||||
browsing_context_actor: String,
|
||||
|
@ -56,6 +70,14 @@ impl Actor for TabDescriptorActor {
|
|||
self.name.clone()
|
||||
}
|
||||
|
||||
/// The tab actor can handle the following messages:
|
||||
///
|
||||
/// - `getTarget`: Returns the surrounding `BrowsingContextActor`.
|
||||
///
|
||||
/// - `getFavicon`: Should return the tab favicon, but it is not yet supported.
|
||||
///
|
||||
/// - `getWatcher`: Returns a `WatcherActor` linked to the tab's `BrowsingContext`. It is used
|
||||
/// to describe the debugging capabilities of this tab.
|
||||
fn handle_message(
|
||||
&self,
|
||||
registry: &ActorRegistry,
|
||||
|
@ -83,7 +105,15 @@ impl Actor for TabDescriptorActor {
|
|||
});
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
// TODO: Unexpected message getWatcher when inspecting tab (create watcher actor)
|
||||
"getWatcher" => {
|
||||
let ctx_actor = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
|
||||
let watcher = registry.find::<WatcherActor>(&ctx_actor.watcher);
|
||||
let _ = stream.write_json_packet(&GetWatcherReply {
|
||||
from: self.name(),
|
||||
watcher: watcher.encodable(),
|
||||
});
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
_ => ActorMessageStatus::Ignored,
|
||||
})
|
||||
}
|
||||
|
@ -105,21 +135,22 @@ impl TabDescriptorActor {
|
|||
|
||||
pub fn encodable(&self, registry: &ActorRegistry, selected: bool) -> TabDescriptorActorMsg {
|
||||
let ctx_actor = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
|
||||
|
||||
let browser_id = ctx_actor.active_pipeline.get().index.0.get();
|
||||
let browsing_context_id = ctx_actor.browsing_context_id.index.0.get();
|
||||
let title = ctx_actor.title.borrow().clone();
|
||||
let url = ctx_actor.url.borrow().clone();
|
||||
|
||||
TabDescriptorActorMsg {
|
||||
actor: self.name(),
|
||||
browsing_context_id: ctx_actor.browsing_context_id.index.0.get(),
|
||||
browser_id: ctx_actor.active_pipeline.get().index.0.get(),
|
||||
browsing_context_id,
|
||||
browser_id,
|
||||
is_zombie_tab: false,
|
||||
outer_window_id: ctx_actor.active_pipeline.get().index.0.get(),
|
||||
outer_window_id: browser_id,
|
||||
selected,
|
||||
title,
|
||||
traits: DescriptorTraits {
|
||||
watcher: true,
|
||||
supports_reload_descriptor: false,
|
||||
supports_reload_descriptor: true,
|
||||
},
|
||||
url,
|
||||
}
|
||||
|
|
259
components/devtools/actors/watcher.rs
Normal file
259
components/devtools/actors/watcher.rs
Normal file
|
@ -0,0 +1,259 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
//! Liberally derived from the [Firefox JS implementation]
|
||||
//! (https://searchfox.org/mozilla-central/source/devtools/server/actors/watcher.js).
|
||||
//! The watcher is the main entry point when debugging an element. Right now only web views are supported.
|
||||
//! It talks to the devtools remote and lists the capabilities of the inspected target, and it serves
|
||||
//! as a bridge for messages between actors.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::net::TcpStream;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
||||
use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
|
||||
use crate::actors::configuration::{
|
||||
TargetConfigurationActor, TargetConfigurationActorMsg, ThreadConfigurationActor,
|
||||
ThreadConfigurationActorMsg,
|
||||
};
|
||||
use crate::protocol::JsonPacketStream;
|
||||
use crate::{EmptyReplyMsg, StreamId};
|
||||
|
||||
/// Describes the debugged context. It informs the server of which objects can be debugged.
|
||||
/// <https://searchfox.org/mozilla-central/source/devtools/server/actors/watcher/session-context.js>
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SessionContext {
|
||||
is_server_target_switching_enabled: bool,
|
||||
supported_targets: HashMap<&'static str, bool>,
|
||||
supported_resources: HashMap<&'static str, bool>,
|
||||
context_type: SessionContextType,
|
||||
}
|
||||
|
||||
impl SessionContext {
|
||||
pub fn new(context_type: SessionContextType) -> Self {
|
||||
Self {
|
||||
is_server_target_switching_enabled: false,
|
||||
// Right now we only support debugging web views (frames)
|
||||
supported_targets: HashMap::from([
|
||||
("frame", true),
|
||||
("process", false),
|
||||
("worker", false),
|
||||
("service_worker", false),
|
||||
("shared_worker", false),
|
||||
]),
|
||||
// At the moment we are blocking most resources to avoid errors
|
||||
// Support for them will be enabled gradually once the corresponding actors start
|
||||
// working propperly
|
||||
supported_resources: HashMap::from([
|
||||
("console-message", true),
|
||||
("css-change", false),
|
||||
("css-message", false),
|
||||
("css-registered-properties", false),
|
||||
("document-event", true),
|
||||
("Cache", false),
|
||||
("cookies", false),
|
||||
("error-message", true),
|
||||
("extension-storage", false),
|
||||
("indexed-db", false),
|
||||
("local-storage", false),
|
||||
("session-storage", false),
|
||||
("platform-message", false),
|
||||
("network-event", false),
|
||||
("network-event-stacktrace", false),
|
||||
("reflow", false),
|
||||
("stylesheet", false),
|
||||
("source", false),
|
||||
("thread-state", false),
|
||||
("server-sent-event", false),
|
||||
("websocket", false),
|
||||
("jstracer-trace", false),
|
||||
("jstracer-state", false),
|
||||
("last-private-context-exit", false),
|
||||
]),
|
||||
context_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub enum SessionContextType {
|
||||
BrowserElement,
|
||||
_ContextProcess,
|
||||
_WebExtension,
|
||||
_Worker,
|
||||
_All,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct WatchTargetsReply {
|
||||
from: String,
|
||||
#[serde(rename = "type")]
|
||||
type_: String,
|
||||
target: BrowsingContextActorMsg,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct GetTargetConfigurationActorReply {
|
||||
from: String,
|
||||
configuration: TargetConfigurationActorMsg,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct GetThreadConfigurationActorReply {
|
||||
from: String,
|
||||
configuration: ThreadConfigurationActorMsg,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct WatcherTraits {
|
||||
resources: HashMap<&'static str, bool>,
|
||||
#[serde(flatten)]
|
||||
targets: HashMap<&'static str, bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct WatcherActorMsg {
|
||||
actor: String,
|
||||
traits: WatcherTraits,
|
||||
}
|
||||
|
||||
pub struct WatcherActor {
|
||||
name: String,
|
||||
browsing_context_actor: String,
|
||||
session_context: SessionContext,
|
||||
}
|
||||
|
||||
impl Actor for WatcherActor {
|
||||
fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
/// The watcher actor can handle the following messages:
|
||||
///
|
||||
/// - `watchTargets`: Returns a list of objects to debug. Since we only support web views, it
|
||||
/// returns the associated `BrowsingContextActor`. Every target sent creates a
|
||||
/// `target-available-form` event.
|
||||
///
|
||||
/// - `watchResources`: Start watching certain resource types. This sends
|
||||
/// `resource-available-form` events.
|
||||
///
|
||||
/// - `getTargetConfigurationActor`: Returns the configuration actor for a specific target, so
|
||||
/// that the server can update its settings.
|
||||
///
|
||||
/// - `getThreadConfigurationActor`: The same but with the configuration actor for the thread
|
||||
fn handle_message(
|
||||
&self,
|
||||
registry: &ActorRegistry,
|
||||
msg_type: &str,
|
||||
msg: &Map<String, Value>,
|
||||
stream: &mut TcpStream,
|
||||
_id: StreamId,
|
||||
) -> Result<ActorMessageStatus, ()> {
|
||||
Ok(match msg_type {
|
||||
"watchTargets" => {
|
||||
let target = registry
|
||||
.find::<BrowsingContextActor>(&self.browsing_context_actor)
|
||||
.encodable();
|
||||
let _ = stream.write_json_packet(&WatchTargetsReply {
|
||||
from: self.name(),
|
||||
type_: "target-available-form".into(),
|
||||
target,
|
||||
});
|
||||
|
||||
let target = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
|
||||
target.frame_update(stream);
|
||||
|
||||
// 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
|
||||
// extra empty packet to the devtools host to inform that we successfully received
|
||||
// and processed the message so that it can continue
|
||||
let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() });
|
||||
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
"watchResources" => {
|
||||
let Some(resource_types) = msg.get("resourceTypes") else {
|
||||
return Ok(ActorMessageStatus::Ignored);
|
||||
};
|
||||
|
||||
let Some(resource_types) = resource_types.as_array() else {
|
||||
return Ok(ActorMessageStatus::Ignored);
|
||||
};
|
||||
|
||||
for resource in resource_types {
|
||||
let Some(resource) = resource.as_str() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let target =
|
||||
registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
|
||||
|
||||
match resource {
|
||||
"document-event" => {
|
||||
target.document_event(stream);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() });
|
||||
}
|
||||
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
"getTargetConfigurationActor" => {
|
||||
let target = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
|
||||
let target_configuration =
|
||||
registry.find::<TargetConfigurationActor>(&target.target_configuration);
|
||||
|
||||
let _ = stream.write_json_packet(&GetTargetConfigurationActorReply {
|
||||
from: self.name(),
|
||||
configuration: target_configuration.encodable(),
|
||||
});
|
||||
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
"getThreadConfigurationActor" => {
|
||||
let target = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
|
||||
let thread_configuration =
|
||||
registry.find::<ThreadConfigurationActor>(&target.thread_configuration);
|
||||
|
||||
let _ = stream.write_json_packet(&GetThreadConfigurationActorReply {
|
||||
from: self.name(),
|
||||
configuration: thread_configuration.encodable(),
|
||||
});
|
||||
|
||||
ActorMessageStatus::Processed
|
||||
},
|
||||
_ => ActorMessageStatus::Ignored,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WatcherActor {
|
||||
pub fn new(
|
||||
name: String,
|
||||
browsing_context_actor: String,
|
||||
session_context: SessionContext,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
browsing_context_actor,
|
||||
session_context,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encodable(&self) -> WatcherActorMsg {
|
||||
WatcherActorMsg {
|
||||
actor: self.name(),
|
||||
traits: WatcherTraits {
|
||||
resources: self.session_context.supported_resources.clone(),
|
||||
targets: self.session_context.supported_targets.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
//! An actor-based remote devtools server implementation. Only tested with
|
||||
//! nightly Firefox versions at time of writing. Largely based on
|
||||
//! reverse-engineering of Firefox chrome devtool logs and reading of
|
||||
//! [code](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/).
|
||||
//! [code](https://searchfox.org/mozilla-central/source/devtools/server).
|
||||
|
||||
#![crate_name = "devtools"]
|
||||
#![crate_type = "rlib"]
|
||||
|
@ -48,9 +48,10 @@ use crate::actors::worker::{WorkerActor, WorkerType};
|
|||
use crate::protocol::JsonPacketStream;
|
||||
|
||||
mod actor;
|
||||
/// Corresponds to <http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/>
|
||||
/// <https://searchfox.org/mozilla-central/source/devtools/server/actors>
|
||||
mod actors {
|
||||
pub mod browsing_context;
|
||||
pub mod configuration;
|
||||
pub mod console;
|
||||
pub mod device;
|
||||
pub mod emulation;
|
||||
|
@ -68,6 +69,7 @@ mod actors {
|
|||
pub mod tab;
|
||||
pub mod thread;
|
||||
pub mod timeline;
|
||||
pub mod watcher;
|
||||
pub mod worker;
|
||||
}
|
||||
mod protocol;
|
||||
|
@ -113,6 +115,11 @@ struct ResponseStartUpdateMsg {
|
|||
response: ResponseStartMsg,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct EmptyReplyMsg {
|
||||
pub from: String,
|
||||
}
|
||||
|
||||
/// Spin up a devtools server that listens for connections on the specified port.
|
||||
pub fn start_server(port: u16, embedder: EmbedderProxy) -> Sender<DevtoolsControlMsg> {
|
||||
let (sender, receiver) = unbounded();
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Low-level wire protocol implementation. Currently only supports
|
||||
//! [JSON packets](https://wiki.mozilla.org/Remote_Debugging_Protocol_Stream_Transport#JSON_Packets).
|
||||
//! [JSON packets](https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#json-packets).
|
||||
|
||||
use std::error::Error;
|
||||
use std::io::{Read, Write};
|
||||
|
@ -63,7 +63,7 @@ impl JsonPacketStream for TcpStream {
|
|||
}
|
||||
|
||||
fn read_json_packet(&mut self) -> Result<Option<Value>, String> {
|
||||
// https://wiki.mozilla.org/Remote_Debugging_Protocol_Stream_Transport
|
||||
// https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#stream-transport
|
||||
// In short, each JSON packet is [ascii length]:[JSON data of given length]
|
||||
let mut buffer = vec![];
|
||||
loop {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue