diff options
-rw-r--r-- | components/devtools/actor.rs | 29 | ||||
-rw-r--r-- | components/devtools/actors/inspector.rs | 172 | ||||
-rw-r--r-- | components/devtools/lib.rs | 4 | ||||
-rw-r--r-- | components/devtools_traits/lib.rs | 30 | ||||
-rw-r--r-- | components/script/dom/attr.rs | 10 | ||||
-rw-r--r-- | components/script/dom/node.rs | 63 | ||||
-rw-r--r-- | components/script/lib.rs | 1 | ||||
-rw-r--r-- | components/script/script_task.rs | 50 |
8 files changed, 265 insertions, 94 deletions
diff --git a/components/devtools/actor.rs b/components/devtools/actor.rs index f489e7facf4..fae6c8864cf 100644 --- a/components/devtools/actor.rs +++ b/components/devtools/actor.rs @@ -71,6 +71,7 @@ impl<'a> AnyRefExt<'a> for &'a Actor { pub struct ActorRegistry { actors: HashMap<String, Box<Actor+Send+Sized>>, new_actors: RefCell<Vec<Box<Actor+Send+Sized>>>, + script_actors: RefCell<HashMap<String, String>>, next: Cell<u32>, } @@ -80,10 +81,38 @@ impl ActorRegistry { ActorRegistry { actors: HashMap::new(), new_actors: RefCell::new(vec!()), + script_actors: RefCell::new(HashMap::new()), next: Cell::new(0), } } + pub fn register_script_actor(&self, script_id: String, actor: String) { + println!("registering {:s} ({:s})", actor.as_slice(), script_id.as_slice()); + let mut script_actors = self.script_actors.borrow_mut(); + script_actors.insert(script_id, actor); + } + + pub fn script_to_actor(&self, script_id: String) -> String { + if script_id.as_slice() == "" { + return "".to_string(); + } + self.script_actors.borrow().find(&script_id).unwrap().to_string() + } + + pub fn script_actor_registered(&self, script_id: String) -> bool { + self.script_actors.borrow().contains_key(&script_id) + } + + pub fn actor_to_script(&self, actor: String) -> String { + for (key, value) in self.script_actors.borrow().iter() { + println!("checking {:s}", value.as_slice()); + if value.as_slice() == actor.as_slice() { + return key.to_string(); + } + } + fail!("couldn't find actor named {:s}", actor) + } + /// Create a unique name based on a monotonically increasing suffix pub fn new_name(&self, prefix: &str) -> String { let suffix = self.next.get(); diff --git a/components/devtools/actors/inspector.rs b/components/devtools/actors/inspector.rs index 2bf0b0671d3..ac5608ed63a 100644 --- a/components/devtools/actors/inspector.rs +++ b/components/devtools/actors/inspector.rs @@ -4,9 +4,13 @@ /// Liberally derived from the [Firefox JS implementation](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js). +use devtools_traits::{GetRootNode, GetDocumentElement, GetChildren, DevtoolScriptControlMsg}; +use devtools_traits::NodeInfo; + use actor::{Actor, ActorRegistry}; use protocol::JsonPacketSender; +use servo_msg::constellation_msg::PipelineId; use serialize::json; use std::cell::RefCell; use std::io::TcpStream; @@ -16,6 +20,8 @@ pub struct InspectorActor { pub walker: RefCell<Option<String>>, pub pageStyle: RefCell<Option<String>>, pub highlighter: RefCell<Option<String>>, + pub script_chan: Sender<DevtoolScriptControlMsg>, + pub pipeline: PipelineId, } #[deriving(Encodable)] @@ -122,8 +128,59 @@ struct NodeActorMsg { incompleteValue: bool, } +trait NodeInfoToProtocol { + fn encode(self, actors: &ActorRegistry, display: bool) -> NodeActorMsg; +} + +impl NodeInfoToProtocol for NodeInfo { + fn encode(self, actors: &ActorRegistry, display: bool) -> NodeActorMsg { + let actor_name = if !actors.script_actor_registered(self.uniqueId.clone()) { + let name = actors.new_name("node"); + actors.register_script_actor(self.uniqueId, name.clone()); + name + } else { + actors.script_to_actor(self.uniqueId) + }; + + NodeActorMsg { + actor: actor_name, + baseURI: self.baseURI, + parent: actors.script_to_actor(self.parent.clone()), + nodeType: self.nodeType, + namespaceURI: self.namespaceURI, + nodeName: self.nodeName, + numChildren: self.numChildren, + + name: self.name, + publicId: self.publicId, + systemId: self.systemId, + + attrs: self.attrs.move_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, + incompleteValue: self.incompleteValue, + } + } +} + struct WalkerActor { name: String, + script_chan: Sender<DevtoolScriptControlMsg>, + pipeline: PipelineId, } #[deriving(Encodable)] @@ -156,9 +213,9 @@ impl Actor for WalkerActor { } fn handle_message(&self, - _registry: &ActorRegistry, + registry: &ActorRegistry, msg_type: &String, - _msg: &json::Object, + msg: &json::Object, stream: &mut TcpStream) -> bool { match msg_type.as_slice() { "querySelector" => { @@ -170,38 +227,15 @@ impl Actor for WalkerActor { } "documentElement" => { - let msg = DocumentElementReply { - from: self.name(), - node: NodeActorMsg { - actor: "node0".to_string(), - baseURI: "".to_string(), - parent: "".to_string(), - nodeType: 1, //ELEMENT_NODE - namespaceURI: "".to_string(), - nodeName: "html".to_string(), - numChildren: 0, - - name: "".to_string(), - publicId: "".to_string(), - systemId: "".to_string(), - - attrs: vec!(AttrMsg { - namespace: "".to_string(), - name: "manifest".to_string(), - value: "foo.manifest".to_string(), - }), - - pseudoClassLocks: vec!(), + let (tx, rx) = channel(); + self.script_chan.send(GetDocumentElement(self.pipeline, tx)); + let doc_elem_info = rx.recv(); - isDisplayed: true, + let node = doc_elem_info.encode(registry, true); - hasEventListeners: false, - - isDocumentElement: true, - - shortValue: "".to_string(), - incompleteValue: false, - } + let msg = DocumentElementReply { + from: self.name(), + node: node, }; stream.write_json_packet(&msg); true @@ -216,10 +250,19 @@ impl Actor for WalkerActor { } "children" => { + let target = msg.find(&"node".to_string()).unwrap().as_string().unwrap(); + let (tx, rx) = channel(); + self.script_chan.send(GetChildren(self.pipeline, + registry.actor_to_script(target.to_string()), + tx)); + let children = rx.recv(); + let msg = ChildrenReply { hasFirst: true, hasLast: true, - nodes: vec!(), + nodes: children.move_iter().map(|child| { + child.encode(registry, true) + }).collect(), from: self.name(), }; stream.write_json_packet(&msg); @@ -231,26 +274,6 @@ impl Actor for WalkerActor { } } -struct NodeActor { - name: String, -} - -impl Actor for NodeActor { - fn name(&self) -> String { - self.name.clone() - } - - fn handle_message(&self, - _registry: &ActorRegistry, - msg_type: &String, - _msg: &json::Object, - _stream: &mut TcpStream) -> bool { - match msg_type.as_slice() { - _ => false, - } - } -} - #[deriving(Encodable)] struct GetPageStyleReply { from: String, @@ -367,50 +390,19 @@ impl Actor for InspectorActor { 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 node = NodeActor { - name: registry.new_name("node"), - }; - let node_actor_name = node.name(); - registry.register_later(box node); - - //TODO: query script for actual root node - //TODO: extra node actor creation - let node = NodeActorMsg { - actor: node_actor_name, - baseURI: "".to_string(), - parent: "".to_string(), - nodeType: 1, //ELEMENT_NODE - namespaceURI: "".to_string(), - nodeName: "html".to_string(), - numChildren: 1, - - name: "".to_string(), - publicId: "".to_string(), - systemId: "".to_string(), + let (tx, rx) = channel(); + self.script_chan.send(GetRootNode(self.pipeline, tx)); + let root_info = rx.recv(); - attrs: vec!(AttrMsg { - namespace: "".to_string(), - name: "manifest".to_string(), - value: "foo.manifest".to_string(), - }), - - pseudoClassLocks: vec!(), - - isDisplayed: true, - - hasEventListeners: false, - - isDocumentElement: true, - - shortValue: "".to_string(), - incompleteValue: false, - }; + let node = root_info.encode(registry, false); let msg = GetWalkerReply { from: self.name(), diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 5dbce103546..71fd82f5369 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -142,7 +142,7 @@ fn run_server(port: Receiver<DevtoolsControlMsg>) { let (tab, console, inspector) = { let console = ConsoleActor { name: actors.new_name("console"), - script_chan: sender, + script_chan: sender.clone(), pipeline: pipeline, }; let inspector = InspectorActor { @@ -150,6 +150,8 @@ fn run_server(port: Receiver<DevtoolsControlMsg>) { walker: RefCell::new(None), pageStyle: RefCell::new(None), highlighter: RefCell::new(None), + script_chan: sender, + pipeline: pipeline, }; //TODO: send along the current page title and URL let tab = TabActor { diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs index 904f4994fe5..933b9c9f283 100644 --- a/components/devtools_traits/lib.rs +++ b/components/devtools_traits/lib.rs @@ -37,9 +37,39 @@ pub enum EvaluateJSReply { ActorValue(String), } +pub struct AttrInfo { + pub namespace: String, + pub name: String, + pub value: String, +} + +pub struct NodeInfo { + pub uniqueId: String, + pub baseURI: String, + pub parent: String, + pub nodeType: uint, + pub namespaceURI: String, + pub nodeName: String, + pub numChildren: uint, + + pub name: String, + pub publicId: String, + pub systemId: String, + + pub attrs: Vec<AttrInfo>, + + pub isDocumentElement: bool, + + pub shortValue: String, + pub incompleteValue: bool, +} + /// Messages to process in a particular script task, as instructed by a devtools client. pub enum DevtoolScriptControlMsg { EvaluateJS(PipelineId, String, Sender<EvaluateJSReply>), + GetRootNode(PipelineId, Sender<NodeInfo>), + GetDocumentElement(PipelineId, Sender<NodeInfo>), + GetChildren(PipelineId, String, Sender<Vec<NodeInfo>>), } /// Messages to instruct devtools server to update its state relating to a particular diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs index be419eb2a61..ebad173edda 100644 --- a/components/script/dom/attr.rs +++ b/components/script/dom/attr.rs @@ -2,6 +2,7 @@ * 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/. */ +use devtools_traits::AttrInfo; use dom::bindings::codegen::Bindings::AttrBinding; use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::InheritTypes::NodeCast; @@ -149,6 +150,7 @@ pub trait AttrHelpers { fn set_value(&self, set_type: AttrSettingType, value: AttrValue); fn value<'a>(&'a self) -> Ref<'a, AttrValue>; fn local_name<'a>(&'a self) -> &'a Atom; + fn summarize(&self) -> AttrInfo; } impl<'a> AttrHelpers for JSRef<'a, Attr> { @@ -184,6 +186,14 @@ impl<'a> AttrHelpers for JSRef<'a, Attr> { fn local_name<'a>(&'a self) -> &'a Atom { &self.local_name } + + fn summarize(&self) -> AttrInfo { + AttrInfo { + namespace: self.GetNamespaceURI().unwrap_or("".to_string()), + name: self.Name(), + value: self.Value(), + } + } } pub trait AttrHelpersForLayout { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 50af3e637ae..e58f0e888a6 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -4,12 +4,15 @@ //! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements. +use devtools_traits::NodeInfo; use dom::attr::{Attr, AttrHelpers}; use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; +use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods; use dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods}; +use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods; use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTypeCast}; use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, NodeCast, ElementDerived}; @@ -36,7 +39,7 @@ use dom::element::{HTMLInputElementTypeId, HTMLSelectElementTypeId}; use dom::element::{HTMLTextAreaElementTypeId, HTMLOptGroupElementTypeId}; use dom::element::{HTMLOptionElementTypeId, HTMLFieldSetElementTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; -use dom::nodelist::{NodeList}; +use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; @@ -59,6 +62,7 @@ use std::mem; use style; use style::ComputedValues; use sync::Arc; +use uuid; use serialize::{Encoder, Encodable}; @@ -105,6 +109,8 @@ pub struct Node { /// Must be sent back to the layout task to be destroyed when this /// node is finalized. pub layout_data: LayoutDataRef, + + unique_id: RefCell<String>, } impl<S: Encoder<E>, E> Encodable<S, E> for LayoutDataRef { @@ -419,6 +425,9 @@ pub trait NodeHelpers<'m, 'n> { fn query_selector_all(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>>; fn remove_self(&self); + + fn get_unique_id(&self) -> String; + fn summarize(&self) -> NodeInfo; } impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> { @@ -687,6 +696,56 @@ impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> { None => () } } + + fn get_unique_id(&self) -> String { + self.unique_id.borrow().clone() + } + + fn summarize(&self) -> NodeInfo { + if self.unique_id.borrow().as_slice() == "" { + let mut unique_id = self.unique_id.borrow_mut(); + *unique_id = uuid::Uuid::new_v4().to_simple_str(); + } + + NodeInfo { + uniqueId: self.unique_id.borrow().clone(), + baseURI: self.GetBaseURI().unwrap_or("".to_string()), + parent: self.GetParentNode().root().map(|node| node.unique_id.borrow().clone()).unwrap_or("".to_string()), + nodeType: self.NodeType() as uint, + namespaceURI: "".to_string(), //FIXME + nodeName: self.NodeName(), + numChildren: self.ChildNodes().root().Length() as uint, + + //FIXME doctype nodes only + name: "".to_string(), + publicId: "".to_string(), + systemId: "".to_string(), + + attrs: if self.is_element() { + let mut summarized = vec!(); + let elem: &JSRef<Element> = ElementCast::to_ref(self).unwrap(); + let attrs = elem.Attributes().root(); + let mut i = 0; + while i < attrs.Length() { + let attr = attrs.Item(i).unwrap().root(); + summarized.push(attr.summarize()); + i += 1; + } + summarized + } else { + vec!() + }, + + isDocumentElement: + self.owner_doc().root() + .GetDocumentElement() + .map(|elem| NodeCast::from_ref(&*elem.root()) == self) + .unwrap_or(false), + + shortValue: self.GetNodeValue().unwrap_or("".to_string()), //FIXME: truncate + incompleteValue: false, //FIXME: reflect truncation + } + } } /// If the given untrusted node address represents a valid DOM node in the given runtime, @@ -991,6 +1050,8 @@ impl Node { flags: Traceable::new(RefCell::new(NodeFlags::new(type_id))), layout_data: LayoutDataRef::new(), + + unique_id: RefCell::new("".to_string()), } } diff --git a/components/script/lib.rs b/components/script/lib.rs index d7c13cc06a1..4318f4b3f21 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -40,6 +40,7 @@ extern crate style; extern crate sync; extern crate servo_msg = "msg"; extern crate url; +extern crate uuid; pub mod cors; diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 2feb088201c..e49350a1942 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -5,6 +5,7 @@ //! The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing //! and layout tasks. +use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, EventCast}; use dom::bindings::conversions; use dom::bindings::conversions::{FromJSValConvertible, Empty}; @@ -33,8 +34,9 @@ use layout_interface; use page::{Page, IterablePage, Frame}; use devtools_traits; -use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, NewGlobal}; -use devtools_traits::{DevtoolScriptControlMsg, EvaluateJS, EvaluateJSReply}; +use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, NewGlobal, NodeInfo, GetRootNode}; +use devtools_traits::{DevtoolScriptControlMsg, EvaluateJS, EvaluateJSReply, GetDocumentElement}; +use devtools_traits::{GetChildren}; use script_traits::{CompositorEvent, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent}; use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, ScriptTaskFactory}; use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, SendEventMsg, ResizeInactiveMsg}; @@ -501,6 +503,9 @@ impl ScriptTask { FromScript(WorkerPostMessage(addr, data, nbytes)) => Worker::handle_message(addr, data, nbytes), FromScript(WorkerRelease(addr)) => Worker::handle_release(addr), FromDevtools(EvaluateJS(id, s, reply)) => self.handle_evaluate_js(id, s, reply), + FromDevtools(GetRootNode(id, reply)) => self.handle_get_root_node(id, reply), + FromDevtools(GetDocumentElement(id, reply)) => self.handle_get_document_element(id, reply), + FromDevtools(GetChildren(id, node_id, reply)) => self.handle_get_children(id, node_id, reply), } } @@ -530,6 +535,47 @@ impl ScriptTask { }); } + fn handle_get_root_node(&self, pipeline: PipelineId, reply: Sender<NodeInfo>) { + let page = get_page(&*self.page.borrow(), pipeline); + let frame = page.frame(); + let document = frame.get_ref().document.root(); + + let node: &JSRef<Node> = NodeCast::from_ref(&*document); + reply.send(node.summarize()); + } + + fn handle_get_document_element(&self, pipeline: PipelineId, reply: Sender<NodeInfo>) { + let page = get_page(&*self.page.borrow(), pipeline); + let frame = page.frame(); + let document = frame.get_ref().document.root(); + let document_element = document.GetDocumentElement().root().unwrap(); + + let node: &JSRef<Node> = NodeCast::from_ref(&*document_element); + reply.send(node.summarize()); + } + + fn handle_get_children(&self, pipeline: PipelineId, node_id: String, reply: Sender<Vec<NodeInfo>>) { + let page = get_page(&*self.page.borrow(), pipeline); + let frame = page.frame(); + let document = frame.get_ref().document.root(); + let node: &JSRef<Node> = NodeCast::from_ref(&*document); + + let mut children = vec!(); + let mut found_parent = false; + for candidate in node.traverse_preorder() { + if candidate.get_unique_id().as_slice() == node_id.as_slice() { + found_parent = true; + for kid in candidate.children() { + children.push(kid.summarize()); + } + break; + } + } + + assert!(found_parent); + reply.send(children); + } + fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) { debug!("Script: new layout: {:?}", new_layout_info); let NewLayoutInfo { |