mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01: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
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(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue