diff options
-rw-r--r-- | Cargo.lock | 16 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | components/compositing/Cargo.toml | 6 | ||||
-rw-r--r-- | components/compositing/constellation.rs | 7 | ||||
-rw-r--r-- | components/compositing/lib.rs | 1 | ||||
-rw-r--r-- | components/compositing/pipeline.rs | 3 | ||||
-rw-r--r-- | components/devtools/Cargo.toml | 12 | ||||
-rw-r--r-- | components/devtools/lib.rs | 519 | ||||
-rw-r--r-- | components/devtools_traits/Cargo.toml | 8 | ||||
-rw-r--r-- | components/devtools_traits/lib.rs | 33 | ||||
-rw-r--r-- | components/script/Cargo.toml | 3 | ||||
-rw-r--r-- | components/script/lib.rs | 1 | ||||
-rw-r--r-- | components/script/script_task.rs | 36 | ||||
-rw-r--r-- | components/script_traits/Cargo.toml | 3 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 5 | ||||
-rw-r--r-- | components/util/opts.rs | 5 | ||||
-rw-r--r-- | ports/cef/Cargo.toml | 3 | ||||
-rw-r--r-- | ports/cef/core.rs | 1 | ||||
-rw-r--r-- | src/lib.rs | 9 |
19 files changed, 670 insertions, 4 deletions
diff --git a/Cargo.lock b/Cargo.lock index a7baeb33a8e..43c64bcd7e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,7 @@ name = "servo" version = "0.0.1" dependencies = [ "compositing 0.0.1", + "devtools 0.0.1", "gfx 0.0.1", "layout 0.0.1", "msg 0.0.1", @@ -60,6 +61,8 @@ dependencies = [ "azure 0.1.0 (git+https://github.com/servo/rust-azure#9c5567b79d8b87e8ef3b48c5842f453978035d21)", "core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics#04bd18a4eb83a645a1a32326a33149ba2d0e81be)", "core_text 0.1.0 (git+https://github.com/servo/rust-core-text#e2280222889c030df27ded9a378c14a0e31ab463)", + "devtools 0.0.1", + "devtools_traits 0.0.1", "geom 0.1.0 (git+https://github.com/servo/rust-geom#2982b770db6e5e3270305e0fd6b8068f6f80a489)", "gfx 0.0.1", "glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#dd1a111c827994886d2cdebf91a1838603256390)", @@ -106,6 +109,17 @@ dependencies = [ ] [[package]] +name = "devtools" +version = "0.0.1" +dependencies = [ + "devtools_traits 0.0.1", +] + +[[package]] +name = "devtools_traits" +version = "0.0.1" + +[[package]] name = "egl" version = "0.1.0" source = "git+https://github.com/servo/rust-egl#48b85e30d557ab2ee536730a73dd86a8160d618b" @@ -364,6 +378,7 @@ version = "0.0.1" dependencies = [ "canvas 0.0.1", "cssparser 0.1.0 (git+https://github.com/servo/rust-cssparser#42346400a6629b17a48d06f0a9b28ae498947c6f)", + "devtools_traits 0.0.1", "encoding 0.1.0 (git+https://github.com/lifthrasiir/rust-encoding#12b6610adff6eddc060691888c36017cd3ad57f7)", "geom 0.1.0 (git+https://github.com/servo/rust-geom#2982b770db6e5e3270305e0fd6b8068f6f80a489)", "gfx 0.0.1", @@ -383,6 +398,7 @@ dependencies = [ name = "script_traits" version = "0.0.1" dependencies = [ + "devtools_traits 0.0.1", "geom 0.1.0 (git+https://github.com/servo/rust-geom#2982b770db6e5e3270305e0fd6b8068f6f80a489)", "msg 0.0.1", "net 0.0.1", diff --git a/Cargo.toml b/Cargo.toml index 2472f99be99..0561ad480f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,5 +46,8 @@ path = "components/layout" [dependencies.gfx] path = "components/gfx" +[dependencies.devtools] +path = "components/devtools" + [dependencies.url] git = "https://github.com/servo/rust-url" diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index d9b6adc57d2..e306ae8fd94 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -25,6 +25,12 @@ path = "../net" [dependencies.util] path = "../util" +[dependencies.devtools] +path = "../devtools" + +[dependencies.devtools_traits] +path = "../devtools_traits" + [dependencies.alert] git = "https://github.com/servo/rust-alert" diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index add70aaf39c..70b7e387773 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use compositor_task::{CompositorChan, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds}; +use devtools_traits::DevtoolsControlChan; use std::collections::hashmap::{HashMap, HashSet}; use geom::rect::{Rect, TypedRect}; use geom::scale_factor::ScaleFactor; @@ -41,6 +42,7 @@ pub struct Constellation<LTF, STF> { pub compositor_chan: CompositorChan, pub resource_task: ResourceTask, pub image_cache_task: ImageCacheTask, + devtools_chan: Option<DevtoolsControlChan>, pipelines: HashMap<PipelineId, Rc<Pipeline>>, font_cache_task: FontCacheTask, navigation_context: NavigationContext, @@ -244,7 +246,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { resource_task: ResourceTask, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, - time_profiler_chan: TimeProfilerChan) + time_profiler_chan: TimeProfilerChan, + devtools_chan: Option<DevtoolsControlChan>) -> ConstellationChan { let (constellation_port, constellation_chan) = ConstellationChan::new(); let constellation_chan_clone = constellation_chan.clone(); @@ -254,6 +257,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { chan: constellation_chan_clone, request_port: constellation_port, compositor_chan: compositor_chan, + devtools_chan: devtools_chan, resource_task: resource_task, image_cache_task: image_cache_task, font_cache_task: font_cache_task, @@ -295,6 +299,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> { subpage_id, self.chan.clone(), self.compositor_chan.clone(), + self.devtools_chan.clone(), self.image_cache_task.clone(), self.font_cache_task.clone(), self.resource_task.clone(), diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index f7644e8fc7f..83f9a9b61d2 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -16,6 +16,7 @@ extern crate debug; extern crate alert; extern crate azure; +extern crate devtools_traits; extern crate geom; extern crate gfx; #[cfg(not(target_os="android"))] diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index 8dd8c1f0de7..41ae22b13f7 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -7,6 +7,7 @@ use layout_traits::{LayoutTaskFactory, LayoutControlChan}; use script_traits::{ScriptControlChan, ScriptTaskFactory}; use script_traits::{AttachLayoutMsg, LoadMsg, NewLayoutInfo, ExitPipelineMsg}; +use devtools_traits::DevtoolsControlChan; use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked}; use gfx::render_task::{RenderChan, RenderTask}; use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId}; @@ -49,6 +50,7 @@ impl Pipeline { subpage_id: Option<SubpageId>, constellation_chan: ConstellationChan, compositor_chan: CompositorChan, + devtools_chan: Option<DevtoolsControlChan>, image_cache_task: ImageCacheTask, font_cache_task: FontCacheTask, resource_task: ResourceTask, @@ -82,6 +84,7 @@ impl Pipeline { failure.clone(), resource_task.clone(), image_cache_task.clone(), + devtools_chan, window_size); ScriptControlChan(script_chan) } diff --git a/components/devtools/Cargo.toml b/components/devtools/Cargo.toml new file mode 100644 index 00000000000..d27e6bfed5a --- /dev/null +++ b/components/devtools/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "devtools" +version = "0.0.1" +authors = ["The Servo Project Developers"] + +[lib] +name = "devtools" +path = "lib.rs" +crate-type = ["dylib"] + +[dependencies.devtools_traits] +path = "../devtools_traits" diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs new file mode 100644 index 00000000000..991ddeb5376 --- /dev/null +++ b/components/devtools/lib.rs @@ -0,0 +1,519 @@ +/* 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/. */ + +#![crate_name = "devtools"] +#![crate_type = "rlib"] + +#![comment = "The Servo Parallel Browser Project"] +#![license = "MPL"] + +#![feature(phase)] + +#![feature(phase)] +#[phase(plugin, link)] +extern crate log; + +extern crate collections; +extern crate core; +extern crate devtools_traits; +extern crate debug; +extern crate std; +extern crate serialize; +extern crate sync; + +use devtools_traits::{ServerExitMsg, DevtoolsControlMsg, NewGlobal}; + +use collections::TreeMap; +use std::any::{Any, AnyRefExt}; +use std::collections::hashmap::HashMap; +use std::comm; +use std::comm::{Disconnected, Empty}; +use std::io::{TcpListener, TcpStream}; +use std::io::{Acceptor, Listener, EndOfFile, IoError, TimedOut}; +use std::num; +use std::task::TaskBuilder; +use serialize::{json, Encodable}; +use sync::{Arc, Mutex}; + +#[deriving(Encodable)] +struct ActorTraits { + sources: bool +} + +#[deriving(Encodable)] +struct RootActorMsg { + from: String, + applicationType: String, + traits: ActorTraits, +} + +struct RootActor { + tabs: Vec<String>, +} + +#[deriving(Encodable)] +struct ErrorReply { + from: String, + error: String, + message: String, +} + +#[deriving(Encodable)] +struct TabActorMsg { + actor: String, + title: String, + url: String, + outerWindowID: uint, + consoleActor: String, +} + +struct TabActor { + name: String, + title: String, + url: String, +} + +#[deriving(Encodable)] +struct ListTabsReply { + from: String, + selected: uint, + tabs: Vec<TabActorMsg>, +} + +#[deriving(Encodable)] +struct TabTraits { + reconfigure: bool, +} + +#[deriving(Encodable)] +struct TabAttachedReply { + from: String, + __type__: String, + threadActor: String, + cacheEnabled: bool, + javascriptEnabled: bool, + traits: TabTraits, +} + +#[deriving(Encodable)] +struct TabDetachedReply { + from: String, + __type__: String, +} + +#[deriving(Encodable)] +struct StartedListenersReply { + from: String, + nativeConsoleAPI: bool, + startedListeners: Vec<String>, +} + +#[deriving(Encodable)] +struct ConsoleAPIMessage { + _type: String, +} + +#[deriving(Encodable)] +struct PageErrorMessage { + _type: String, + errorMessage: String, + sourceName: String, + lineText: String, + lineNumber: uint, + columnNumber: uint, + category: String, + timeStamp: uint, + warning: bool, + error: bool, + exception: bool, + strict: bool, + private: bool, +} + +#[deriving(Encodable)] +struct LogMessage { + _type: String, + timeStamp: uint, + message: String, +} + +#[deriving(Encodable)] +enum ConsoleMessageType { + ConsoleAPIType(ConsoleAPIMessage), + PageErrorType(PageErrorMessage), + LogMessageType(LogMessage), +} + +#[deriving(Encodable)] +struct GetCachedMessagesReply { + from: String, + messages: Vec<json::Object>, +} + +#[deriving(Encodable)] +struct StopListenersReply { + from: String, + stoppedListeners: Vec<String>, +} + +#[deriving(Encodable)] +struct AutocompleteReply { + from: String, + matches: Vec<String>, + matchProp: String, +} + +#[deriving(Encodable)] +struct EvaluateJSReply { + from: String, + input: String, + result: json::Json, + timestamp: uint, + exception: json::Json, + exceptionMessage: String, + helperResult: json::Json, +} + +struct ActorRegistry { + actors: HashMap<String, Box<Actor+Send+Sized>>, +} + +impl ActorRegistry { + fn new() -> ActorRegistry { + ActorRegistry { + actors: HashMap::new(), + } + } + + fn register(&mut self, actor: Box<Actor+Send+Sized>) { + self.actors.insert(actor.name().to_string(), actor); + } + + fn find<'a, T: 'static>(&'a self, name: &str) -> &'a T { + let actor: &Actor+Send+Sized = *self.actors.find(&name.to_string()).unwrap(); + (actor as &Any).downcast_ref::<T>().unwrap() + } + + fn handle_message(&self, msg: &json::Object, stream: &mut TcpStream) { + let to = msg.find(&"to".to_string()).unwrap().as_string().unwrap(); + match self.actors.find(&to.to_string()) { + None => println!("message received for unknown actor \"{:s}\"", to), + Some(actor) => { + let msg_type = msg.find(&"type".to_string()).unwrap() + .as_string().unwrap(); + if !actor.handle_message(self, &msg_type.to_string(), msg, stream) { + println!("unexpected message type \"{:s}\" found for actor \"{:s}\"", + msg_type, to); + } + } + } + } +} + +trait Actor: Any { + fn handle_message(&self, + registry: &ActorRegistry, + msg_type: &String, + msg: &json::Object, + stream: &mut TcpStream) -> bool; + fn name(&self) -> String; +} + +impl Actor for RootActor { + fn name(&self) -> String { + "root".to_string() + } + + fn handle_message(&self, + registry: &ActorRegistry, + msg_type: &String, + _msg: &json::Object, + stream: &mut TcpStream) -> bool { + match msg_type.as_slice() { + "listAddons" => { + let actor = ErrorReply { + from: "root".to_string(), + error: "noAddons".to_string(), + message: "This root actor has no browser addons.".to_string(), + }; + stream.write_json_packet(&actor); + true + } + "listTabs" => { + let actor = ListTabsReply { + from: "root".to_string(), + selected: 0, + tabs: self.tabs.iter().map(|tab| { + registry.find::<TabActor>(tab.as_slice()).encodable() + }).collect() + }; + stream.write_json_packet(&actor); + true + } + _ => false + } + } +} + +impl RootActor { + fn encodable(&self) -> RootActorMsg { + RootActorMsg { + from: "root".to_string(), + applicationType: "browser".to_string(), + traits: ActorTraits { + sources: true, + }, + } + } +} + +impl Actor for TabActor { + 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() { + "attach" => { + let msg = TabAttachedReply { + from: self.name(), + __type__: "tabAttached".to_string(), + threadActor: self.name(), + cacheEnabled: false, + javascriptEnabled: true, + traits: TabTraits { + reconfigure: true, + }, + }; + stream.write_json_packet(&msg); + true + } + "detach" => { + let msg = TabDetachedReply { + from: self.name(), + __type__: "detached".to_string(), + }; + stream.write_json_packet(&msg); + true + } + "startListeners" => { + let msg = StartedListenersReply { + from: self.name(), + nativeConsoleAPI: true, + startedListeners: + vec!("PageError".to_string(), "ConsoleAPI".to_string()), + }; + stream.write_json_packet(&msg); + true + } + "getCachedMessages" => { + let types = msg.find(&"messageTypes".to_string()).unwrap().as_list().unwrap(); + let mut messages = vec!(); + for msg_type in types.iter() { + let msg_type = msg_type.as_string().unwrap(); + match msg_type.as_slice() { + "ConsoleAPI" => { + //XXX need more info about consoleapi properties + } + "PageError" => { + let message = PageErrorMessage { + _type: msg_type.to_string(), + sourceName: "".to_string(), + lineText: "".to_string(), + lineNumber: 0, + columnNumber: 0, + category: "".to_string(), + warning: false, + error: true, + exception: false, + strict: false, + private: false, + timeStamp: 0, + errorMessage: "page error test".to_string(), + }; + messages.push(json::from_str(json::encode(&message).as_slice()).unwrap().as_object().unwrap().clone()); + } + "LogMessage" => { + let message = LogMessage { + _type: msg_type.to_string(), + timeStamp: 0, + message: "log message test".to_string(), + }; + messages.push(json::from_str(json::encode(&message).as_slice()).unwrap().as_object().unwrap().clone()); + } + s => println!("unrecognized message type requested: \"{:s}\"", s), + } + } + let msg = GetCachedMessagesReply { + from: self.name(), + messages: messages, + }; + stream.write_json_packet(&msg); + true + } + "stopListeners" => { + let msg = StopListenersReply { + from: self.name(), + stoppedListeners: msg.find(&"listeners".to_string()) + .unwrap() + .as_list() + .unwrap_or(&vec!()) + .iter() + .map(|listener| listener.as_string().unwrap().to_string()) + .collect(), + }; + stream.write_json_packet(&msg); + true + } + "autocomplete" => { + let msg = AutocompleteReply { + from: self.name(), + matches: vec!(), + matchProp: "".to_string(), + }; + stream.write_json_packet(&msg); + true + } + "evaluateJS" => { + let msg = EvaluateJSReply { + from: self.name(), + input: msg.find(&"text".to_string()).unwrap().as_string().unwrap().to_string(), + result: json::Object(TreeMap::new()), + timestamp: 0, + exception: json::Object(TreeMap::new()), + exceptionMessage: "".to_string(), + helperResult: json::Object(TreeMap::new()), + }; + stream.write_json_packet(&msg); + true + } + _ => false + } + } +} + +impl TabActor { + fn encodable(&self) -> TabActorMsg { + TabActorMsg { + actor: self.name(), + title: self.title.clone(), + url: self.url.clone(), + outerWindowID: 0, + consoleActor: self.name(), + } + } +} + +trait JsonPacketSender { + fn write_json_packet<'a, T: Encodable<json::Encoder<'a>,IoError>>(&mut self, obj: &T); +} + +impl JsonPacketSender for TcpStream { + fn write_json_packet<'a, T: Encodable<json::Encoder<'a>,IoError>>(&mut self, obj: &T) { + let s = json::encode(obj).replace("__type__", "type"); + println!("<- {:s}", s); + self.write_str(s.len().to_string().as_slice()).unwrap(); + self.write_u8(':' as u8).unwrap(); + self.write_str(s.as_slice()).unwrap(); + } +} + +pub fn start_server() -> Sender<DevtoolsControlMsg> { + let (chan, port) = comm::channel(); + TaskBuilder::new().named("devtools").spawn(proc() { + run_server(port) + }); + chan +} + +static POLL_TIMEOUT: u64 = 300; + +fn run_server(port: Receiver<DevtoolsControlMsg>) { + let listener = TcpListener::bind("127.0.0.1", 6000); + + // bind the listener to the specified address + let mut acceptor = listener.listen().unwrap(); + acceptor.set_timeout(Some(POLL_TIMEOUT)); + + let mut registry = ActorRegistry::new(); + + let tab = box TabActor { + name: "tab1".to_string(), + title: "Performing Layout".to_string(), + url: "about-mozilla.html".to_string(), + }; + + let root = box RootActor { + tabs: vec!(tab.name().to_string()), + }; + + registry.register(tab); + registry.register(root); + + let actors = Arc::new(Mutex::new(registry)); + + fn handle_client(actors: Arc<Mutex<ActorRegistry>>, mut stream: TcpStream) { + println!("connection established to {:?}", stream.peer_name().unwrap()); + + { + let mut actors = actors.lock(); + let msg = actors.find::<RootActor>("root").encodable(); + stream.write_json_packet(&msg); + } + + 'outer: loop { + let mut buffer = vec!(); + loop { + let colon = ':' as u8; + match stream.read_byte() { + Ok(c) if c != colon => buffer.push(c as u8), + Ok(_) => { + let packet_len_str = String::from_utf8(buffer).unwrap(); + let packet_len = num::from_str_radix(packet_len_str.as_slice(), 10).unwrap(); + let packet_buf = stream.read_exact(packet_len).unwrap(); + let packet = String::from_utf8(packet_buf).unwrap(); + println!("{:s}", packet); + let json_packet = json::from_str(packet.as_slice()).unwrap(); + actors.lock().handle_message(json_packet.as_object().unwrap(), + &mut stream); + break; + } + Err(ref e) if e.kind == EndOfFile => { + println!("\nEOF"); + break 'outer; + }, + _ => { + println!("\nconnection error"); + break 'outer; + } + } + } + } + } + + // accept connections and process them, spawning a new tasks for each one + for stream in acceptor.incoming() { + match stream { + Err(ref e) if e.kind == TimedOut => { + match port.try_recv() { + Ok(ServerExitMsg) | Err(Disconnected) => break, + Ok(NewGlobal(_)) => { /*TODO*/ }, + Err(Empty) => acceptor.set_timeout(Some(POLL_TIMEOUT)), + } + } + Err(_e) => { /* connection failed */ } + Ok(stream) => { + let actors = actors.clone(); + spawn(proc() { + // connection succeeded + handle_client(actors, stream.clone()) + }) + } + } + } +} diff --git a/components/devtools_traits/Cargo.toml b/components/devtools_traits/Cargo.toml new file mode 100644 index 00000000000..ee897936dbb --- /dev/null +++ b/components/devtools_traits/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "devtools_traits" +version = "0.0.1" +authors = ["The Servo Project Developers"] + +[lib] +name = "devtools_traits" +path = "lib.rs" diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs new file mode 100644 index 00000000000..823acd0ab8f --- /dev/null +++ b/components/devtools_traits/lib.rs @@ -0,0 +1,33 @@ +/* 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/. */ + +#![crate_name = "devtools_traits"] +#![crate_type = "rlib"] + +#![comment = "The Servo Parallel Browser Project"] +#![license = "MPL"] + +pub type DevtoolsControlChan = Sender<DevtoolsControlMsg>; +pub type DevtoolsControlPort = Receiver<DevtoolScriptControlMsg>; + +pub enum DevtoolsControlMsg { + NewGlobal(Sender<DevtoolScriptControlMsg>), + ServerExitMsg +} + +pub enum EvaluateJSReply { + VoidValue, + NullValue, + NumberValue(f64), + StringValue(String), + ActorValue(String), +} + +pub enum DevtoolScriptControlMsg { + EvaluateJS(String, Sender<EvaluateJSReply>), +} + +pub enum ScriptDevtoolControlMsg { + ReportConsoleMsg(String), +} diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 1748a57956d..8bf75b57b20 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -24,6 +24,9 @@ path = "../net" [dependencies.script_traits] path = "../script_traits" +[dependencies.devtools_traits] +path = "../devtools_traits" + [dependencies.style] path = "../style" diff --git a/components/script/lib.rs b/components/script/lib.rs index 9f3effaa368..d7c13cc06a1 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -16,6 +16,7 @@ extern crate log; extern crate debug; +extern crate devtools_traits; extern crate cssparser; extern crate collections; extern crate geom; diff --git a/components/script/script_task.rs b/components/script/script_task.rs index a1f2cd27b22..20f5eb3ded5 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -31,6 +31,8 @@ use layout_interface::ContentChangedDocumentDamage; use layout_interface; use page::{Page, IterablePage, Frame}; +use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, NewGlobal}; +use devtools_traits::{DevtoolScriptControlMsg, EvaluateJS}; use script_traits::{CompositorEvent, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent}; use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, ScriptTaskFactory}; use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, SendEventMsg, ResizeInactiveMsg}; @@ -157,6 +159,12 @@ pub struct ScriptTask { /// A handle to the compositor for communicating ready state messages. compositor: Box<ScriptListener>, + /// For providing instructions to an optional devtools server. + _devtools_chan: Option<DevtoolsControlChan>, + /// For receiving commands from an optional devtools server. Will be ignored if + /// no such server exists. + devtools_port: DevtoolsControlPort, + /// The JavaScript runtime. js_runtime: js::rust::rt, /// The JSContext. @@ -240,6 +248,7 @@ impl ScriptTaskFactory for ScriptTask { failure_msg: Failure, resource_task: ResourceTask, image_cache_task: ImageCacheTask, + devtools_chan: Option<DevtoolsControlChan>, window_size: WindowSizeData) { let ConstellationChan(const_chan) = constellation_chan.clone(); let (script_chan, script_port) = channel(); @@ -255,6 +264,7 @@ impl ScriptTaskFactory for ScriptTask { constellation_chan, resource_task, image_cache_task, + devtools_chan, window_size); let mut failsafe = ScriptMemoryFailsafe::new(&*script_task); script_task.start(); @@ -277,6 +287,7 @@ impl ScriptTask { constellation_chan: ConstellationChan, resource_task: ResourceTask, img_cache_task: ImageCacheTask, + devtools_chan: Option<DevtoolsControlChan>, window_size: WindowSizeData) -> Rc<ScriptTask> { let (js_runtime, js_context) = ScriptTask::new_rt_and_cx(); @@ -299,6 +310,14 @@ impl ScriptTask { resource_task.clone(), constellation_chan.clone(), js_context.clone()); + + // Notify devtools that a new script global exists. + //FIXME: Move this into handle_load after we create a window instead. + let (devtools_sender, devtools_receiver) = channel(); + devtools_chan.as_ref().map(|chan| { + chan.send(NewGlobal(devtools_sender.clone())); + }); + Rc::new(ScriptTask { page: RefCell::new(Rc::new(page)), @@ -311,6 +330,8 @@ impl ScriptTask { control_port: control_port, constellation_chan: constellation_chan, compositor: compositor, + _devtools_chan: devtools_chan, + devtools_port: devtools_receiver, js_runtime: js_runtime, js_context: RefCell::new(Some(js_context)), @@ -392,6 +413,7 @@ impl ScriptTask { enum MixedMessage { FromConstellation(ConstellationControlMsg), FromScript(ScriptMsg), + FromDevtools(DevtoolScriptControlMsg), } // Store new resizes, and gather all other events. @@ -402,20 +424,25 @@ impl ScriptTask { let sel = Select::new(); let mut port1 = sel.handle(&self.port); let mut port2 = sel.handle(&self.control_port); + let mut port3 = sel.handle(&self.devtools_port); unsafe { port1.add(); port2.add(); + port3.add(); } let ret = sel.wait(); if ret == port1.id() { FromScript(self.port.recv()) } else if ret == port2.id() { FromConstellation(self.control_port.recv()) + } else if ret == port3.id() { + FromDevtools(self.devtools_port.recv()) } else { fail!("unexpected select result") } }; + // Squash any pending resize events in the queue. loop { match event { // This has to be handled before the ResizeMsg below, @@ -434,9 +461,15 @@ impl ScriptTask { } } + // If any of our input sources has an event pending, we'll perform another iteration + // and check for more resize events. If there are no events pending, we'll move + // on and execute the sequential non-resize events we've seen. match self.control_port.try_recv() { Err(_) => match self.port.try_recv() { - Err(_) => break, + Err(_) => match self.devtools_port.try_recv() { + Err(_) => break, + Ok(ev) => event = FromDevtools(ev), + }, Ok(ev) => event = FromScript(ev), }, Ok(ev) => event = FromConstellation(ev), @@ -463,6 +496,7 @@ impl ScriptTask { FromScript(DOMMessage(..)) => fail!("unexpected message"), FromScript(WorkerPostMessage(addr, data, nbytes)) => Worker::handle_message(addr, data, nbytes), FromScript(WorkerRelease(addr)) => Worker::handle_release(addr), + FromDevtools(EvaluateJS(_s, _reply)) => {/*TODO*/} } } diff --git a/components/script_traits/Cargo.toml b/components/script_traits/Cargo.toml index 3bc1beda99f..4183b3909de 100644 --- a/components/script_traits/Cargo.toml +++ b/components/script_traits/Cargo.toml @@ -13,6 +13,9 @@ path = "../msg" [dependencies.net] path = "../net" +[dependencies.devtools_traits] +path = "../devtools_traits" + [dependencies.geom] git = "https://github.com/servo/rust-geom" diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 4cead079ff1..fa85bb7c1bd 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -7,6 +7,7 @@ #![deny(unused_imports, unused_variable)] +extern crate devtools_traits; extern crate geom; extern crate servo_msg = "msg"; extern crate servo_net = "net"; @@ -16,9 +17,10 @@ extern crate serialize; // This module contains traits in script used generically // in the rest of Servo. -// The traits are here instead of in layout so +// The traits are here instead of in script so // that these modules won't have to depend on script. +use devtools_traits::DevtoolsControlChan; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData}; use servo_msg::constellation_msg::SubpageId; use servo_msg::compositor_msg::ScriptListener; @@ -91,6 +93,7 @@ pub trait ScriptTaskFactory { failure_msg: Failure, resource_task: ResourceTask, image_cache_task: ImageCacheTask, + devtools_chan: Option<DevtoolsControlChan>, window_size: WindowSizeData); fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel; fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel) -> Box<Any+Send>; diff --git a/components/util/opts.rs b/components/util/opts.rs index 40da68632d8..2145e83af81 100644 --- a/components/util/opts.rs +++ b/components/util/opts.rs @@ -83,6 +83,9 @@ pub struct Opts { /// for debugging purposes. Settings this implies sequential layout /// and render. pub trace_layout: bool, + + /// True if we should start a server to listen to remote Firefox devtools connections. + pub devtools_server: bool, } fn print_usage(app: &str, opts: &[getopts::OptGroup]) { @@ -117,6 +120,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { getopts::optflag("", "show-debug-borders", "Show debugging borders on layers and tiles."), getopts::optflag("", "disable-text-aa", "Disable antialiasing for text rendering."), getopts::optflag("", "trace-layout", "Write layout trace to external file for debugging."), + getopts::optflag("", "devtools", "Start remote devtools server"), getopts::optflag("h", "help", "Print this message") ); @@ -217,6 +221,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { show_debug_borders: opt_match.opt_present("show-debug-borders"), enable_text_antialiasing: !opt_match.opt_present("disable-text-aa"), trace_layout: trace_layout, + devtools_server: opt_match.opt_present("devtools"), }) } diff --git a/ports/cef/Cargo.toml b/ports/cef/Cargo.toml index d88ceb24405..5063307fe3f 100644 --- a/ports/cef/Cargo.toml +++ b/ports/cef/Cargo.toml @@ -32,6 +32,9 @@ path = "../../components/util" [dependencies.style] path = "../../components/style" +[dependencies.devtools] +path = "../../components/devtools" + [dependencies.azure] git = "https://github.com/servo/rust-azure" diff --git a/ports/cef/core.rs b/ports/cef/core.rs index 9ff237f2d82..b6415d27f12 100644 --- a/ports/cef/core.rs +++ b/ports/cef/core.rs @@ -67,6 +67,7 @@ pub extern "C" fn cef_run_message_loop() { show_debug_borders: false, enable_text_antialiasing: true, trace_layout: false, + devtools_server: false, }; native::start(0, 0 as *const *const u8, proc() { servo::run(opts); diff --git a/src/lib.rs b/src/lib.rs index a00d935ade3..8a8fb3217e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ extern crate log; extern crate debug; extern crate compositing; +extern crate devtools; extern crate rustuv; extern crate servo_net = "net"; extern crate servo_msg = "msg"; @@ -94,6 +95,11 @@ pub fn run(opts: opts::Opts) { let (compositor_port, compositor_chan) = CompositorChan::new(); let time_profiler_chan = TimeProfiler::create(opts.time_profiler_period); let memory_profiler_chan = MemoryProfiler::create(opts.memory_profiler_period); + let devtools_chan = if opts.devtools_server { + Some(devtools::start_server()) + } else { + None + }; let opts_clone = opts.clone(); let time_profiler_chan_clone = time_profiler_chan.clone(); @@ -121,7 +127,8 @@ pub fn run(opts: opts::Opts) { resource_task, image_cache_task, font_cache_task, - time_profiler_chan_clone); + time_profiler_chan_clone, + devtools_chan); // Send the URL command to the constellation. let cwd = os::getcwd(); |