diff --git a/components/devtools/actor.rs b/components/devtools/actor.rs index ca2b8576321..73a05bfe5f3 100644 --- a/components/devtools/actor.rs +++ b/components/devtools/actor.rs @@ -12,14 +12,37 @@ use std::sync::{Arc, Mutex}; use base::cross_process_instant::CrossProcessInstant; use base::id::PipelineId; use log::debug; -use serde_json::{Map, Value}; +use serde_json::{Map, Value, json}; use crate::StreamId; +use crate::protocol::{ClientRequest, JsonPacketStream}; -#[derive(PartialEq)] -pub enum ActorMessageStatus { - Processed, - Ignored, +/// Error replies. +/// +/// +#[derive(Debug)] +pub enum ActorError { + MissingParameter, + BadParameterType, + UnrecognizedPacketType, + /// Custom errors, not defined in the protocol docs. + /// This includes send errors, and errors that prevent Servo from sending a reply. + Internal, +} + +impl ActorError { + pub fn name(&self) -> &'static str { + match self { + ActorError::MissingParameter => "missingParameter", + ActorError::BadParameterType => "badParameterType", + ActorError::UnrecognizedPacketType => "unrecognizedPacketType", + // The devtools frontend always checks for specific protocol errors by catching a JS exception `e` whose + // message contains the error name, and checking `e.message.includes("someErrorName")`. As a result, the + // only error name we can safely use for custom errors is the empty string, because any other error name we + // use may be a substring of some upstream error name. + ActorError::Internal => "", + } + } } /// A common trait for all devtools actors that encompasses an immutable name @@ -28,12 +51,12 @@ pub enum ActorMessageStatus { pub(crate) trait Actor: Any + ActorAsAny { fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, stream_id: StreamId, - ) -> Result; + ) -> Result<(), ActorError>; fn name(&self) -> String; fn cleanup(&self, _id: StreamId) {} } @@ -169,8 +192,8 @@ impl ActorRegistry { actor.actor_as_any_mut().downcast_mut::().unwrap() } - /// Attempt to process a message as directed by its `to` property. If the actor is not - /// found or does not indicate that it knew how to process the message, ignore the failure. + /// Attempt to process a message as directed by its `to` property. If the actor is not found, does not support the + /// message, or failed to handle the message, send an error reply instead. pub(crate) fn handle_message( &mut self, msg: &Map, @@ -186,17 +209,20 @@ impl ActorRegistry { }; match self.actors.get(to) { - None => log::warn!("message received for unknown actor \"{}\"", to), + None => { + // + let msg = json!({ "from": to, "error": "noSuchActor" }); + let _ = stream.write_json_packet(&msg); + }, Some(actor) => { let msg_type = msg.get("type").unwrap().as_str().unwrap(); - if actor.handle_message(self, msg_type, msg, stream, stream_id)? != - ActorMessageStatus::Processed - { - log::warn!( - "unexpected message type \"{}\" found for actor \"{}\"", - msg_type, - to - ); + if let Err(error) = ClientRequest::handle(stream, to, |req| { + actor.handle_message(req, self, msg_type, msg, stream_id) + }) { + // + let _ = stream.write_json_packet(&json!({ + "from": actor.name(), "error": error.name() + })); } }, } diff --git a/components/devtools/actors/breakpoint.rs b/components/devtools/actors/breakpoint.rs index 04f2de140b4..ff8f8220e15 100644 --- a/components/devtools/actors/breakpoint.rs +++ b/components/devtools/actors/breakpoint.rs @@ -5,8 +5,8 @@ use serde::Serialize; use crate::EmptyReplyMsg; -use crate::actor::{Actor, ActorMessageStatus}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError}; +use crate::protocol::ClientRequest; #[derive(Serialize)] pub struct BreakpointListActorMsg { @@ -24,27 +24,24 @@ impl Actor for BreakpointListActor { fn handle_message( &self, + request: ClientRequest, _registry: &crate::actor::ActorRegistry, msg_type: &str, _msg: &serde_json::Map, - stream: &mut std::net::TcpStream, _stream_id: crate::StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "setBreakpoint" => { let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "setActiveEventBreakpoints" => { let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index fc5116131a0..c3f00d943fa 100644 --- a/components/devtools/actors/browsing_context.rs +++ b/components/devtools/actors/browsing_context.rs @@ -20,7 +20,7 @@ use ipc_channel::ipc::{self, IpcSender}; use serde::Serialize; use serde_json::{Map, Value}; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::inspector::InspectorActor; use crate::actors::inspector::accessibility::AccessibilityActor; use crate::actors::inspector::css_properties::CssPropertiesActor; @@ -30,7 +30,7 @@ use crate::actors::tab::TabDescriptorActor; use crate::actors::thread::ThreadActor; use crate::actors::watcher::{SessionContext, SessionContextType, WatcherActor}; use crate::id::{DevtoolsBrowserId, DevtoolsBrowsingContextId, DevtoolsOuterWindowId, IdMap}; -use crate::protocol::JsonPacketStream; +use crate::protocol::{ClientRequest, JsonPacketStream}; use crate::resource::ResourceAvailable; use crate::{EmptyReplyMsg, StreamId}; @@ -166,29 +166,28 @@ impl Actor for BrowsingContextActor { fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "listFrames" => { // TODO: Find out what needs to be listed here let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "listWorkers" => { - let _ = stream.write_json_packet(&ListWorkersReply { + request.reply_final(&ListWorkersReply { from: self.name(), // TODO: Find out what needs to be listed here workers: vec![], - }); - ActorMessageStatus::Processed + })? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } fn cleanup(&self, id: StreamId) { @@ -353,8 +352,8 @@ impl BrowsingContextActor { *self.title.borrow_mut() = title; } - pub(crate) fn frame_update(&self, stream: &mut TcpStream) { - let _ = stream.write_json_packet(&FrameUpdateReply { + pub(crate) fn frame_update(&self, request: &mut ClientRequest) { + let _ = request.write_json_packet(&FrameUpdateReply { from: self.name(), type_: "frameUpdate".into(), frames: vec![FrameUpdateMsg { diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index 871399b6e61..2a9b7b58c6c 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -25,11 +25,11 @@ use serde::Serialize; use serde_json::{self, Map, Number, Value}; use uuid::Uuid; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::browsing_context::BrowsingContextActor; use crate::actors::object::ObjectActor; use crate::actors::worker::WorkerActor; -use crate::protocol::JsonPacketStream; +use crate::protocol::{ClientRequest, JsonPacketStream}; use crate::resource::{ResourceArrayType, ResourceAvailable}; use crate::{StreamId, UniqueId}; @@ -304,18 +304,19 @@ impl Actor for ConsoleActor { fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "clearMessagesCache" => { self.cached_events .borrow_mut() .remove(&self.current_unique_id(registry)); - ActorMessageStatus::Processed + // FIXME: need to send a reply here! + return Err(ActorError::UnrecognizedPacketType); }, "getCachedMessages" => { @@ -368,8 +369,7 @@ impl Actor for ConsoleActor { from: self.name(), messages, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "startListeners" => { @@ -384,8 +384,7 @@ impl Actor for ConsoleActor { .collect(), traits: StartedListenersTraits, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "stopListeners" => { @@ -401,8 +400,7 @@ impl Actor for ConsoleActor { .map(|listener| listener.as_str().unwrap().to_owned()) .collect(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, //TODO: implement autocompletion like onAutocomplete in @@ -413,14 +411,12 @@ impl Actor for ConsoleActor { matches: vec![], match_prop: "".to_owned(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "evaluateJS" => { let msg = self.evaluate_js(registry, msg); - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "evaluateJSAsync" => { @@ -431,14 +427,12 @@ impl Actor for ConsoleActor { }; // Emit an eager reply so that the client starts listening // for an async event with the resultID - if stream.write_json_packet(&early_reply).is_err() { - return Ok(ActorMessageStatus::Processed); - } + let stream = request.reply(&early_reply)?; if msg.get("eager").and_then(|v| v.as_bool()).unwrap_or(false) { // We don't support the side-effect free evaluation that eager evalaution // really needs. - return Ok(ActorMessageStatus::Processed); + return Ok(()); } let reply = self.evaluate_js(registry, msg).unwrap(); @@ -454,8 +448,7 @@ impl Actor for ConsoleActor { helper_result: reply.helper_result, }; // Send the data from evaluateJS along with a resultID - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + stream.write_json_packet(&msg)? }, "setPreferences" => { @@ -463,11 +456,11 @@ impl Actor for ConsoleActor { from: self.name(), updated: vec![], }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/device.rs b/components/devtools/actors/device.rs index 5e68d898bc4..2c49eaedc02 100644 --- a/components/devtools/actors/device.rs +++ b/components/devtools/actors/device.rs @@ -2,14 +2,12 @@ * 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/. */ -use std::net::TcpStream; - use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::{ActorDescription, JsonPacketStream, Method}; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::{ActorDescription, ClientRequest, Method}; #[derive(Serialize)] struct GetDescriptionReply { @@ -46,13 +44,13 @@ impl Actor for DeviceActor { } fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "getDescription" => { let msg = GetDescriptionReply { from: self.name(), @@ -64,12 +62,12 @@ impl Actor for DeviceActor { brand_name: "Servo".to_string(), }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/framerate.rs b/components/devtools/actors/framerate.rs index 1ea5f007170..d97b15576d5 100644 --- a/components/devtools/actors/framerate.rs +++ b/components/devtools/actors/framerate.rs @@ -3,7 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::mem; -use std::net::TcpStream; use base::id::PipelineId; use devtools_traits::DevtoolScriptControlMsg; @@ -11,8 +10,9 @@ use ipc_channel::ipc::IpcSender; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::timeline::HighResolutionStamp; +use crate::protocol::ClientRequest; pub struct FramerateActor { name: String, @@ -29,13 +29,13 @@ impl Actor for FramerateActor { fn handle_message( &self, + _request: ClientRequest, _registry: &ActorRegistry, _msg_type: &str, _msg: &Map, - _stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(ActorMessageStatus::Ignored) + ) -> Result<(), ActorError> { + Err(ActorError::UnrecognizedPacketType) } } diff --git a/components/devtools/actors/inspector.rs b/components/devtools/actors/inspector.rs index 28a4d7ccf94..087e6c630ca 100644 --- a/components/devtools/actors/inspector.rs +++ b/components/devtools/actors/inspector.rs @@ -6,7 +6,6 @@ use std::cell::RefCell; use std::collections::HashMap; -use std::net::TcpStream; use devtools_traits::DevtoolScriptControlMsg; use devtools_traits::DevtoolScriptControlMsg::GetRootNode; @@ -15,13 +14,13 @@ use serde::Serialize; use serde_json::{self, Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::browsing_context::BrowsingContextActor; use crate::actors::inspector::highlighter::{HighlighterActor, HighlighterMsg}; use crate::actors::inspector::node::NodeInfoToProtocol; use crate::actors::inspector::page_style::{PageStyleActor, PageStyleMsg}; use crate::actors::inspector::walker::{WalkerActor, WalkerMsg}; -use crate::protocol::JsonPacketStream; +use crate::protocol::ClientRequest; pub mod accessibility; pub mod css_properties; @@ -73,19 +72,19 @@ impl Actor for InspectorActor { fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { + ) -> Result<(), ActorError> { let browsing_context = registry.find::(&self.browsing_context); let pipeline = browsing_context.active_pipeline_id.get(); - Ok(match msg_type { + match msg_type { "getWalker" => { let (tx, rx) = ipc::channel().unwrap(); self.script_chan.send(GetRootNode(pipeline, tx)).unwrap(); - let root_info = rx.recv().unwrap().ok_or(())?; + let root_info = rx.recv().unwrap().ok_or(ActorError::Internal)?; let name = self .walker @@ -121,8 +120,7 @@ impl Actor for InspectorActor { root, }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getPageStyle" => { @@ -149,8 +147,7 @@ impl Actor for InspectorActor { ]), }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "supportsHighlighters" => { @@ -158,8 +155,7 @@ impl Actor for InspectorActor { from: self.name(), value: true, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getHighlighterByType" => { @@ -180,11 +176,10 @@ impl Actor for InspectorActor { actor: self.highlighter.borrow().clone().unwrap(), }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/inspector/accessibility.rs b/components/devtools/actors/inspector/accessibility.rs index 07ec8e24398..5f5db6cd358 100644 --- a/components/devtools/actors/inspector/accessibility.rs +++ b/components/devtools/actors/inspector/accessibility.rs @@ -5,14 +5,12 @@ //! The Accessibility actor is responsible for the Accessibility tab in the DevTools page. Right //! now it is a placeholder for future functionality. -use std::net::TcpStream; - use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; #[derive(Serialize)] struct BootstrapState { @@ -75,20 +73,19 @@ impl Actor for AccessibilityActor { /// inspector Walker actor) fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "bootstrap" => { let msg = BootstrapReply { from: self.name(), state: BootstrapState { enabled: false }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getSimulator" => { // TODO: Create actual simulator @@ -97,8 +94,7 @@ impl Actor for AccessibilityActor { from: self.name(), simulator: ActorMsg { actor: simulator }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getTraits" => { let msg = GetTraitsReply { @@ -107,8 +103,7 @@ impl Actor for AccessibilityActor { tabbing_order: true, }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getWalker" => { // TODO: Create actual accessible walker @@ -117,11 +112,11 @@ impl Actor for AccessibilityActor { from: self.name(), walker: ActorMsg { actor: walker }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/inspector/css_properties.rs b/components/devtools/actors/inspector/css_properties.rs index 6fae7bc86ce..ef3c4b5696b 100644 --- a/components/devtools/actors/inspector/css_properties.rs +++ b/components/devtools/actors/inspector/css_properties.rs @@ -6,15 +6,14 @@ //! alternative names use std::collections::HashMap; -use std::net::TcpStream; use devtools_traits::CssDatabaseProperty; use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; pub struct CssPropertiesActor { name: String, @@ -38,23 +37,20 @@ impl Actor for CssPropertiesActor { /// inspector can show the available options fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { - "getCSSDatabase" => { - let _ = stream.write_json_packet(&GetCssDatabaseReply { - from: self.name(), - properties: &self.properties, - }); - - ActorMessageStatus::Processed - }, - _ => ActorMessageStatus::Ignored, - }) + ) -> Result<(), ActorError> { + match msg_type { + "getCSSDatabase" => request.reply_final(&GetCssDatabaseReply { + from: self.name(), + properties: &self.properties, + })?, + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/inspector/highlighter.rs b/components/devtools/actors/inspector/highlighter.rs index 0dbf2b1e52f..1a5f755af78 100644 --- a/components/devtools/actors/inspector/highlighter.rs +++ b/components/devtools/actors/inspector/highlighter.rs @@ -5,16 +5,14 @@ //! Handles highlighting selected DOM nodes in the inspector. At the moment it only replies and //! changes nothing on Servo's side. -use std::net::TcpStream; - use base::id::PipelineId; use devtools_traits::DevtoolScriptControlMsg; use ipc_channel::ipc::IpcSender; use serde::Serialize; use serde_json::{self, Map, Value}; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; use crate::{EmptyReplyMsg, StreamId}; #[derive(Serialize)] @@ -46,22 +44,20 @@ impl Actor for HighlighterActor { /// - `hide`: Disables highlighting for the selected node fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "show" => { let Some(node_actor) = msg.get("node") else { - // TODO: send missing parameter error - return Ok(ActorMessageStatus::Ignored); + return Err(ActorError::MissingParameter); }; let Some(node_actor_name) = node_actor.as_str() else { - // TODO: send invalid parameter error - return Ok(ActorMessageStatus::Ignored); + return Err(ActorError::BadParameterType); }; if node_actor_name.starts_with("inspector") { @@ -71,8 +67,7 @@ impl Actor for HighlighterActor { from: self.name(), value: false, }; - let _ = stream.write_json_packet(&msg); - return Ok(ActorMessageStatus::Processed); + return request.reply_final(&msg); } self.instruct_script_thread_to_highlight_node( @@ -83,20 +78,19 @@ impl Actor for HighlighterActor { from: self.name(), value: true, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "hide" => { self.instruct_script_thread_to_highlight_node(None, registry); let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/inspector/layout.rs b/components/devtools/actors/inspector/layout.rs index 41ba640ca69..bf0bbf437e6 100644 --- a/components/devtools/actors/inspector/layout.rs +++ b/components/devtools/actors/inspector/layout.rs @@ -5,14 +5,12 @@ //! The layout actor informs the DevTools client of the layout properties of the document, such as //! grids or flexboxes. It acts as a placeholder for now. -use std::net::TcpStream; - use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; #[derive(Serialize)] pub struct LayoutInspectorActorMsg { @@ -47,21 +45,20 @@ impl Actor for LayoutInspectorActor { /// - `getCurrentFlexbox`: Returns the active flexbox, non functional at the moment fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "getGrids" => { let msg = GetGridsReply { from: self.name(), // TODO: Actually create a list of grids grids: vec![], }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getCurrentFlexbox" => { let msg = GetCurrentFlexboxReply { @@ -69,12 +66,14 @@ impl Actor for LayoutInspectorActor { // TODO: Create and return the current flexbox object flexbox: None, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } + + fn cleanup(&self, _id: StreamId) {} } impl LayoutInspectorActor { diff --git a/components/devtools/actors/inspector/node.rs b/components/devtools/actors/inspector/node.rs index a731f15b2d8..0ef3221ebc8 100644 --- a/components/devtools/actors/inspector/node.rs +++ b/components/devtools/actors/inspector/node.rs @@ -7,7 +7,6 @@ use std::cell::RefCell; use std::collections::HashMap; -use std::net::TcpStream; use base::id::PipelineId; use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement, ModifyAttribute}; @@ -16,9 +15,9 @@ use ipc_channel::ipc::{self, IpcSender}; use serde::Serialize; use serde_json::{self, Map, Value}; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::inspector::walker::WalkerActor; -use crate::protocol::JsonPacketStream; +use crate::protocol::ClientRequest; use crate::{EmptyReplyMsg, StreamId}; /// Text node type constant. This is defined again to avoid depending on `script`, where it is defined originally. @@ -113,15 +112,19 @@ impl Actor for NodeActor { /// - `getUniqueSelector`: Returns the display name of this node fn handle_message( &self, + mut request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "modifyAttributes" => { - let mods = msg.get("modifications").ok_or(())?.as_array().ok_or(())?; + let mods = msg + .get("modifications") + .ok_or(ActorError::MissingParameter)? + .as_array() + .ok_or(ActorError::BadParameterType)?; let modifications: Vec<_> = mods .iter() .filter_map(|json_mod| { @@ -130,7 +133,7 @@ impl Actor for NodeActor { .collect(); let walker = registry.find::(&self.walker); - walker.new_mutations(stream, &self.name, &modifications); + walker.new_mutations(&mut request, &self.name, &modifications); self.script_chan .send(ModifyAttribute( @@ -138,11 +141,10 @@ impl Actor for NodeActor { registry.actor_to_script(self.name()), modifications, )) - .map_err(|_| ())?; + .map_err(|_| ActorError::Internal)?; let reply = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply)? }, "getUniqueSelector" => { @@ -150,7 +152,10 @@ impl Actor for NodeActor { self.script_chan .send(GetDocumentElement(self.pipeline, tx)) .unwrap(); - let doc_elem_info = rx.recv().map_err(|_| ())?.ok_or(())?; + let doc_elem_info = rx + .recv() + .map_err(|_| ActorError::Internal)? + .ok_or(ActorError::Internal)?; let node = doc_elem_info.encode( registry, true, @@ -163,12 +168,12 @@ impl Actor for NodeActor { from: self.name(), value: node.display_name, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/inspector/page_style.rs b/components/devtools/actors/inspector/page_style.rs index 9ac1958406e..5fa07dec3fd 100644 --- a/components/devtools/actors/inspector/page_style.rs +++ b/components/devtools/actors/inspector/page_style.rs @@ -8,7 +8,6 @@ use std::collections::HashMap; use std::collections::hash_map::Entry; use std::iter::once; -use std::net::TcpStream; use base::id::PipelineId; use devtools_traits::DevtoolScriptControlMsg::{GetLayout, GetSelectors}; @@ -18,11 +17,11 @@ use serde::Serialize; use serde_json::{self, Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::inspector::node::NodeActor; use crate::actors::inspector::style_rule::{AppliedRule, ComputedDeclaration, StyleRuleActor}; use crate::actors::inspector::walker::{WalkerActor, find_child}; -use crate::protocol::JsonPacketStream; +use crate::protocol::ClientRequest; #[derive(Serialize)] struct GetAppliedReply { @@ -114,30 +113,34 @@ impl Actor for PageStyleActor { /// - `isPositionEditable`: Informs whether you can change a style property in the inspector. fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { - "getApplied" => self.get_applied(msg, registry, stream)?, - "getComputed" => self.get_computed(msg, registry, stream)?, - "getLayout" => self.get_layout(msg, registry, stream)?, - "isPositionEditable" => self.is_position_editable(stream), - _ => ActorMessageStatus::Ignored, - }) + ) -> Result<(), ActorError> { + match msg_type { + "getApplied" => self.get_applied(request, msg, registry), + "getComputed" => self.get_computed(request, msg, registry), + "getLayout" => self.get_layout(request, msg, registry), + "isPositionEditable" => self.is_position_editable(request), + _ => Err(ActorError::UnrecognizedPacketType), + } } } impl PageStyleActor { fn get_applied( &self, + request: ClientRequest, msg: &Map, registry: &ActorRegistry, - stream: &mut TcpStream, - ) -> Result { - let target = msg.get("node").ok_or(())?.as_str().ok_or(())?; + ) -> Result<(), ActorError> { + let target = msg + .get("node") + .ok_or(ActorError::MissingParameter)? + .as_str() + .ok_or(ActorError::BadParameterType)?; let node = registry.find::(target); let walker = registry.find::(&node.walker); let entries: Vec<_> = find_child( @@ -214,17 +217,20 @@ impl PageStyleActor { entries, from: self.name(), }; - let _ = stream.write_json_packet(&msg); - Ok(ActorMessageStatus::Processed) + request.reply_final(&msg) } fn get_computed( &self, + request: ClientRequest, msg: &Map, registry: &ActorRegistry, - stream: &mut TcpStream, - ) -> Result { - let target = msg.get("node").ok_or(())?.as_str().ok_or(())?; + ) -> Result<(), ActorError> { + let target = msg + .get("node") + .ok_or(ActorError::MissingParameter)? + .as_str() + .ok_or(ActorError::BadParameterType)?; let node_actor = registry.find::(target); let computed = (|| match node_actor .style_rules @@ -249,18 +255,22 @@ impl PageStyleActor { computed, from: self.name(), }; - let _ = stream.write_json_packet(&msg); - Ok(ActorMessageStatus::Processed) + request.reply_final(&msg) } fn get_layout( &self, + request: ClientRequest, msg: &Map, registry: &ActorRegistry, - stream: &mut TcpStream, - ) -> Result { - let target = msg.get("node").ok_or(())?.as_str().ok_or(())?; - let (computed_node_sender, computed_node_receiver) = ipc::channel().map_err(|_| ())?; + ) -> Result<(), ActorError> { + let target = msg + .get("node") + .ok_or(ActorError::MissingParameter)? + .as_str() + .ok_or(ActorError::BadParameterType)?; + let (computed_node_sender, computed_node_receiver) = + ipc::channel().map_err(|_| ActorError::Internal)?; self.script_chan .send(GetLayout( self.pipeline, @@ -288,7 +298,10 @@ impl PageStyleActor { padding_left, width, height, - } = computed_node_receiver.recv().map_err(|_| ())?.ok_or(())?; + } = computed_node_receiver + .recv() + .map_err(|_| ActorError::Internal)? + .ok_or(ActorError::Internal)?; let msg_auto_margins = msg .get("autoMargins") .and_then(Value::as_bool) @@ -333,18 +346,16 @@ impl PageStyleActor { width, height, }; - let msg = serde_json::to_string(&msg).map_err(|_| ())?; - let msg = serde_json::from_str::(&msg).map_err(|_| ())?; - let _ = stream.write_json_packet(&msg); - Ok(ActorMessageStatus::Processed) + let msg = serde_json::to_string(&msg).map_err(|_| ActorError::Internal)?; + let msg = serde_json::from_str::(&msg).map_err(|_| ActorError::Internal)?; + request.reply_final(&msg) } - fn is_position_editable(&self, stream: &mut TcpStream) -> ActorMessageStatus { + fn is_position_editable(&self, request: ClientRequest) -> Result<(), ActorError> { let msg = IsPositionEditableReply { from: self.name(), value: false, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg) } } diff --git a/components/devtools/actors/inspector/style_rule.rs b/components/devtools/actors/inspector/style_rule.rs index c3b3c1afbb5..5883ee7deb8 100644 --- a/components/devtools/actors/inspector/style_rule.rs +++ b/components/devtools/actors/inspector/style_rule.rs @@ -7,7 +7,6 @@ //! A group is either the html style attribute or one selector from one stylesheet. use std::collections::HashMap; -use std::net::TcpStream; use devtools_traits::DevtoolScriptControlMsg::{ GetAttributeStyle, GetComputedStyle, GetDocumentElement, GetStylesheetStyle, ModifyRule, @@ -17,10 +16,10 @@ use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::inspector::node::NodeActor; use crate::actors::inspector::walker::WalkerActor; -use crate::protocol::JsonPacketStream; +use crate::protocol::ClientRequest; const ELEMENT_STYLE_TYPE: u32 = 100; @@ -98,16 +97,20 @@ impl Actor for StyleRuleActor { /// when returning the list of rules. fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "setRuleText" => { // Parse the modifications sent from the client - let mods = msg.get("modifications").ok_or(())?.as_array().ok_or(())?; + let mods = msg + .get("modifications") + .ok_or(ActorError::MissingParameter)? + .as_array() + .ok_or(ActorError::BadParameterType)?; let modifications: Vec<_> = mods .iter() .filter_map(|json_mod| { @@ -125,13 +128,13 @@ impl Actor for StyleRuleActor { registry.actor_to_script(self.node.clone()), modifications, )) - .map_err(|_| ())?; + .map_err(|_| ActorError::Internal)?; - let _ = stream.write_json_packet(&self.encodable(registry)); - ActorMessageStatus::Processed + request.reply_final(&self.encodable(registry))? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/inspector/walker.rs b/components/devtools/actors/inspector/walker.rs index 9b3447df7bf..7b551a1dbdd 100644 --- a/components/devtools/actors/inspector/walker.rs +++ b/components/devtools/actors/inspector/walker.rs @@ -5,7 +5,6 @@ //! The walker actor is responsible for traversing the DOM tree in various ways to create new nodes use std::cell::RefCell; -use std::net::TcpStream; use base::id::PipelineId; use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement}; @@ -14,10 +13,10 @@ use ipc_channel::ipc::{self, IpcSender}; use serde::Serialize; use serde_json::{self, Map, Value}; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::inspector::layout::{LayoutInspectorActor, LayoutInspectorActorMsg}; use crate::actors::inspector::node::{NodeActorMsg, NodeInfoToProtocol}; -use crate::protocol::JsonPacketStream; +use crate::protocol::{ClientRequest, JsonPacketStream}; use crate::{EmptyReplyMsg, StreamId}; #[derive(Serialize)] @@ -64,7 +63,7 @@ struct GetLayoutInspectorReply { } #[derive(Serialize)] -struct WatchRootNodeReply { +struct WatchRootNodeNotification { #[serde(rename = "type")] type_: String, from: String, @@ -94,7 +93,7 @@ struct GetOffsetParentReply { } #[derive(Serialize)] -struct NewMutationsReply { +struct NewMutationsNotification { from: String, #[serde(rename = "type")] type_: String, @@ -123,24 +122,31 @@ impl Actor for WalkerActor { /// node and its ascendents fn handle_message( &self, + mut request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "children" => { - let target = msg.get("node").ok_or(())?.as_str().ok_or(())?; - let (tx, rx) = ipc::channel().map_err(|_| ())?; + let target = msg + .get("node") + .ok_or(ActorError::MissingParameter)? + .as_str() + .ok_or(ActorError::BadParameterType)?; + let (tx, rx) = ipc::channel().map_err(|_| ActorError::Internal)?; self.script_chan .send(GetChildren( self.pipeline, registry.actor_to_script(target.into()), tx, )) - .map_err(|_| ())?; - let children = rx.recv().map_err(|_| ())?.ok_or(())?; + .map_err(|_| ActorError::Internal)?; + let children = rx + .recv() + .map_err(|_| ActorError::Internal)? + .ok_or(ActorError::Internal)?; let msg = ChildrenReply { has_first: true, @@ -159,20 +165,21 @@ impl Actor for WalkerActor { .collect(), from: self.name(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "clearPseudoClassLocks" => { let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "documentElement" => { - let (tx, rx) = ipc::channel().map_err(|_| ())?; + let (tx, rx) = ipc::channel().map_err(|_| ActorError::Internal)?; self.script_chan .send(GetDocumentElement(self.pipeline, tx)) - .map_err(|_| ())?; - let doc_elem_info = rx.recv().map_err(|_| ())?.ok_or(())?; + .map_err(|_| ActorError::Internal)?; + let doc_elem_info = rx + .recv() + .map_err(|_| ActorError::Internal)? + .ok_or(ActorError::Internal)?; let node = doc_elem_info.encode( registry, true, @@ -185,8 +192,7 @@ impl Actor for WalkerActor { from: self.name(), node, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getLayoutInspector" => { // TODO: Create actual layout inspector actor @@ -198,8 +204,7 @@ impl Actor for WalkerActor { from: self.name(), actor, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getMutations" => { let msg = GetMutationsReply { @@ -216,20 +221,26 @@ impl Actor for WalkerActor { }) .collect(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getOffsetParent" => { let msg = GetOffsetParentReply { from: self.name(), node: None, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "querySelector" => { - let selector = msg.get("selector").ok_or(())?.as_str().ok_or(())?; - let node = msg.get("node").ok_or(())?.as_str().ok_or(())?; + let selector = msg + .get("selector") + .ok_or(ActorError::MissingParameter)? + .as_str() + .ok_or(ActorError::BadParameterType)?; + let node = msg + .get("node") + .ok_or(ActorError::MissingParameter)? + .as_str() + .ok_or(ActorError::BadParameterType)?; let mut hierarchy = find_child( &self.script_chan, self.pipeline, @@ -239,39 +250,38 @@ impl Actor for WalkerActor { vec![], |msg| msg.display_name == selector, ) - .map_err(|_| ())?; + .map_err(|_| ActorError::Internal)?; hierarchy.reverse(); - let node = hierarchy.pop().ok_or(())?; + let node = hierarchy.pop().ok_or(ActorError::Internal)?; let msg = QuerySelectorReply { from: self.name(), node, new_parents: hierarchy, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "watchRootNode" => { - let msg = WatchRootNodeReply { + let msg = WatchRootNodeNotification { type_: "root-available".into(), from: self.name(), node: self.root_node.clone(), }; - let _ = stream.write_json_packet(&msg); + let _ = request.write_json_packet(&msg); let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } impl WalkerActor { pub(crate) fn new_mutations( &self, - stream: &mut TcpStream, + request: &mut ClientRequest, target: &str, modifications: &[AttrModification], ) { @@ -279,7 +289,7 @@ impl WalkerActor { let mut mutations = self.mutations.borrow_mut(); mutations.extend(modifications.iter().cloned().map(|m| (m, target.into()))); } - let _ = stream.write_json_packet(&NewMutationsReply { + let _ = request.write_json_packet(&NewMutationsNotification { from: self.name(), type_: "newMutations".into(), }); diff --git a/components/devtools/actors/memory.rs b/components/devtools/actors/memory.rs index c6e58aff50c..0dbc6cfef19 100644 --- a/components/devtools/actors/memory.rs +++ b/components/devtools/actors/memory.rs @@ -2,13 +2,12 @@ * 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/. */ -use std::net::TcpStream; - use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -36,13 +35,13 @@ impl Actor for MemoryActor { fn handle_message( &self, + _request: ClientRequest, _registry: &ActorRegistry, _msg_type: &str, _msg: &Map, - _stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(ActorMessageStatus::Ignored) + ) -> Result<(), ActorError> { + Err(ActorError::UnrecognizedPacketType) } } diff --git a/components/devtools/actors/network_event.rs b/components/devtools/actors/network_event.rs index 565cdfcdfd7..7c59a1729ad 100644 --- a/components/devtools/actors/network_event.rs +++ b/components/devtools/actors/network_event.rs @@ -5,7 +5,6 @@ //! Liberally derived from the [Firefox JS implementation](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webconsole.js). //! Handles interaction with the remote web console on network events (HTTP requests, responses) in Servo. -use std::net::TcpStream; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use chrono::{Local, LocalResult, TimeZone}; @@ -16,9 +15,9 @@ use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::network_handler::Cause; -use crate::protocol::JsonPacketStream; +use crate::protocol::ClientRequest; pub struct NetworkEventActor { pub name: String, @@ -202,13 +201,13 @@ impl Actor for NetworkEventActor { fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "getRequestHeaders" => { let mut headers = Vec::new(); let mut raw_headers_string = "".to_owned(); @@ -232,8 +231,7 @@ impl Actor for NetworkEventActor { header_size: headers_size, raw_headers: raw_headers_string, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getRequestCookies" => { let mut cookies = Vec::new(); @@ -248,8 +246,7 @@ impl Actor for NetworkEventActor { from: self.name(), cookies, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getRequestPostData" => { let msg = GetRequestPostDataReply { @@ -257,8 +254,7 @@ impl Actor for NetworkEventActor { post_data: self.request_body.clone(), post_data_discarded: self.request_body.is_none(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getResponseHeaders" => { if let Some(ref response_headers) = self.response_headers_raw { @@ -282,9 +278,11 @@ impl Actor for NetworkEventActor { header_size: headers_size, raw_headers: raw_headers_string, }; - let _ = stream.write_json_packet(&msg); + request.reply_final(&msg)?; + } else { + // FIXME: what happens when there are no response headers? + return Err(ActorError::Internal); } - ActorMessageStatus::Processed }, "getResponseCookies" => { let mut cookies = Vec::new(); @@ -300,8 +298,7 @@ impl Actor for NetworkEventActor { from: self.name(), cookies, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getResponseContent" => { let msg = GetResponseContentReply { @@ -309,8 +306,7 @@ impl Actor for NetworkEventActor { content: self.response_body.clone(), content_discarded: self.response_body.is_none(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getEventTimings" => { // TODO: This is a fake timings msg @@ -323,8 +319,7 @@ impl Actor for NetworkEventActor { timings: timings_obj, total_time: total, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getSecurityInfo" => { // TODO: Send the correct values for securityInfo. @@ -334,11 +329,11 @@ impl Actor for NetworkEventActor { state: "insecure".to_owned(), }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/object.rs b/components/devtools/actors/object.rs index 2e0e47e74e7..15b7466de10 100644 --- a/components/devtools/actors/object.rs +++ b/components/devtools/actors/object.rs @@ -2,12 +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/. */ -use std::net::TcpStream; - use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; pub struct ObjectActor { pub name: String, @@ -20,14 +19,14 @@ impl Actor for ObjectActor { } fn handle_message( &self, + _request: ClientRequest, _: &ActorRegistry, _: &str, _: &Map, - _: &mut TcpStream, _: StreamId, - ) -> Result { + ) -> Result<(), ActorError> { // TODO: Handle enumSymbols for console object inspection - Ok(ActorMessageStatus::Ignored) + Err(ActorError::UnrecognizedPacketType) } } diff --git a/components/devtools/actors/performance.rs b/components/devtools/actors/performance.rs index bf64b9a261c..e0b9a6862ef 100644 --- a/components/devtools/actors/performance.rs +++ b/components/devtools/actors/performance.rs @@ -5,14 +5,12 @@ // TODO: Is this actor still relevant? #![allow(dead_code)] -use std::net::TcpStream; - use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::{ActorDescription, JsonPacketStream, Method}; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::{ActorDescription, ClientRequest, Method}; pub struct PerformanceActor { name: String, @@ -62,13 +60,13 @@ impl Actor for PerformanceActor { fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "connect" => { let msg = ConnectReply { from: self.name(), @@ -82,8 +80,7 @@ impl Actor for PerformanceActor { }, }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "canCurrentlyRecord" => { let msg = CanCurrentlyRecordReply { @@ -93,11 +90,11 @@ impl Actor for PerformanceActor { errors: vec![], }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/preference.rs b/components/devtools/actors/preference.rs index c66c910df60..c64aa583b5d 100644 --- a/components/devtools/actors/preference.rs +++ b/components/devtools/actors/preference.rs @@ -2,16 +2,13 @@ * 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/. */ -use std::net::TcpStream; - -use log::warn; use serde::Serialize; use serde_json::{Map, Value}; use servo_config::pref; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; pub struct PreferenceActor { name: String, @@ -30,43 +27,44 @@ impl Actor for PreferenceActor { fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - let Some(key) = msg.get("value").and_then(|v| v.as_str()) else { - warn!("PreferenceActor: handle_message: value is not a string"); - return Ok(ActorMessageStatus::Ignored); - }; + ) -> Result<(), ActorError> { + let key = msg + .get("value") + .ok_or(ActorError::MissingParameter)? + .as_str() + .ok_or(ActorError::BadParameterType)?; // TODO: Map more preferences onto their Servo values. - Ok(match key { + match key { "dom.serviceWorkers.enabled" => { - self.write_bool(pref!(dom_serviceworker_enabled), stream) + self.write_bool(request, pref!(dom_serviceworker_enabled)) }, - _ => self.handle_missing_preference(msg_type, stream), - }) + _ => self.handle_missing_preference(request, msg_type), + } } } impl PreferenceActor { fn handle_missing_preference( &self, + request: ClientRequest, msg_type: &str, - stream: &mut TcpStream, - ) -> ActorMessageStatus { + ) -> Result<(), ActorError> { match msg_type { - "getBoolPref" => self.write_bool(false, stream), - "getCharPref" => self.write_char("".into(), stream), - "getIntPref" => self.write_int(0, stream), - "getFloatPref" => self.write_float(0., stream), - _ => ActorMessageStatus::Ignored, + "getBoolPref" => self.write_bool(request, false), + "getCharPref" => self.write_char(request, "".into()), + "getIntPref" => self.write_int(request, 0), + "getFloatPref" => self.write_float(request, 0.), + _ => Err(ActorError::UnrecognizedPacketType), } } - fn write_bool(&self, pref_value: bool, stream: &mut TcpStream) -> ActorMessageStatus { + fn write_bool(&self, request: ClientRequest, pref_value: bool) -> Result<(), ActorError> { #[derive(Serialize)] struct BoolReply { from: String, @@ -77,11 +75,10 @@ impl PreferenceActor { from: self.name.clone(), value: pref_value, }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply) } - fn write_char(&self, pref_value: String, stream: &mut TcpStream) -> ActorMessageStatus { + fn write_char(&self, request: ClientRequest, pref_value: String) -> Result<(), ActorError> { #[derive(Serialize)] struct CharReply { from: String, @@ -92,11 +89,10 @@ impl PreferenceActor { from: self.name.clone(), value: pref_value, }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply) } - fn write_int(&self, pref_value: i64, stream: &mut TcpStream) -> ActorMessageStatus { + fn write_int(&self, request: ClientRequest, pref_value: i64) -> Result<(), ActorError> { #[derive(Serialize)] struct IntReply { from: String, @@ -107,11 +103,10 @@ impl PreferenceActor { from: self.name.clone(), value: pref_value, }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply) } - fn write_float(&self, pref_value: f64, stream: &mut TcpStream) -> ActorMessageStatus { + fn write_float(&self, request: ClientRequest, pref_value: f64) -> Result<(), ActorError> { #[derive(Serialize)] struct FloatReply { from: String, @@ -122,7 +117,6 @@ impl PreferenceActor { from: self.name.clone(), value: pref_value, }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply) } } diff --git a/components/devtools/actors/process.rs b/components/devtools/actors/process.rs index 1a3f8fbb64f..25c4eb425fd 100644 --- a/components/devtools/actors/process.rs +++ b/components/devtools/actors/process.rs @@ -6,15 +6,13 @@ //! //! [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::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::root::DescriptorTraits; -use crate::protocol::JsonPacketStream; +use crate::protocol::ClientRequest; #[derive(Serialize)] struct ListWorkersReply { @@ -46,24 +44,24 @@ impl Actor for ProcessActor { /// - `listWorkers`: Returns a list of web workers, not supported yet. fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "listWorkers" => { let reply = ListWorkersReply { from: self.name(), workers: vec![], }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/reflow.rs b/components/devtools/actors/reflow.rs index 49e514c3ad5..483dc547457 100644 --- a/components/devtools/actors/reflow.rs +++ b/components/devtools/actors/reflow.rs @@ -4,12 +4,11 @@ //! This actor is used for protocol purposes, it forwards the reflow events to clients. -use std::net::TcpStream; - use serde_json::{Map, Value}; -use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; +use crate::{EmptyReplyMsg, StreamId}; pub struct ReflowActor { name: String, @@ -25,20 +24,24 @@ impl Actor for ReflowActor { /// - `start`: Does nothing yet. This doesn't need a reply like other messages. fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - _stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "start" => { // TODO: Create an observer on "reflows" events - ActorMessageStatus::Processed + let msg = EmptyReplyMsg { from: self.name() }; + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } + + fn cleanup(&self, _id: StreamId) {} } impl ReflowActor { diff --git a/components/devtools/actors/root.rs b/components/devtools/actors/root.rs index 01ec4caedb1..648b4e11eeb 100644 --- a/components/devtools/actors/root.rs +++ b/components/devtools/actors/root.rs @@ -10,19 +10,18 @@ //! [Firefox JS implementation]: https://searchfox.org/mozilla-central/source/devtools/server/actors/root.js use std::cell::RefCell; -use std::net::TcpStream; use serde::Serialize; use serde_json::{Map, Value, json}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, 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}; +use crate::protocol::{ActorDescription, ClientRequest}; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -116,13 +115,6 @@ struct GetProcessResponse { process_descriptor: ProcessActorMsg, } -#[derive(Serialize)] -struct ErrorResponse { - from: String, - error: String, - message: String, -} - pub struct RootActor { pub tabs: Vec, pub workers: Vec, @@ -140,27 +132,25 @@ impl Actor for RootActor { fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "connect" => { let message = json!({ "from": "root", }); - let _ = stream.write_json_packet(&message); - ActorMessageStatus::Processed + request.reply_final(&message)? }, "listAddons" => { let actor = ListAddonsReply { from: "root".to_owned(), addons: vec![], }; - let _ = stream.write_json_packet(&actor); - ActorMessageStatus::Processed + request.reply_final(&actor)? }, "listProcesses" => { @@ -169,8 +159,7 @@ impl Actor for RootActor { from: self.name(), processes: vec![process], }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply)? }, // TODO: Unexpected message getTarget for process (when inspecting) @@ -180,8 +169,7 @@ impl Actor for RootActor { from: self.name(), process_descriptor: process, }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply)? }, "getRoot" => { @@ -192,8 +180,7 @@ impl Actor for RootActor { device_actor: self.device.clone(), preference_actor: self.preference.clone(), }; - let _ = stream.write_json_packet(&actor); - ActorMessageStatus::Processed + request.reply_final(&actor)? }, "listTabs" => { @@ -213,8 +200,7 @@ impl Actor for RootActor { }) .collect(), }; - let _ = stream.write_json_packet(&actor); - ActorMessageStatus::Processed + request.reply_final(&actor)? }, "listServiceWorkerRegistrations" => { @@ -222,8 +208,7 @@ impl Actor for RootActor { from: self.name(), registrations: vec![], }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply)? }, "listWorkers" => { @@ -235,26 +220,24 @@ impl Actor for RootActor { .map(|name| registry.find::(name).encodable()) .collect(), }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply)? }, "getTab" => { - let Some(serde_json::Value::Number(browser_id)) = msg.get("browserId") else { - return Ok(ActorMessageStatus::Ignored); - }; - - let browser_id = browser_id.as_u64().unwrap(); + let browser_id = msg + .get("browserId") + .ok_or(ActorError::MissingParameter)? + .as_u64() + .ok_or(ActorError::BadParameterType)?; let Some(tab) = self.get_tab_msg_by_browser_id(registry, browser_id as u32) else { - return Ok(ActorMessageStatus::Ignored); + return Err(ActorError::Internal); }; let reply = GetTabReply { from: self.name(), tab, }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply)? }, "protocolDescription" => { @@ -265,24 +248,12 @@ impl Actor for RootActor { device: DeviceActor::description(), }, }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => { - let reply = ErrorResponse { - from: self.name(), - error: "unrecognizedPacketType".to_owned(), - message: format!( - "Actor {} does not recognize the packet type '{}'", - self.name(), - msg_type, - ), - }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Ignored - }, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/source.rs b/components/devtools/actors/source.rs index 9f8dfcde3cd..0d4bfc521d6 100644 --- a/components/devtools/actors/source.rs +++ b/components/devtools/actors/source.rs @@ -4,7 +4,6 @@ use std::cell::RefCell; use std::collections::BTreeSet; -use std::net::TcpStream; use base::id::PipelineId; use serde::Serialize; @@ -12,8 +11,8 @@ use serde_json::{Map, Value}; use servo_url::ServoUrl; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; /// A `sourceForm` as used in responses to thread `sources` requests. /// @@ -134,13 +133,13 @@ impl Actor for SourceActor { fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { // Client has requested contents of the source. "source" => { let reply = SourceContentReply { @@ -154,10 +153,10 @@ impl Actor for SourceActor { .unwrap_or("") .to_owned(), }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed + request.reply_final(&reply)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/stylesheets.rs b/components/devtools/actors/stylesheets.rs index e6fd955295c..f7dbc733e1d 100644 --- a/components/devtools/actors/stylesheets.rs +++ b/components/devtools/actors/stylesheets.rs @@ -2,14 +2,12 @@ * 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/. */ -use std::net::TcpStream; - use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -28,24 +26,24 @@ impl Actor for StyleSheetsActor { } fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "getStyleSheets" => { let msg = GetStyleSheetsReply { from: self.name(), style_sheets: vec![], }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/tab.rs b/components/devtools/actors/tab.rs index 4a1c60b06c3..c77417cd927 100644 --- a/components/devtools/actors/tab.rs +++ b/components/devtools/actors/tab.rs @@ -9,17 +9,15 @@ //! //! [Firefox JS implementation]: https://searchfox.org/mozilla-central/source/devtools/server/actors/descriptors/tab.js -use std::net::TcpStream; - use serde::Serialize; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, 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::protocol::ClientRequest; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -89,42 +87,40 @@ impl Actor for TabDescriptorActor { /// to describe the debugging capabilities of this tab. fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "getTarget" => { let frame = registry .find::(&self.browsing_context_actor) .encodable(); - let _ = stream.write_json_packet(&GetTargetReply { + request.reply_final(&GetTargetReply { from: self.name(), frame, - }); - ActorMessageStatus::Processed + })? }, "getFavicon" => { // TODO: Return a favicon when available - let _ = stream.write_json_packet(&GetFaviconReply { + request.reply_final(&GetFaviconReply { from: self.name(), favicon: String::new(), - }); - ActorMessageStatus::Processed + })? }, "getWatcher" => { let ctx_actor = registry.find::(&self.browsing_context_actor); let watcher = registry.find::(&ctx_actor.watcher); - let _ = stream.write_json_packet(&GetWatcherReply { + request.reply_final(&GetWatcherReply { from: self.name(), watcher: watcher.encodable(), - }); - ActorMessageStatus::Processed + })? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/thread.rs b/components/devtools/actors/thread.rs index e0a1e2d5e3b..102590d0ab6 100644 --- a/components/devtools/actors/thread.rs +++ b/components/devtools/actors/thread.rs @@ -2,14 +2,12 @@ * 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/. */ -use std::net::TcpStream; - use serde::Serialize; use serde_json::{Map, Value}; use super::source::{SourceManager, SourcesReply}; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::{ClientRequest, JsonPacketStream}; use crate::{EmptyReplyMsg, StreamId}; #[derive(Serialize)] @@ -71,13 +69,13 @@ impl Actor for ThreadActor { fn handle_message( &self, + mut request: ClientRequest, registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "attach" => { let msg = ThreadAttached { from: self.name(), @@ -92,9 +90,8 @@ impl Actor for ThreadActor { type_: "attached".to_owned(), }, }; - let _ = stream.write_json_packet(&msg); - let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() }); - ActorMessageStatus::Processed + request.write_json_packet(&msg)?; + request.reply_final(&EmptyReplyMsg { from: self.name() })? }, "resume" => { @@ -102,9 +99,8 @@ impl Actor for ThreadActor { from: self.name(), type_: "resumed".to_owned(), }; - let _ = stream.write_json_packet(&msg); - let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() }); - ActorMessageStatus::Processed + request.write_json_packet(&msg)?; + request.reply_final(&EmptyReplyMsg { from: self.name() })? }, "interrupt" => { @@ -112,14 +108,11 @@ impl Actor for ThreadActor { from: self.name(), type_: "interrupted".to_owned(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.write_json_packet(&msg)?; + request.reply_final(&EmptyReplyMsg { from: self.name() })? }, - "reconfigure" => { - let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() }); - ActorMessageStatus::Processed - }, + "reconfigure" => request.reply_final(&EmptyReplyMsg { from: self.name() })?, // Client has attached to the thread and wants to load script sources. // @@ -128,10 +121,10 @@ impl Actor for ThreadActor { from: self.name(), sources: self.source_manager.source_forms(registry), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/timeline.rs b/components/devtools/actors/timeline.rs index 0a91d7ebdd4..2f60cb647a5 100644 --- a/components/devtools/actors/timeline.rs +++ b/components/devtools/actors/timeline.rs @@ -6,7 +6,6 @@ #![allow(dead_code)] use std::cell::RefCell; -use std::error::Error; use std::net::TcpStream; use std::sync::{Arc, Mutex}; use std::thread; @@ -21,10 +20,10 @@ use serde::{Serialize, Serializer}; use serde_json::{Map, Value}; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::framerate::FramerateActor; use crate::actors::memory::{MemoryActor, TimelineMemoryReply}; -use crate::protocol::JsonPacketStream; +use crate::protocol::{ClientRequest, JsonPacketStream}; pub struct TimelineActor { name: String, @@ -191,13 +190,13 @@ impl Actor for TimelineActor { fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "start" => { **self.is_recording.lock().as_mut().unwrap() = true; @@ -211,7 +210,7 @@ impl Actor for TimelineActor { .unwrap(); //TODO: support multiple connections by using root actor's streams instead. - *self.stream.borrow_mut() = stream.try_clone().ok(); + *self.stream.borrow_mut() = request.try_clone_stream().ok(); // init memory actor if let Some(with_memory) = msg.get("withMemory") { @@ -236,7 +235,7 @@ impl Actor for TimelineActor { self.name(), registry.shareable(), registry.start_stamp(), - stream.try_clone().unwrap(), + request.try_clone_stream().unwrap(), self.memory_actor.borrow().clone(), self.framerate_actor.borrow().clone(), ); @@ -250,8 +249,7 @@ impl Actor for TimelineActor { CrossProcessInstant::now(), ), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "stop" => { @@ -263,7 +261,6 @@ impl Actor for TimelineActor { ), }; - let _ = stream.write_json_packet(&msg); self.script_sender .send(DropTimelineMarkers( self.pipeline_id, @@ -282,7 +279,7 @@ impl Actor for TimelineActor { **self.is_recording.lock().as_mut().unwrap() = false; self.stream.borrow_mut().take(); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "isRecording" => { @@ -291,12 +288,12 @@ impl Actor for TimelineActor { value: *self.is_recording.lock().unwrap(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } @@ -330,7 +327,7 @@ impl Emitter { } } - fn send(&mut self, markers: Vec) -> Result<(), Box> { + fn send(&mut self, markers: Vec) -> Result<(), ActorError> { let end_time = CrossProcessInstant::now(); let reply = MarkersEmitterReply { type_: "markers".to_owned(), diff --git a/components/devtools/actors/watcher.rs b/components/devtools/actors/watcher.rs index e17e184d251..4b904acb368 100644 --- a/components/devtools/actors/watcher.rs +++ b/components/devtools/actors/watcher.rs @@ -24,7 +24,7 @@ use self::network_parent::{NetworkParentActor, NetworkParentActorMsg}; use super::breakpoint::BreakpointListActor; use super::thread::ThreadActor; use super::worker::WorkerMsg; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg}; use crate::actors::root::RootActor; use crate::actors::watcher::target_configuration::{ @@ -33,7 +33,7 @@ use crate::actors::watcher::target_configuration::{ use crate::actors::watcher::thread_configuration::{ ThreadConfigurationActor, ThreadConfigurationActorMsg, }; -use crate::protocol::JsonPacketStream; +use crate::protocol::{ClientRequest, JsonPacketStream}; use crate::resource::{ResourceArrayType, ResourceAvailable}; use crate::{EmptyReplyMsg, IdMap, StreamId, WorkerActor}; @@ -230,15 +230,15 @@ impl Actor for WatcherActor { /// - `getThreadConfigurationActor`: The same but with the configuration actor for the thread fn handle_message( &self, + mut request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { + ) -> Result<(), ActorError> { let target = registry.find::(&self.browsing_context_actor); let root = registry.find::("root"); - Ok(match msg_type { + match msg_type { "watchTargets" => { // As per logs we either get targetType as "frame" or "worker" let target_type = msg @@ -252,9 +252,9 @@ impl Actor for WatcherActor { type_: "target-available-form".into(), target: TargetActorMsg::BrowsingContext(target.encodable()), }; - let _ = stream.write_json_packet(&msg); + let _ = request.write_json_packet(&msg); - target.frame_update(stream); + target.frame_update(&mut request); } else if target_type == "worker" { for worker_name in &root.workers { let worker = registry.find::(worker_name); @@ -263,11 +263,10 @@ impl Actor for WatcherActor { type_: "target-available-form".into(), target: TargetActorMsg::Worker(worker.encodable()), }; - let _ = stream.write_json_packet(&worker_msg); + let _ = request.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 @@ -275,15 +274,14 @@ impl Actor for WatcherActor { // extra empty packet to the devtools host to inform that we successfully received // and processed the message so that it can continue let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "watchResources" => { let Some(resource_types) = msg.get("resourceTypes") else { - return Ok(ActorMessageStatus::Ignored); + return Err(ActorError::MissingParameter); }; let Some(resource_types) = resource_types.as_array() else { - return Ok(ActorMessageStatus::Ignored); + return Err(ActorError::BadParameterType); }; for resource in resource_types { @@ -311,7 +309,7 @@ impl Actor for WatcherActor { event, "document-event".into(), ResourceArrayType::Available, - stream, + &mut request, ); } }, @@ -321,7 +319,7 @@ impl Actor for WatcherActor { thread_actor.source_manager.source_forms(registry), "source".into(), ResourceArrayType::Available, - stream, + &mut request, ); for worker_name in &root.workers { @@ -332,7 +330,7 @@ impl Actor for WatcherActor { thread.source_manager.source_forms(registry), "source".into(), ResourceArrayType::Available, - stream, + &mut request, ); } }, @@ -340,19 +338,16 @@ impl Actor for WatcherActor { "network-event" => {}, _ => warn!("resource {} not handled yet", resource), } - - let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); } - ActorMessageStatus::Processed + let msg = EmptyReplyMsg { from: self.name() }; + request.reply_final(&msg)? }, "getParentBrowsingContextID" => { let msg = GetParentBrowsingContextIDReply { from: self.name(), browsing_context_id: target.browsing_context_id.value(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getNetworkParentActor" => { let network_parent = registry.find::(&self.network_parent); @@ -360,8 +355,7 @@ impl Actor for WatcherActor { from: self.name(), network: network_parent.encodable(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getTargetConfigurationActor" => { let target_configuration = @@ -370,8 +364,7 @@ impl Actor for WatcherActor { from: self.name(), configuration: target_configuration.encodable(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getThreadConfigurationActor" => { let thread_configuration = @@ -380,24 +373,23 @@ impl Actor for WatcherActor { from: self.name(), configuration: thread_configuration.encodable(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, "getBreakpointListActor" => { let breakpoint_list_name = registry.new_name("breakpoint-list"); let breakpoint_list = BreakpointListActor::new(breakpoint_list_name.clone()); registry.register_later(Box::new(breakpoint_list)); - let _ = stream.write_json_packet(&GetBreakpointListActorReply { + request.reply_final(&GetBreakpointListActorReply { from: self.name(), breakpoint_list: GetBreakpointListActorReplyInner { actor: breakpoint_list_name, }, - }); - ActorMessageStatus::Processed + })? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/watcher/network_parent.rs b/components/devtools/actors/watcher/network_parent.rs index 31217455200..30e7625ff2d 100644 --- a/components/devtools/actors/watcher/network_parent.rs +++ b/components/devtools/actors/watcher/network_parent.rs @@ -2,13 +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/. */ -use std::net::TcpStream; - use serde::Serialize; use serde_json::{Map, Value}; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; use crate::{EmptyReplyMsg, StreamId}; #[derive(Serialize)] @@ -30,20 +28,20 @@ impl Actor for NetworkParentActor { /// - `setSaveRequestAndResponseBodies`: Doesn't do anything yet fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "setSaveRequestAndResponseBodies" => { let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/watcher/target_configuration.rs b/components/devtools/actors/watcher/target_configuration.rs index 0d366e81475..3eedc8e8264 100644 --- a/components/devtools/actors/watcher/target_configuration.rs +++ b/components/devtools/actors/watcher/target_configuration.rs @@ -6,17 +6,16 @@ //! This actor manages the configuration flags that the devtools host can apply to the targets. use std::collections::HashMap; -use std::net::TcpStream; use embedder_traits::Theme; use log::warn; use serde::Serialize; use serde_json::{Map, Value}; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actor::{Actor, ActorError, ActorRegistry}; use crate::actors::browsing_context::BrowsingContextActor; use crate::actors::tab::TabDescriptorActor; -use crate::protocol::JsonPacketStream; +use crate::protocol::ClientRequest; use crate::{EmptyReplyMsg, RootActor, StreamId}; #[derive(Serialize)] @@ -48,22 +47,19 @@ impl Actor for TargetConfigurationActor { /// - `updateConfiguration`: Receives new configuration flags from the devtools host. fn handle_message( &self, + request: ClientRequest, registry: &ActorRegistry, msg_type: &str, msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "updateConfiguration" => { - let config = match msg.get("configuration").and_then(|v| v.as_object()) { - Some(config) => config, - None => { - let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - return Ok(ActorMessageStatus::Processed); - }, - }; + let config = msg + .get("configuration") + .ok_or(ActorError::MissingParameter)? + .as_object() + .ok_or(ActorError::BadParameterType)?; if let Some(scheme) = config.get("colorSchemeSimulation").and_then(|v| v.as_str()) { let theme = match scheme { "dark" => Theme::Dark, @@ -76,17 +72,19 @@ impl Actor for TargetConfigurationActor { let browsing_context_name = tab_actor.browsing_context(); let browsing_context_actor = registry.find::(&browsing_context_name); - browsing_context_actor.simulate_color_scheme(theme)?; + browsing_context_actor + .simulate_color_scheme(theme) + .map_err(|_| ActorError::Internal)?; } else { warn!("No active tab for updateConfiguration"); } } let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/watcher/thread_configuration.rs b/components/devtools/actors/watcher/thread_configuration.rs index 7659db42e3c..5dd5ee1fef1 100644 --- a/components/devtools/actors/watcher/thread_configuration.rs +++ b/components/devtools/actors/watcher/thread_configuration.rs @@ -6,13 +6,12 @@ //! This actor manages the configuration flags that the devtools host can apply to 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::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::ClientRequest; use crate::{EmptyReplyMsg, StreamId}; #[derive(Serialize)] @@ -35,21 +34,21 @@ impl Actor for ThreadConfigurationActor { /// - `updateConfiguration`: Receives new configuration flags from the devtools host. fn handle_message( &self, + request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, _id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "updateConfiguration" => { // TODO: Actually update configuration let msg = EmptyReplyMsg { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + request.reply_final(&msg)? }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } } diff --git a/components/devtools/actors/worker.rs b/components/devtools/actors/worker.rs index f3ca4f2aed7..a680839739c 100644 --- a/components/devtools/actors/worker.rs +++ b/components/devtools/actors/worker.rs @@ -15,8 +15,8 @@ use serde_json::{Map, Value}; use servo_url::ServoUrl; use crate::StreamId; -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::protocol::JsonPacketStream; +use crate::actor::{Actor, ActorError, ActorRegistry}; +use crate::protocol::{ClientRequest, JsonPacketStream}; use crate::resource::ResourceAvailable; #[derive(Clone, Copy)] @@ -68,30 +68,28 @@ impl Actor for WorkerActor { } fn handle_message( &self, + mut request: ClientRequest, _registry: &ActorRegistry, msg_type: &str, _msg: &Map, - stream: &mut TcpStream, stream_id: StreamId, - ) -> Result { - Ok(match msg_type { + ) -> Result<(), ActorError> { + match msg_type { "attach" => { let msg = AttachedReply { from: self.name(), type_: "attached".to_owned(), url: self.url.as_str().to_owned(), }; - if stream.write_json_packet(&msg).is_err() { - return Ok(ActorMessageStatus::Processed); - } + // FIXME: we don’t send an actual reply (message without type), which seems to be a bug? + request.write_json_packet(&msg)?; self.streams .borrow_mut() - .insert(stream_id, stream.try_clone().unwrap()); + .insert(stream_id, request.try_clone_stream().unwrap()); // FIXME: fix messages to not require forging a pipeline for worker messages self.script_chan .send(WantsLiveNotifications(TEST_PIPELINE_ID, true)) .unwrap(); - ActorMessageStatus::Processed }, "connect" => { @@ -101,8 +99,8 @@ impl Actor for WorkerActor { thread_actor: self.thread.clone(), console_actor: self.console.clone(), }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed + // FIXME: we don’t send an actual reply (message without type), which seems to be a bug? + request.write_json_packet(&msg)?; }, "detach" => { @@ -110,13 +108,14 @@ impl Actor for WorkerActor { from: self.name(), type_: "detached".to_string(), }; - let _ = stream.write_json_packet(&msg); self.cleanup(stream_id); - ActorMessageStatus::Processed + // FIXME: we don’t send an actual reply (message without type), which seems to be a bug? + request.write_json_packet(&msg)?; }, - _ => ActorMessageStatus::Ignored, - }) + _ => return Err(ActorError::UnrecognizedPacketType), + }; + Ok(()) } fn cleanup(&self, stream_id: StreamId) { diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index dd02c7bbe77..02ca7af703e 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -28,7 +28,7 @@ use devtools_traits::{ }; use embedder_traits::{AllowOrDeny, EmbedderMsg, EmbedderProxy}; use ipc_channel::ipc::{self, IpcSender}; -use log::trace; +use log::{trace, warn}; use resource::{ResourceArrayType, ResourceAvailable}; use serde::Serialize; use servo_rand::RngCore; @@ -646,8 +646,8 @@ fn allow_devtools_client(stream: &mut TcpStream, embedder: &EmbedderProxy, token fn handle_client(actors: Arc>, mut stream: TcpStream, stream_id: StreamId) { log::info!("Connection established to {}", stream.peer_addr().unwrap()); let msg = actors.lock().unwrap().find::("root").encodable(); - if let Err(e) = stream.write_json_packet(&msg) { - log::warn!("Error writing response: {:?}", e); + if let Err(error) = stream.write_json_packet(&msg) { + warn!("Failed to send initial packet from root actor: {error:?}"); return; } diff --git a/components/devtools/protocol.rs b/components/devtools/protocol.rs index ed12e1b4112..d3be1e75bc7 100644 --- a/components/devtools/protocol.rs +++ b/components/devtools/protocol.rs @@ -5,13 +5,14 @@ //! Low-level wire protocol implementation. Currently only supports //! [JSON packets](https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#json-packets). -use std::error::Error; use std::io::{Read, Write}; use std::net::TcpStream; use log::debug; use serde::Serialize; -use serde_json::{self, Value}; +use serde_json::{self, Value, json}; + +use crate::actor::ActorError; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -29,42 +30,18 @@ pub struct Method { } pub trait JsonPacketStream { - fn write_json_packet(&mut self, obj: &T) -> Result<(), Box>; - - #[allow(dead_code)] - fn write_merged_json_packet( - &mut self, - base: &T, - extra: &U, - ) -> Result<(), Box>; + fn write_json_packet(&mut self, message: &T) -> Result<(), ActorError>; fn read_json_packet(&mut self) -> Result, String>; } impl JsonPacketStream for TcpStream { - fn write_json_packet(&mut self, obj: &T) -> Result<(), Box> { - let s = serde_json::to_string(obj)?; + fn write_json_packet(&mut self, message: &T) -> Result<(), ActorError> { + let s = serde_json::to_string(message).map_err(|_| ActorError::Internal)?; debug!("<- {}", s); - write!(self, "{}:{}", s.len(), s)?; + write!(self, "{}:{}", s.len(), s).map_err(|_| ActorError::Internal)?; Ok(()) } - fn write_merged_json_packet( - &mut self, - base: &T, - extra: &U, - ) -> Result<(), Box> { - let mut obj = serde_json::to_value(base)?; - let obj = obj.as_object_mut().unwrap(); - let extra = serde_json::to_value(extra)?; - let extra = extra.as_object().unwrap(); - - for (key, value) in extra { - obj.insert(key.to_owned(), value.to_owned()); - } - - self.write_json_packet(obj) - } - fn read_json_packet(&mut self) -> Result, String> { // 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] @@ -102,3 +79,106 @@ impl JsonPacketStream for TcpStream { } } } + +/// Wrapper around a client stream that guarantees request/reply invariants. +/// +/// Client messages, which are always requests, are dispatched to Actor instances one at a time via +/// [`crate::Actor::handle_message`]. Each request must be paired with exactly one reply from the +/// same actor the request was sent to, where a reply is a message with no type (if a message from +/// the server has a type, it’s a notification, not a reply). +/// +/// Failing to reply to a request will almost always permanently break that actor, because either +/// the client gets stuck waiting for a reply, or the client receives the reply for a subsequent +/// request as if it was the reply for the current request. If an actor fails to reply to a request, +/// we want the dispatcher ([`crate::ActorRegistry::handle_message`]) to send an error of type +/// `unrecognizedPacketType`, to keep the conversation for that actor in sync. +/// +/// Since replies come in all shapes and sizes, we want to allow Actor types to send replies without +/// having to return them to the dispatcher. This wrapper type allows the dispatcher to check if a +/// valid reply was sent, and guarantees that if the actor tries to send a reply, it’s actually a +/// valid reply (see [`Self::is_valid_reply`]). +/// +/// It does not currently guarantee anything about messages sent via the [`TcpStream`] released via +/// [`Self::try_clone_stream`] or the return value of [`Self::reply`]. +pub struct ClientRequest<'req, 'sent> { + /// Client stream. + stream: &'req mut TcpStream, + /// Expected actor name. + actor_name: &'req str, + /// Sent flag, allowing ActorRegistry to check for unhandled requests. + sent: &'sent mut bool, +} + +impl ClientRequest<'_, '_> { + /// Run the given handler, with a new request that wraps the given client stream and expected actor name. + /// + /// Returns [`ActorError::UnrecognizedPacketType`] if the actor did not send a reply. + pub fn handle<'req>( + client: &'req mut TcpStream, + actor_name: &'req str, + handler: impl FnOnce(ClientRequest<'req, '_>) -> Result<(), ActorError>, + ) -> Result<(), ActorError> { + let mut sent = false; + let request = ClientRequest { + stream: client, + actor_name, + sent: &mut sent, + }; + handler(request)?; + + if sent { + Ok(()) + } else { + Err(ActorError::UnrecognizedPacketType) + } + } +} + +impl<'req> ClientRequest<'req, '_> { + /// Send the given reply to the request being handled. + /// + /// If successful, sets the sent flag and returns the underlying stream, + /// allowing other messages to be sent after replying to a request. + pub fn reply(self, reply: &T) -> Result<&'req mut TcpStream, ActorError> { + debug_assert!(self.is_valid_reply(reply), "Message is not a valid reply"); + self.stream.write_json_packet(reply)?; + *self.sent = true; + Ok(self.stream) + } + + /// Like `reply`, but for cases where the actor no longer needs the stream. + pub fn reply_final(self, reply: &T) -> Result<(), ActorError> { + debug_assert!(self.is_valid_reply(reply), "Message is not a valid reply"); + let _stream = self.reply(reply)?; + Ok(()) + } + + pub fn try_clone_stream(&self) -> std::io::Result { + self.stream.try_clone() + } + + /// Return true iff the given message is a reply (has no `type` or `to`), and is from the expected actor. + /// + /// This incurs a runtime conversion to a BTreeMap, so it should only be used in debug assertions. + fn is_valid_reply(&self, message: &T) -> bool { + let reply = json!(message); + reply.get("from").and_then(|from| from.as_str()) == Some(self.actor_name) && + reply.get("to").is_none() && + reply.get("type").is_none() + } +} + +/// Actors can also send other messages before replying to a request. +impl JsonPacketStream for ClientRequest<'_, '_> { + fn write_json_packet(&mut self, message: &T) -> Result<(), ActorError> { + debug_assert!( + !self.is_valid_reply(message), + "Replies must use reply() or reply_final()" + ); + self.stream.write_json_packet(message) + } + + fn read_json_packet(&mut self) -> Result, String> { + self.stream.read_json_packet() + } +} diff --git a/components/devtools/resource.rs b/components/devtools/resource.rs index a4ae5f5d39e..2f5b0af3dbd 100644 --- a/components/devtools/resource.rs +++ b/components/devtools/resource.rs @@ -2,8 +2,6 @@ * 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/. */ -use std::net::TcpStream; - use serde::Serialize; use crate::protocol::JsonPacketStream; @@ -24,22 +22,22 @@ pub(crate) struct ResourceAvailableReply { pub(crate) trait ResourceAvailable { fn actor_name(&self) -> String; - fn resource_array( + fn resource_array( &self, resource: T, resource_type: String, array_type: ResourceArrayType, - stream: &mut TcpStream, + stream: &mut S, ) { self.resources_array(vec![resource], resource_type, array_type, stream); } - fn resources_array( + fn resources_array( &self, resources: Vec, resource_type: String, array_type: ResourceArrayType, - stream: &mut TcpStream, + stream: &mut S, ) { let msg = ResourceAvailableReply:: { from: self.actor_name(),