mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
This should make it somewhat easier to experiment with alternative representations in the future. To reduce churn, this commit leaves the String field public, though. Also, this will allow us to use the default String type to represent the IDL USVString type, which explicitly forbids unpaired surrogates, ans as such is a better match to the Rust String type.
581 lines
18 KiB
Rust
581 lines
18 KiB
Rust
/* 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 http://mozilla.org/MPL/2.0/. */
|
|
|
|
//! Liberally derived from the [Firefox JS implementation]
|
|
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js).
|
|
|
|
use actor::{Actor, ActorMessageStatus, ActorRegistry};
|
|
use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement, GetRootNode};
|
|
use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute};
|
|
use devtools_traits::{ComputedNodeLayout, DevtoolScriptControlMsg, NodeInfo};
|
|
use ipc_channel::ipc::{self, IpcSender};
|
|
use msg::constellation_msg::PipelineId;
|
|
use protocol::JsonPacketStream;
|
|
use rustc_serialize::json::{self, Json, ToJson};
|
|
use std::cell::RefCell;
|
|
use std::collections::BTreeMap;
|
|
use std::net::TcpStream;
|
|
use std::sync::mpsc::channel;
|
|
|
|
pub struct InspectorActor {
|
|
pub name: String,
|
|
pub walker: RefCell<Option<String>>,
|
|
pub pageStyle: RefCell<Option<String>>,
|
|
pub highlighter: RefCell<Option<String>>,
|
|
pub script_chan: IpcSender<DevtoolScriptControlMsg>,
|
|
pub pipeline: PipelineId,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct GetHighlighterReply {
|
|
highligter: HighlighterMsg, // sic.
|
|
from: String,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct HighlighterMsg {
|
|
actor: String,
|
|
}
|
|
|
|
struct HighlighterActor {
|
|
name: String,
|
|
}
|
|
|
|
pub struct NodeActor {
|
|
pub name: String,
|
|
script_chan: IpcSender<DevtoolScriptControlMsg>,
|
|
pipeline: PipelineId,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct ShowBoxModelReply {
|
|
from: String,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
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: &json::Object,
|
|
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
|
|
Ok(match msg_type {
|
|
"showBoxModel" => {
|
|
let msg = ShowBoxModelReply {
|
|
from: self.name(),
|
|
};
|
|
stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
}
|
|
|
|
"hideBoxModel" => {
|
|
let msg = HideBoxModelReply {
|
|
from: self.name(),
|
|
};
|
|
stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
}
|
|
|
|
_ => ActorMessageStatus::Ignored,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
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: &json::Object,
|
|
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
|
|
Ok(match msg_type {
|
|
"modifyAttributes" => {
|
|
let target = msg.get("to").unwrap().as_string().unwrap();
|
|
let mods = msg.get("modifications").unwrap().as_array().unwrap();
|
|
let modifications = mods.iter().map(|json_mod| {
|
|
json::decode(&json_mod.to_string()).unwrap()
|
|
}).collect();
|
|
|
|
self.script_chan.send(ModifyAttribute(self.pipeline,
|
|
registry.actor_to_script(target.to_owned()),
|
|
modifications))
|
|
.unwrap();
|
|
let reply = ModifyAttributeReply {
|
|
from: self.name(),
|
|
};
|
|
stream.write_json_packet(&reply);
|
|
ActorMessageStatus::Processed
|
|
}
|
|
|
|
_ => ActorMessageStatus::Ignored,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct GetWalkerReply {
|
|
from: String,
|
|
walker: WalkerMsg,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct WalkerMsg {
|
|
actor: String,
|
|
root: NodeActorMsg,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct AttrMsg {
|
|
namespace: String,
|
|
name: String,
|
|
value: String,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct NodeActorMsg {
|
|
actor: String,
|
|
baseURI: String,
|
|
parent: String,
|
|
nodeType: u16,
|
|
namespaceURI: String,
|
|
nodeName: String,
|
|
numChildren: usize,
|
|
|
|
name: String,
|
|
publicId: String,
|
|
systemId: String,
|
|
|
|
attrs: Vec<AttrMsg>,
|
|
|
|
pseudoClassLocks: Vec<String>,
|
|
|
|
isDisplayed: bool,
|
|
|
|
hasEventListeners: bool,
|
|
|
|
isDocumentElement: bool,
|
|
|
|
shortValue: String,
|
|
incompleteValue: bool,
|
|
}
|
|
|
|
trait NodeInfoToProtocol {
|
|
fn encode(self,
|
|
actors: &ActorRegistry,
|
|
display: bool,
|
|
script_chan: IpcSender<DevtoolScriptControlMsg>,
|
|
pipeline: PipelineId) -> NodeActorMsg;
|
|
}
|
|
|
|
impl NodeInfoToProtocol for NodeInfo {
|
|
fn encode(self,
|
|
actors: &ActorRegistry,
|
|
display: bool,
|
|
script_chan: IpcSender<DevtoolScriptControlMsg>,
|
|
pipeline: PipelineId) -> NodeActorMsg {
|
|
let actor_name = if !actors.script_actor_registered(self.uniqueId.clone()) {
|
|
let name = actors.new_name("node");
|
|
let node_actor = NodeActor {
|
|
name: name.clone(),
|
|
script_chan: script_chan,
|
|
pipeline: pipeline.clone(),
|
|
};
|
|
actors.register_script_actor(self.uniqueId, name.clone());
|
|
actors.register_later(box node_actor);
|
|
name
|
|
} else {
|
|
actors.script_to_actor(self.uniqueId)
|
|
};
|
|
|
|
NodeActorMsg {
|
|
actor: actor_name,
|
|
baseURI: self.baseURI.0,
|
|
parent: actors.script_to_actor(self.parent.clone()),
|
|
nodeType: self.nodeType,
|
|
namespaceURI: self.namespaceURI.0,
|
|
nodeName: self.nodeName.0,
|
|
numChildren: self.numChildren,
|
|
|
|
name: self.name.0,
|
|
publicId: self.publicId.0,
|
|
systemId: self.systemId.0,
|
|
|
|
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.isDocumentElement,
|
|
|
|
shortValue: self.shortValue.0,
|
|
incompleteValue: self.incompleteValue,
|
|
}
|
|
}
|
|
}
|
|
|
|
struct WalkerActor {
|
|
name: String,
|
|
script_chan: IpcSender<DevtoolScriptControlMsg>,
|
|
pipeline: PipelineId,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct QuerySelectorReply {
|
|
from: String,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct DocumentElementReply {
|
|
from: String,
|
|
node: NodeActorMsg,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct ClearPseudoclassesReply {
|
|
from: String,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct ChildrenReply {
|
|
hasFirst: bool,
|
|
hasLast: bool,
|
|
nodes: Vec<NodeActorMsg>,
|
|
from: String,
|
|
}
|
|
|
|
impl Actor for WalkerActor {
|
|
fn name(&self) -> String {
|
|
self.name.clone()
|
|
}
|
|
|
|
fn handle_message(&self,
|
|
registry: &ActorRegistry,
|
|
msg_type: &str,
|
|
msg: &json::Object,
|
|
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
|
|
Ok(match msg_type {
|
|
"querySelector" => {
|
|
let msg = QuerySelectorReply {
|
|
from: self.name(),
|
|
};
|
|
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();
|
|
let node = doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline);
|
|
|
|
let msg = DocumentElementReply {
|
|
from: self.name(),
|
|
node: node,
|
|
};
|
|
stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
}
|
|
|
|
"clearPseudoClassLocks" => {
|
|
let msg = ClearPseudoclassesReply {
|
|
from: self.name(),
|
|
};
|
|
stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
}
|
|
|
|
"children" => {
|
|
let target = msg.get("node").unwrap().as_string().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();
|
|
|
|
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(),
|
|
};
|
|
stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
}
|
|
|
|
_ => ActorMessageStatus::Ignored,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct GetPageStyleReply {
|
|
from: String,
|
|
pageStyle: PageStyleMsg,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct PageStyleMsg {
|
|
actor: String,
|
|
}
|
|
|
|
struct PageStyleActor {
|
|
name: String,
|
|
script_chan: IpcSender<DevtoolScriptControlMsg>,
|
|
pipeline: PipelineId,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct GetAppliedReply {
|
|
entries: Vec<AppliedEntry>,
|
|
rules: Vec<AppliedRule>,
|
|
sheets: Vec<AppliedSheet>,
|
|
from: String,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct GetComputedReply {
|
|
computed: Vec<u32>, //XXX all css props
|
|
from: String,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct AppliedEntry {
|
|
rule: String,
|
|
pseudoElement: Json,
|
|
isSystem: bool,
|
|
matchedSelectors: Vec<String>,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct AppliedRule {
|
|
actor: String,
|
|
__type__: u32,
|
|
href: String,
|
|
cssText: String,
|
|
line: u32,
|
|
column: u32,
|
|
parentStyleSheet: String,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct AppliedSheet {
|
|
actor: String,
|
|
href: String,
|
|
nodeHref: String,
|
|
disabled: bool,
|
|
title: String,
|
|
system: bool,
|
|
styleSheetIndex: isize,
|
|
ruleCount: usize,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
struct GetLayoutReply {
|
|
width: i32,
|
|
height: i32,
|
|
autoMargins: Json,
|
|
from: String,
|
|
}
|
|
|
|
#[derive(RustcEncodable)]
|
|
#[allow(dead_code)]
|
|
struct AutoMargins {
|
|
top: String,
|
|
bottom: String,
|
|
left: String,
|
|
right: String,
|
|
}
|
|
|
|
impl Actor for PageStyleActor {
|
|
fn name(&self) -> String {
|
|
self.name.clone()
|
|
}
|
|
|
|
fn handle_message(&self,
|
|
registry: &ActorRegistry,
|
|
msg_type: &str,
|
|
msg: &json::Object,
|
|
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
|
|
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(),
|
|
};
|
|
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(),
|
|
};
|
|
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_string().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 { width, height } = rx.recv().unwrap();
|
|
|
|
let auto_margins = msg.get("autoMargins")
|
|
.and_then(&Json::as_boolean).unwrap_or(false);
|
|
|
|
//TODO: the remaining layout properties (margin, border, padding, position)
|
|
// as specified in getLayout in
|
|
// http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
|
|
let msg = GetLayoutReply {
|
|
width: width.round() as i32,
|
|
height: height.round() as i32,
|
|
autoMargins: if auto_margins {
|
|
//TODO: real values like processMargins in
|
|
// http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
|
|
let mut m = BTreeMap::new();
|
|
m.insert("top".to_owned(), "auto".to_owned().to_json());
|
|
m.insert("bottom".to_owned(), "auto".to_owned().to_json());
|
|
m.insert("left".to_owned(), "auto".to_owned().to_json());
|
|
m.insert("right".to_owned(), "auto".to_owned().to_json());
|
|
Json::Object(m)
|
|
} else {
|
|
Json::Null
|
|
},
|
|
from: self.name(),
|
|
};
|
|
stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
}
|
|
|
|
_ => ActorMessageStatus::Ignored,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Actor for InspectorActor {
|
|
fn name(&self) -> String {
|
|
self.name.clone()
|
|
}
|
|
|
|
fn handle_message(&self,
|
|
registry: &ActorRegistry,
|
|
msg_type: &str,
|
|
_msg: &json::Object,
|
|
stream: &mut TcpStream) -> Result<ActorMessageStatus, ()> {
|
|
Ok(match msg_type {
|
|
"getWalker" => {
|
|
if self.walker.borrow().is_none() {
|
|
let walker = WalkerActor {
|
|
name: registry.new_name("walker"),
|
|
script_chan: self.script_chan.clone(),
|
|
pipeline: self.pipeline,
|
|
};
|
|
let mut walker_name = self.walker.borrow_mut();
|
|
*walker_name = Some(walker.name());
|
|
registry.register_later(box walker);
|
|
}
|
|
|
|
let (tx, rx) = ipc::channel().unwrap();
|
|
self.script_chan.send(GetRootNode(self.pipeline, tx)).unwrap();
|
|
let root_info = rx.recv().unwrap();
|
|
|
|
let node = root_info.encode(registry, false, self.script_chan.clone(), self.pipeline);
|
|
|
|
let msg = GetWalkerReply {
|
|
from: self.name(),
|
|
walker: WalkerMsg {
|
|
actor: self.walker.borrow().clone().unwrap(),
|
|
root: node,
|
|
}
|
|
};
|
|
stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
}
|
|
|
|
"getPageStyle" => {
|
|
if self.pageStyle.borrow().is_none() {
|
|
let style = PageStyleActor {
|
|
name: registry.new_name("pageStyle"),
|
|
script_chan: self.script_chan.clone(),
|
|
pipeline: self.pipeline,
|
|
};
|
|
let mut pageStyle = self.pageStyle.borrow_mut();
|
|
*pageStyle = Some(style.name());
|
|
registry.register_later(box style);
|
|
}
|
|
|
|
let msg = GetPageStyleReply {
|
|
from: self.name(),
|
|
pageStyle: PageStyleMsg {
|
|
actor: self.pageStyle.borrow().clone().unwrap(),
|
|
},
|
|
};
|
|
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" => {
|
|
if self.highlighter.borrow().is_none() {
|
|
let highlighter_actor = HighlighterActor {
|
|
name: registry.new_name("highlighter"),
|
|
};
|
|
let mut highlighter = self.highlighter.borrow_mut();
|
|
*highlighter = Some(highlighter_actor.name());
|
|
registry.register_later(box highlighter_actor);
|
|
}
|
|
|
|
let msg = GetHighlighterReply {
|
|
from: self.name(),
|
|
highligter: HighlighterMsg {
|
|
actor: self.highlighter.borrow().clone().unwrap(),
|
|
},
|
|
};
|
|
stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
}
|
|
|
|
_ => ActorMessageStatus::Ignored,
|
|
})
|
|
}
|
|
}
|