diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | components/devtools/Cargo.toml | 1 | ||||
-rw-r--r-- | components/devtools/actors/browsing_context.rs | 181 | ||||
-rw-r--r-- | components/devtools/actors/console.rs | 179 | ||||
-rw-r--r-- | components/devtools/actors/inspector.rs | 16 | ||||
-rw-r--r-- | components/devtools/actors/root.rs | 13 | ||||
-rw-r--r-- | components/devtools/actors/worker.rs | 130 | ||||
-rw-r--r-- | components/devtools/lib.rs | 312 | ||||
-rw-r--r-- | components/devtools_traits/lib.rs | 20 | ||||
-rw-r--r-- | components/script/dom/dedicatedworkerglobalscope.rs | 14 | ||||
-rw-r--r-- | components/script/dom/document.rs | 8 | ||||
-rw-r--r-- | components/script/dom/serviceworkerregistration.rs | 2 | ||||
-rw-r--r-- | components/script/dom/window.rs | 12 | ||||
-rw-r--r-- | components/script/dom/worker.rs | 33 | ||||
-rw-r--r-- | components/script/dom/workerglobalscope.rs | 3 | ||||
-rw-r--r-- | components/script/script_thread.rs | 20 | ||||
-rw-r--r-- | components/script/serviceworker_manager.rs | 15 |
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) => ®istry.find::<BrowsingContextActor>(bc).script_chan, + Root::DedicatedWorker(worker) => ®istry.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(®istry, &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(), |