diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index 1d5dbfaf4c6..4a667d48556 100644 --- a/components/devtools/actors/browsing_context.rs +++ b/components/devtools/actors/browsing_context.rs @@ -9,6 +9,7 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::net::TcpStream; +use std::time::{SystemTime, UNIX_EPOCH}; use base::id::{BrowsingContextId, PipelineId}; use devtools_traits::DevtoolScriptControlMsg::{self, WantsLiveNotifications}; @@ -18,18 +19,16 @@ use serde::Serialize; use serde_json::{Map, Value}; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::actors::configuration::{TargetConfigurationActor, ThreadConfigurationActor}; -use crate::actors::emulation::EmulationActor; +use crate::actors::inspector::accessibility::AccessibilityActor; +use crate::actors::inspector::css_properties::CssPropertiesActor; use crate::actors::inspector::InspectorActor; -use crate::actors::performance::PerformanceActor; -use crate::actors::profiler::ProfilerActor; +use crate::actors::reflow::ReflowActor; use crate::actors::stylesheets::StyleSheetsActor; use crate::actors::tab::TabDescriptorActor; use crate::actors::thread::ThreadActor; -use crate::actors::timeline::TimelineActor; use crate::actors::watcher::{SessionContext, SessionContextType, WatcherActor}; use crate::protocol::JsonPacketStream; -use crate::StreamId; +use crate::{EmptyReplyMsg, StreamId}; #[derive(Serialize)] struct FrameUpdateReply { @@ -116,7 +115,12 @@ struct TabNavigated { #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct BrowsingContextTraits { + frames: bool, is_browsing_context: bool, + log_in_page: bool, + navigation: bool, + supports_top_level_target_flag: bool, + watchpoints: bool, } #[derive(Serialize)] @@ -130,28 +134,29 @@ pub struct BrowsingContextActorMsg { #[serde(rename = "browsingContextID")] browsing_context_id: u32, is_top_level_target: bool, - console_actor: String, - thread_actor: String, traits: BrowsingContextTraits, + // Implemented actors + accessibility_actor: String, + console_actor: String, + css_properties_actor: String, + inspector_actor: String, + reflow_actor: String, + style_sheets_actor: String, + thread_actor: String, // Part of the official protocol, but not yet implemented. - // emulation_actor: String, - // inspector_actor: String, - // timeline_actor: String, - // profiler_actor: String, - // performance_actor: String, - // style_sheets_actor: String, - // storage_actor: String, - // memory_actor: String, - // framerate_actor: String, - // reflow_actor: String, - // css_properties_actor: String, // animations_actor: String, - // web_extension_inspected_window_actor: String, - // accessibility_actor: String, - // screenshot_actor: String, // changes_actor: String, - // web_socket_actor: String, + // framerate_actor: String, // manifest_actor: String, + // memory_actor: String, + // network_content_actor: String, + // objects_manager: String, + // performance_actor: String, + // resonsive_actor: String, + // storage_actor: String, + // tracer_actor: String, + // web_extension_inspected_window_actor: String, + // web_socket_actor: String, } /// The browsing context actor encompasses all of the other supporting actors when debugging a web @@ -163,16 +168,13 @@ pub(crate) struct BrowsingContextActor { pub url: RefCell, pub active_pipeline: Cell, pub browsing_context_id: BrowsingContextId, + pub accessibility: String, pub console: String, - pub _emulation: String, - pub _inspector: String, - pub _performance: String, - pub _profiler: String, - pub _style_sheets: String, - pub target_configuration: String, - pub thread_configuration: String, + pub css_properties: String, + pub inspector: String, + pub reflow: String, + pub style_sheets: String, pub thread: String, - pub _timeline: String, pub _tab: String, pub script_chan: IpcSender, pub streams: RefCell>, @@ -187,12 +189,20 @@ impl Actor for BrowsingContextActor { fn handle_message( &self, _registry: &ActorRegistry, - _msg_type: &str, + msg_type: &str, _msg: &Map, - _stream: &mut TcpStream, + stream: &mut TcpStream, _id: StreamId, ) -> Result { - Ok(ActorMessageStatus::Ignored) + Ok(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 + }, + _ => ActorMessageStatus::Ignored, + }) } fn cleanup(&self, id: StreamId) { @@ -217,7 +227,9 @@ impl BrowsingContextActor { let name = actors.new_name("target"); let DevtoolsPageInfo { title, url } = page_info; - let emulation = EmulationActor::new(actors.new_name("emulation")); + let accessibility = AccessibilityActor::new(actors.new_name("accessibility")); + + let css_properties = CssPropertiesActor::new(actors.new_name("css-properties")); let inspector = InspectorActor { name: actors.new_name("inspector"), @@ -228,29 +240,16 @@ impl BrowsingContextActor { browsing_context: name.clone(), }; - let performance = PerformanceActor::new(actors.new_name("performance")); + let reflow = ReflowActor::new(actors.new_name("reflow")); - let profiler = ProfilerActor::new(actors.new_name("profiler")); - - // the strange switch between styleSheets and stylesheets is due - // to an inconsistency in devtools. See Bug #1498893 in bugzilla let style_sheets = StyleSheetsActor::new(actors.new_name("stylesheets")); let tabdesc = TabDescriptorActor::new(actors, name.clone()); - let target_configuration = - TargetConfigurationActor::new(actors.new_name("target-configuration")); - - let thread_configuration = - ThreadConfigurationActor::new(actors.new_name("thread-configuration")); - - let thread = ThreadActor::new(actors.new_name("context")); - - let timeline = - TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender.clone()); + let thread = ThreadActor::new(actors.new_name("thread")); let watcher = WatcherActor::new( - actors.new_name("watcher"), + actors, name.clone(), SessionContext::new(SessionContextType::BrowserElement), ); @@ -262,31 +261,25 @@ impl BrowsingContextActor { url: RefCell::new(url.into_string()), active_pipeline: Cell::new(pipeline), browsing_context_id: id, + accessibility: accessibility.name(), console, - _emulation: emulation.name(), - _inspector: inspector.name(), - _performance: performance.name(), - _profiler: profiler.name(), + css_properties: css_properties.name(), + inspector: inspector.name(), + reflow: reflow.name(), streams: RefCell::new(HashMap::new()), - _style_sheets: style_sheets.name(), + style_sheets: style_sheets.name(), _tab: tabdesc.name(), - target_configuration: target_configuration.name(), - thread_configuration: thread_configuration.name(), thread: thread.name(), - _timeline: timeline.name(), watcher: watcher.name(), }; - actors.register(Box::new(emulation)); + actors.register(Box::new(accessibility)); + actors.register(Box::new(css_properties)); actors.register(Box::new(inspector)); - actors.register(Box::new(performance)); - actors.register(Box::new(profiler)); + actors.register(Box::new(reflow)); actors.register(Box::new(style_sheets)); actors.register(Box::new(tabdesc)); - actors.register(Box::new(target_configuration)); - actors.register(Box::new(thread_configuration)); actors.register(Box::new(thread)); - actors.register(Box::new(timeline)); actors.register(Box::new(watcher)); target @@ -297,6 +290,11 @@ impl BrowsingContextActor { actor: self.name(), traits: BrowsingContextTraits { is_browsing_context: true, + frames: true, + log_in_page: false, + navigation: true, + supports_top_level_target_flag: true, + watchpoints: true, }, title: self.title.borrow().clone(), url: self.url.borrow().clone(), @@ -305,14 +303,13 @@ impl BrowsingContextActor { //FIXME: shouldn't ignore pipeline namespace field outer_window_id: self.active_pipeline.get().index.0.get(), is_top_level_target: true, + accessibility_actor: self.accessibility.clone(), console_actor: self.console.clone(), + css_properties_actor: self.css_properties.clone(), + inspector_actor: self.inspector.clone(), + reflow_actor: self.reflow.clone(), + style_sheets_actor: self.style_sheets.clone(), thread_actor: self.thread.clone(), - // emulation_actor: self.emulation.clone(), - // inspector_actor: self.inspector.clone(), - // performance_actor: self.performance.clone(), - // profiler_actor: self.profiler.clone(), - // style_sheets_actor: self.style_sheets.clone(), - // timeline_actor: self.timeline.clone(), } } @@ -369,10 +366,7 @@ impl BrowsingContextActor { pub(crate) fn document_event(&self, stream: &mut TcpStream) { // TODO: This is a hacky way of sending the 3 messages // Figure out if there needs work to be done here, ensure the page is loaded - for (i, &name) in ["dom-loading", "dom-interactive", "dom-complete"] - .iter() - .enumerate() - { + for &name in ["dom-loading", "dom-interactive", "dom-complete"].iter() { let _ = stream.write_json_packet(&ResourceAvailableReply { from: self.name(), type_: "resource-available-form".into(), @@ -381,7 +375,10 @@ impl BrowsingContextActor { name: name.into(), new_uri: None, resource_type: "document-event".into(), - time: i as u64, + time: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64, title: Some(self.title.borrow().clone()), url: Some(self.url.borrow().clone()), }], diff --git a/components/devtools/actors/emulation.rs b/components/devtools/actors/emulation.rs deleted file mode 100644 index 6e91bea0037..00000000000 --- a/components/devtools/actors/emulation.rs +++ /dev/null @@ -1,37 +0,0 @@ -/* 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/. */ - -use std::net::TcpStream; - -use serde_json::{Map, Value}; - -use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; -use crate::StreamId; - -pub struct EmulationActor { - pub name: String, -} - -impl Actor for EmulationActor { - fn name(&self) -> String { - self.name.clone() - } - - fn handle_message( - &self, - _registry: &ActorRegistry, - _msg_type: &str, - _msg: &Map, - _stream: &mut TcpStream, - _id: StreamId, - ) -> Result { - Ok(ActorMessageStatus::Ignored) - } -} - -impl EmulationActor { - pub fn new(name: String) -> EmulationActor { - EmulationActor { name } - } -} diff --git a/components/devtools/actors/inspector.rs b/components/devtools/actors/inspector.rs index 042fc2bcfd7..0e1ecd86177 100644 --- a/components/devtools/actors/inspector.rs +++ b/components/devtools/actors/inspector.rs @@ -4,140 +4,38 @@ //! Liberally derived from the [Firefox JS implementation](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js). -#![allow(non_snake_case)] // NOTE: To be removed on the inspector specific pr - use std::cell::RefCell; +use std::collections::HashMap; use std::net::TcpStream; -use base::id::PipelineId; -use devtools_traits::DevtoolScriptControlMsg::{ - GetChildren, GetDocumentElement, GetLayout, GetRootNode, ModifyAttribute, -}; -use devtools_traits::{ComputedNodeLayout, DevtoolScriptControlMsg, NodeInfo}; +use devtools_traits::DevtoolScriptControlMsg; +use devtools_traits::DevtoolScriptControlMsg::GetRootNode; use ipc_channel::ipc::{self, IpcSender}; use serde::Serialize; use serde_json::{self, Map, Value}; use crate::actor::{Actor, ActorMessageStatus, 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::StreamId; -pub struct InspectorActor { - pub name: String, - pub walker: RefCell>, - pub page_style: RefCell>, - pub highlighter: RefCell>, - pub script_chan: IpcSender, - pub browsing_context: String, -} +pub mod accessibility; +pub mod css_properties; +pub mod highlighter; +pub mod layout; +pub mod node; +pub mod page_style; +pub mod walker; #[derive(Serialize)] -struct GetHighlighterReply { - highligter: HighlighterMsg, // sic. +#[serde(rename_all = "camelCase")] +struct GetPageStyleReply { from: String, -} - -#[derive(Serialize)] -struct HighlighterMsg { - actor: String, -} - -struct HighlighterActor { - name: String, -} - -pub struct NodeActor { - pub name: String, - script_chan: IpcSender, - pipeline: PipelineId, -} - -#[derive(Serialize)] -struct ShowBoxModelReply { - from: String, -} - -#[derive(Serialize)] -struct HideBoxModelReply { - from: String, -} - -impl Actor for HighlighterActor { - fn name(&self) -> String { - self.name.clone() - } - - fn handle_message( - &self, - _registry: &ActorRegistry, - msg_type: &str, - _msg: &Map, - stream: &mut TcpStream, - _id: StreamId, - ) -> Result { - Ok(match msg_type { - "showBoxModel" => { - let msg = ShowBoxModelReply { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed - }, - - "hideBoxModel" => { - let msg = HideBoxModelReply { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed - }, - - _ => ActorMessageStatus::Ignored, - }) - } -} - -#[derive(Serialize)] -struct ModifyAttributeReply { - from: String, -} - -impl Actor for NodeActor { - fn name(&self) -> String { - self.name.clone() - } - - fn handle_message( - &self, - registry: &ActorRegistry, - msg_type: &str, - msg: &Map, - stream: &mut TcpStream, - _id: StreamId, - ) -> Result { - Ok(match msg_type { - "modifyAttributes" => { - let target = msg.get("to").unwrap().as_str().unwrap(); - let mods = msg.get("modifications").unwrap().as_array().unwrap(); - let modifications = mods - .iter() - .map(|json_mod| { - serde_json::from_str(&serde_json::to_string(json_mod).unwrap()).unwrap() - }) - .collect(); - - self.script_chan - .send(ModifyAttribute( - self.pipeline, - registry.actor_to_script(target.to_owned()), - modifications, - )) - .unwrap(); - let reply = ModifyAttributeReply { from: self.name() }; - let _ = stream.write_json_packet(&reply); - ActorMessageStatus::Processed - }, - - _ => ActorMessageStatus::Ignored, - }) - } + page_style: PageStyleMsg, } #[derive(Serialize)] @@ -147,452 +45,24 @@ struct GetWalkerReply { } #[derive(Serialize)] -struct WalkerMsg { - actor: String, - root: NodeActorMsg, -} - -#[derive(Serialize)] -struct AttrMsg { - namespace: String, - name: String, - value: String, -} - -#[derive(Serialize)] -struct NodeActorMsg { - actor: String, - baseURI: String, - parent: String, - nodeType: u16, - namespaceURI: String, - nodeName: String, - numChildren: usize, - - name: String, - publicId: String, - systemId: String, - - attrs: Vec, - - pseudoClassLocks: Vec, - - isDisplayed: bool, - - hasEventListeners: bool, - - isDocumentElement: bool, - - shortValue: String, - incompleteValue: bool, -} - -trait NodeInfoToProtocol { - fn encode( - self, - actors: &ActorRegistry, - display: bool, - script_chan: IpcSender, - pipeline: PipelineId, - ) -> NodeActorMsg; -} - -impl NodeInfoToProtocol for NodeInfo { - fn encode( - self, - actors: &ActorRegistry, - display: bool, - script_chan: IpcSender, - pipeline: PipelineId, - ) -> NodeActorMsg { - let actor_name = if !actors.script_actor_registered(self.unique_id.clone()) { - let name = actors.new_name("node"); - let node_actor = NodeActor { - name: name.clone(), - script_chan, - pipeline, - }; - actors.register_script_actor(self.unique_id, name.clone()); - actors.register_later(Box::new(node_actor)); - name - } else { - actors.script_to_actor(self.unique_id) - }; - - NodeActorMsg { - actor: actor_name, - baseURI: self.base_uri, - parent: actors.script_to_actor(self.parent.clone()), - nodeType: self.node_type, - namespaceURI: self.namespace_uri, - nodeName: self.node_name, - numChildren: self.num_children, - - name: self.name, - publicId: self.public_id, - systemId: self.system_id, - - attrs: self - .attrs - .into_iter() - .map(|attr| AttrMsg { - namespace: attr.namespace, - name: attr.name, - value: attr.value, - }) - .collect(), - - pseudoClassLocks: vec![], //TODO get this data from script - - isDisplayed: display, - - hasEventListeners: false, //TODO get this data from script - - isDocumentElement: self.is_document_element, - - shortValue: self.short_value, - incompleteValue: self.incomplete_value, - } - } -} - -struct WalkerActor { - name: String, - script_chan: IpcSender, - pipeline: PipelineId, -} - -#[derive(Serialize)] -struct QuerySelectorReply { +struct SupportsHighlightersReply { from: String, + value: bool, } #[derive(Serialize)] -struct DocumentElementReply { +struct GetHighlighterReply { from: String, - node: NodeActorMsg, + highlighter: HighlighterMsg, } -#[derive(Serialize)] -struct ClearPseudoclassesReply { - from: String, -} - -#[derive(Serialize)] -struct ChildrenReply { - hasFirst: bool, - hasLast: bool, - nodes: Vec, - from: String, -} - -impl Actor for WalkerActor { - fn name(&self) -> String { - self.name.clone() - } - - fn handle_message( - &self, - registry: &ActorRegistry, - msg_type: &str, - msg: &Map, - stream: &mut TcpStream, - _id: StreamId, - ) -> Result { - Ok(match msg_type { - "querySelector" => { - let msg = QuerySelectorReply { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed - }, - - "documentElement" => { - let (tx, rx) = ipc::channel().unwrap(); - self.script_chan - .send(GetDocumentElement(self.pipeline, tx)) - .unwrap(); - let doc_elem_info = rx.recv().unwrap().ok_or(())?; - let node = - doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline); - - let msg = DocumentElementReply { - from: self.name(), - node, - }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed - }, - - "clearPseudoClassLocks" => { - let msg = ClearPseudoclassesReply { from: self.name() }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed - }, - - "children" => { - let target = msg.get("node").unwrap().as_str().unwrap(); - let (tx, rx) = ipc::channel().unwrap(); - self.script_chan - .send(GetChildren( - self.pipeline, - registry.actor_to_script(target.to_owned()), - tx, - )) - .unwrap(); - let children = rx.recv().unwrap().ok_or(())?; - - let msg = ChildrenReply { - hasFirst: true, - hasLast: true, - nodes: children - .into_iter() - .map(|child| { - child.encode(registry, true, self.script_chan.clone(), self.pipeline) - }) - .collect(), - from: self.name(), - }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed - }, - - _ => ActorMessageStatus::Ignored, - }) - } -} - -#[derive(Serialize)] -struct GetPageStyleReply { - from: String, - pageStyle: PageStyleMsg, -} - -#[derive(Serialize)] -struct PageStyleMsg { - actor: String, -} - -struct PageStyleActor { - name: String, - script_chan: IpcSender, - pipeline: PipelineId, -} - -#[derive(Serialize)] -struct GetAppliedReply { - entries: Vec, - rules: Vec, - sheets: Vec, - from: String, -} - -#[derive(Serialize)] -struct GetComputedReply { - computed: Vec, //XXX all css props - from: String, -} - -#[derive(Serialize)] -struct AppliedEntry { - rule: String, - pseudoElement: Value, - isSystem: bool, - matchedSelectors: Vec, -} - -#[derive(Serialize)] -struct AppliedRule { - actor: String, - #[serde(rename = "type")] - type_: String, - href: String, - cssText: String, - line: u32, - column: u32, - parentStyleSheet: String, -} - -#[derive(Serialize)] -struct AppliedSheet { - actor: String, - href: String, - nodeHref: String, - disabled: bool, - title: String, - system: bool, - styleSheetIndex: isize, - ruleCount: usize, -} - -#[derive(Serialize)] -struct GetLayoutReply { - from: String, - - display: String, - position: String, - #[serde(rename = "z-index")] - zIndex: String, - #[serde(rename = "box-sizing")] - boxSizing: String, - - // Would be nice to use a proper struct, blocked by - // https://github.com/serde-rs/serde/issues/43 - autoMargins: serde_json::value::Value, - #[serde(rename = "margin-top")] - marginTop: String, - #[serde(rename = "margin-right")] - marginRight: String, - #[serde(rename = "margin-bottom")] - marginBottom: String, - #[serde(rename = "margin-left")] - marginLeft: String, - - #[serde(rename = "border-top-width")] - borderTopWidth: String, - #[serde(rename = "border-right-width")] - borderRightWidth: String, - #[serde(rename = "border-bottom-width")] - borderBottomWidth: String, - #[serde(rename = "border-left-width")] - borderLeftWidth: String, - - #[serde(rename = "padding-top")] - paddingTop: String, - #[serde(rename = "padding-right")] - paddingRight: String, - #[serde(rename = "padding-bottom")] - paddingBottom: String, - #[serde(rename = "padding-left")] - paddingLeft: String, - - width: f32, - height: f32, -} - -impl Actor for PageStyleActor { - fn name(&self) -> String { - self.name.clone() - } - - fn handle_message( - &self, - registry: &ActorRegistry, - msg_type: &str, - msg: &Map, - stream: &mut TcpStream, - _id: StreamId, - ) -> Result { - Ok(match msg_type { - "getApplied" => { - //TODO: query script for relevant applied styles to node (msg.node) - let msg = GetAppliedReply { - entries: vec![], - rules: vec![], - sheets: vec![], - from: self.name(), - }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed - }, - - "getComputed" => { - //TODO: query script for relevant computed styles on node (msg.node) - let msg = GetComputedReply { - computed: vec![], - from: self.name(), - }; - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed - }, - - //TODO: query script for box layout properties of node (msg.node) - "getLayout" => { - let target = msg.get("node").unwrap().as_str().unwrap(); - let (tx, rx) = ipc::channel().unwrap(); - self.script_chan - .send(GetLayout( - self.pipeline, - registry.actor_to_script(target.to_owned()), - tx, - )) - .unwrap(); - let ComputedNodeLayout { - display, - position, - z_index: zIndex, - box_sizing: boxSizing, - auto_margins: autoMargins, - margin_top: marginTop, - margin_right: marginRight, - margin_bottom: marginBottom, - margin_left: marginLeft, - border_top_width: borderTopWidth, - border_right_width: borderRightWidth, - border_bottom_width: borderBottomWidth, - border_left_width: borderLeftWidth, - padding_top: paddingTop, - padding_right: paddingRight, - padding_bottom: paddingBottom, - padding_left: paddingLeft, - width, - height, - } = rx.recv().unwrap().ok_or(())?; - - let auto_margins = msg - .get("autoMargins") - .and_then(Value::as_bool) - .unwrap_or(false); - - // http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js - let msg = GetLayoutReply { - from: self.name(), - display, - position, - zIndex, - boxSizing, - autoMargins: if auto_margins { - let mut m = Map::new(); - let auto = serde_json::value::Value::String("auto".to_owned()); - if autoMargins.top { - m.insert("top".to_owned(), auto.clone()); - } - if autoMargins.right { - m.insert("right".to_owned(), auto.clone()); - } - if autoMargins.bottom { - m.insert("bottom".to_owned(), auto.clone()); - } - if autoMargins.left { - m.insert("left".to_owned(), auto); - } - serde_json::value::Value::Object(m) - } else { - serde_json::value::Value::Null - }, - marginTop, - marginRight, - marginBottom, - marginLeft, - borderTopWidth, - borderRightWidth, - borderBottomWidth, - borderLeftWidth, - paddingTop, - paddingRight, - paddingBottom, - paddingLeft, - width, - height, - }; - let msg = serde_json::to_string(&msg).unwrap(); - let msg = serde_json::from_str::(&msg).unwrap(); - let _ = stream.write_json_packet(&msg); - ActorMessageStatus::Processed - }, - - _ => ActorMessageStatus::Ignored, - }) - } +pub struct InspectorActor { + pub name: String, + pub walker: RefCell>, + pub page_style: RefCell>, + pub highlighter: RefCell>, + pub script_chan: IpcSender, + pub browsing_context: String, } impl Actor for InspectorActor { @@ -612,28 +82,29 @@ impl Actor for InspectorActor { let pipeline = browsing_context.active_pipeline.get(); Ok(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 = root_info.encode(registry, false, self.script_chan.clone(), pipeline); + if self.walker.borrow().is_none() { let walker = WalkerActor { name: registry.new_name("walker"), script_chan: self.script_chan.clone(), pipeline, + root_node: root.clone(), }; let mut walker_name = self.walker.borrow_mut(); *walker_name = Some(walker.name()); registry.register_later(Box::new(walker)); } - let (tx, rx) = ipc::channel().unwrap(); - self.script_chan.send(GetRootNode(pipeline, tx)).unwrap(); - let root_info = rx.recv().unwrap().ok_or(())?; - - let node = root_info.encode(registry, false, self.script_chan.clone(), pipeline); - let msg = GetWalkerReply { from: self.name(), walker: WalkerMsg { actor: self.walker.borrow().clone().unwrap(), - root: node, + root, }, }; let _ = stream.write_json_packet(&msg); @@ -643,29 +114,41 @@ impl Actor for InspectorActor { "getPageStyle" => { if self.page_style.borrow().is_none() { let style = PageStyleActor { - name: registry.new_name("pageStyle"), + name: registry.new_name("page-style"), script_chan: self.script_chan.clone(), pipeline, }; - let mut pageStyle = self.page_style.borrow_mut(); - *pageStyle = Some(style.name()); + let mut page_style = self.page_style.borrow_mut(); + *page_style = Some(style.name()); registry.register_later(Box::new(style)); } let msg = GetPageStyleReply { from: self.name(), - pageStyle: PageStyleMsg { + page_style: PageStyleMsg { actor: self.page_style.borrow().clone().unwrap(), + traits: HashMap::from([ + ("fontStretchLevel4".into(), true), + ("fontStyleLevel4".into(), true), + ("fontVariations".into(), true), + ("fontWeightLevel4".into(), true), + ]), }, }; let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, - //TODO: this is an old message; try adding highlightable to the root traits instead - // and support getHighlighter instead - //"highlight" => {} - "getHighlighter" => { + "supportsHighlighters" => { + let msg = SupportsHighlightersReply { + from: self.name(), + value: true, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + + "getHighlighterByType" => { if self.highlighter.borrow().is_none() { let highlighter_actor = HighlighterActor { name: registry.new_name("highlighter"), @@ -677,7 +160,7 @@ impl Actor for InspectorActor { let msg = GetHighlighterReply { from: self.name(), - highligter: HighlighterMsg { + highlighter: HighlighterMsg { actor: self.highlighter.borrow().clone().unwrap(), }, }; diff --git a/components/devtools/actors/inspector/accessibility.rs b/components/devtools/actors/inspector/accessibility.rs new file mode 100644 index 00000000000..f5a40b9995e --- /dev/null +++ b/components/devtools/actors/inspector/accessibility.rs @@ -0,0 +1,132 @@ +/* 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/. */ + +//! 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::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::protocol::JsonPacketStream; +use crate::StreamId; + +#[derive(Serialize)] +struct BootstrapState { + enabled: bool, +} + +#[derive(Serialize)] +struct BootstrapReply { + from: String, + state: BootstrapState, +} + +#[derive(Serialize)] +struct GetSimulatorReply { + from: String, + simulator: ActorMsg, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct AccessibilityTraits { + tabbing_order: bool, +} + +#[derive(Serialize)] +struct GetTraitsReply { + from: String, + traits: AccessibilityTraits, +} + +#[derive(Serialize)] +struct ActorMsg { + actor: String, +} + +#[derive(Serialize)] +struct GetWalkerReply { + from: String, + walker: ActorMsg, +} + +pub struct AccessibilityActor { + name: String, +} + +impl Actor for AccessibilityActor { + fn name(&self) -> String { + self.name.clone() + } + + /// The accesibility actor can handle the following messages: + /// + /// - `bootstrap`: It is required but it doesn't do anything yet + /// + /// - `getSimulator`: Returns a new Simulator actor + /// + /// - `getTraits`: Informs the DevTools client about the configuration of the accessibility actor + /// + /// - `getWalker`: Returns a new AccessibleWalker actor (not to be confused with the general + /// inspector Walker actor) + fn handle_message( + &self, + registry: &ActorRegistry, + msg_type: &str, + _msg: &Map, + stream: &mut TcpStream, + _id: StreamId, + ) -> Result { + Ok(match msg_type { + "bootstrap" => { + let msg = BootstrapReply { + from: self.name(), + state: BootstrapState { enabled: false }, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "getSimulator" => { + // TODO: Create actual simulator + let simulator = registry.new_name("simulator"); + let msg = GetSimulatorReply { + from: self.name(), + simulator: ActorMsg { actor: simulator }, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "getTraits" => { + let msg = GetTraitsReply { + from: self.name(), + traits: AccessibilityTraits { + tabbing_order: true, + }, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "getWalker" => { + // TODO: Create actual accessible walker + let walker = registry.new_name("accesiblewalker"); + let msg = GetWalkerReply { + from: self.name(), + walker: ActorMsg { actor: walker }, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + _ => ActorMessageStatus::Ignored, + }) + } +} + +impl AccessibilityActor { + pub fn new(name: String) -> Self { + Self { name } + } +} diff --git a/components/devtools/actors/inspector/css_properties.rs b/components/devtools/actors/inspector/css_properties.rs new file mode 100644 index 00000000000..cdd27b82b38 --- /dev/null +++ b/components/devtools/actors/inspector/css_properties.rs @@ -0,0 +1,81 @@ +/* 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/. */ + +//! This actor holds a database of available css properties, their supported values and +//! alternative names + +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::StreamId; + +pub struct CssPropertiesActor { + name: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct CssDatabaseProperty { + is_inherited: bool, + values: Vec<&'static str>, + supports: Vec<&'static str>, + subproperties: Vec<&'static str>, +} + +#[derive(Serialize)] +struct GetCssDatabaseReply { + properties: HashMap<&'static str, CssDatabaseProperty>, + from: String, +} + +impl Actor for CssPropertiesActor { + fn name(&self) -> String { + self.name.clone() + } + + /// The css properties actor can handle the following messages: + /// + /// - `getCSSDatabase`: Returns a big list of every supported css property so that the + /// inspector can show the available options + fn handle_message( + &self, + _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(), + // TODO: Fill this programatically with other properties + properties: HashMap::from([( + "color", + CssDatabaseProperty { + is_inherited: true, + values: vec!["color"], + supports: vec!["color"], + subproperties: vec!["color"], + }, + )]), + }); + + ActorMessageStatus::Processed + }, + _ => ActorMessageStatus::Ignored, + }) + } +} + +impl CssPropertiesActor { + pub fn new(name: String) -> Self { + Self { name } + } +} diff --git a/components/devtools/actors/inspector/highlighter.rs b/components/devtools/actors/inspector/highlighter.rs new file mode 100644 index 00000000000..f75d25f2175 --- /dev/null +++ b/components/devtools/actors/inspector/highlighter.rs @@ -0,0 +1,69 @@ +/* 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/. */ + +//! 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 serde::Serialize; +use serde_json::{self, Map, Value}; + +use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::protocol::JsonPacketStream; +use crate::{EmptyReplyMsg, StreamId}; + +#[derive(Serialize)] +pub struct HighlighterMsg { + pub actor: String, +} + +pub struct HighlighterActor { + pub name: String, +} + +#[derive(Serialize)] +struct ShowReply { + from: String, + value: bool, +} + +impl Actor for HighlighterActor { + fn name(&self) -> String { + self.name.clone() + } + + /// The highligher actor can handle the following messages: + /// + /// - `show`: Enables highlighting for the selected node + /// + /// - `hide`: Disables highlighting for the selected node + fn handle_message( + &self, + _registry: &ActorRegistry, + msg_type: &str, + _msg: &Map, + stream: &mut TcpStream, + _id: StreamId, + ) -> Result { + Ok(match msg_type { + "show" => { + let msg = ShowReply { + from: self.name(), + value: true, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + + "hide" => { + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + + _ => ActorMessageStatus::Ignored, + }) + } +} diff --git a/components/devtools/actors/inspector/layout.rs b/components/devtools/actors/inspector/layout.rs new file mode 100644 index 00000000000..ab848b6b8fb --- /dev/null +++ b/components/devtools/actors/inspector/layout.rs @@ -0,0 +1,88 @@ +/* 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/. */ + +//! 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::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::protocol::JsonPacketStream; +use crate::StreamId; + +#[derive(Serialize)] +pub struct LayoutInspectorActorMsg { + actor: String, +} + +pub struct LayoutInspectorActor { + name: String, +} + +#[derive(Serialize)] +pub struct GetGridsReply { + from: String, + grids: Vec, +} + +#[derive(Serialize)] +pub struct GetCurrentFlexboxReply { + from: String, + flexbox: Option<()>, +} + +impl Actor for LayoutInspectorActor { + fn name(&self) -> String { + self.name.clone() + } + + /// The layout inspector actor can handle the following messages: + /// + /// - `getGrids`: Returns a list of CSS grids, non functional at the moment + /// + /// - `getCurrentFlexbox`: Returns the active flexbox, non functional at the moment + fn handle_message( + &self, + _registry: &ActorRegistry, + msg_type: &str, + _msg: &Map, + stream: &mut TcpStream, + _id: StreamId, + ) -> Result { + Ok(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 + }, + "getCurrentFlexbox" => { + let msg = GetCurrentFlexboxReply { + from: self.name(), + // TODO: Create and return the current flexbox object + flexbox: None, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + _ => ActorMessageStatus::Ignored, + }) + } +} + +impl LayoutInspectorActor { + pub fn new(name: String) -> Self { + Self { name } + } + + pub fn encodable(&self) -> LayoutInspectorActorMsg { + LayoutInspectorActorMsg { actor: self.name() } + } +} diff --git a/components/devtools/actors/inspector/node.rs b/components/devtools/actors/inspector/node.rs new file mode 100644 index 00000000000..a27c0637c58 --- /dev/null +++ b/components/devtools/actors/inspector/node.rs @@ -0,0 +1,204 @@ +/* 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/. */ + +//! This actor represents one DOM node. It is created by the Walker actor when it is traversing the +//! document tree. + +use std::collections::HashMap; +use std::net::TcpStream; + +use base::id::PipelineId; +use devtools_traits::DevtoolScriptControlMsg::{GetDocumentElement, ModifyAttribute}; +use devtools_traits::{DevtoolScriptControlMsg, NodeInfo}; +use ipc_channel::ipc::{self, IpcSender}; +use serde::Serialize; +use serde_json::{self, Map, Value}; + +use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::protocol::JsonPacketStream; +use crate::{EmptyReplyMsg, StreamId}; + +#[derive(Serialize)] +struct GetUniqueSelectorReply { + from: String, + value: String, +} + +#[derive(Clone, Serialize)] +struct AttrMsg { + name: String, + value: String, +} + +#[derive(Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NodeActorMsg { + pub actor: String, + #[serde(rename = "baseURI")] + base_uri: String, + causes_overflow: bool, + container_type: Option<()>, + pub display_name: String, + display_type: Option, + is_after_pseudo_element: bool, + is_anonymous: bool, + is_before_pseudo_element: bool, + is_direct_shadow_host_child: Option, + is_displayed: bool, + #[serde(rename = "isInHTMLDocument")] + is_in_html_document: Option, + is_marker_pseudo_element: bool, + is_native_anonymous: bool, + is_scrollable: bool, + is_shadow_root: bool, + is_top_level_document: bool, + node_name: String, + node_type: u16, + node_value: Option<()>, + pub num_children: usize, + #[serde(skip_serializing_if = "String::is_empty")] + parent: String, + shadow_root_mode: Option<()>, + traits: HashMap, + attrs: Vec, +} + +pub struct NodeActor { + name: String, + script_chan: IpcSender, + pipeline: PipelineId, +} + +impl Actor for NodeActor { + fn name(&self) -> String { + self.name.clone() + } + + /// The node actor can handle the following messages: + /// + /// - `modifyAttributes`: Asks the script to change a value in the attribute of the + /// corresponding node + /// + /// - `getUniqueSelector`: Returns the display name of this node + fn handle_message( + &self, + registry: &ActorRegistry, + msg_type: &str, + msg: &Map, + stream: &mut TcpStream, + _id: StreamId, + ) -> Result { + Ok(match msg_type { + "modifyAttributes" => { + let target = msg.get("to").ok_or(())?.as_str().ok_or(())?; + let mods = msg.get("modifications").ok_or(())?.as_array().ok_or(())?; + let modifications = mods + .iter() + .filter_map(|json_mod| { + serde_json::from_str(&serde_json::to_string(json_mod).ok()?).ok() + }) + .collect(); + self.script_chan + .send(ModifyAttribute( + self.pipeline, + registry.actor_to_script(target.to_owned()), + modifications, + )) + .map_err(|_| ())?; + + let reply = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&reply); + ActorMessageStatus::Processed + }, + + "getUniqueSelector" => { + let (tx, rx) = ipc::channel().unwrap(); + self.script_chan + .send(GetDocumentElement(self.pipeline, tx)) + .unwrap(); + let doc_elem_info = rx.recv().map_err(|_| ())?.ok_or(())?; + let node = + doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline); + + let msg = GetUniqueSelectorReply { + from: self.name(), + value: node.display_name, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + + _ => ActorMessageStatus::Ignored, + }) + } +} + +pub trait NodeInfoToProtocol { + fn encode( + self, + actors: &ActorRegistry, + display: bool, + script_chan: IpcSender, + pipeline: PipelineId, + ) -> NodeActorMsg; +} + +impl NodeInfoToProtocol for NodeInfo { + fn encode( + self, + actors: &ActorRegistry, + display: bool, + script_chan: IpcSender, + pipeline: PipelineId, + ) -> NodeActorMsg { + let actor = if !actors.script_actor_registered(self.unique_id.clone()) { + let name = actors.new_name("node"); + let node_actor = NodeActor { + name: name.clone(), + script_chan, + pipeline, + }; + actors.register_script_actor(self.unique_id, name.clone()); + actors.register_later(Box::new(node_actor)); + name + } else { + actors.script_to_actor(self.unique_id) + }; + + NodeActorMsg { + actor, + base_uri: self.base_uri, + causes_overflow: false, + container_type: None, + display_name: self.node_name.clone().to_lowercase(), + display_type: Some("block".into()), + is_after_pseudo_element: false, + is_anonymous: false, + is_before_pseudo_element: false, + is_direct_shadow_host_child: None, + is_displayed: display, + is_in_html_document: Some(true), + is_marker_pseudo_element: false, + is_native_anonymous: false, + is_scrollable: false, + is_shadow_root: false, + is_top_level_document: self.is_top_level_document, + node_name: self.node_name, + node_type: self.node_type, + node_value: None, + num_children: self.num_children, + parent: actors.script_to_actor(self.parent.clone()), + shadow_root_mode: None, + traits: HashMap::new(), + attrs: self + .attrs + .into_iter() + .map(|attr| AttrMsg { + name: attr.name, + value: attr.value, + }) + .collect(), + } + } +} diff --git a/components/devtools/actors/inspector/page_style.rs b/components/devtools/actors/inspector/page_style.rs new file mode 100644 index 00000000000..dc69df13584 --- /dev/null +++ b/components/devtools/actors/inspector/page_style.rs @@ -0,0 +1,263 @@ +/* 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/. */ + +//! The page style actor is responsible of informing the DevTools client of the different style +//! properties applied, including the attributes and layout of each element. + +use std::collections::HashMap; +use std::net::TcpStream; + +use base::id::PipelineId; +use devtools_traits::DevtoolScriptControlMsg::GetLayout; +use devtools_traits::{ComputedNodeLayout, DevtoolScriptControlMsg}; +use ipc_channel::ipc::{self, IpcSender}; +use serde::Serialize; +use serde_json::{self, Map, Value}; + +use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::protocol::JsonPacketStream; +use crate::StreamId; + +#[derive(Serialize)] +struct GetAppliedReply { + entries: Vec, + rules: Vec, + sheets: Vec, + from: String, +} + +#[derive(Serialize)] +struct GetComputedReply { + computed: Vec, //XXX all css props + from: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct AppliedEntry { + rule: String, + pseudo_element: Value, + is_system: bool, + matched_selectors: Vec, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct AppliedRule { + actor: String, + #[serde(rename = "type")] + type_: String, + href: String, + css_text: String, + line: u32, + column: u32, + parent_style_sheet: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct AppliedSheet { + actor: String, + href: String, + node_href: String, + disabled: bool, + title: String, + system: bool, + style_sheet_index: isize, + rule_count: usize, +} + +#[derive(Serialize)] +#[serde(rename_all = "kebab-case")] +struct GetLayoutReply { + from: String, + + display: String, + position: String, + z_index: String, + box_sizing: String, + + // Would be nice to use a proper struct, blocked by + // https://github.com/serde-rs/serde/issues/43 + auto_margins: serde_json::value::Value, + margin_top: String, + margin_right: String, + margin_bottom: String, + margin_left: String, + + border_top_width: String, + border_right_width: String, + border_bottom_width: String, + border_left_width: String, + + padding_top: String, + padding_right: String, + padding_bottom: String, + padding_left: String, + + width: f32, + height: f32, +} + +#[derive(Serialize)] +pub struct IsPositionEditableReply { + pub from: String, + pub value: bool, +} + +#[derive(Serialize)] +pub struct PageStyleMsg { + pub actor: String, + pub traits: HashMap, +} + +pub struct PageStyleActor { + pub name: String, + pub script_chan: IpcSender, + pub pipeline: PipelineId, +} + +impl Actor for PageStyleActor { + fn name(&self) -> String { + self.name.clone() + } + + /// The page style actor can handle the following messages: + /// + /// - `getApplied`: Returns the applied styles for a node, placeholder + /// + /// - `getComputed`: Returns the computed styles for a node, placeholder + /// + /// - `getLayout`: Returns the box layout properties for a node, placeholder + /// + /// - `isPositionEditable`: Informs whether you can change a style property in the inspector + fn handle_message( + &self, + registry: &ActorRegistry, + msg_type: &str, + msg: &Map, + stream: &mut TcpStream, + _id: StreamId, + ) -> Result { + Ok(match msg_type { + "getApplied" => { + // TODO: Query script for relevant applied styles to node (msg.node) + let msg = GetAppliedReply { + entries: vec![], + rules: vec![], + sheets: vec![], + from: self.name(), + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + + "getComputed" => { + // TODO: Query script for relevant computed styles on node (msg.node) + let msg = GetComputedReply { + computed: vec![], + from: self.name(), + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + + "getLayout" => { + // TODO: Query script for box layout properties of node (msg.node) + let target = msg.get("node").ok_or(())?.as_str().ok_or(())?; + let (tx, rx) = ipc::channel().map_err(|_| ())?; + self.script_chan + .send(GetLayout( + self.pipeline, + registry.actor_to_script(target.to_owned()), + tx, + )) + .unwrap(); + let ComputedNodeLayout { + display, + position, + z_index, + box_sizing, + auto_margins, + margin_top, + margin_right, + margin_bottom, + margin_left, + border_top_width, + border_right_width, + border_bottom_width, + border_left_width, + padding_top, + padding_right, + padding_bottom, + padding_left, + width, + height, + } = rx.recv().map_err(|_| ())?.ok_or(())?; + + let msg_auto_margins = msg + .get("autoMargins") + .and_then(Value::as_bool) + .unwrap_or(false); + + // https://searchfox.org/mozilla-central/source/devtools/server/actors/page-style.js + let msg = GetLayoutReply { + from: self.name(), + display, + position, + z_index, + box_sizing, + auto_margins: if msg_auto_margins { + let mut m = Map::new(); + let auto = serde_json::value::Value::String("auto".to_owned()); + if auto_margins.top { + m.insert("top".to_owned(), auto.clone()); + } + if auto_margins.right { + m.insert("right".to_owned(), auto.clone()); + } + if auto_margins.bottom { + m.insert("bottom".to_owned(), auto.clone()); + } + if auto_margins.left { + m.insert("left".to_owned(), auto); + } + serde_json::value::Value::Object(m) + } else { + serde_json::value::Value::Null + }, + margin_top, + margin_right, + margin_bottom, + margin_left, + border_top_width, + border_right_width, + border_bottom_width, + border_left_width, + padding_top, + padding_right, + padding_bottom, + padding_left, + 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); + ActorMessageStatus::Processed + }, + + "isPositionEditable" => { + let msg = IsPositionEditableReply { + from: self.name(), + value: false, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + + _ => ActorMessageStatus::Ignored, + }) + } +} diff --git a/components/devtools/actors/inspector/walker.rs b/components/devtools/actors/inspector/walker.rs new file mode 100644 index 00000000000..8588de77ace --- /dev/null +++ b/components/devtools/actors/inspector/walker.rs @@ -0,0 +1,263 @@ +/* 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/. */ + +//! The walker actor is responsible for traversing the DOM tree in various ways to create new nodes + +use std::net::TcpStream; + +use base::id::PipelineId; +use devtools_traits::DevtoolScriptControlMsg; +use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement}; +use ipc_channel::ipc::{self, IpcSender}; +use serde::Serialize; +use serde_json::{self, Map, Value}; + +use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actors::inspector::layout::{LayoutInspectorActor, LayoutInspectorActorMsg}; +use crate::actors::inspector::node::{NodeActorMsg, NodeInfoToProtocol}; +use crate::protocol::JsonPacketStream; +use crate::{EmptyReplyMsg, StreamId}; + +#[derive(Serialize)] +pub struct WalkerMsg { + pub actor: String, + pub root: NodeActorMsg, +} + +pub struct WalkerActor { + pub name: String, + pub script_chan: IpcSender, + pub pipeline: PipelineId, + pub root_node: NodeActorMsg, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct QuerySelectorReply { + from: String, + node: NodeActorMsg, + new_parents: Vec, +} + +#[derive(Serialize)] +struct DocumentElementReply { + from: String, + node: NodeActorMsg, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct ChildrenReply { + has_first: bool, + has_last: bool, + nodes: Vec, + from: String, +} + +#[derive(Serialize)] +struct GetLayoutInspectorReply { + actor: LayoutInspectorActorMsg, + from: String, +} + +#[derive(Serialize)] +struct WatchRootNodeReply { + #[serde(rename = "type")] + type_: String, + from: String, + node: NodeActorMsg, +} + +#[derive(Serialize)] +struct GetOffsetParentReply { + from: String, + node: Option<()>, +} + +impl Actor for WalkerActor { + fn name(&self) -> String { + self.name.clone() + } + + /// The walker actor can handle the following messages: + /// + /// - `children`: Returns a list of children nodes of the specified node + /// + /// - `clearPseudoClassLocks`: Placeholder + /// + /// - `documentElement`: Returns the base document element node + /// + /// - `getLayoutInspector`: Returns the Layout inspector actor, placeholder + /// + /// - `getOffsetParent`: Placeholder + /// + /// - `querySelector`: Recursively looks for the specified selector in the tree, reutrning the + /// node and its ascendents + fn handle_message( + &self, + registry: &ActorRegistry, + msg_type: &str, + msg: &Map, + stream: &mut TcpStream, + _id: StreamId, + ) -> Result { + Ok(match msg_type { + "children" => { + let target = msg.get("node").ok_or(())?.as_str().ok_or(())?; + let (tx, rx) = ipc::channel().map_err(|_| ())?; + self.script_chan + .send(GetChildren( + self.pipeline, + registry.actor_to_script(target.into()), + tx, + )) + .map_err(|_| ())?; + let children = rx.recv().map_err(|_| ())?.ok_or(())?; + + let msg = ChildrenReply { + has_first: true, + has_last: true, + nodes: children + .into_iter() + .map(|child| { + child.encode(registry, true, self.script_chan.clone(), self.pipeline) + }) + .collect(), + from: self.name(), + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "clearPseudoClassLocks" => { + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "documentElement" => { + let (tx, rx) = ipc::channel().map_err(|_| ())?; + self.script_chan + .send(GetDocumentElement(self.pipeline, tx)) + .map_err(|_| ())?; + let doc_elem_info = rx.recv().map_err(|_| ())?.ok_or(())?; + let node = + doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline); + + let msg = DocumentElementReply { + from: self.name(), + node, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "getLayoutInspector" => { + // TODO: Create actual layout inspector actor + let layout = LayoutInspectorActor::new(registry.new_name("layout")); + let actor = layout.encodable(); + registry.register_later(Box::new(layout)); + + let msg = GetLayoutInspectorReply { + from: self.name(), + actor, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "getOffsetParent" => { + let msg = GetOffsetParentReply { + from: self.name(), + node: None, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "querySelector" => { + let selector = msg.get("selector").ok_or(())?.as_str().ok_or(())?; + let node = msg.get("node").ok_or(())?.as_str().ok_or(())?; + let mut hierarchy = find_child( + &self.script_chan, + self.pipeline, + registry, + selector, + node, + vec![], + ) + .map_err(|_| ())?; + hierarchy.reverse(); + let node = hierarchy.pop().ok_or(())?; + + let msg = QuerySelectorReply { + from: self.name(), + node, + new_parents: hierarchy, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "watchRootNode" => { + let msg = WatchRootNodeReply { + type_: "root-available".into(), + from: self.name(), + node: self.root_node.clone(), + }; + let _ = stream.write_json_packet(&msg); + + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + _ => ActorMessageStatus::Ignored, + }) + } +} + +/// Recursively searches for a child with the specified selector +/// If it is found, returns a list with the child and all of its ancestors. +fn find_child( + script_chan: &IpcSender, + pipeline: PipelineId, + registry: &ActorRegistry, + selector: &str, + node: &str, + mut hierarchy: Vec, +) -> Result, Vec> { + let (tx, rx) = ipc::channel().unwrap(); + script_chan + .send(GetChildren( + pipeline, + registry.actor_to_script(node.into()), + tx, + )) + .unwrap(); + let children = rx.recv().unwrap().ok_or(vec![])?; + + for child in children { + let msg = child.encode(registry, true, script_chan.clone(), pipeline); + if msg.display_name == selector { + hierarchy.push(msg); + return Ok(hierarchy); + }; + + if msg.num_children == 0 { + continue; + } + + match find_child( + script_chan, + pipeline, + registry, + selector, + &msg.actor, + hierarchy, + ) { + Ok(mut hierarchy) => { + hierarchy.push(msg); + return Ok(hierarchy); + }, + Err(e) => { + hierarchy = e; + }, + } + } + Err(hierarchy) +} diff --git a/components/devtools/actors/performance.rs b/components/devtools/actors/performance.rs index cfdacc5acc6..0644e2665ec 100644 --- a/components/devtools/actors/performance.rs +++ b/components/devtools/actors/performance.rs @@ -2,6 +2,9 @@ * 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/. */ +// TODO: Is this actor still relevant? +#![allow(dead_code)] + use std::net::TcpStream; use serde::Serialize; diff --git a/components/devtools/actors/profiler.rs b/components/devtools/actors/reflow.rs similarity index 50% rename from components/devtools/actors/profiler.rs rename to components/devtools/actors/reflow.rs index 38c3d03f8cc..7df26826813 100644 --- a/components/devtools/actors/profiler.rs +++ b/components/devtools/actors/reflow.rs @@ -2,6 +2,8 @@ * 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/. */ +//! This actor is used for protocol purposes, it forwards the reflow events to clients. + use std::net::TcpStream; use serde_json::{Map, Value}; @@ -9,29 +11,38 @@ use serde_json::{Map, Value}; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::StreamId; -pub struct ProfilerActor { +pub struct ReflowActor { name: String, } -impl Actor for ProfilerActor { +impl Actor for ReflowActor { fn name(&self) -> String { self.name.clone() } + /// The reflow actor can handle the following messages: + /// + /// - `start`: Does nothing yet. This doesn't need a reply like other messages. fn handle_message( &self, _registry: &ActorRegistry, - _msg_type: &str, + msg_type: &str, _msg: &Map, _stream: &mut TcpStream, _id: StreamId, ) -> Result { - Ok(ActorMessageStatus::Ignored) + Ok(match msg_type { + "start" => { + // TODO: Create an observer on "reflows" events + ActorMessageStatus::Processed + }, + _ => ActorMessageStatus::Ignored, + }) } } -impl ProfilerActor { - pub fn new(name: String) -> ProfilerActor { - ProfilerActor { name } +impl ReflowActor { + pub fn new(name: String) -> Self { + Self { name } } } diff --git a/components/devtools/actors/root.rs b/components/devtools/actors/root.rs index efe58c2de71..cbe0dad1beb 100644 --- a/components/devtools/actors/root.rs +++ b/components/devtools/actors/root.rs @@ -52,7 +52,6 @@ struct GetRootReply { #[derive(Serialize)] struct ListTabsReply { from: String, - selected: u32, tabs: Vec, } @@ -182,7 +181,6 @@ impl Actor for RootActor { "listTabs" => { let actor = ListTabsReply { from: "root".to_owned(), - selected: 0, tabs: self .tabs .iter() diff --git a/components/devtools/actors/tab.rs b/components/devtools/actors/tab.rs index 6d54ef2cb6f..e6538e38a38 100644 --- a/components/devtools/actors/tab.rs +++ b/components/devtools/actors/tab.rs @@ -124,7 +124,7 @@ impl TabDescriptorActor { actors: &mut ActorRegistry, browsing_context_actor: String, ) -> TabDescriptorActor { - let name = actors.new_name("tabDescription"); + let name = actors.new_name("tab-description"); let root = actors.find_mut::("root"); root.tabs.push(name.clone()); TabDescriptorActor { diff --git a/components/devtools/actors/thread.rs b/components/devtools/actors/thread.rs index f3416b8f721..6e272cd3d28 100644 --- a/components/devtools/actors/thread.rs +++ b/components/devtools/actors/thread.rs @@ -9,7 +9,7 @@ use serde_json::{Map, Value}; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; -use crate::StreamId; +use crate::{EmptyReplyMsg, StreamId}; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -49,11 +49,6 @@ struct ThreadInterruptedReply { type_: String, } -#[derive(Serialize)] -struct ReconfigureReply { - from: String, -} - #[derive(Serialize)] struct SourcesReply { from: String, @@ -63,11 +58,6 @@ struct SourcesReply { #[derive(Serialize)] enum Source {} -#[derive(Serialize)] -struct VoidAttachedReply { - from: String, -} - pub struct ThreadActor { name: String, } @@ -107,7 +97,7 @@ impl Actor for ThreadActor { }, }; let _ = stream.write_json_packet(&msg); - let _ = stream.write_json_packet(&VoidAttachedReply { from: self.name() }); + let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() }); ActorMessageStatus::Processed }, @@ -117,7 +107,7 @@ impl Actor for ThreadActor { type_: "resumed".to_owned(), }; let _ = stream.write_json_packet(&msg); - let _ = stream.write_json_packet(&VoidAttachedReply { from: self.name() }); + let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() }); ActorMessageStatus::Processed }, @@ -131,14 +121,14 @@ impl Actor for ThreadActor { }, "reconfigure" => { - let _ = stream.write_json_packet(&ReconfigureReply { from: self.name() }); + let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() }); ActorMessageStatus::Processed }, "sources" => { let msg = SourcesReply { from: self.name(), - sources: vec![], + sources: vec![], // TODO: Add sources for the debugger here }; let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed diff --git a/components/devtools/actors/timeline.rs b/components/devtools/actors/timeline.rs index a625ebf24cd..7932c54aa5c 100644 --- a/components/devtools/actors/timeline.rs +++ b/components/devtools/actors/timeline.rs @@ -2,6 +2,9 @@ * 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/. */ +// TODO: Is this actor still relevant? +#![allow(dead_code)] + use std::cell::RefCell; use std::error::Error; use std::net::TcpStream; diff --git a/components/devtools/actors/watcher.rs b/components/devtools/actors/watcher.rs index d772d2ae75e..55a19da8039 100644 --- a/components/devtools/actors/watcher.rs +++ b/components/devtools/actors/watcher.rs @@ -11,18 +11,26 @@ use std::collections::HashMap; use std::net::TcpStream; +use log::warn; use serde::Serialize; use serde_json::{Map, Value}; +use self::network_parent::{NetworkParentActor, NetworkParentActorMsg}; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg}; -use crate::actors::configuration::{ - TargetConfigurationActor, TargetConfigurationActorMsg, ThreadConfigurationActor, - ThreadConfigurationActorMsg, +use crate::actors::watcher::target_configuration::{ + TargetConfigurationActor, TargetConfigurationActorMsg, +}; +use crate::actors::watcher::thread_configuration::{ + ThreadConfigurationActor, ThreadConfigurationActorMsg, }; use crate::protocol::JsonPacketStream; use crate::{EmptyReplyMsg, StreamId}; +pub mod network_parent; +pub mod target_configuration; +pub mod thread_configuration; + /// Describes the debugged context. It informs the server of which objects can be debugged. /// #[derive(Serialize)] @@ -54,7 +62,7 @@ impl SessionContext { ("css-change", false), ("css-message", false), ("css-registered-properties", false), - ("document-event", true), + ("document-event", false), ("Cache", false), ("cookies", false), ("error-message", true), @@ -97,6 +105,19 @@ struct WatchTargetsReply { target: BrowsingContextActorMsg, } +#[derive(Serialize)] +struct GetParentBrowsingContextIDReply { + from: String, + #[serde(rename = "browsingContextID")] + browsing_context_id: u32, +} + +#[derive(Serialize)] +struct GetNetworkParentActorReply { + from: String, + network: NetworkParentActorMsg, +} + #[derive(Serialize)] struct GetTargetConfigurationActorReply { from: String, @@ -125,6 +146,9 @@ pub struct WatcherActorMsg { pub struct WatcherActor { name: String, browsing_context_actor: String, + network_parent: String, + target_configuration: String, + thread_configuration: String, session_context: SessionContext, } @@ -142,6 +166,9 @@ impl Actor for WatcherActor { /// - `watchResources`: Start watching certain resource types. This sends /// `resource-available-form` events. /// + /// - `getNetworkParentActor`: Returns the network parent actor. It doesn't seem to do much at + /// the moment. + /// /// - `getTargetConfigurationActor`: Returns the configuration actor for a specific target, so /// that the server can update its settings. /// @@ -154,33 +181,30 @@ impl Actor for WatcherActor { stream: &mut TcpStream, _id: StreamId, ) -> Result { + let target = registry.find::(&self.browsing_context_actor); Ok(match msg_type { "watchTargets" => { - let target = registry - .find::(&self.browsing_context_actor) - .encodable(); - let _ = stream.write_json_packet(&WatchTargetsReply { + let msg = WatchTargetsReply { from: self.name(), type_: "target-available-form".into(), - target, - }); + target: target.encodable(), + }; + let _ = stream.write_json_packet(&msg); - let target = registry.find::(&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() }); - + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); 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); }; @@ -189,41 +213,55 @@ impl Actor for WatcherActor { let Some(resource) = resource.as_str() else { continue; }; - - let target = - registry.find::(&self.browsing_context_actor); - - if resource == "document-event" { - target.document_event(stream); + match resource { + "document-event" => { + target.document_event(stream); + }, + "console-message" | "error-message" => {}, + _ => warn!("resource {} not handled yet", resource), } - let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() }); + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); } - + ActorMessageStatus::Processed + }, + "getParentBrowsingContextID" => { + let browsing_context_id = target.browsing_context_id.index.0.get(); + let msg = GetParentBrowsingContextIDReply { + from: self.name(), + browsing_context_id, + }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + "getNetworkParentActor" => { + let network_parent = registry.find::(&self.network_parent); + let msg = GetNetworkParentActorReply { + from: self.name(), + network: network_parent.encodable(), + }; + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "getTargetConfigurationActor" => { - let target = registry.find::(&self.browsing_context_actor); let target_configuration = - registry.find::(&target.target_configuration); - - let _ = stream.write_json_packet(&GetTargetConfigurationActorReply { + registry.find::(&self.target_configuration); + let msg = GetTargetConfigurationActorReply { from: self.name(), configuration: target_configuration.encodable(), - }); - + }; + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, "getThreadConfigurationActor" => { - let target = registry.find::(&self.browsing_context_actor); let thread_configuration = - registry.find::(&target.thread_configuration); - - let _ = stream.write_json_packet(&GetThreadConfigurationActorReply { + registry.find::(&self.thread_configuration); + let msg = GetThreadConfigurationActorReply { from: self.name(), configuration: thread_configuration.encodable(), - }); - + }; + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, _ => ActorMessageStatus::Ignored, @@ -233,15 +271,30 @@ impl Actor for WatcherActor { impl WatcherActor { pub fn new( - name: String, + actors: &mut ActorRegistry, browsing_context_actor: String, session_context: SessionContext, ) -> Self { - Self { - name, + let network_parent = NetworkParentActor::new(actors.new_name("network-parent")); + let target_configuration = + TargetConfigurationActor::new(actors.new_name("target-configuration")); + let thread_configuration = + ThreadConfigurationActor::new(actors.new_name("thread-configuration")); + + let watcher = Self { + name: actors.new_name("watcher"), browsing_context_actor, + network_parent: network_parent.name(), + target_configuration: target_configuration.name(), + thread_configuration: thread_configuration.name(), session_context, - } + }; + + actors.register(Box::new(network_parent)); + actors.register(Box::new(target_configuration)); + actors.register(Box::new(thread_configuration)); + + watcher } pub fn encodable(&self) -> WatcherActorMsg { diff --git a/components/devtools/actors/watcher/network_parent.rs b/components/devtools/actors/watcher/network_parent.rs new file mode 100644 index 00000000000..31217455200 --- /dev/null +++ b/components/devtools/actors/watcher/network_parent.rs @@ -0,0 +1,58 @@ +/* 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/. */ + +use std::net::TcpStream; + +use serde::Serialize; +use serde_json::{Map, Value}; + +use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::protocol::JsonPacketStream; +use crate::{EmptyReplyMsg, StreamId}; + +#[derive(Serialize)] +pub struct NetworkParentActorMsg { + actor: String, +} + +pub struct NetworkParentActor { + name: String, +} + +impl Actor for NetworkParentActor { + fn name(&self) -> String { + self.name.clone() + } + + /// The network parent actor can handle the following messages: + /// + /// - `setSaveRequestAndResponseBodies`: Doesn't do anything yet + fn handle_message( + &self, + _registry: &ActorRegistry, + msg_type: &str, + _msg: &Map, + stream: &mut TcpStream, + _id: StreamId, + ) -> Result { + Ok(match msg_type { + "setSaveRequestAndResponseBodies" => { + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + _ => ActorMessageStatus::Ignored, + }) + } +} + +impl NetworkParentActor { + pub fn new(name: String) -> Self { + Self { name } + } + + pub fn encodable(&self) -> NetworkParentActorMsg { + NetworkParentActorMsg { actor: self.name() } + } +} diff --git a/components/devtools/actors/configuration.rs b/components/devtools/actors/watcher/target_configuration.rs similarity index 66% rename from components/devtools/actors/configuration.rs rename to components/devtools/actors/watcher/target_configuration.rs index 853c984d39b..7b83cdde698 100644 --- a/components/devtools/actors/configuration.rs +++ b/components/devtools/actors/watcher/target_configuration.rs @@ -3,8 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! Liberally derived from -//! and -//! These actors manage the configuration flags that the devtools host can apply to the targets and threads. +//! This actor manages the configuration flags that the devtools host can apply to the targets. use std::collections::HashMap; use std::net::TcpStream; @@ -35,16 +34,6 @@ pub struct TargetConfigurationActor { supported_options: HashMap<&'static str, bool>, } -#[derive(Serialize)] -pub struct ThreadConfigurationActorMsg { - actor: String, -} - -pub struct ThreadConfigurationActor { - name: String, - _configuration: HashMap<&'static str, bool>, -} - impl Actor for TargetConfigurationActor { fn name(&self) -> String { self.name.clone() @@ -64,8 +53,8 @@ impl Actor for TargetConfigurationActor { Ok(match msg_type { "updateConfiguration" => { // TODO: Actually update configuration - let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() }); - + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, _ => ActorMessageStatus::Ignored, @@ -110,44 +99,3 @@ impl TargetConfigurationActor { } } } - -impl Actor for ThreadConfigurationActor { - fn name(&self) -> String { - self.name.clone() - } - - /// The thread configuration actor can handle the following messages: - /// - /// - `updateConfiguration`: Receives new configuration flags from the devtools host. - fn handle_message( - &self, - _registry: &ActorRegistry, - msg_type: &str, - _msg: &Map, - stream: &mut TcpStream, - _id: StreamId, - ) -> Result { - Ok(match msg_type { - "updateConfiguration" => { - // TODO: Actually update configuration - let _ = stream.write_json_packet(&EmptyReplyMsg { from: self.name() }); - - ActorMessageStatus::Processed - }, - _ => ActorMessageStatus::Ignored, - }) - } -} - -impl ThreadConfigurationActor { - pub fn new(name: String) -> Self { - Self { - name, - _configuration: HashMap::new(), - } - } - - pub fn encodable(&self) -> ThreadConfigurationActorMsg { - ThreadConfigurationActorMsg { actor: self.name() } - } -} diff --git a/components/devtools/actors/watcher/thread_configuration.rs b/components/devtools/actors/watcher/thread_configuration.rs new file mode 100644 index 00000000000..7659db42e3c --- /dev/null +++ b/components/devtools/actors/watcher/thread_configuration.rs @@ -0,0 +1,67 @@ +/* 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 +//! 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::{EmptyReplyMsg, StreamId}; + +#[derive(Serialize)] +pub struct ThreadConfigurationActorMsg { + actor: String, +} + +pub struct ThreadConfigurationActor { + name: String, + _configuration: HashMap<&'static str, bool>, +} + +impl Actor for ThreadConfigurationActor { + fn name(&self) -> String { + self.name.clone() + } + + /// The thread configuration actor can handle the following messages: + /// + /// - `updateConfiguration`: Receives new configuration flags from the devtools host. + fn handle_message( + &self, + _registry: &ActorRegistry, + msg_type: &str, + _msg: &Map, + stream: &mut TcpStream, + _id: StreamId, + ) -> Result { + Ok(match msg_type { + "updateConfiguration" => { + // TODO: Actually update configuration + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); + ActorMessageStatus::Processed + }, + _ => ActorMessageStatus::Ignored, + }) + } +} + +impl ThreadConfigurationActor { + pub fn new(name: String) -> Self { + Self { + name, + _configuration: HashMap::new(), + } + } + + pub fn encodable(&self) -> ThreadConfigurationActorMsg { + ThreadConfigurationActorMsg { actor: self.name() } + } +} diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 6b6b407d3bd..00690ee485f 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -28,7 +28,7 @@ use devtools_traits::{ }; use embedder_traits::{EmbedderMsg, EmbedderProxy, PromptDefinition, PromptOrigin, PromptResult}; use ipc_channel::ipc::{self, IpcSender}; -use log::{debug, warn}; +use log::{debug, trace, warn}; use serde::Serialize; use servo_rand::RngCore; @@ -50,10 +50,8 @@ mod actor; /// mod actors { pub mod browsing_context; - pub mod configuration; pub mod console; pub mod device; - pub mod emulation; pub mod framerate; pub mod inspector; pub mod memory; @@ -62,7 +60,7 @@ mod actors { pub mod performance; pub mod preference; pub mod process; - pub mod profiler; + pub mod reflow; pub mod root; pub mod stylesheets; pub mod tab; @@ -279,6 +277,7 @@ fn run_server( // We need separate actor representations for each script global that exists; // clients can theoretically connect to multiple globals simultaneously. // TODO: move this into the root or target modules? + #[allow(clippy::too_many_arguments)] fn handle_new_global( actors: Arc>, ids: (BrowsingContextId, PipelineId, Option), @@ -608,7 +607,7 @@ fn run_server( let mut next_id = StreamId(0); while let Ok(msg) = receiver.recv() { - debug!("{:?}", msg); + trace!("{:?}", msg); match msg { DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::AddClient(stream)) => { let actors = actors.clone(); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 635d1cdf245..5e818348665 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1113,30 +1113,18 @@ impl Node { pub fn summarize(&self) -> NodeInfo { let USVString(base_uri) = self.BaseURI(); + let node_type = self.NodeType(); NodeInfo { unique_id: self.unique_id(), base_uri, parent: self .GetParentNode() .map_or("".to_owned(), |node| node.unique_id()), - node_type: self.NodeType(), - namespace_uri: String::new(), //FIXME + node_type, + is_top_level_document: node_type == NodeConstants::DOCUMENT_NODE, node_name: String::from(self.NodeName()), num_children: self.ChildNodes().Length() as usize, - - //FIXME doctype nodes only - name: String::new(), - public_id: String::new(), - system_id: String::new(), attrs: self.downcast().map(Element::summarize).unwrap_or(vec![]), - - is_document_element: self - .owner_doc() - .GetDocumentElement() - .map_or(false, |elem| elem.upcast::() == self), - - short_value: self.GetNodeValue().map(String::from).unwrap_or_default(), //FIXME: truncate - incomplete_value: false, //FIXME: reflect truncation } } diff --git a/components/shared/devtools/lib.rs b/components/shared/devtools/lib.rs index 05e68e35838..af0de598da3 100644 --- a/components/shared/devtools/lib.rs +++ b/components/shared/devtools/lib.rs @@ -124,17 +124,10 @@ pub struct NodeInfo { pub base_uri: String, pub parent: String, pub node_type: u16, - #[serde(rename = "namespaceURI")] - pub namespace_uri: String, pub node_name: String, pub num_children: usize, - pub name: String, - pub public_id: String, - pub system_id: String, pub attrs: Vec, - pub is_document_element: bool, - pub short_value: String, - pub incomplete_value: bool, + pub is_top_level_document: bool, } pub struct StartedTimelineMarker {