aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--components/devtools/Cargo.toml1
-rw-r--r--components/devtools/actors/browsing_context.rs181
-rw-r--r--components/devtools/actors/console.rs179
-rw-r--r--components/devtools/actors/inspector.rs16
-rw-r--r--components/devtools/actors/root.rs13
-rw-r--r--components/devtools/actors/worker.rs130
-rw-r--r--components/devtools/lib.rs312
-rw-r--r--components/devtools_traits/lib.rs20
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs14
-rw-r--r--components/script/dom/document.rs8
-rw-r--r--components/script/dom/serviceworkerregistration.rs2
-rw-r--r--components/script/dom/window.rs12
-rw-r--r--components/script/dom/worker.rs33
-rw-r--r--components/script/dom/workerglobalscope.rs3
-rw-r--r--components/script/script_thread.rs20
-rw-r--r--components/script/serviceworker_manager.rs15
17 files changed, 694 insertions, 266 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b9e13dd85bf..da563a7e7ff 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1192,6 +1192,7 @@ dependencies = [
"msg",
"serde",
"serde_json",
+ "servo_url",
"time",
"uuid",
]
diff --git a/components/devtools/Cargo.toml b/components/devtools/Cargo.toml
index 69d440f0f7a..8b7c6d6e199 100644
--- a/components/devtools/Cargo.toml
+++ b/components/devtools/Cargo.toml
@@ -22,5 +22,6 @@ log = "0.4"
msg = {path = "../msg"}
serde = "1.0"
serde_json = "1.0"
+servo_url = { path = "../url" }
time = "0.1"
uuid = {version = "0.8", features = ["v4"]}
diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs
index bdc468f9f84..b90d84d04bd 100644
--- a/components/devtools/actors/browsing_context.rs
+++ b/components/devtools/actors/browsing_context.rs
@@ -8,10 +8,22 @@
//! Supports dynamic attaching and detaching which control notifications of navigation, etc.
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 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 std::cell::{Cell, RefCell};
use std::net::TcpStream;
#[derive(Serialize)]
@@ -108,8 +120,8 @@ pub struct BrowsingContextActorMsg {
pub struct BrowsingContextActor {
pub name: String,
- pub title: String,
- pub url: String,
+ pub title: RefCell<String>,
+ pub url: RefCell<String>,
pub console: String,
pub emulation: String,
pub inspector: String,
@@ -118,6 +130,10 @@ pub struct BrowsingContextActor {
pub performance: String,
pub styleSheets: 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 {
@@ -127,7 +143,7 @@ impl Actor for BrowsingContextActor {
fn handle_message(
&self,
- registry: &ActorRegistry,
+ _registry: &ActorRegistry,
msg_type: &str,
msg: &Map<String, Value>,
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(val) = options.get("performReload") {
if val.as_bool().unwrap_or(false) {
- let console_actor = registry.find::<ConsoleActor>(&self.console);
- let _ = console_actor
+ let _ = self
.script_chan
- .send(DevtoolScriptControlMsg::Reload(console_actor.pipeline));
+ .send(DevtoolScriptControlMsg::Reload(self.active_pipeline.get()));
}
}
}
@@ -159,38 +174,31 @@ impl Actor for BrowsingContextActor {
javascriptEnabled: true,
traits: AttachedTraits {
reconfigure: false,
- frames: false,
+ frames: true,
logInPage: false,
canRewind: false,
watchpoints: false,
},
};
- let console_actor = registry.find::<ConsoleActor>(&self.console);
- console_actor
- .streams
- .borrow_mut()
- .push(stream.try_clone().unwrap());
+ self.streams.borrow_mut().push(stream.try_clone().unwrap());
stream.write_json_packet(&msg);
- console_actor
- .script_chan
- .send(WantsLiveNotifications(console_actor.pipeline, true))
+ self.script_chan
+ .send(WantsLiveNotifications(self.active_pipeline.get(), true))
.unwrap();
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.
"detach" => {
let msg = BrowsingContextDetachedReply {
from: self.name(),
type_: "detached".to_owned(),
};
- let console_actor = registry.find::<ConsoleActor>(&self.console);
- console_actor.streams.borrow_mut().pop();
+ self.streams.borrow_mut().pop();
stream.write_json_packet(&msg);
- console_actor
- .script_chan
- .send(WantsLiveNotifications(console_actor.pipeline, false))
+ self.script_chan
+ .send(WantsLiveNotifications(self.active_pipeline.get(), false))
.unwrap();
ActorMessageStatus::Processed
},
@@ -199,10 +207,11 @@ impl Actor for BrowsingContextActor {
let msg = ListFramesReply {
from: self.name(),
frames: vec![FrameMsg {
- id: 0, //FIXME should match outerwindow id
+ //FIXME: shouldn't ignore pipeline namespace field
+ id: self.active_pipeline.get().index.0.get(),
parentID: 0,
- url: self.url.clone(),
- title: self.title.clone(),
+ url: self.url.borrow().clone(),
+ title: self.title.borrow().clone(),
}],
};
stream.write_json_packet(&msg);
@@ -224,16 +233,82 @@ impl Actor for 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: RefCell::new(String::from(title)),
+ url: RefCell::new(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 {
BrowsingContextActorMsg {
actor: self.name(),
traits: BrowsingContextTraits {
isBrowsingContext: true,
},
- title: self.title.clone(),
- url: self.url.clone(),
- browsingContextId: 0, //FIXME should come from constellation
- outerWindowID: 0, //FIXME: this should probably be the pipeline id
+ title: self.title.borrow().clone(),
+ url: self.url.borrow().clone(),
+ //FIXME: shouldn't ignore pipeline namespace field
+ 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(),
emulationActor: self.emulation.clone(),
inspectorActor: self.inspector.clone(),
@@ -243,4 +318,52 @@ impl BrowsingContextActor {
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);
+ }
+ *self.url.borrow_mut() = url.as_str().to_owned();
+ if let Some(ref t) = title {
+ *self.title.borrow_mut() = t.clone();
+ }
+
+ 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);
+ }
+ }
+
+ pub(crate) fn title_changed(&self, pipeline: PipelineId, title: String) {
+ if pipeline != self.active_pipeline.get() {
+ return;
+ }
+ *self.title.borrow_mut() = title;
+ }
+}
+
+#[derive(Serialize)]
+struct TabNavigated {
+ from: String,
+ #[serde(rename = "type")]
+ type_: String,
+ url: String,
+ title: Option<String>,
+ nativeConsoleAPI: bool,
+ state: String,
+ isFrameSwitching: bool,
}
diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs
index 845e39075b1..2a8b016b93d 100644
--- a/components/devtools/actors/console.rs
+++ b/components/devtools/actors/console.rs
@@ -8,19 +8,23 @@
//! inspection, JS evaluation, autocompletion) in Servo.
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
+use crate::actors::browsing_context::BrowsingContextActor;
use crate::actors::object::ObjectActor;
+use crate::actors::worker::WorkerActor;
use crate::protocol::JsonPacketStream;
-use crate::{ConsoleAPICall, ConsoleMessage, ConsoleMsg, PageErrorMsg};
+use crate::UniqueId;
use devtools_traits::CachedConsoleMessage;
+use devtools_traits::ConsoleMessage;
use devtools_traits::EvaluateJSReply::{ActorValue, BooleanValue, StringValue};
use devtools_traits::EvaluateJSReply::{NullValue, NumberValue, VoidValue};
use devtools_traits::{
CachedConsoleMessageTypes, ConsoleAPI, DevtoolScriptControlMsg, LogLevel, PageError,
};
use ipc_channel::ipc::{self, IpcSender};
-use msg::constellation_msg::PipelineId;
+use msg::constellation_msg::TEST_PIPELINE_ID;
use serde_json::{self, Map, Number, Value};
-use std::cell::RefCell;
+use std::cell::{RefCell, RefMut};
+use std::collections::HashMap;
use std::net::TcpStream;
use time::precise_time_ns;
use uuid::Uuid;
@@ -104,15 +108,52 @@ struct SetPreferencesReply {
updated: Vec<String>,
}
-pub struct ConsoleActor {
+pub(crate) enum Root {
+ BrowsingContext(String),
+ DedicatedWorker(String),
+}
+
+pub(crate) struct ConsoleActor {
pub name: String,
- pub pipeline: PipelineId,
- pub script_chan: IpcSender<DevtoolScriptControlMsg>,
- pub streams: RefCell<Vec<TcpStream>>,
- pub cached_events: RefCell<Vec<CachedConsoleMessage>>,
+ pub root: Root,
+ pub cached_events: RefCell<HashMap<UniqueId, Vec<CachedConsoleMessage>>>,
}
impl ConsoleActor {
+ fn script_chan<'a>(
+ &self,
+ registry: &'a ActorRegistry,
+ ) -> &'a IpcSender<DevtoolScriptControlMsg> {
+ match &self.root {
+ Root::BrowsingContext(bc) => &registry.find::<BrowsingContextActor>(bc).script_chan,
+ Root::DedicatedWorker(worker) => &registry.find::<WorkerActor>(worker).script_chan,
+ }
+ }
+
+ fn streams_mut<'a>(&self, registry: &'a ActorRegistry) -> RefMut<'a, Vec<TcpStream>> {
+ match &self.root {
+ Root::BrowsingContext(bc) => registry
+ .find::<BrowsingContextActor>(bc)
+ .streams
+ .borrow_mut(),
+ Root::DedicatedWorker(worker) => {
+ registry.find::<WorkerActor>(worker).streams.borrow_mut()
+ },
+ }
+ }
+
+ fn current_unique_id(&self, registry: &ActorRegistry) -> UniqueId {
+ match &self.root {
+ Root::BrowsingContext(bc) => UniqueId::Pipeline(
+ registry
+ .find::<BrowsingContextActor>(bc)
+ .active_pipeline
+ .get(),
+ ),
+ Root::DedicatedWorker(w) => UniqueId::Worker(registry.find::<WorkerActor>(w).id),
+ }
+ }
+
fn evaluateJS(
&self,
registry: &ActorRegistry,
@@ -120,9 +161,15 @@ impl ConsoleActor {
) -> Result<EvaluateJSReply, ()> {
let input = msg.get("text").unwrap().as_str().unwrap().to_owned();
let (chan, port) = ipc::channel().unwrap();
- self.script_chan
+ // FIXME: redesign messages so we don't have to fake pipeline ids when
+ // communicating with workers.
+ let pipeline = match self.current_unique_id(registry) {
+ UniqueId::Pipeline(p) => p,
+ UniqueId::Worker(_) => TEST_PIPELINE_ID,
+ };
+ self.script_chan(registry)
.send(DevtoolScriptControlMsg::EvaluateJS(
- self.pipeline,
+ pipeline,
input.clone(),
chan,
))
@@ -191,21 +238,35 @@ impl ConsoleActor {
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,
+ id: UniqueId,
+ registry: &ActorRegistry,
+ ) {
self.cached_events
.borrow_mut()
+ .entry(id.clone())
+ .or_insert(vec![])
.push(CachedConsoleMessage::PageError(page_error.clone()));
- let msg = PageErrorMsg {
- from: self.name(),
- type_: "pageError".to_owned(),
- pageError: page_error,
- };
- for stream in &mut *self.streams.borrow_mut() {
- stream.write_json_packet(&msg);
+ if id == self.current_unique_id(registry) {
+ let msg = PageErrorMsg {
+ from: self.name(),
+ type_: "pageError".to_owned(),
+ pageError: page_error,
+ };
+ for stream in &mut *self.streams_mut(registry) {
+ 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,
+ id: UniqueId,
+ registry: &ActorRegistry,
+ ) {
let level = match console_message.logLevel {
LogLevel::Debug => "debug",
LogLevel::Info => "info",
@@ -216,6 +277,8 @@ impl ConsoleActor {
.to_owned();
self.cached_events
.borrow_mut()
+ .entry(id.clone())
+ .or_insert(vec![])
.push(CachedConsoleMessage::ConsoleAPI(ConsoleAPI {
type_: "ConsoleAPI".to_owned(),
level: level.clone(),
@@ -226,20 +289,22 @@ impl ConsoleActor {
private: false,
arguments: vec![console_message.message.clone()],
}));
- let msg = ConsoleAPICall {
- from: self.name(),
- type_: "consoleAPICall".to_owned(),
- message: ConsoleMsg {
- level: level,
- timeStamp: precise_time_ns(),
- arguments: vec![console_message.message],
- filename: console_message.filename,
- lineNumber: console_message.lineNumber,
- columnNumber: console_message.columnNumber,
- },
- };
- for stream in &mut *self.streams.borrow_mut() {
- stream.write_json_packet(&msg);
+ if id == self.current_unique_id(registry) {
+ let msg = ConsoleAPICall {
+ from: self.name(),
+ type_: "consoleAPICall".to_owned(),
+ message: ConsoleMsg {
+ level: level,
+ timeStamp: precise_time_ns(),
+ arguments: vec![console_message.message],
+ filename: console_message.filename,
+ lineNumber: console_message.lineNumber,
+ columnNumber: console_message.columnNumber,
+ },
+ };
+ for stream in &mut *self.streams_mut(registry) {
+ stream.write_json_packet(&msg);
+ }
}
}
}
@@ -257,6 +322,13 @@ impl Actor for ConsoleActor {
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
Ok(match msg_type {
+ "clearMessagesCache" => {
+ self.cached_events
+ .borrow_mut()
+ .remove(&self.current_unique_id(registry));
+ ActorMessageStatus::Processed
+ },
+
"getCachedMessages" => {
let str_types = msg
.get("messageTypes")
@@ -276,7 +348,13 @@ impl Actor for ConsoleActor {
};
}
let mut messages = vec![];
- for event in self.cached_events.borrow().iter() {
+ for event in self
+ .cached_events
+ .borrow()
+ .get(&self.current_unique_id(registry))
+ .unwrap_or(&vec![])
+ .iter()
+ {
let include = match event {
CachedConsoleMessage::PageError(_)
if message_types.contains(CachedConsoleMessageTypes::PAGE_ERROR) =>
@@ -365,6 +443,13 @@ impl Actor for ConsoleActor {
// Emit an eager reply so that the client starts listening
// for an async event with the resultID
stream.write_json_packet(&early_reply);
+
+ if msg.get("eager").and_then(|v| v.as_bool()).unwrap_or(false) {
+ // We don't support the side-effect free evaluation that eager evalaution
+ // really needs.
+ return Ok(ActorMessageStatus::Processed);
+ }
+
let reply = self.evaluateJS(&registry, &msg).unwrap();
let msg = EvaluateJSEvent {
from: self.name(),
@@ -395,3 +480,29 @@ impl Actor for ConsoleActor {
})
}
}
+
+#[derive(Serialize)]
+struct ConsoleAPICall {
+ from: String,
+ #[serde(rename = "type")]
+ type_: String,
+ message: ConsoleMsg,
+}
+
+#[derive(Serialize)]
+struct ConsoleMsg {
+ level: String,
+ timeStamp: u64,
+ arguments: Vec<String>,
+ filename: String,
+ lineNumber: usize,
+ columnNumber: usize,
+}
+
+#[derive(Serialize)]
+struct PageErrorMsg {
+ from: String,
+ #[serde(rename = "type")]
+ type_: String,
+ pageError: PageError,
+}
diff --git a/components/devtools/actors/inspector.rs b/components/devtools/actors/inspector.rs
index cd3e5bf323c..d2f0929ac89 100644
--- a/components/devtools/actors/inspector.rs
+++ b/components/devtools/actors/inspector.rs
@@ -6,6 +6,7 @@
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js).
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
+use crate::actors::browsing_context::BrowsingContextActor;
use crate::protocol::JsonPacketStream;
use devtools_traits::DevtoolScriptControlMsg::{GetChildren, GetDocumentElement, GetRootNode};
use devtools_traits::DevtoolScriptControlMsg::{GetLayout, ModifyAttribute};
@@ -22,7 +23,7 @@ pub struct InspectorActor {
pub pageStyle: RefCell<Option<String>>,
pub highlighter: RefCell<Option<String>>,
pub script_chan: IpcSender<DevtoolScriptControlMsg>,
- pub pipeline: PipelineId,
+ pub browsing_context: String,
}
#[derive(Serialize)]
@@ -596,13 +597,15 @@ impl Actor for InspectorActor {
_msg: &Map<String, Value>,
stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
+ let browsing_context = registry.find::<BrowsingContextActor>(&self.browsing_context);
+ let pipeline = browsing_context.active_pipeline.get();
Ok(match msg_type {
"getWalker" => {
if self.walker.borrow().is_none() {
let walker = WalkerActor {
name: registry.new_name("walker"),
script_chan: self.script_chan.clone(),
- pipeline: self.pipeline,
+ pipeline: pipeline,
};
let mut walker_name = self.walker.borrow_mut();
*walker_name = Some(walker.name());
@@ -610,13 +613,10 @@ impl Actor for InspectorActor {
}
let (tx, rx) = ipc::channel().unwrap();
- self.script_chan
- .send(GetRootNode(self.pipeline, tx))
- .unwrap();
+ self.script_chan.send(GetRootNode(pipeline, tx)).unwrap();
let root_info = rx.recv().unwrap().ok_or(())?;
- let node =
- root_info.encode(registry, false, self.script_chan.clone(), self.pipeline);
+ let node = root_info.encode(registry, false, self.script_chan.clone(), pipeline);
let msg = GetWalkerReply {
from: self.name(),
@@ -634,7 +634,7 @@ impl Actor for InspectorActor {
let style = PageStyleActor {
name: registry.new_name("pageStyle"),
script_chan: self.script_chan.clone(),
- pipeline: self.pipeline,
+ pipeline: pipeline,
};
let mut pageStyle = self.pageStyle.borrow_mut();
*pageStyle = Some(style.name());
diff --git a/components/devtools/actors/root.rs b/components/devtools/actors/root.rs
index da7483ffb34..0010afd3b9f 100644
--- a/components/devtools/actors/root.rs
+++ b/components/devtools/actors/root.rs
@@ -10,6 +10,7 @@ use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
use crate::actors::device::DeviceActor;
use crate::actors::performance::PerformanceActor;
+use crate::actors::worker::{WorkerActor, WorkerMsg};
use crate::protocol::{ActorDescription, JsonPacketStream};
use serde_json::{Map, Value};
use std::net::TcpStream;
@@ -73,11 +74,6 @@ struct ListWorkersReply {
}
#[derive(Serialize)]
-struct WorkerMsg {
- id: u32,
-}
-
-#[derive(Serialize)]
struct ListServiceWorkerRegistrationsReply {
from: String,
registrations: Vec<u32>, // TODO: follow actual JSON structure.
@@ -110,6 +106,7 @@ struct GetProcessResponse {
pub struct RootActor {
pub tabs: Vec<String>,
+ pub workers: Vec<String>,
pub performance: String,
pub device: String,
pub preference: String,
@@ -203,7 +200,11 @@ impl Actor for RootActor {
"listWorkers" => {
let reply = ListWorkersReply {
from: self.name(),
- workers: vec![],
+ workers: self
+ .workers
+ .iter()
+ .map(|name| registry.find::<WorkerActor>(name).encodable())
+ .collect(),
};
stream.write_json_packet(&reply);
ActorMessageStatus::Processed
diff --git a/components/devtools/actors/worker.rs b/components/devtools/actors/worker.rs
index 082d719de20..fd578d0d3dc 100644
--- a/components/devtools/actors/worker.rs
+++ b/components/devtools/actors/worker.rs
@@ -3,14 +3,49 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
-use devtools_traits::WorkerId;
+use crate::protocol::JsonPacketStream;
+use devtools_traits::DevtoolScriptControlMsg::WantsLiveNotifications;
+use devtools_traits::{DevtoolScriptControlMsg, WorkerId};
+use ipc_channel::ipc::IpcSender;
+use msg::constellation_msg::TEST_PIPELINE_ID;
use serde_json::{Map, Value};
+use servo_url::ServoUrl;
+use std::cell::RefCell;
use std::net::TcpStream;
+#[derive(Clone, Copy)]
+#[allow(dead_code)]
+pub enum WorkerType {
+ Dedicated = 0,
+ Shared = 1,
+ Service = 2,
+}
+
pub struct WorkerActor {
pub name: String,
pub console: String,
+ pub thread: String,
pub id: WorkerId,
+ pub url: ServoUrl,
+ pub type_: WorkerType,
+ pub script_chan: IpcSender<DevtoolScriptControlMsg>,
+ pub streams: RefCell<Vec<TcpStream>>,
+}
+
+impl WorkerActor {
+ pub(crate) fn encodable(&self) -> WorkerMsg {
+ WorkerMsg {
+ actor: self.name.clone(),
+ consoleActor: self.console.clone(),
+ threadActor: self.thread.clone(),
+ id: self.id.0.to_string(),
+ url: self.url.to_string(),
+ traits: WorkerTraits {
+ isParentInterceptEnabled: false,
+ },
+ type_: self.type_ as u32,
+ }
+ }
}
impl Actor for WorkerActor {
@@ -19,11 +54,94 @@ impl Actor for WorkerActor {
}
fn handle_message(
&self,
- _: &ActorRegistry,
- _: &str,
- _: &Map<String, Value>,
- _: &mut TcpStream,
+ _registry: &ActorRegistry,
+ msg_type: &str,
+ _msg: &Map<String, Value>,
+ stream: &mut TcpStream,
) -> Result<ActorMessageStatus, ()> {
- Ok(ActorMessageStatus::Processed)
+ Ok(match msg_type {
+ "attach" => {
+ let msg = AttachedReply {
+ from: self.name(),
+ type_: "attached".to_owned(),
+ url: self.url.as_str().to_owned(),
+ };
+ self.streams.borrow_mut().push(stream.try_clone().unwrap());
+ stream.write_json_packet(&msg);
+ // FIXME: fix messages to not require forging a pipeline for worker messages
+ self.script_chan
+ .send(WantsLiveNotifications(TEST_PIPELINE_ID, true))
+ .unwrap();
+ ActorMessageStatus::Processed
+ },
+
+ "connect" => {
+ let msg = ConnectReply {
+ from: self.name(),
+ type_: "connected".to_owned(),
+ threadActor: self.thread.clone(),
+ consoleActor: self.console.clone(),
+ };
+ stream.write_json_packet(&msg);
+ ActorMessageStatus::Processed
+ },
+
+ "detach" => {
+ let msg = DetachedReply {
+ from: self.name(),
+ type_: "detached".to_string(),
+ };
+ // FIXME: we should ensure we're removing the correct stream.
+ self.streams.borrow_mut().pop();
+ stream.write_json_packet(&msg);
+ self.script_chan
+ .send(WantsLiveNotifications(TEST_PIPELINE_ID, false))
+ .unwrap();
+ ActorMessageStatus::Processed
+ },
+
+ _ => ActorMessageStatus::Ignored,
+ })
}
}
+
+#[derive(Serialize)]
+struct DetachedReply {
+ from: String,
+ #[serde(rename = "type")]
+ type_: String,
+}
+
+#[derive(Serialize)]
+struct AttachedReply {
+ from: String,
+ #[serde(rename = "type")]
+ type_: String,
+ url: String,
+}
+
+#[derive(Serialize)]
+struct ConnectReply {
+ from: String,
+ #[serde(rename = "type")]
+ type_: String,
+ threadActor: String,
+ consoleActor: String,
+}
+
+#[derive(Serialize)]
+struct WorkerTraits {
+ isParentInterceptEnabled: bool,
+}
+
+#[derive(Serialize)]
+pub(crate) struct WorkerMsg {
+ actor: String,
+ consoleActor: String,
+ threadActor: String,
+ id: String,
+ url: String,
+ traits: WorkerTraits,
+ #[serde(rename = "type")]
+ type_: u32,
+}
diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs
index 661d6ac94e1..2227be98549 100644
--- a/components/devtools/lib.rs
+++ b/components/devtools/lib.rs
@@ -19,31 +19,27 @@ extern crate serde;
use crate::actor::{Actor, ActorRegistry};
use crate::actors::browsing_context::BrowsingContextActor;
-use crate::actors::console::ConsoleActor;
+use crate::actors::console::{ConsoleActor, Root};
use crate::actors::device::DeviceActor;
-use crate::actors::emulation::EmulationActor;
use crate::actors::framerate::FramerateActor;
-use crate::actors::inspector::InspectorActor;
use crate::actors::network_event::{EventActor, NetworkEventActor, ResponseStartMsg};
use crate::actors::performance::PerformanceActor;
use crate::actors::preference::PreferenceActor;
use crate::actors::process::ProcessActor;
-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::actors::worker::WorkerActor;
+use crate::actors::worker::{WorkerActor, WorkerType};
use crate::protocol::JsonPacketStream;
use crossbeam_channel::{unbounded, Receiver, Sender};
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 embedder_traits::{EmbedderMsg, EmbedderProxy, PromptDefinition, PromptOrigin, PromptResult};
use ipc_channel::ipc::{self, IpcSender};
-use msg::constellation_msg::PipelineId;
+use msg::constellation_msg::{BrowsingContextId, PipelineId};
use std::borrow::ToOwned;
-use std::cell::RefCell;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::net::{Shutdown, TcpListener, TcpStream};
@@ -74,30 +70,10 @@ mod actors {
}
mod protocol;
-#[derive(Serialize)]
-struct ConsoleAPICall {
- from: String,
- #[serde(rename = "type")]
- type_: String,
- message: ConsoleMsg,
-}
-
-#[derive(Serialize)]
-struct ConsoleMsg {
- level: String,
- timeStamp: u64,
- arguments: Vec<String>,
- filename: String,
- lineNumber: usize,
- columnNumber: usize,
-}
-
-#[derive(Serialize)]
-struct PageErrorMsg {
- from: String,
- #[serde(rename = "type")]
- type_: String,
- pageError: PageError,
+#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+enum UniqueId {
+ Pipeline(PipelineId),
+ Worker(WorkerId),
}
#[derive(Serialize)]
@@ -181,6 +157,7 @@ fn run_server(
let root = Box::new(RootActor {
tabs: vec![],
+ workers: vec![],
device: device.name(),
performance: performance.name(),
preference: preference.name(),
@@ -198,10 +175,11 @@ fn run_server(
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_workers: HashMap<(PipelineId, WorkerId), String> = HashMap::new();
+ let mut actor_workers: HashMap<WorkerId, String> = HashMap::new();
/// Process the input from a single devtools client until EOF.
fn handle_client(actors: Arc<Mutex<ActorRegistry>>, mut stream: TcpStream) {
@@ -243,129 +221,139 @@ fn run_server(
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);
+ }
+
+ fn handle_title_changed(
+ actors: Arc<Mutex<ActorRegistry>>,
+ pipelines: &HashMap<PipelineId, BrowsingContextId>,
+ browsing_contexts: &HashMap<BrowsingContextId, String>,
+ pipeline: PipelineId,
+ title: String,
+ ) {
+ let bc = match pipelines.get(&pipeline) {
+ Some(bc) => bc,
+ None => return,
+ };
+ let name = match browsing_contexts.get(&bc) {
+ Some(name) => name,
+ None => return,
+ };
+ let actors = actors.lock().unwrap();
+ let browsing_context = actors.find::<BrowsingContextActor>(name);
+ browsing_context.title_changed(pipeline, title);
+ }
+
// We need separate actor representations for each script global that exists;
// clients can theoretically connect to multiple globals simultaneously.
// TODO: move this into the root or target modules?
fn handle_new_global(
actors: Arc<Mutex<ActorRegistry>>,
- ids: (PipelineId, Option<WorkerId>),
+ ids: (BrowsingContextId, PipelineId, Option<WorkerId>),
script_sender: IpcSender<DevtoolScriptControlMsg>,
- actor_pipelines: &mut HashMap<PipelineId, String>,
- actor_workers: &mut HashMap<(PipelineId, WorkerId), String>,
+ browsing_contexts: &mut HashMap<BrowsingContextId, String>,
+ pipelines: &mut HashMap<PipelineId, BrowsingContextId>,
+ actor_workers: &mut HashMap<WorkerId, String>,
page_info: DevtoolsPageInfo,
) {
let mut actors = actors.lock().unwrap();
- let (pipeline, worker_id) = ids;
-
- //TODO: move all this actor creation into a constructor method on BrowsingContextActor
- 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 inspector = InspectorActor {
- name: actors.new_name("inspector"),
- walker: RefCell::new(None),
- pageStyle: RefCell::new(None),
- highlighter: RefCell::new(None),
- script_chan: script_sender.clone(),
- pipeline: pipeline,
- };
+ let (browsing_context, pipeline, worker_id) = ids;
- let timeline = TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender);
+ let console_name = actors.new_name("console");
- let profiler = ProfilerActor::new(actors.new_name("profiler"));
- let performance = PerformanceActor::new(actors.new_name("performance"));
+ let parent_actor = if let Some(id) = worker_id {
+ assert!(pipelines.get(&pipeline).is_some());
+ assert!(browsing_contexts.get(&browsing_context).is_some());
- // 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 thread_name = thread.name();
+ actors.register(Box::new(thread));
- 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 worker_name = actors.new_name("worker");
+ let worker = WorkerActor {
+ name: worker_name.clone(),
+ console: console_name.clone(),
+ thread: thread_name,
+ id: id,
+ url: page_info.url.clone(),
+ type_: WorkerType::Dedicated,
+ script_chan: script_sender,
+ streams: Default::default(),
};
-
let root = actors.find_mut::<RootActor>("root");
- root.tabs.push(target.name.clone());
-
- (
- target,
- console,
- emulation,
- inspector,
- timeline,
- profiler,
- performance,
- styleSheets,
- thread,
+ root.workers.push(worker.name.clone());
+
+ actor_workers.insert(id, worker_name.clone());
+ actors.register(Box::new(worker));
+
+ Root::DedicatedWorker(worker_name)
+ } else {
+ pipelines.insert(pipeline, browsing_context);
+ Root::BrowsingContext(
+ 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,
+ &mut *actors,
+ );
+ let name = browsing_context_actor.name();
+ browsing_contexts.insert(browsing_context, name.clone());
+ actors.register(Box::new(browsing_context_actor));
+ name
+ },
)
};
- if let Some(id) = worker_id {
- let worker = WorkerActor {
- name: actors.new_name("worker"),
- console: console.name(),
- id: id,
- };
- actor_workers.insert((pipeline, id), worker.name.clone());
- actors.register(Box::new(worker));
- }
+ let console = ConsoleActor {
+ name: console_name,
+ cached_events: Default::default(),
+ root: parent_actor,
+ };
- actor_pipelines.insert(pipeline, target.name.clone());
- actors.register(Box::new(target));
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(
actors: Arc<Mutex<ActorRegistry>>,
id: PipelineId,
+ worker_id: Option<WorkerId>,
page_error: PageError,
- actor_pipelines: &HashMap<PipelineId, String>,
+ browsing_contexts: &HashMap<BrowsingContextId, String>,
+ actor_workers: &HashMap<WorkerId, String>,
+ pipelines: &HashMap<PipelineId, BrowsingContextId>,
) {
- let console_actor_name =
- match find_console_actor(actors.clone(), id, None, &HashMap::new(), actor_pipelines) {
- Some(name) => name,
- None => return,
- };
+ let console_actor_name = match find_console_actor(
+ actors.clone(),
+ id,
+ worker_id,
+ actor_workers,
+ browsing_contexts,
+ pipelines,
+ ) {
+ Some(name) => name,
+ None => return,
+ };
let actors = actors.lock().unwrap();
let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
- console_actor.handle_page_error(page_error);
+ let id = worker_id.map_or(UniqueId::Pipeline(id), UniqueId::Worker);
+ console_actor.handle_page_error(page_error, id, &*actors);
}
fn handle_console_message(
@@ -373,37 +361,42 @@ fn run_server(
id: PipelineId,
worker_id: Option<WorkerId>,
console_message: ConsoleMessage,
- actor_pipelines: &HashMap<PipelineId, String>,
- actor_workers: &HashMap<(PipelineId, WorkerId), String>,
+ browsing_contexts: &HashMap<BrowsingContextId, String>,
+ actor_workers: &HashMap<WorkerId, String>,
+ pipelines: &HashMap<PipelineId, BrowsingContextId>,
) {
let console_actor_name = match find_console_actor(
actors.clone(),
id,
worker_id,
actor_workers,
- actor_pipelines,
+ browsing_contexts,
+ pipelines,
) {
Some(name) => name,
None => return,
};
let actors = actors.lock().unwrap();
let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
- console_actor.handle_console_api(console_message);
+ let id = worker_id.map_or(UniqueId::Pipeline(id), UniqueId::Worker);
+ console_actor.handle_console_api(console_message, id, &*actors);
}
fn find_console_actor(
actors: Arc<Mutex<ActorRegistry>>,
- id: PipelineId,
+ pipeline: PipelineId,
worker_id: Option<WorkerId>,
- actor_workers: &HashMap<(PipelineId, WorkerId), String>,
- actor_pipelines: &HashMap<PipelineId, String>,
+ actor_workers: &HashMap<WorkerId, String>,
+ browsing_contexts: &HashMap<BrowsingContextId, String>,
+ pipelines: &HashMap<PipelineId, BrowsingContextId>,
) -> Option<String> {
let actors = actors.lock().unwrap();
if let Some(worker_id) = worker_id {
- let actor_name = (*actor_workers).get(&(id, worker_id))?;
+ let actor_name = actor_workers.get(&worker_id)?;
Some(actors.find::<WorkerActor>(actor_name).console.clone())
} else {
- let actor_name = (*actor_pipelines).get(&id)?;
+ let id = pipelines.get(&pipeline)?;
+ let actor_name = browsing_contexts.get(id)?;
Some(
actors
.find::<BrowsingContextActor>(actor_name)
@@ -416,9 +409,10 @@ fn run_server(
fn handle_network_event(
actors: Arc<Mutex<ActorRegistry>>,
mut connections: Vec<TcpStream>,
- actor_pipelines: &HashMap<PipelineId, String>,
+ browsing_contexts: &HashMap<BrowsingContextId, String>,
actor_requests: &mut HashMap<String, String>,
- actor_workers: &HashMap<(PipelineId, WorkerId), String>,
+ actor_workers: &HashMap<WorkerId, String>,
+ pipelines: &HashMap<PipelineId, BrowsingContextId>,
pipeline_id: PipelineId,
request_id: String,
network_event: NetworkEvent,
@@ -428,7 +422,8 @@ fn run_server(
pipeline_id,
None,
actor_workers,
- actor_pipelines,
+ browsing_contexts,
+ pipelines,
) {
Some(name) => name,
None => return,
@@ -589,6 +584,7 @@ fn run_server(
.expect("Thread spawning failed");
while let Ok(msg) = receiver.recv() {
+ debug!("{:?}", msg);
match msg {
DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::AddClient(stream)) => {
let actors = actors.clone();
@@ -602,6 +598,16 @@ fn run_server(
actor_name,
tick,
)) => handle_framerate_tick(actors.clone(), actor_name, tick),
+ DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::TitleChanged(
+ pipeline,
+ title,
+ )) => handle_title_changed(
+ actors.clone(),
+ &pipelines,
+ &browsing_contexts,
+ pipeline,
+ title,
+ ),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::NewGlobal(
ids,
script_sender,
@@ -610,10 +616,15 @@ fn run_server(
actors.clone(),
ids,
script_sender,
- &mut actor_pipelines,
+ &mut browsing_contexts,
+ &mut pipelines,
&mut actor_workers,
pageinfo,
),
+ DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::Navigate(
+ browsing_context,
+ state,
+ )) => handle_navigate(actors.clone(), &browsing_contexts, browsing_context, state),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ConsoleAPI(
id,
console_message,
@@ -623,13 +634,22 @@ fn run_server(
id,
worker_id,
console_message,
- &actor_pipelines,
+ &browsing_contexts,
&actor_workers,
+ &pipelines,
),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportPageError(
id,
page_error,
- )) => handle_page_error(actors.clone(), id, page_error, &actor_pipelines),
+ )) => handle_page_error(
+ actors.clone(),
+ id,
+ None,
+ page_error,
+ &browsing_contexts,
+ &actor_workers,
+ &pipelines,
+ ),
DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportCSSError(
id,
css_error,
@@ -646,8 +666,9 @@ fn run_server(
id,
None,
console_message,
- &actor_pipelines,
+ &browsing_contexts,
&actor_workers,
+ &pipelines,
)
},
DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent(
@@ -667,9 +688,10 @@ fn run_server(
handle_network_event(
actors.clone(),
connections,
- &actor_pipelines,
+ &browsing_contexts,
&mut actor_requests,
&actor_workers,
+ &pipelines,
pipeline_id,
request_id,
network_event,
diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs
index 5396d64fade..7196c3a5b32 100644
--- a/components/devtools_traits/lib.rs
+++ b/components/devtools_traits/lib.rs
@@ -21,7 +21,7 @@ extern crate serde;
use http::method::Method;
use http::HeaderMap;
use ipc_channel::ipc::IpcSender;
-use msg::constellation_msg::PipelineId;
+use msg::constellation_msg::{BrowsingContextId, PipelineId};
use servo_url::ServoUrl;
use std::net::TcpStream;
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.
// Extend these fields if we need more information.
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DevtoolsPageInfo {
pub title: String,
pub url: ServoUrl,
@@ -65,16 +65,27 @@ pub enum ChromeToDevtoolsControlMsg {
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)]
/// Events that the devtools server must act upon.
pub enum ScriptToDevtoolsControlMsg {
/// A new global object was created, associated with a particular pipeline.
/// The means of communicating directly with it are provided.
NewGlobal(
- (PipelineId, Option<WorkerId>),
+ (BrowsingContextId, PipelineId, Option<WorkerId>),
IpcSender<DevtoolScriptControlMsg>,
DevtoolsPageInfo,
),
+ /// The given browsing context is performing a navigation.
+ Navigate(BrowsingContextId, NavigationState),
/// A particular page has invoked the console API.
ConsoleAPI(PipelineId, ConsoleMessage, Option<WorkerId>),
/// An animation frame with the given timestamp was processed in a script thread.
@@ -86,6 +97,9 @@ pub enum ScriptToDevtoolsControlMsg {
/// Report a page error for the given pipeline
ReportPageError(PipelineId, PageError),
+
+ /// Report a page title change
+ TitleChanged(PipelineId, String),
}
/// Serialized JS return values
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index 150f7920fff..a4970afcca4 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -43,7 +43,7 @@ use js::jsapi::JS_AddInterruptCallback;
use js::jsapi::{Heap, JSContext, JSObject};
use js::jsval::UndefinedValue;
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
-use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId};
+use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId};
use net_traits::image_cache::ImageCache;
use net_traits::request::{CredentialsMode, Destination, ParserMetadata};
use net_traits::request::{Referrer, RequestBuilder, RequestMode};
@@ -180,6 +180,7 @@ pub struct DedicatedWorkerGlobalScope {
parent_sender: Box<dyn ScriptChan + Send>,
#[ignore_malloc_size_of = "Arc"]
image_cache: Arc<dyn ImageCache>,
+ browsing_context: Option<BrowsingContextId>,
}
impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope {
@@ -221,6 +222,7 @@ impl DedicatedWorkerGlobalScope {
receiver: Receiver<DedicatedWorkerScriptMsg>,
closing: Arc<AtomicBool>,
image_cache: Arc<dyn ImageCache>,
+ browsing_context: Option<BrowsingContextId>,
) -> DedicatedWorkerGlobalScope {
DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(
@@ -237,6 +239,7 @@ impl DedicatedWorkerGlobalScope {
parent_sender: parent_sender,
worker: DomRefCell::new(None),
image_cache: image_cache,
+ browsing_context,
}
}
@@ -253,6 +256,7 @@ impl DedicatedWorkerGlobalScope {
receiver: Receiver<DedicatedWorkerScriptMsg>,
closing: Arc<AtomicBool>,
image_cache: Arc<dyn ImageCache>,
+ browsing_context: Option<BrowsingContextId>,
) -> DomRoot<DedicatedWorkerGlobalScope> {
let cx = runtime.cx();
let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited(
@@ -267,6 +271,7 @@ impl DedicatedWorkerGlobalScope {
receiver,
closing,
image_cache,
+ browsing_context,
));
unsafe { DedicatedWorkerGlobalScopeBinding::Wrap(SafeJSContext::from_ptr(cx), scope) }
}
@@ -286,6 +291,7 @@ impl DedicatedWorkerGlobalScope {
worker_type: WorkerType,
closing: Arc<AtomicBool>,
image_cache: Arc<dyn ImageCache>,
+ browsing_context: Option<BrowsingContextId>,
) {
let serialized_worker_url = worker_url.to_string();
let name = format!("WebWorker for {}", serialized_worker_url);
@@ -354,6 +360,7 @@ impl DedicatedWorkerGlobalScope {
receiver,
closing,
image_cache,
+ browsing_context,
);
// FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
// registration (#6631), so we instead use a random number and cross our fingers.
@@ -467,6 +474,7 @@ impl DedicatedWorkerGlobalScope {
}
fn handle_mixed_message(&self, msg: MixedMessage) {
+ // FIXME(#26324): `self.worker` is None in devtools messages.
match msg {
MixedMessage::FromDevtools(msg) => match msg {
DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => {
@@ -551,6 +559,10 @@ impl DedicatedWorkerGlobalScope {
.expect("Sending to parent failed");
Ok(())
}
+
+ pub(crate) fn browsing_context(&self) -> Option<BrowsingContextId> {
+ self.browsing_context
+ }
}
#[allow(unsafe_code)]
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 2d0f2ce1c26..3606ffc7c9d 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -934,6 +934,14 @@ impl Document {
pub fn title_changed(&self) {
if self.browsing_context().is_some() {
self.send_title_to_embedder();
+ let global = self.window.upcast::<GlobalScope>();
+ if let Some(ref chan) = global.devtools_chan() {
+ let title = String::from(self.Title());
+ let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged(
+ global.pipeline_id(),
+ title,
+ ));
+ }
}
}
diff --git a/components/script/dom/serviceworkerregistration.rs b/components/script/dom/serviceworkerregistration.rs
index f242da12404..9bb578e0d59 100644
--- a/components/script/dom/serviceworkerregistration.rs
+++ b/components/script/dom/serviceworkerregistration.rs
@@ -112,7 +112,7 @@ impl ServiceWorkerRegistration {
let worker_id = WorkerId(Uuid::new_v4());
let devtools_chan = global.devtools_chan().cloned();
- let init = prepare_workerscope_init(&global, None);
+ let init = prepare_workerscope_init(&global, None, None);
ScopeThings {
script_url: script_url,
init: init,
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 9f37ec2240a..c23efc961ee 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -2008,15 +2008,21 @@ impl Window {
// Step 8
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
// If browsingContext is a nested browsing context,
// 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.
// Step 12, 13
- ScriptThread::navigate(pipeline_id, load_data, replace);
+ ScriptThread::navigate(
+ window_proxy.browsing_context_id(),
+ pipeline_id,
+ load_data,
+ replace,
+ );
};
}
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
index d040d7d7468..c9555c88517 100644
--- a/components/script/dom/worker.rs
+++ b/components/script/dom/worker.rs
@@ -20,6 +20,7 @@ use crate::dom::dedicatedworkerglobalscope::{
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageevent::MessageEvent;
+use crate::dom::window::Window;
use crate::dom::workerglobalscope::prepare_workerscope_init;
use crate::realms::enter_realm;
use crate::script_runtime::JSContext;
@@ -95,23 +96,34 @@ impl Worker {
pipeline_id: global.pipeline_id(),
};
+ let browsing_context = global
+ .downcast::<Window>()
+ .map(|w| w.window_proxy().browsing_context_id())
+ .or_else(|| {
+ global
+ .downcast::<DedicatedWorkerGlobalScope>()
+ .and_then(|w| w.browsing_context())
+ });
+
let (devtools_sender, devtools_receiver) = ipc::channel().unwrap();
let worker_id = WorkerId(Uuid::new_v4());
if let Some(ref chan) = global.devtools_chan() {
let pipeline_id = global.pipeline_id();
let title = format!("Worker for {}", worker_url);
- let page_info = DevtoolsPageInfo {
- title: title,
- url: worker_url.clone(),
- };
- let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal(
- (pipeline_id, Some(worker_id)),
- devtools_sender.clone(),
- page_info,
- ));
+ if let Some(browsing_context) = browsing_context {
+ let page_info = DevtoolsPageInfo {
+ title: title,
+ url: worker_url.clone(),
+ };
+ let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal(
+ (browsing_context, pipeline_id, Some(worker_id)),
+ devtools_sender.clone(),
+ page_info,
+ ));
+ }
}
- let init = prepare_workerscope_init(global, Some(devtools_sender));
+ let init = prepare_workerscope_init(global, Some(devtools_sender), Some(worker_id));
DedicatedWorkerGlobalScope::run_worker_scope(
init,
@@ -126,6 +138,7 @@ impl Worker {
worker_options.type_,
closing,
global.image_cache(),
+ browsing_context,
);
Ok(worker)
diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs
index 15eb79858fe..684760c3197 100644
--- a/components/script/dom/workerglobalscope.rs
+++ b/components/script/dom/workerglobalscope.rs
@@ -62,6 +62,7 @@ use uuid::Uuid;
pub fn prepare_workerscope_init(
global: &GlobalScope,
devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>,
+ worker_id: Option<WorkerId>,
) -> WorkerGlobalScopeInit {
let init = WorkerGlobalScopeInit {
resource_threads: global.resource_threads().clone(),
@@ -71,7 +72,7 @@ pub fn prepare_workerscope_init(
from_devtools_sender: devtools_sender,
script_to_constellation_chan: global.script_to_constellation_chan().clone(),
scheduler_chan: global.scheduler_chan().clone(),
- worker_id: WorkerId(Uuid::new_v4()),
+ worker_id: worker_id.unwrap_or_else(|| WorkerId(Uuid::new_v4())),
pipeline_id: global.pipeline_id(),
origin: global.origin().immutable().clone(),
is_headless: global.is_headless(),
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 606ac81493e..16fd866f1b9 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -93,7 +93,7 @@ use canvas_traits::webgl::WebGLPipeline;
use crossbeam_channel::{unbounded, Receiver, Sender};
use devtools_traits::CSSError;
use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo};
-use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
+use devtools_traits::{NavigationState, ScriptToDevtoolsControlMsg, WorkerId};
use embedder_traits::{EmbedderMsg, EventLoopWaker};
use euclid::default::{Point2D, Rect};
use euclid::Vector2D;
@@ -947,6 +947,7 @@ impl ScriptThread {
/// Step 13 of https://html.spec.whatwg.org/multipage/#navigate
pub fn navigate(
+ browsing_context: BrowsingContextId,
pipeline_id: PipelineId,
mut load_data: LoadData,
replace: HistoryEntryReplacement,
@@ -985,6 +986,12 @@ impl ScriptThread {
.queue(task, global.upcast())
.expect("Enqueing navigate js task on the DOM manipulation task source failed");
} 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_sender
.send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace)))
@@ -3341,7 +3348,7 @@ impl ScriptThread {
self.notify_devtools(
document.Title(),
final_url.clone(),
- (incomplete.pipeline_id, None),
+ (incomplete.browsing_context_id, incomplete.pipeline_id, None),
);
let parse_input = DOMString::new();
@@ -3372,7 +3379,7 @@ impl ScriptThread {
&self,
title: DOMString,
url: ServoUrl,
- ids: (PipelineId, Option<WorkerId>),
+ (bc, p, w): (BrowsingContextId, PipelineId, Option<WorkerId>),
) {
if let Some(ref chan) = self.devtools_chan {
let page_info = DevtoolsPageInfo {
@@ -3380,11 +3387,14 @@ impl ScriptThread {
url: url,
};
chan.send(ScriptToDevtoolsControlMsg::NewGlobal(
- ids,
+ (bc, p, w),
self.devtools_sender.clone(),
- page_info,
+ page_info.clone(),
))
.unwrap();
+
+ let state = NavigationState::Stop(p, page_info);
+ let _ = chan.send(ScriptToDevtoolsControlMsg::Navigate(bc, state));
}
}
diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs
index 83bf1dbf312..73ead01fe17 100644
--- a/components/script/serviceworker_manager.rs
+++ b/components/script/serviceworker_manager.rs
@@ -11,7 +11,6 @@ use crate::dom::abstractworker::WorkerScriptMsg;
use crate::dom::serviceworkerglobalscope::{ServiceWorkerGlobalScope, ServiceWorkerScriptMsg};
use crate::dom::serviceworkerregistration::longest_prefix_match;
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
-use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg};
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use net_traits::{CoreResourceMsg, CustomResponseMediator};
@@ -79,19 +78,7 @@ impl ServiceWorkerManager {
let scope_things = self.registered_workers.get(&scope_url);
if let Some(scope_things) = scope_things {
let (sender, receiver) = unbounded();
- let (devtools_sender, devtools_receiver) = ipc::channel().unwrap();
- if let Some(ref chan) = scope_things.devtools_chan {
- let title = format!("ServiceWorker for {}", scope_things.script_url);
- let page_info = DevtoolsPageInfo {
- title: title,
- url: scope_things.script_url.clone(),
- };
- let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal(
- (scope_things.init.pipeline_id, Some(scope_things.worker_id)),
- devtools_sender,
- page_info,
- ));
- };
+ let (_devtools_sender, devtools_receiver) = ipc::channel().unwrap();
ServiceWorkerGlobalScope::run_serviceworker_scope(
scope_things.clone(),
sender.clone(),