diff options
-rw-r--r-- | components/devtools/actors/inspector.rs | 73 | ||||
-rw-r--r-- | components/devtools_traits/lib.rs | 23 | ||||
-rw-r--r-- | components/script/devtools.rs | 106 | ||||
-rw-r--r-- | components/script/lib.rs | 1 | ||||
-rw-r--r-- | components/script/script_task.rs | 96 | ||||
-rw-r--r-- | tests/wpt/metadata/dom/traversal/NodeFilter-constants.html.ini | 2 |
6 files changed, 208 insertions, 93 deletions
diff --git a/components/devtools/actors/inspector.rs b/components/devtools/actors/inspector.rs index ef5cc400a33..01c6498ef1e 100644 --- a/components/devtools/actors/inspector.rs +++ b/components/devtools/actors/inspector.rs @@ -5,7 +5,7 @@ /// 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::{GetLayout, NodeInfo}; +use devtools_traits::{GetLayout, NodeInfo, ModifyAttribute}; use actor::{Actor, ActorRegistry}; use protocol::JsonPacketStream; @@ -41,6 +41,12 @@ struct HighlighterActor { name: String, } +pub struct NodeActor { + pub name: String, + script_chan: Sender<DevtoolScriptControlMsg>, + pipeline: PipelineId, +} + #[deriving(Encodable)] struct ShowBoxModelReply { from: String, @@ -52,7 +58,7 @@ struct HideBoxModelReply { } impl Actor for HighlighterActor { - fn name(&self) -> String { + fn name(&self) -> String { self.name.clone() } @@ -84,6 +90,44 @@ impl Actor for HighlighterActor { } #[deriving(Encodable)] +struct ModifyAttributeReply{ + from: String, +} + +impl Actor for NodeActor { + fn name(&self) -> String { + self.name.clone() + } + + fn handle_message(&self, + registry: &ActorRegistry, + msg_type: &String, + msg: &json::JsonObject, + stream: &mut TcpStream) -> bool { + match msg_type.as_slice() { + "modifyAttributes" => { + let target = msg.get(&"to".to_string()).unwrap().as_string().unwrap(); + let mods = msg.get(&"modifications".to_string()).unwrap().as_list().unwrap(); + let modifications = mods.iter().map(|json_mod| { + json::decode(json_mod.to_string().as_slice()).unwrap() + }).collect(); + + self.script_chan.send(ModifyAttribute(self.pipeline, + registry.actor_to_script(target.to_string()), + modifications)); + let reply = ModifyAttributeReply{ + from: self.name(), + }; + stream.write_json_packet(&reply); + true + } + + _ => false, + } + } +} + +#[deriving(Encodable)] struct GetWalkerReply { from: String, walker: WalkerMsg, @@ -131,14 +175,28 @@ struct NodeActorMsg { } trait NodeInfoToProtocol { - fn encode(self, actors: &ActorRegistry, display: bool) -> NodeActorMsg; + fn encode(self, + actors: &ActorRegistry, + display: bool, + script_chan: Sender<DevtoolScriptControlMsg>, + pipeline: PipelineId) -> NodeActorMsg; } impl NodeInfoToProtocol for NodeInfo { - fn encode(self, actors: &ActorRegistry, display: bool) -> NodeActorMsg { + fn encode(self, + actors: &ActorRegistry, + display: bool, + script_chan: Sender<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) @@ -232,8 +290,7 @@ impl Actor for WalkerActor { let (tx, rx) = channel(); self.script_chan.send(GetDocumentElement(self.pipeline, tx)); let doc_elem_info = rx.recv(); - - let node = doc_elem_info.encode(registry, true); + let node = doc_elem_info.encode(registry, true, self.script_chan.clone(), self.pipeline); let msg = DocumentElementReply { from: self.name(), @@ -263,7 +320,7 @@ impl Actor for WalkerActor { hasFirst: true, hasLast: true, nodes: children.into_iter().map(|child| { - child.encode(registry, true) + child.encode(registry, true, self.script_chan.clone(), self.pipeline) }).collect(), from: self.name(), }; @@ -453,7 +510,7 @@ impl Actor for InspectorActor { self.script_chan.send(GetRootNode(self.pipeline, tx)); let root_info = rx.recv(); - let node = root_info.encode(registry, false); + let node = root_info.encode(registry, false, self.script_chan.clone(), self.pipeline); let msg = GetWalkerReply { from: self.name(), diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs index 8a6406c0e61..5c437bd6e56 100644 --- a/components/devtools_traits/lib.rs +++ b/components/devtools_traits/lib.rs @@ -11,11 +11,13 @@ #![allow(non_snake_case)] extern crate "msg" as servo_msg; +extern crate serialize; /// This module contains shared types and messages for use by devtools/script. /// The traits are here instead of in script so that the devtools crate can be /// modified independently of the rest of Servo. +use serialize::{Decodable, Decoder}; use servo_msg::constellation_msg::PipelineId; pub type DevtoolsControlChan = Sender<DevtoolsControlMsg>; @@ -73,6 +75,7 @@ pub enum DevtoolScriptControlMsg { GetDocumentElement(PipelineId, Sender<NodeInfo>), GetChildren(PipelineId, String, Sender<Vec<NodeInfo>>), GetLayout(PipelineId, String, Sender<(f32, f32)>), + ModifyAttribute(PipelineId, String, Vec<Modification>), } /// Messages to instruct devtools server to update its state relating to a particular @@ -81,3 +84,23 @@ pub enum ScriptDevtoolControlMsg { /// Report a new JS error message ReportConsoleMsg(String), } + +#[deriving(Encodable)] +pub struct Modification{ + pub attributeName: String, + pub newValue: Option<String>, +} + +impl<D:Decoder<E>, E> Decodable<D, E> for Modification { + fn decode(d: &mut D) -> Result<Modification, E> { + d.read_struct("Modification", 2u, |d| + Ok(Modification { + attributeName: try!(d.read_struct_field("attributeName", 0u, |d| Decodable::decode(d))), + newValue: match d.read_struct_field("newValue", 1u, |d| Decodable::decode(d)) { + Ok(opt) => opt, + Err(_) => None + } + }) + ) + } +} diff --git a/components/script/devtools.rs b/components/script/devtools.rs new file mode 100644 index 00000000000..8d7a5d35bad --- /dev/null +++ b/components/script/devtools.rs @@ -0,0 +1,106 @@ +/* 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/. */ + +use devtools_traits; +use devtools_traits::{EvaluateJSReply, NodeInfo, Modification}; +use dom::bindings::conversions; +use dom::bindings::conversions::FromJSValConvertible; +use dom::bindings::js::{JSRef, Temporary, OptionalRootable}; +use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast}; +use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; +use dom::bindings::codegen::Bindings::DOMRectBinding::{DOMRectMethods}; +use dom::bindings::codegen::Bindings::ElementBinding::{ElementMethods}; +use dom::node::{Node, NodeHelpers}; +use dom::window::{WindowHelpers}; +use dom::element::Element; +use dom::document::DocumentHelpers; +use page::Page; +use servo_msg::constellation_msg::PipelineId; +use script_task::get_page; +use std::rc::Rc; + + +pub fn handle_evaluate_js(page: &Rc<Page>, pipeline: PipelineId, eval: String, reply: Sender<EvaluateJSReply>){ + let page = get_page(&*page, pipeline); + let frame = page.frame(); + let window = frame.as_ref().unwrap().window.root(); + let cx = window.get_cx(); + let rval = window.evaluate_js_with_result(eval.as_slice()); + + reply.send(if rval.is_undefined() { + devtools_traits::VoidValue + } else if rval.is_boolean() { + devtools_traits::BooleanValue(rval.to_boolean()) + } else if rval.is_double() { + devtools_traits::NumberValue(FromJSValConvertible::from_jsval(cx, rval, ()).unwrap()) + } else if rval.is_string() { + //FIXME: use jsstring_to_str when jsval grows to_jsstring + devtools_traits::StringValue(FromJSValConvertible::from_jsval(cx, rval, conversions::Default).unwrap()) + } else { + //FIXME: jsvals don't have an is_int32/is_number yet + assert!(rval.is_object_or_null()); + panic!("object values unimplemented") + }); +} + +pub fn handle_get_root_node(page: &Rc<Page>, pipeline: PipelineId, reply: Sender<NodeInfo>) { + let page = get_page(&*page, pipeline); + let frame = page.frame(); + let document = frame.as_ref().unwrap().document.root(); + + let node: JSRef<Node> = NodeCast::from_ref(*document); + reply.send(node.summarize()); +} + +pub fn handle_get_document_element(page: &Rc<Page>, pipeline: PipelineId, reply: Sender<NodeInfo>) { + let page = get_page(&*page, pipeline); + let frame = page.frame(); + let document = frame.as_ref().unwrap().document.root(); + let document_element = document.GetDocumentElement().root().unwrap(); + + let node: JSRef<Node> = NodeCast::from_ref(*document_element); + reply.send(node.summarize()); +} + +fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String) -> Temporary<Node> { + let page = get_page(&*page, pipeline); + let frame = page.frame(); + let document = frame.as_ref().unwrap().document.root(); + let node: JSRef<Node> = NodeCast::from_ref(*document); + + for candidate in node.traverse_preorder() { + if candidate.get_unique_id().as_slice() == node_id.as_slice() { + return Temporary::from_rooted(candidate); + } + } + + panic!("couldn't find node with unique id {:s}", node_id) +} + +pub fn handle_get_children(page: &Rc<Page>, pipeline: PipelineId, node_id: String, reply: Sender<Vec<NodeInfo>>) { + let parent = find_node_by_unique_id(&*page, pipeline, node_id).root(); + let children = parent.children().map(|child| child.summarize()).collect(); + reply.send(children); +} + +pub fn handle_get_layout(page: &Rc<Page>, pipeline: PipelineId, node_id: String, reply: Sender<(f32, f32)>) { + let node = find_node_by_unique_id(&*page, pipeline, node_id).root(); + let elem: JSRef<Element> = ElementCast::to_ref(*node).expect("should be getting layout of element"); + let rect = elem.GetBoundingClientRect().root(); + reply.send((rect.Width(), rect.Height())); +} + +pub fn handle_modify_attribute(page: &Rc<Page>, pipeline: PipelineId, node_id: String, modifications: Vec<Modification>) { + let node = find_node_by_unique_id(&*page, pipeline, node_id).root(); + let elem: JSRef<Element> = ElementCast::to_ref(*node).expect("should be getting layout of element"); + + for modification in modifications.iter(){ + match modification.newValue { + Some(ref string) => { + let _ = elem.SetAttribute(modification.attributeName.clone(), string.clone()); + }, + None => elem.RemoveAttribute(modification.attributeName.clone()), + } + } +} diff --git a/components/script/lib.rs b/components/script/lib.rs index 1e524ec7a62..3470cd63652 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -223,3 +223,4 @@ pub mod page; pub mod script_task; mod timers; pub mod textinput; +mod devtools; diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 35d51589d85..fdd4795ab25 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -7,13 +7,10 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyStateValues}; -use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods; -use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, EventCast, ElementCast}; -use dom::bindings::conversions; +use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, EventCast}; use dom::bindings::conversions::{FromJSValConvertible, Empty}; use dom::bindings::global; use dom::bindings::js::{JS, JSRef, RootCollection, Temporary, OptionalRootable}; @@ -36,11 +33,11 @@ use layout_interface::{ScriptLayoutChan, LayoutChan, NoQuery, ReflowForDisplay}; use layout_interface; use page::{Page, IterablePage, Frame}; use timers::TimerId; +use devtools; -use devtools_traits; -use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, NewGlobal, NodeInfo, GetRootNode}; -use devtools_traits::{DevtoolScriptControlMsg, EvaluateJS, EvaluateJSReply, GetDocumentElement}; -use devtools_traits::{GetChildren, GetLayout}; +use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, NewGlobal, GetRootNode}; +use devtools_traits::{DevtoolScriptControlMsg, EvaluateJS, GetDocumentElement}; +use devtools_traits::{GetChildren, GetLayout, ModifyAttribute}; use script_traits::{CompositorEvent, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent}; use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, ScriptTaskFactory}; use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, ViewportMsg, SendEventMsg}; @@ -553,11 +550,12 @@ impl ScriptTask { FromScript(DOMMessage(..)) => panic!("unexpected message"), 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), - FromDevtools(GetLayout(id, node_id, reply)) => self.handle_get_layout(id, node_id, reply), + FromDevtools(EvaluateJS(id, s, reply)) => devtools::handle_evaluate_js(&*self.page.borrow(), id, s, reply), + FromDevtools(GetRootNode(id, reply)) => devtools::handle_get_root_node(&*self.page.borrow(), id, reply), + FromDevtools(GetDocumentElement(id, reply)) => devtools::handle_get_document_element(&*self.page.borrow(), id, reply), + FromDevtools(GetChildren(id, node_id, reply)) => devtools::handle_get_children(&*self.page.borrow(), id, node_id, reply), + FromDevtools(GetLayout(id, node_id, reply)) => devtools::handle_get_layout(&*self.page.borrow(), id, node_id, reply), + FromDevtools(ModifyAttribute(id, node_id, modifications)) => devtools::handle_modify_attribute(&*self.page.borrow(), id, node_id, modifications), } } @@ -569,76 +567,6 @@ impl ScriptTask { true } - fn handle_evaluate_js(&self, pipeline: PipelineId, eval: String, reply: Sender<EvaluateJSReply>) { - let page = get_page(&*self.page.borrow(), pipeline); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); - let cx = window.get_cx(); - let rval = window.evaluate_js_with_result(eval.as_slice()); - - reply.send(if rval.is_undefined() { - devtools_traits::VoidValue - } else if rval.is_boolean() { - devtools_traits::BooleanValue(rval.to_boolean()) - } else if rval.is_double() { - devtools_traits::NumberValue(FromJSValConvertible::from_jsval(cx, rval, ()).unwrap()) - } else if rval.is_string() { - //FIXME: use jsstring_to_str when jsval grows to_jsstring - devtools_traits::StringValue(FromJSValConvertible::from_jsval(cx, rval, conversions::Default).unwrap()) - } else { - //FIXME: jsvals don't have an is_int32/is_number yet - assert!(rval.is_object_or_null()); - panic!("object values unimplemented") - }); - } - - 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.as_ref().unwrap().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.as_ref().unwrap().document.root(); - let document_element = document.GetDocumentElement().root().unwrap(); - - let node: JSRef<Node> = NodeCast::from_ref(*document_element); - reply.send(node.summarize()); - } - - fn find_node_by_unique_id(&self, pipeline: PipelineId, node_id: String) -> Temporary<Node> { - let page = get_page(&*self.page.borrow(), pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); - let node: JSRef<Node> = NodeCast::from_ref(*document); - - for candidate in node.traverse_preorder() { - if candidate.get_unique_id().as_slice() == node_id.as_slice() { - return Temporary::from_rooted(candidate); - } - } - - panic!("couldn't find node with unique id {:s}", node_id) - } - - fn handle_get_children(&self, pipeline: PipelineId, node_id: String, reply: Sender<Vec<NodeInfo>>) { - let parent = self.find_node_by_unique_id(pipeline, node_id).root(); - let children = parent.children().map(|child| child.summarize()).collect(); - reply.send(children); - } - - fn handle_get_layout(&self, pipeline: PipelineId, node_id: String, reply: Sender<(f32, f32)>) { - let node = self.find_node_by_unique_id(pipeline, node_id).root(); - let elem: JSRef<Element> = ElementCast::to_ref(*node).expect("should be getting layout of element"); - let rect = elem.GetBoundingClientRect().root(); - reply.send((rect.Width(), rect.Height())); - } - fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) { let NewLayoutInfo { old_pipeline_id, @@ -1210,7 +1138,7 @@ fn shut_down_layout(page_tree: &Rc<Page>, rt: *mut JSRuntime) { } -fn get_page(page: &Rc<Page>, pipeline_id: PipelineId) -> Rc<Page> { +pub fn get_page(page: &Rc<Page>, pipeline_id: PipelineId) -> Rc<Page> { page.find(pipeline_id).expect("ScriptTask: received an event \ message for a layout channel that is not associated with this script task.\ This is a bug.") diff --git a/tests/wpt/metadata/dom/traversal/NodeFilter-constants.html.ini b/tests/wpt/metadata/dom/traversal/NodeFilter-constants.html.ini index 521226ec902..4b135cbd6c7 100644 --- a/tests/wpt/metadata/dom/traversal/NodeFilter-constants.html.ini +++ b/tests/wpt/metadata/dom/traversal/NodeFilter-constants.html.ini @@ -1,3 +1,3 @@ [NodeFilter-constants.html] type: testharness - expected: ERROR + expected: ERROR
\ No newline at end of file |