Support navigating browsing contexts in the devtools.

Break the association between pipelines and browsing context actors.
Now there is one browsing context actor per actual browsing context,
and individual actors keep track of known pipelines as necessary.
There is also one console/performance/timeline/inspector/etc. actor
per browsing context.

This also centralizes more information in the browsing context actor.
Rather than duplicating state for the active pipeline in actors that
need to use it, each actor now remembers the name of its associated
browsing context actor and obtains that state whenever it's necessary.
This commit is contained in:
Josh Matthews 2020-04-25 12:21:18 -04:00
parent 0540c4a284
commit 7c48644cad
9 changed files with 348 additions and 184 deletions

View file

@ -8,10 +8,22 @@
//! Supports dynamic attaching and detaching which control notifications of navigation, etc. //! Supports dynamic attaching and detaching which control notifications of navigation, etc.
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::actors::console::ConsoleActor; use crate::actors::emulation::EmulationActor;
use crate::actors::inspector::InspectorActor;
use crate::actors::performance::PerformanceActor;
use crate::actors::profiler::ProfilerActor;
use crate::actors::root::RootActor;
use crate::actors::stylesheets::StyleSheetsActor;
use crate::actors::thread::ThreadActor;
use crate::actors::timeline::TimelineActor;
use crate::protocol::JsonPacketStream; use crate::protocol::JsonPacketStream;
use devtools_traits::DevtoolScriptControlMsg::{self, WantsLiveNotifications}; use devtools_traits::DevtoolScriptControlMsg::{self, WantsLiveNotifications};
use devtools_traits::DevtoolsPageInfo;
use devtools_traits::NavigationState;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::{BrowsingContextId, PipelineId};
use serde_json::{Map, Value}; use serde_json::{Map, Value};
use std::cell::{Cell, RefCell};
use std::net::TcpStream; use std::net::TcpStream;
#[derive(Serialize)] #[derive(Serialize)]
@ -118,6 +130,10 @@ pub struct BrowsingContextActor {
pub performance: String, pub performance: String,
pub styleSheets: String, pub styleSheets: String,
pub thread: String, pub thread: String,
pub streams: RefCell<Vec<TcpStream>>,
pub browsing_context_id: BrowsingContextId,
pub active_pipeline: Cell<PipelineId>,
pub script_chan: IpcSender<DevtoolScriptControlMsg>,
} }
impl Actor for BrowsingContextActor { impl Actor for BrowsingContextActor {
@ -127,7 +143,7 @@ impl Actor for BrowsingContextActor {
fn handle_message( fn handle_message(
&self, &self,
registry: &ActorRegistry, _registry: &ActorRegistry,
msg_type: &str, msg_type: &str,
msg: &Map<String, Value>, msg: &Map<String, Value>,
stream: &mut TcpStream, stream: &mut TcpStream,
@ -137,10 +153,9 @@ impl Actor for BrowsingContextActor {
if let Some(options) = msg.get("options").and_then(|o| o.as_object()) { if let Some(options) = msg.get("options").and_then(|o| o.as_object()) {
if let Some(val) = options.get("performReload") { if let Some(val) = options.get("performReload") {
if val.as_bool().unwrap_or(false) { if val.as_bool().unwrap_or(false) {
let console_actor = registry.find::<ConsoleActor>(&self.console); let _ = self
let _ = console_actor
.script_chan .script_chan
.send(DevtoolScriptControlMsg::Reload(console_actor.pipeline)); .send(DevtoolScriptControlMsg::Reload(self.active_pipeline.get()));
} }
} }
} }
@ -165,32 +180,25 @@ impl Actor for BrowsingContextActor {
watchpoints: false, watchpoints: false,
}, },
}; };
let console_actor = registry.find::<ConsoleActor>(&self.console); self.streams.borrow_mut().push(stream.try_clone().unwrap());
console_actor
.streams
.borrow_mut()
.push(stream.try_clone().unwrap());
stream.write_json_packet(&msg); stream.write_json_packet(&msg);
console_actor self.script_chan
.script_chan .send(WantsLiveNotifications(self.active_pipeline.get(), true))
.send(WantsLiveNotifications(console_actor.pipeline, true))
.unwrap(); .unwrap();
ActorMessageStatus::Processed ActorMessageStatus::Processed
}, },
//FIXME: The current implementation won't work for multiple connections. Need to ensure 105 //FIXME: The current implementation won't work for multiple connections. Need to ensure
// that the correct stream is removed. // that the correct stream is removed.
"detach" => { "detach" => {
let msg = BrowsingContextDetachedReply { let msg = BrowsingContextDetachedReply {
from: self.name(), from: self.name(),
type_: "detached".to_owned(), type_: "detached".to_owned(),
}; };
let console_actor = registry.find::<ConsoleActor>(&self.console); self.streams.borrow_mut().pop();
console_actor.streams.borrow_mut().pop();
stream.write_json_packet(&msg); stream.write_json_packet(&msg);
console_actor self.script_chan
.script_chan .send(WantsLiveNotifications(self.active_pipeline.get(), false))
.send(WantsLiveNotifications(console_actor.pipeline, false))
.unwrap(); .unwrap();
ActorMessageStatus::Processed ActorMessageStatus::Processed
}, },
@ -224,6 +232,70 @@ impl Actor for BrowsingContextActor {
} }
impl BrowsingContextActor { impl BrowsingContextActor {
pub(crate) fn new(
console: String,
id: BrowsingContextId,
page_info: DevtoolsPageInfo,
pipeline: PipelineId,
script_sender: IpcSender<DevtoolScriptControlMsg>,
actors: &mut ActorRegistry,
) -> BrowsingContextActor {
let emulation = EmulationActor::new(actors.new_name("emulation"));
let name = actors.new_name("target");
let inspector = InspectorActor {
name: actors.new_name("inspector"),
walker: RefCell::new(None),
pageStyle: RefCell::new(None),
highlighter: RefCell::new(None),
script_chan: script_sender.clone(),
browsing_context: name.clone(),
};
let timeline =
TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender.clone());
let profiler = ProfilerActor::new(actors.new_name("profiler"));
let performance = PerformanceActor::new(actors.new_name("performance"));
// the strange switch between styleSheets and stylesheets is due
// to an inconsistency in devtools. See Bug #1498893 in bugzilla
let styleSheets = StyleSheetsActor::new(actors.new_name("stylesheets"));
let thread = ThreadActor::new(actors.new_name("context"));
let DevtoolsPageInfo { title, url } = page_info;
let target = BrowsingContextActor {
name: name,
script_chan: script_sender,
title: String::from(title),
url: url.into_string(),
console: console,
emulation: emulation.name(),
inspector: inspector.name(),
timeline: timeline.name(),
profiler: profiler.name(),
performance: performance.name(),
styleSheets: styleSheets.name(),
thread: thread.name(),
streams: RefCell::new(Vec::new()),
browsing_context_id: id,
active_pipeline: Cell::new(pipeline),
};
actors.register(Box::new(emulation));
actors.register(Box::new(inspector));
actors.register(Box::new(timeline));
actors.register(Box::new(profiler));
actors.register(Box::new(performance));
actors.register(Box::new(styleSheets));
actors.register(Box::new(thread));
let root = actors.find_mut::<RootActor>("root");
root.tabs.push(target.name.clone());
target
}
pub fn encodable(&self) -> BrowsingContextActorMsg { pub fn encodable(&self) -> BrowsingContextActorMsg {
BrowsingContextActorMsg { BrowsingContextActorMsg {
actor: self.name(), actor: self.name(),
@ -232,8 +304,10 @@ impl BrowsingContextActor {
}, },
title: self.title.clone(), title: self.title.clone(),
url: self.url.clone(), url: self.url.clone(),
browsingContextId: 0, //FIXME should come from constellation //FIXME: shouldn't ignore pipeline namespace field
outerWindowID: 0, //FIXME: this should probably be the pipeline id browsingContextId: self.browsing_context_id.index.0.get(),
//FIXME: shouldn't ignore pipeline namespace field
outerWindowID: self.active_pipeline.get().index.0.get(),
consoleActor: self.console.clone(), consoleActor: self.console.clone(),
emulationActor: self.emulation.clone(), emulationActor: self.emulation.clone(),
inspectorActor: self.inspector.clone(), inspectorActor: self.inspector.clone(),
@ -243,4 +317,40 @@ impl BrowsingContextActor {
styleSheetsActor: self.styleSheets.clone(), styleSheetsActor: self.styleSheets.clone(),
} }
} }
pub(crate) fn navigate(&self, state: NavigationState) {
let (pipeline, title, url, state) = match state {
NavigationState::Start(url) => (None, None, url, "start"),
NavigationState::Stop(pipeline, info) => {
(Some(pipeline), Some(info.title), info.url, "stop")
},
};
if let Some(p) = pipeline {
self.active_pipeline.set(p);
}
let msg = TabNavigated {
from: self.name(),
type_: "tabNavigated".to_owned(),
url: url.as_str().to_owned(),
title: title,
nativeConsoleAPI: true,
state: state.to_owned(),
isFrameSwitching: false,
};
for stream in &mut *self.streams.borrow_mut() {
stream.write_json_packet(&msg);
}
}
}
#[derive(Serialize)]
struct TabNavigated {
from: String,
#[serde(rename = "type")]
type_: String,
url: String,
title: Option<String>,
nativeConsoleAPI: bool,
state: String,
isFrameSwitching: bool,
} }

View file

@ -8,6 +8,7 @@
//! inspection, JS evaluation, autocompletion) in Servo. //! inspection, JS evaluation, autocompletion) in Servo.
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::actors::browsing_context::BrowsingContextActor;
use crate::actors::object::ObjectActor; use crate::actors::object::ObjectActor;
use crate::protocol::JsonPacketStream; use crate::protocol::JsonPacketStream;
use crate::{ConsoleAPICall, ConsoleMessage, ConsoleMsg, PageErrorMsg}; use crate::{ConsoleAPICall, ConsoleMessage, ConsoleMsg, PageErrorMsg};
@ -17,10 +18,11 @@ use devtools_traits::EvaluateJSReply::{NullValue, NumberValue, VoidValue};
use devtools_traits::{ use devtools_traits::{
CachedConsoleMessageTypes, ConsoleAPI, DevtoolScriptControlMsg, LogLevel, PageError, CachedConsoleMessageTypes, ConsoleAPI, DevtoolScriptControlMsg, LogLevel, PageError,
}; };
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use serde_json::{self, Map, Number, Value}; use serde_json::{self, Map, Number, Value};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use std::net::TcpStream; use std::net::TcpStream;
use time::precise_time_ns; use time::precise_time_ns;
use uuid::Uuid; use uuid::Uuid;
@ -106,10 +108,8 @@ struct SetPreferencesReply {
pub struct ConsoleActor { pub struct ConsoleActor {
pub name: String, pub name: String,
pub pipeline: PipelineId, pub browsing_context: String,
pub script_chan: IpcSender<DevtoolScriptControlMsg>, pub cached_events: RefCell<HashMap<PipelineId, Vec<CachedConsoleMessage>>>,
pub streams: RefCell<Vec<TcpStream>>,
pub cached_events: RefCell<Vec<CachedConsoleMessage>>,
} }
impl ConsoleActor { impl ConsoleActor {
@ -118,11 +118,13 @@ impl ConsoleActor {
registry: &ActorRegistry, registry: &ActorRegistry,
msg: &Map<String, Value>, msg: &Map<String, Value>,
) -> Result<EvaluateJSReply, ()> { ) -> Result<EvaluateJSReply, ()> {
let browsing_context = registry.find::<BrowsingContextActor>(&self.browsing_context);
let input = msg.get("text").unwrap().as_str().unwrap().to_owned(); let input = msg.get("text").unwrap().as_str().unwrap().to_owned();
let (chan, port) = ipc::channel().unwrap(); let (chan, port) = ipc::channel().unwrap();
self.script_chan browsing_context
.script_chan
.send(DevtoolScriptControlMsg::EvaluateJS( .send(DevtoolScriptControlMsg::EvaluateJS(
self.pipeline, browsing_context.active_pipeline.get(),
input.clone(), input.clone(),
chan, chan,
)) ))
@ -191,21 +193,35 @@ impl ConsoleActor {
std::result::Result::Ok(reply) std::result::Result::Ok(reply)
} }
pub(crate) fn handle_page_error(&self, page_error: PageError) { pub(crate) fn handle_page_error(
&self,
page_error: PageError,
pipeline: PipelineId,
browsing_context: &BrowsingContextActor,
) {
self.cached_events self.cached_events
.borrow_mut() .borrow_mut()
.entry(pipeline)
.or_insert(vec![])
.push(CachedConsoleMessage::PageError(page_error.clone())); .push(CachedConsoleMessage::PageError(page_error.clone()));
let msg = PageErrorMsg { if browsing_context.active_pipeline.get() == pipeline {
from: self.name(), let msg = PageErrorMsg {
type_: "pageError".to_owned(), from: self.name(),
pageError: page_error, type_: "pageError".to_owned(),
}; pageError: page_error,
for stream in &mut *self.streams.borrow_mut() { };
stream.write_json_packet(&msg); for stream in &mut *browsing_context.streams.borrow_mut() {
stream.write_json_packet(&msg);
}
} }
} }
pub(crate) fn handle_console_api(&self, console_message: ConsoleMessage) { pub(crate) fn handle_console_api(
&self,
console_message: ConsoleMessage,
pipeline: PipelineId,
browsing_context: &BrowsingContextActor,
) {
let level = match console_message.logLevel { let level = match console_message.logLevel {
LogLevel::Debug => "debug", LogLevel::Debug => "debug",
LogLevel::Info => "info", LogLevel::Info => "info",
@ -216,6 +232,8 @@ impl ConsoleActor {
.to_owned(); .to_owned();
self.cached_events self.cached_events
.borrow_mut() .borrow_mut()
.entry(pipeline)
.or_insert(vec![])
.push(CachedConsoleMessage::ConsoleAPI(ConsoleAPI { .push(CachedConsoleMessage::ConsoleAPI(ConsoleAPI {
type_: "ConsoleAPI".to_owned(), type_: "ConsoleAPI".to_owned(),
level: level.clone(), level: level.clone(),
@ -226,20 +244,22 @@ impl ConsoleActor {
private: false, private: false,
arguments: vec![console_message.message.clone()], arguments: vec![console_message.message.clone()],
})); }));
let msg = ConsoleAPICall { if browsing_context.active_pipeline.get() == pipeline {
from: self.name(), let msg = ConsoleAPICall {
type_: "consoleAPICall".to_owned(), from: self.name(),
message: ConsoleMsg { type_: "consoleAPICall".to_owned(),
level: level, message: ConsoleMsg {
timeStamp: precise_time_ns(), level: level,
arguments: vec![console_message.message], timeStamp: precise_time_ns(),
filename: console_message.filename, arguments: vec![console_message.message],
lineNumber: console_message.lineNumber, filename: console_message.filename,
columnNumber: console_message.columnNumber, lineNumber: console_message.lineNumber,
}, columnNumber: console_message.columnNumber,
}; },
for stream in &mut *self.streams.borrow_mut() { };
stream.write_json_packet(&msg); for stream in &mut *browsing_context.streams.borrow_mut() {
stream.write_json_packet(&msg);
}
} }
} }
} }
@ -275,8 +295,16 @@ impl Actor for ConsoleActor {
s => debug!("unrecognized message type requested: \"{}\"", s), s => debug!("unrecognized message type requested: \"{}\"", s),
}; };
} }
let browsing_context =
registry.find::<BrowsingContextActor>(&self.browsing_context);
let mut messages = vec![]; let mut messages = vec![];
for event in self.cached_events.borrow().iter() { for event in self
.cached_events
.borrow()
.get(&browsing_context.active_pipeline.get())
.unwrap_or(&vec![])
.iter()
{
let include = match event { let include = match event {
CachedConsoleMessage::PageError(_) CachedConsoleMessage::PageError(_)
if message_types.contains(CachedConsoleMessageTypes::PAGE_ERROR) => if message_types.contains(CachedConsoleMessageTypes::PAGE_ERROR) =>

View file

@ -6,6 +6,7 @@
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js). //! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js).
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::actors::browsing_context::BrowsingContextActor;
use crate::protocol::JsonPacketStream; use crate::protocol::JsonPacketStream;
use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement, GetRootNode}; use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement, GetRootNode};
use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute}; use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute};
@ -22,7 +23,7 @@ pub struct InspectorActor {
pub pageStyle: RefCell<Option<String>>, pub pageStyle: RefCell<Option<String>>,
pub highlighter: RefCell<Option<String>>, pub highlighter: RefCell<Option<String>>,
pub script_chan: IpcSender<DevtoolScriptControlMsg>, pub script_chan: IpcSender<DevtoolScriptControlMsg>,
pub pipeline: PipelineId, pub browsing_context: String,
} }
#[derive(Serialize)] #[derive(Serialize)]
@ -596,13 +597,15 @@ impl Actor for InspectorActor {
_msg: &Map<String, Value>, _msg: &Map<String, Value>,
stream: &mut TcpStream, stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> { ) -> Result<ActorMessageStatus, ()> {
let browsing_context = registry.find::<BrowsingContextActor>(&self.browsing_context);
let pipeline = browsing_context.active_pipeline.get();
Ok(match msg_type { Ok(match msg_type {
"getWalker" => { "getWalker" => {
if self.walker.borrow().is_none() { if self.walker.borrow().is_none() {
let walker = WalkerActor { let walker = WalkerActor {
name: registry.new_name("walker"), name: registry.new_name("walker"),
script_chan: self.script_chan.clone(), script_chan: self.script_chan.clone(),
pipeline: self.pipeline, pipeline: pipeline,
}; };
let mut walker_name = self.walker.borrow_mut(); let mut walker_name = self.walker.borrow_mut();
*walker_name = Some(walker.name()); *walker_name = Some(walker.name());
@ -610,13 +613,10 @@ impl Actor for InspectorActor {
} }
let (tx, rx) = ipc::channel().unwrap(); let (tx, rx) = ipc::channel().unwrap();
self.script_chan self.script_chan.send(GetRootNode(pipeline, tx)).unwrap();
.send(GetRootNode(self.pipeline, tx))
.unwrap();
let root_info = rx.recv().unwrap().ok_or(())?; let root_info = rx.recv().unwrap().ok_or(())?;
let node = let node = root_info.encode(registry, false, self.script_chan.clone(), pipeline);
root_info.encode(registry, false, self.script_chan.clone(), self.pipeline);
let msg = GetWalkerReply { let msg = GetWalkerReply {
from: self.name(), from: self.name(),
@ -634,7 +634,7 @@ impl Actor for InspectorActor {
let style = PageStyleActor { let style = PageStyleActor {
name: registry.new_name("pageStyle"), name: registry.new_name("pageStyle"),
script_chan: self.script_chan.clone(), script_chan: self.script_chan.clone(),
pipeline: self.pipeline, pipeline: pipeline,
}; };
let mut pageStyle = self.pageStyle.borrow_mut(); let mut pageStyle = self.pageStyle.borrow_mut();
*pageStyle = Some(style.name()); *pageStyle = Some(style.name());

View file

@ -21,29 +21,24 @@ use crate::actor::{Actor, ActorRegistry};
use crate::actors::browsing_context::BrowsingContextActor; use crate::actors::browsing_context::BrowsingContextActor;
use crate::actors::console::ConsoleActor; use crate::actors::console::ConsoleActor;
use crate::actors::device::DeviceActor; use crate::actors::device::DeviceActor;
use crate::actors::emulation::EmulationActor;
use crate::actors::framerate::FramerateActor; use crate::actors::framerate::FramerateActor;
use crate::actors::inspector::InspectorActor;
use crate::actors::network_event::{EventActor, NetworkEventActor, ResponseStartMsg}; use crate::actors::network_event::{EventActor, NetworkEventActor, ResponseStartMsg};
use crate::actors::performance::PerformanceActor; use crate::actors::performance::PerformanceActor;
use crate::actors::preference::PreferenceActor; use crate::actors::preference::PreferenceActor;
use crate::actors::process::ProcessActor; use crate::actors::process::ProcessActor;
use crate::actors::profiler::ProfilerActor;
use crate::actors::root::RootActor; use crate::actors::root::RootActor;
use crate::actors::stylesheets::StyleSheetsActor;
use crate::actors::thread::ThreadActor;
use crate::actors::timeline::TimelineActor;
use crate::actors::worker::WorkerActor; use crate::actors::worker::WorkerActor;
use crate::protocol::JsonPacketStream; use crate::protocol::JsonPacketStream;
use crossbeam_channel::{unbounded, Receiver, Sender}; use crossbeam_channel::{unbounded, Receiver, Sender};
use devtools_traits::{ChromeToDevtoolsControlMsg, ConsoleMessage, DevtoolsControlMsg}; use devtools_traits::{ChromeToDevtoolsControlMsg, ConsoleMessage, DevtoolsControlMsg};
use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo, LogLevel, NetworkEvent}; use devtools_traits::{
DevtoolScriptControlMsg, DevtoolsPageInfo, LogLevel, NavigationState, NetworkEvent,
};
use devtools_traits::{PageError, ScriptToDevtoolsControlMsg, WorkerId}; use devtools_traits::{PageError, ScriptToDevtoolsControlMsg, WorkerId};
use embedder_traits::{EmbedderMsg, EmbedderProxy, PromptDefinition, PromptOrigin, PromptResult}; use embedder_traits::{EmbedderMsg, EmbedderProxy, PromptDefinition, PromptOrigin, PromptResult};
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use msg::constellation_msg::PipelineId; use msg::constellation_msg::{BrowsingContextId, PipelineId};
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::cell::RefCell;
use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap; use std::collections::HashMap;
use std::net::{Shutdown, TcpListener, TcpStream}; use std::net::{Shutdown, TcpListener, TcpStream};
@ -198,7 +193,8 @@ fn run_server(
let mut accepted_connections: Vec<TcpStream> = Vec::new(); let mut accepted_connections: Vec<TcpStream> = Vec::new();
let mut actor_pipelines: HashMap<PipelineId, String> = HashMap::new(); let mut browsing_contexts: HashMap<BrowsingContextId, String> = HashMap::new();
let mut pipelines: HashMap<PipelineId, BrowsingContextId> = HashMap::new();
let mut actor_requests: HashMap<String, String> = HashMap::new(); let mut actor_requests: HashMap<String, String> = HashMap::new();
let mut actor_workers: HashMap<(PipelineId, WorkerId), String> = HashMap::new(); let mut actor_workers: HashMap<(PipelineId, WorkerId), String> = HashMap::new();
@ -243,91 +239,65 @@ fn run_server(
framerate_actor.add_tick(tick); framerate_actor.add_tick(tick);
} }
fn handle_navigate(
actors: Arc<Mutex<ActorRegistry>>,
browsing_contexts: &HashMap<BrowsingContextId, String>,
browsing_context: BrowsingContextId,
state: NavigationState,
) {
let actor_name = browsing_contexts.get(&browsing_context).unwrap();
actors
.lock()
.unwrap()
.find::<BrowsingContextActor>(actor_name)
.navigate(state);
}
// We need separate actor representations for each script global that exists; // We need separate actor representations for each script global that exists;
// clients can theoretically connect to multiple globals simultaneously. // clients can theoretically connect to multiple globals simultaneously.
// TODO: move this into the root or target modules? // TODO: move this into the root or target modules?
fn handle_new_global( fn handle_new_global(
actors: Arc<Mutex<ActorRegistry>>, actors: Arc<Mutex<ActorRegistry>>,
ids: (PipelineId, Option<WorkerId>), ids: (Option<BrowsingContextId>, PipelineId, Option<WorkerId>),
script_sender: IpcSender<DevtoolScriptControlMsg>, script_sender: IpcSender<DevtoolScriptControlMsg>,
actor_pipelines: &mut HashMap<PipelineId, String>, browsing_contexts: &mut HashMap<BrowsingContextId, String>,
pipelines: &mut HashMap<PipelineId, BrowsingContextId>,
actor_workers: &mut HashMap<(PipelineId, WorkerId), String>, actor_workers: &mut HashMap<(PipelineId, WorkerId), String>,
page_info: DevtoolsPageInfo, page_info: DevtoolsPageInfo,
) { ) {
let mut actors = actors.lock().unwrap(); let mut actors = actors.lock().unwrap();
let (pipeline, worker_id) = ids; let (browsing_context, pipeline, worker_id) = ids;
//TODO: move all this actor creation into a constructor method on BrowsingContextActor let console_name = actors.new_name("console");
let (
target,
console,
emulation,
inspector,
timeline,
profiler,
performance,
styleSheets,
thread,
) = {
let console = ConsoleActor {
name: actors.new_name("console"),
script_chan: script_sender.clone(),
pipeline: pipeline,
streams: RefCell::new(Vec::new()),
cached_events: RefCell::new(Vec::new()),
};
let emulation = EmulationActor::new(actors.new_name("emulation")); let browsing_context_name = if let Some(browsing_context) = browsing_context {
pipelines.insert(pipeline, browsing_context);
if let Some(actor) = browsing_contexts.get(&browsing_context) {
actor.to_owned()
} else {
let browsing_context_actor = BrowsingContextActor::new(
console_name.clone(),
browsing_context,
page_info,
pipeline,
script_sender.clone(),
&mut *actors,
);
let name = browsing_context_actor.name();
browsing_contexts.insert(browsing_context, name.clone());
actors.register(Box::new(browsing_context_actor));
name
}
} else {
"".to_owned()
};
let inspector = InspectorActor { // XXXjdm this new actor is useless if it's not a new worker global
name: actors.new_name("inspector"), let console = ConsoleActor {
walker: RefCell::new(None), name: console_name,
pageStyle: RefCell::new(None), cached_events: Default::default(),
highlighter: RefCell::new(None), browsing_context: browsing_context_name,
script_chan: script_sender.clone(),
pipeline: pipeline,
};
let timeline = TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender);
let profiler = ProfilerActor::new(actors.new_name("profiler"));
let performance = PerformanceActor::new(actors.new_name("performance"));
// the strange switch between styleSheets and stylesheets is due
// to an inconsistency in devtools. See Bug #1498893 in bugzilla
let styleSheets = StyleSheetsActor::new(actors.new_name("stylesheets"));
let thread = ThreadActor::new(actors.new_name("context"));
let DevtoolsPageInfo { title, url } = page_info;
let target = BrowsingContextActor {
name: actors.new_name("target"),
title: String::from(title),
url: url.into_string(),
console: console.name(),
emulation: emulation.name(),
inspector: inspector.name(),
timeline: timeline.name(),
profiler: profiler.name(),
performance: performance.name(),
styleSheets: styleSheets.name(),
thread: thread.name(),
};
let root = actors.find_mut::<RootActor>("root");
root.tabs.push(target.name.clone());
(
target,
console,
emulation,
inspector,
timeline,
profiler,
performance,
styleSheets,
thread,
)
}; };
if let Some(id) = worker_id { if let Some(id) = worker_id {
@ -336,36 +306,39 @@ fn run_server(
console: console.name(), console: console.name(),
id: id, id: id,
}; };
let root = actors.find_mut::<RootActor>("root");
root.tabs.push(worker.name.clone());
actor_workers.insert((pipeline, id), worker.name.clone()); actor_workers.insert((pipeline, id), worker.name.clone());
actors.register(Box::new(worker)); actors.register(Box::new(worker));
} }
actor_pipelines.insert(pipeline, target.name.clone());
actors.register(Box::new(target));
actors.register(Box::new(console)); actors.register(Box::new(console));
actors.register(Box::new(emulation));
actors.register(Box::new(inspector));
actors.register(Box::new(timeline));
actors.register(Box::new(profiler));
actors.register(Box::new(performance));
actors.register(Box::new(styleSheets));
actors.register(Box::new(thread));
} }
fn handle_page_error( fn handle_page_error(
actors: Arc<Mutex<ActorRegistry>>, actors: Arc<Mutex<ActorRegistry>>,
id: PipelineId, id: PipelineId,
page_error: PageError, page_error: PageError,
actor_pipelines: &HashMap<PipelineId, String>, browsing_contexts: &HashMap<BrowsingContextId, String>,
pipelines: &HashMap<PipelineId, BrowsingContextId>,
) { ) {
let console_actor_name = let console_actor_name = match find_console_actor(
match find_console_actor(actors.clone(), id, None, &HashMap::new(), actor_pipelines) { actors.clone(),
Some(name) => name, id,
None => return, None,
}; &HashMap::new(),
browsing_contexts,
pipelines,
) {
Some(name) => name,
None => return,
};
let actors = actors.lock().unwrap(); let actors = actors.lock().unwrap();
let console_actor = actors.find::<ConsoleActor>(&console_actor_name); let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
console_actor.handle_page_error(page_error); let browsing_context_actor =
actors.find::<BrowsingContextActor>(&console_actor.browsing_context);
console_actor.handle_page_error(page_error, id, &browsing_context_actor);
} }
fn handle_console_message( fn handle_console_message(
@ -373,37 +346,43 @@ fn run_server(
id: PipelineId, id: PipelineId,
worker_id: Option<WorkerId>, worker_id: Option<WorkerId>,
console_message: ConsoleMessage, console_message: ConsoleMessage,
actor_pipelines: &HashMap<PipelineId, String>, browsing_contexts: &HashMap<BrowsingContextId, String>,
actor_workers: &HashMap<(PipelineId, WorkerId), String>, actor_workers: &HashMap<(PipelineId, WorkerId), String>,
pipelines: &HashMap<PipelineId, BrowsingContextId>,
) { ) {
let console_actor_name = match find_console_actor( let console_actor_name = match find_console_actor(
actors.clone(), actors.clone(),
id, id,
worker_id, worker_id,
actor_workers, actor_workers,
actor_pipelines, browsing_contexts,
pipelines,
) { ) {
Some(name) => name, Some(name) => name,
None => return, None => return,
}; };
let actors = actors.lock().unwrap(); let actors = actors.lock().unwrap();
let console_actor = actors.find::<ConsoleActor>(&console_actor_name); let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
console_actor.handle_console_api(console_message); let browsing_context_actor =
actors.find::<BrowsingContextActor>(&console_actor.browsing_context);
console_actor.handle_console_api(console_message, id, &browsing_context_actor);
} }
fn find_console_actor( fn find_console_actor(
actors: Arc<Mutex<ActorRegistry>>, actors: Arc<Mutex<ActorRegistry>>,
id: PipelineId, pipeline: PipelineId,
worker_id: Option<WorkerId>, worker_id: Option<WorkerId>,
actor_workers: &HashMap<(PipelineId, WorkerId), String>, actor_workers: &HashMap<(PipelineId, WorkerId), String>,
actor_pipelines: &HashMap<PipelineId, String>, browsing_contexts: &HashMap<BrowsingContextId, String>,
pipelines: &HashMap<PipelineId, BrowsingContextId>,
) -> Option<String> { ) -> Option<String> {
let actors = actors.lock().unwrap(); let actors = actors.lock().unwrap();
if let Some(worker_id) = worker_id { if let Some(worker_id) = worker_id {
let actor_name = (*actor_workers).get(&(id, worker_id))?; let actor_name = (*actor_workers).get(&(pipeline, worker_id))?;
Some(actors.find::<WorkerActor>(actor_name).console.clone()) Some(actors.find::<WorkerActor>(actor_name).console.clone())
} else { } else {
let actor_name = (*actor_pipelines).get(&id)?; let id = pipelines.get(&pipeline)?;
let actor_name = browsing_contexts.get(id)?;
Some( Some(
actors actors
.find::<BrowsingContextActor>(actor_name) .find::<BrowsingContextActor>(actor_name)
@ -416,9 +395,10 @@ fn run_server(
fn handle_network_event( fn handle_network_event(
actors: Arc<Mutex<ActorRegistry>>, actors: Arc<Mutex<ActorRegistry>>,
mut connections: Vec<TcpStream>, mut connections: Vec<TcpStream>,
actor_pipelines: &HashMap<PipelineId, String>, browsing_contexts: &HashMap<BrowsingContextId, String>,
actor_requests: &mut HashMap<String, String>, actor_requests: &mut HashMap<String, String>,
actor_workers: &HashMap<(PipelineId, WorkerId), String>, actor_workers: &HashMap<(PipelineId, WorkerId), String>,
pipelines: &HashMap<PipelineId, BrowsingContextId>,
pipeline_id: PipelineId, pipeline_id: PipelineId,
request_id: String, request_id: String,
network_event: NetworkEvent, network_event: NetworkEvent,
@ -428,7 +408,8 @@ fn run_server(
pipeline_id, pipeline_id,
None, None,
actor_workers, actor_workers,
actor_pipelines, browsing_contexts,
pipelines,
) { ) {
Some(name) => name, Some(name) => name,
None => return, None => return,
@ -610,10 +591,15 @@ fn run_server(
actors.clone(), actors.clone(),
ids, ids,
script_sender, script_sender,
&mut actor_pipelines, &mut browsing_contexts,
&mut pipelines,
&mut actor_workers, &mut actor_workers,
pageinfo, pageinfo,
), ),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::Navigate(
browsing_context,
state,
)) => handle_navigate(actors.clone(), &browsing_contexts, browsing_context, state),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ConsoleAPI( DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ConsoleAPI(
id, id,
console_message, console_message,
@ -623,13 +609,20 @@ fn run_server(
id, id,
worker_id, worker_id,
console_message, console_message,
&actor_pipelines, &browsing_contexts,
&actor_workers, &actor_workers,
&pipelines,
), ),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportPageError( DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportPageError(
id, id,
page_error, page_error,
)) => handle_page_error(actors.clone(), id, page_error, &actor_pipelines), )) => handle_page_error(
actors.clone(),
id,
page_error,
&browsing_contexts,
&pipelines,
),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportCSSError( DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportCSSError(
id, id,
css_error, css_error,
@ -646,8 +639,9 @@ fn run_server(
id, id,
None, None,
console_message, console_message,
&actor_pipelines, &browsing_contexts,
&actor_workers, &actor_workers,
&pipelines,
) )
}, },
DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent( DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent(
@ -667,9 +661,10 @@ fn run_server(
handle_network_event( handle_network_event(
actors.clone(), actors.clone(),
connections, connections,
&actor_pipelines, &browsing_contexts,
&mut actor_requests, &mut actor_requests,
&actor_workers, &actor_workers,
&pipelines,
pipeline_id, pipeline_id,
request_id, request_id,
network_event, network_event,

View file

@ -21,7 +21,7 @@ extern crate serde;
use http::method::Method; use http::method::Method;
use http::HeaderMap; use http::HeaderMap;
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::{BrowsingContextId, PipelineId};
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::net::TcpStream; use std::net::TcpStream;
use time::{self, Duration, Tm}; use time::{self, Duration, Tm};
@ -29,7 +29,7 @@ use uuid::Uuid;
// Information would be attached to NewGlobal to be received and show in devtools. // Information would be attached to NewGlobal to be received and show in devtools.
// Extend these fields if we need more information. // Extend these fields if we need more information.
#[derive(Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DevtoolsPageInfo { pub struct DevtoolsPageInfo {
pub title: String, pub title: String,
pub url: ServoUrl, pub url: ServoUrl,
@ -65,16 +65,27 @@ pub enum ChromeToDevtoolsControlMsg {
NetworkEvent(String, NetworkEvent), NetworkEvent(String, NetworkEvent),
} }
/// The state of a page navigation.
#[derive(Debug, Deserialize, Serialize)]
pub enum NavigationState {
/// A browsing context is about to navigate to a given URL.
Start(ServoUrl),
/// A browsing context has completed navigating to the provided pipeline.
Stop(PipelineId, DevtoolsPageInfo),
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
/// Events that the devtools server must act upon. /// Events that the devtools server must act upon.
pub enum ScriptToDevtoolsControlMsg { pub enum ScriptToDevtoolsControlMsg {
/// A new global object was created, associated with a particular pipeline. /// A new global object was created, associated with a particular pipeline.
/// The means of communicating directly with it are provided. /// The means of communicating directly with it are provided.
NewGlobal( NewGlobal(
(PipelineId, Option<WorkerId>), (Option<BrowsingContextId>, PipelineId, Option<WorkerId>),
IpcSender<DevtoolScriptControlMsg>, IpcSender<DevtoolScriptControlMsg>,
DevtoolsPageInfo, DevtoolsPageInfo,
), ),
/// The given browsing context is performing a navigation.
Navigate(BrowsingContextId, NavigationState),
/// A particular page has invoked the console API. /// A particular page has invoked the console API.
ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>), ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>),
/// An animation frame with the given timestamp was processed in a script thread. /// An animation frame with the given timestamp was processed in a script thread.

View file

@ -2008,15 +2008,21 @@ impl Window {
// Step 8 // Step 8
if doc.prompt_to_unload(false) { if doc.prompt_to_unload(false) {
if self.window_proxy().parent().is_some() { let window_proxy = self.window_proxy();
if window_proxy.parent().is_some() {
// Step 10 // Step 10
// If browsingContext is a nested browsing context, // If browsingContext is a nested browsing context,
// then put it in the delaying load events mode. // then put it in the delaying load events mode.
self.window_proxy().start_delaying_load_events_mode(); window_proxy.start_delaying_load_events_mode();
} }
// TODO: step 11, navigationType. // TODO: step 11, navigationType.
// Step 12, 13 // Step 12, 13
ScriptThread::navigate(pipeline_id, load_data, replace); ScriptThread::navigate(
window_proxy.browsing_context_id(),
pipeline_id,
load_data,
replace,
);
}; };
} }

View file

@ -105,7 +105,7 @@ impl Worker {
url: worker_url.clone(), url: worker_url.clone(),
}; };
let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal( let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal(
(pipeline_id, Some(worker_id)), (None, pipeline_id, Some(worker_id)),
devtools_sender.clone(), devtools_sender.clone(),
page_info, page_info,
)); ));

View file

@ -93,7 +93,7 @@ use canvas_traits::webgl::WebGLPipeline;
use crossbeam_channel::{unbounded, Receiver, Sender}; use crossbeam_channel::{unbounded, Receiver, Sender};
use devtools_traits::CSSError; use devtools_traits::CSSError;
use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo}; use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo};
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use devtools_traits::{NavigationState, ScriptToDevtoolsControlMsg, WorkerId};
use embedder_traits::{EmbedderMsg, EventLoopWaker}; use embedder_traits::{EmbedderMsg, EventLoopWaker};
use euclid::default::{Point2D, Rect}; use euclid::default::{Point2D, Rect};
use euclid::Vector2D; use euclid::Vector2D;
@ -947,6 +947,7 @@ impl ScriptThread {
/// Step 13 of https://html.spec.whatwg.org/multipage/#navigate /// Step 13 of https://html.spec.whatwg.org/multipage/#navigate
pub fn navigate( pub fn navigate(
browsing_context: BrowsingContextId,
pipeline_id: PipelineId, pipeline_id: PipelineId,
mut load_data: LoadData, mut load_data: LoadData,
replace: HistoryEntryReplacement, replace: HistoryEntryReplacement,
@ -985,6 +986,12 @@ impl ScriptThread {
.queue(task, global.upcast()) .queue(task, global.upcast())
.expect("Enqueing navigate js task on the DOM manipulation task source failed"); .expect("Enqueing navigate js task on the DOM manipulation task source failed");
} else { } else {
if let Some(ref sender) = script_thread.devtools_chan {
let _ = sender.send(ScriptToDevtoolsControlMsg::Navigate(
browsing_context, NavigationState::Start(load_data.url.clone())
));
}
script_thread script_thread
.script_sender .script_sender
.send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace))) .send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace)))
@ -3338,7 +3345,7 @@ impl ScriptThread {
self.notify_devtools( self.notify_devtools(
document.Title(), document.Title(),
final_url.clone(), final_url.clone(),
(incomplete.pipeline_id, None), (incomplete.browsing_context_id, incomplete.pipeline_id, None),
); );
let parse_input = DOMString::new(); let parse_input = DOMString::new();
@ -3369,7 +3376,7 @@ impl ScriptThread {
&self, &self,
title: DOMString, title: DOMString,
url: ServoUrl, url: ServoUrl,
ids: (PipelineId, Option<WorkerId>), (bc, p, w): (BrowsingContextId, PipelineId, Option<WorkerId>),
) { ) {
if let Some(ref chan) = self.devtools_chan { if let Some(ref chan) = self.devtools_chan {
let page_info = DevtoolsPageInfo { let page_info = DevtoolsPageInfo {
@ -3377,11 +3384,14 @@ impl ScriptThread {
url: url, url: url,
}; };
chan.send(ScriptToDevtoolsControlMsg::NewGlobal( chan.send(ScriptToDevtoolsControlMsg::NewGlobal(
ids, (Some(bc), p, w),
self.devtools_sender.clone(), self.devtools_sender.clone(),
page_info, page_info.clone(),
)) ))
.unwrap(); .unwrap();
let state = NavigationState::Stop(p, page_info);
let _ = chan.send(ScriptToDevtoolsControlMsg::Navigate(bc, state));
} }
} }

View file

@ -87,7 +87,11 @@ impl ServiceWorkerManager {
url: scope_things.script_url.clone(), url: scope_things.script_url.clone(),
}; };
let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal( let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal(
(scope_things.init.pipeline_id, Some(scope_things.worker_id)), (
None,
scope_things.init.pipeline_id,
Some(scope_things.worker_id),
),
devtools_sender, devtools_sender,
page_info, page_info,
)); ));