aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock16
-rw-r--r--Cargo.toml3
-rw-r--r--components/compositing/Cargo.toml6
-rw-r--r--components/compositing/constellation.rs7
-rw-r--r--components/compositing/lib.rs1
-rw-r--r--components/compositing/pipeline.rs3
-rw-r--r--components/devtools/Cargo.toml12
-rw-r--r--components/devtools/lib.rs519
-rw-r--r--components/devtools_traits/Cargo.toml8
-rw-r--r--components/devtools_traits/lib.rs33
-rw-r--r--components/script/Cargo.toml3
-rw-r--r--components/script/lib.rs1
-rw-r--r--components/script/script_task.rs36
-rw-r--r--components/script_traits/Cargo.toml3
-rw-r--r--components/script_traits/lib.rs5
-rw-r--r--components/util/opts.rs5
-rw-r--r--ports/cef/Cargo.toml3
-rw-r--r--ports/cef/core.rs1
-rw-r--r--src/lib.rs9
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();