From 7c48644cad88fc86c4324b554f1b4edf2a3b4db0 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sat, 25 Apr 2020 12:21:18 -0400 Subject: [PATCH 1/7] Support navigating browsing contexts in the devtools. Break the association between pipelines and browsing context actors. Now there is one browsing context actor per actual browsing context, and individual actors keep track of known pipelines as necessary. There is also one console/performance/timeline/inspector/etc. actor per browsing context. This also centralizes more information in the browsing context actor. Rather than duplicating state for the active pipeline in actors that need to use it, each actor now remembers the name of its associated browsing context actor and obtains that state whenever it's necessary. --- .../devtools/actors/browsing_context.rs | 152 ++++++++++-- components/devtools/actors/console.rs | 90 +++++--- components/devtools/actors/inspector.rs | 16 +- components/devtools/lib.rs | 217 +++++++++--------- components/devtools_traits/lib.rs | 17 +- components/script/dom/window.rs | 12 +- components/script/dom/worker.rs | 2 +- components/script/script_thread.rs | 20 +- components/script/serviceworker_manager.rs | 6 +- 9 files changed, 348 insertions(+), 184 deletions(-) diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index bdc468f9f84..01c153a0d1b 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)] @@ -118,6 +130,10 @@ pub struct BrowsingContextActor { pub performance: String, pub styleSheets: String, pub thread: String, + pub streams: RefCell>, + pub browsing_context_id: BrowsingContextId, + pub active_pipeline: Cell, + pub script_chan: IpcSender, } 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, 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::(&self.console); - let _ = console_actor + let _ = self .script_chan - .send(DevtoolScriptControlMsg::Reload(console_actor.pipeline)); + .send(DevtoolScriptControlMsg::Reload(self.active_pipeline.get())); } } } @@ -165,32 +180,25 @@ impl Actor for BrowsingContextActor { watchpoints: false, }, }; - let console_actor = registry.find::(&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::(&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 }, @@ -224,6 +232,70 @@ impl Actor for BrowsingContextActor { } impl BrowsingContextActor { + pub(crate) fn new( + console: String, + id: BrowsingContextId, + page_info: DevtoolsPageInfo, + pipeline: PipelineId, + script_sender: IpcSender, + actors: &mut ActorRegistry, + ) -> BrowsingContextActor { + let emulation = EmulationActor::new(actors.new_name("emulation")); + + let name = actors.new_name("target"); + + let inspector = InspectorActor { + name: actors.new_name("inspector"), + walker: RefCell::new(None), + pageStyle: RefCell::new(None), + highlighter: RefCell::new(None), + script_chan: script_sender.clone(), + browsing_context: name.clone(), + }; + + let timeline = + TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender.clone()); + + let profiler = ProfilerActor::new(actors.new_name("profiler")); + let performance = PerformanceActor::new(actors.new_name("performance")); + + // the strange switch between styleSheets and stylesheets is due + // to an inconsistency in devtools. See Bug #1498893 in bugzilla + let styleSheets = StyleSheetsActor::new(actors.new_name("stylesheets")); + let thread = ThreadActor::new(actors.new_name("context")); + + let DevtoolsPageInfo { title, url } = page_info; + let target = BrowsingContextActor { + name: name, + script_chan: script_sender, + title: String::from(title), + url: url.into_string(), + console: console, + emulation: emulation.name(), + inspector: inspector.name(), + timeline: timeline.name(), + profiler: profiler.name(), + performance: performance.name(), + styleSheets: styleSheets.name(), + thread: thread.name(), + streams: RefCell::new(Vec::new()), + browsing_context_id: id, + active_pipeline: Cell::new(pipeline), + }; + + actors.register(Box::new(emulation)); + actors.register(Box::new(inspector)); + actors.register(Box::new(timeline)); + actors.register(Box::new(profiler)); + actors.register(Box::new(performance)); + actors.register(Box::new(styleSheets)); + actors.register(Box::new(thread)); + + let root = actors.find_mut::("root"); + root.tabs.push(target.name.clone()); + target + } + pub fn encodable(&self) -> BrowsingContextActorMsg { BrowsingContextActorMsg { actor: self.name(), @@ -232,8 +304,10 @@ impl BrowsingContextActor { }, 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 + //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 +317,40 @@ 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); + } + let msg = TabNavigated { + from: self.name(), + type_: "tabNavigated".to_owned(), + url: url.as_str().to_owned(), + title: title, + nativeConsoleAPI: true, + state: state.to_owned(), + isFrameSwitching: false, + }; + for stream in &mut *self.streams.borrow_mut() { + stream.write_json_packet(&msg); + } + } +} + +#[derive(Serialize)] +struct TabNavigated { + from: String, + #[serde(rename = "type")] + type_: String, + url: String, + title: Option, + nativeConsoleAPI: bool, + state: String, + isFrameSwitching: bool, } diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index 845e39075b1..8c07c324e6d 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -8,6 +8,7 @@ //! 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::protocol::JsonPacketStream; use crate::{ConsoleAPICall, ConsoleMessage, ConsoleMsg, PageErrorMsg}; @@ -17,10 +18,11 @@ use devtools_traits::EvaluateJSReply::{NullValue, NumberValue, VoidValue}; use devtools_traits::{ CachedConsoleMessageTypes, ConsoleAPI, DevtoolScriptControlMsg, LogLevel, PageError, }; -use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::ipc; use msg::constellation_msg::PipelineId; use serde_json::{self, Map, Number, Value}; use std::cell::RefCell; +use std::collections::HashMap; use std::net::TcpStream; use time::precise_time_ns; use uuid::Uuid; @@ -106,10 +108,8 @@ struct SetPreferencesReply { pub struct ConsoleActor { pub name: String, - pub pipeline: PipelineId, - pub script_chan: IpcSender, - pub streams: RefCell>, - pub cached_events: RefCell>, + pub browsing_context: String, + pub cached_events: RefCell>>, } impl ConsoleActor { @@ -118,11 +118,13 @@ impl ConsoleActor { registry: &ActorRegistry, msg: &Map, ) -> Result { + let browsing_context = registry.find::(&self.browsing_context); let input = msg.get("text").unwrap().as_str().unwrap().to_owned(); let (chan, port) = ipc::channel().unwrap(); - self.script_chan + browsing_context + .script_chan .send(DevtoolScriptControlMsg::EvaluateJS( - self.pipeline, + browsing_context.active_pipeline.get(), input.clone(), chan, )) @@ -191,21 +193,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, + pipeline: PipelineId, + browsing_context: &BrowsingContextActor, + ) { self.cached_events .borrow_mut() + .entry(pipeline) + .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 browsing_context.active_pipeline.get() == pipeline { + let msg = PageErrorMsg { + from: self.name(), + type_: "pageError".to_owned(), + pageError: page_error, + }; + for stream in &mut *browsing_context.streams.borrow_mut() { + stream.write_json_packet(&msg); + } } } - pub(crate) fn handle_console_api(&self, console_message: ConsoleMessage) { + pub(crate) fn handle_console_api( + &self, + console_message: ConsoleMessage, + pipeline: PipelineId, + browsing_context: &BrowsingContextActor, + ) { let level = match console_message.logLevel { LogLevel::Debug => "debug", LogLevel::Info => "info", @@ -216,6 +232,8 @@ impl ConsoleActor { .to_owned(); self.cached_events .borrow_mut() + .entry(pipeline) + .or_insert(vec![]) .push(CachedConsoleMessage::ConsoleAPI(ConsoleAPI { type_: "ConsoleAPI".to_owned(), level: level.clone(), @@ -226,20 +244,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 browsing_context.active_pipeline.get() == pipeline { + 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 *browsing_context.streams.borrow_mut() { + stream.write_json_packet(&msg); + } } } } @@ -275,8 +295,16 @@ impl Actor for ConsoleActor { s => debug!("unrecognized message type requested: \"{}\"", s), }; } + let browsing_context = + registry.find::(&self.browsing_context); let mut messages = vec![]; - for event in self.cached_events.borrow().iter() { + for event in self + .cached_events + .borrow() + .get(&browsing_context.active_pipeline.get()) + .unwrap_or(&vec![]) + .iter() + { let include = match event { CachedConsoleMessage::PageError(_) if message_types.contains(CachedConsoleMessageTypes::PAGE_ERROR) => 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>, pub highlighter: RefCell>, pub script_chan: IpcSender, - pub pipeline: PipelineId, + pub browsing_context: String, } #[derive(Serialize)] @@ -596,13 +597,15 @@ impl Actor for InspectorActor { _msg: &Map, stream: &mut TcpStream, ) -> Result { + let browsing_context = registry.find::(&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/lib.rs b/components/devtools/lib.rs index 661d6ac94e1..6d9bd938e14 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -21,29 +21,24 @@ use crate::actor::{Actor, ActorRegistry}; use crate::actors::browsing_context::BrowsingContextActor; use crate::actors::console::ConsoleActor; 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::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}; @@ -198,7 +193,8 @@ fn run_server( let mut accepted_connections: Vec = Vec::new(); - let mut actor_pipelines: HashMap = HashMap::new(); + let mut browsing_contexts: HashMap = HashMap::new(); + let mut pipelines: HashMap = HashMap::new(); let mut actor_requests: HashMap = HashMap::new(); let mut actor_workers: HashMap<(PipelineId, WorkerId), String> = HashMap::new(); @@ -243,91 +239,65 @@ fn run_server( framerate_actor.add_tick(tick); } + fn handle_navigate( + actors: Arc>, + browsing_contexts: &HashMap, + browsing_context: BrowsingContextId, + state: NavigationState, + ) { + let actor_name = browsing_contexts.get(&browsing_context).unwrap(); + actors + .lock() + .unwrap() + .find::(actor_name) + .navigate(state); + } + // 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>, - ids: (PipelineId, Option), + ids: (Option, PipelineId, Option), script_sender: IpcSender, - actor_pipelines: &mut HashMap, + browsing_contexts: &mut HashMap, + pipelines: &mut HashMap, actor_workers: &mut HashMap<(PipelineId, WorkerId), String>, page_info: DevtoolsPageInfo, ) { let mut actors = actors.lock().unwrap(); - let (pipeline, worker_id) = ids; + let (browsing_context, pipeline, worker_id) = ids; - //TODO: move all this actor creation into a constructor method on BrowsingContextActor - let ( - 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 console_name = actors.new_name("console"); - let emulation = EmulationActor::new(actors.new_name("emulation")); + let browsing_context_name = if let Some(browsing_context) = browsing_context { + pipelines.insert(pipeline, browsing_context); + if let Some(actor) = browsing_contexts.get(&browsing_context) { + actor.to_owned() + } else { + let browsing_context_actor = BrowsingContextActor::new( + console_name.clone(), + browsing_context, + page_info, + pipeline, + script_sender.clone(), + &mut *actors, + ); + let name = browsing_context_actor.name(); + browsing_contexts.insert(browsing_context, name.clone()); + actors.register(Box::new(browsing_context_actor)); + name + } + } else { + "".to_owned() + }; - let inspector = InspectorActor { - 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 timeline = TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender); - - let profiler = ProfilerActor::new(actors.new_name("profiler")); - let performance = PerformanceActor::new(actors.new_name("performance")); - - // the strange switch between styleSheets and stylesheets is due - // to an inconsistency in devtools. See Bug #1498893 in bugzilla - let styleSheets = StyleSheetsActor::new(actors.new_name("stylesheets")); - let thread = ThreadActor::new(actors.new_name("context")); - - let DevtoolsPageInfo { title, url } = page_info; - let target = BrowsingContextActor { - name: actors.new_name("target"), - title: String::from(title), - url: url.into_string(), - console: console.name(), - emulation: emulation.name(), - inspector: inspector.name(), - timeline: timeline.name(), - profiler: profiler.name(), - performance: performance.name(), - styleSheets: styleSheets.name(), - thread: thread.name(), - }; - - let root = actors.find_mut::("root"); - root.tabs.push(target.name.clone()); - - ( - target, - console, - emulation, - inspector, - timeline, - profiler, - performance, - styleSheets, - thread, - ) + // XXXjdm this new actor is useless if it's not a new worker global + let console = ConsoleActor { + name: console_name, + cached_events: Default::default(), + browsing_context: browsing_context_name, }; if let Some(id) = worker_id { @@ -336,36 +306,39 @@ fn run_server( console: console.name(), id: id, }; + let root = actors.find_mut::("root"); + root.tabs.push(worker.name.clone()); + actor_workers.insert((pipeline, id), worker.name.clone()); actors.register(Box::new(worker)); } - actor_pipelines.insert(pipeline, target.name.clone()); - actors.register(Box::new(target)); actors.register(Box::new(console)); - actors.register(Box::new(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>, id: PipelineId, page_error: PageError, - actor_pipelines: &HashMap, + browsing_contexts: &HashMap, + pipelines: &HashMap, ) { - 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, + None, + &HashMap::new(), + browsing_contexts, + pipelines, + ) { + Some(name) => name, + None => return, + }; let actors = actors.lock().unwrap(); let console_actor = actors.find::(&console_actor_name); - console_actor.handle_page_error(page_error); + let browsing_context_actor = + actors.find::(&console_actor.browsing_context); + console_actor.handle_page_error(page_error, id, &browsing_context_actor); } fn handle_console_message( @@ -373,37 +346,43 @@ fn run_server( id: PipelineId, worker_id: Option, console_message: ConsoleMessage, - actor_pipelines: &HashMap, + browsing_contexts: &HashMap, actor_workers: &HashMap<(PipelineId, WorkerId), String>, + pipelines: &HashMap, ) { 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::(&console_actor_name); - console_actor.handle_console_api(console_message); + let browsing_context_actor = + actors.find::(&console_actor.browsing_context); + console_actor.handle_console_api(console_message, id, &browsing_context_actor); } fn find_console_actor( actors: Arc>, - id: PipelineId, + pipeline: PipelineId, worker_id: Option, actor_workers: &HashMap<(PipelineId, WorkerId), String>, - actor_pipelines: &HashMap, + browsing_contexts: &HashMap, + pipelines: &HashMap, ) -> Option { 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(&(pipeline, worker_id))?; Some(actors.find::(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::(actor_name) @@ -416,9 +395,10 @@ fn run_server( fn handle_network_event( actors: Arc>, mut connections: Vec, - actor_pipelines: &HashMap, + browsing_contexts: &HashMap, actor_requests: &mut HashMap, actor_workers: &HashMap<(PipelineId, WorkerId), String>, + pipelines: &HashMap, pipeline_id: PipelineId, request_id: String, network_event: NetworkEvent, @@ -428,7 +408,8 @@ fn run_server( pipeline_id, None, actor_workers, - actor_pipelines, + browsing_contexts, + pipelines, ) { Some(name) => name, None => return, @@ -610,10 +591,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 +609,20 @@ 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, + page_error, + &browsing_contexts, + &pipelines, + ), DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportCSSError( id, css_error, @@ -646,8 +639,9 @@ fn run_server( id, None, console_message, - &actor_pipelines, + &browsing_contexts, &actor_workers, + &pipelines, ) }, DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent( @@ -667,9 +661,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..f55e0990588 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), + (Option, PipelineId, Option), IpcSender, DevtoolsPageInfo, ), + /// The given browsing context is performing a navigation. + Navigate(BrowsingContextId, NavigationState), /// A particular page has invoked the console API. ConsoleAPI(PipelineId, ConsoleMessage, Option), /// An animation frame with the given timestamp was processed in a script thread. 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..9c0cc91273a 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -105,7 +105,7 @@ impl Worker { url: worker_url.clone(), }; let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal( - (pipeline_id, Some(worker_id)), + (None, pipeline_id, Some(worker_id)), devtools_sender.clone(), page_info, )); diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 7a672851ba6..7431bcf1032 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))) @@ -3338,7 +3345,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(); @@ -3369,7 +3376,7 @@ impl ScriptThread { &self, title: DOMString, url: ServoUrl, - ids: (PipelineId, Option), + (bc, p, w): (BrowsingContextId, PipelineId, Option), ) { if let Some(ref chan) = self.devtools_chan { let page_info = DevtoolsPageInfo { @@ -3377,11 +3384,14 @@ impl ScriptThread { url: url, }; chan.send(ScriptToDevtoolsControlMsg::NewGlobal( - ids, + (Some(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..0383d7bf6da 100644 --- a/components/script/serviceworker_manager.rs +++ b/components/script/serviceworker_manager.rs @@ -87,7 +87,11 @@ impl ServiceWorkerManager { url: scope_things.script_url.clone(), }; let _ = chan.send(ScriptToDevtoolsControlMsg::NewGlobal( - (scope_things.init.pipeline_id, Some(scope_things.worker_id)), + ( + None, + scope_things.init.pipeline_id, + Some(scope_things.worker_id), + ), devtools_sender, page_info, )); From bce4ec5b70f98c98a996b05767dfa7c1915fa0ce Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sat, 25 Apr 2020 12:46:49 -0400 Subject: [PATCH 2/7] Support clearing cached events in console actor. --- components/devtools/actors/console.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index 8c07c324e6d..3f23a0c0ee5 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -277,6 +277,13 @@ impl Actor for ConsoleActor { stream: &mut TcpStream, ) -> Result { Ok(match msg_type { + "clearMessagesCache" => { + let browsing_context = + registry.find::(&self.browsing_context); + self.cached_events.borrow_mut().remove(&browsing_context.active_pipeline.get()); + ActorMessageStatus::Processed + } + "getCachedMessages" => { let str_types = msg .get("messageTypes") From 565e9432c61d8f57db32f2376c2ccff82e9798a8 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sun, 26 Apr 2020 17:34:52 -0400 Subject: [PATCH 3/7] Support connecting to worker globals from remote devtools. --- Cargo.lock | 1 + components/devtools/Cargo.toml | 1 + components/devtools/actors/console.rs | 97 +++++++--- components/devtools/actors/root.rs | 13 +- components/devtools/actors/worker.rs | 130 +++++++++++++- components/devtools/lib.rs | 167 ++++++++++-------- components/devtools_traits/lib.rs | 2 +- .../script/dom/dedicatedworkerglobalscope.rs | 14 +- .../script/dom/serviceworkerregistration.rs | 2 +- components/script/dom/worker.rs | 33 ++-- components/script/dom/workerglobalscope.rs | 3 +- components/script/script_thread.rs | 2 +- components/script/serviceworker_manager.rs | 19 +- 13 files changed, 340 insertions(+), 144 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4f59fc9b40..b515c91219f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1189,6 +1189,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/console.rs b/components/devtools/actors/console.rs index 3f23a0c0ee5..6a6ef1243d4 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -10,7 +10,9 @@ 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::UniqueId; use crate::{ConsoleAPICall, ConsoleMessage, ConsoleMsg, PageErrorMsg}; use devtools_traits::CachedConsoleMessage; use devtools_traits::EvaluateJSReply::{ActorValue, BooleanValue, StringValue}; @@ -18,10 +20,10 @@ use devtools_traits::EvaluateJSReply::{NullValue, NumberValue, VoidValue}; use devtools_traits::{ CachedConsoleMessageTypes, ConsoleAPI, DevtoolScriptControlMsg, LogLevel, PageError, }; -use ipc_channel::ipc; -use msg::constellation_msg::PipelineId; +use ipc_channel::ipc::{self, IpcSender}; +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; @@ -106,25 +108,68 @@ struct SetPreferencesReply { updated: Vec, } -pub struct ConsoleActor { +pub(crate) enum Root { + BrowsingContext(String), + DedicatedWorker(String), +} + +pub(crate) struct ConsoleActor { pub name: String, - pub browsing_context: String, - pub cached_events: RefCell>>, + pub root: Root, + pub cached_events: RefCell>>, } impl ConsoleActor { + fn script_chan<'a>( + &self, + registry: &'a ActorRegistry, + ) -> &'a IpcSender { + match &self.root { + Root::BrowsingContext(bc) => ®istry.find::(bc).script_chan, + Root::DedicatedWorker(worker) => ®istry.find::(worker).script_chan, + } + } + + fn streams_mut<'a>(&self, registry: &'a ActorRegistry) -> RefMut<'a, Vec> { + match &self.root { + Root::BrowsingContext(bc) => registry + .find::(bc) + .streams + .borrow_mut(), + Root::DedicatedWorker(worker) => { + registry.find::(worker).streams.borrow_mut() + }, + } + } + + fn current_unique_id(&self, registry: &ActorRegistry) -> UniqueId { + match &self.root { + Root::BrowsingContext(bc) => UniqueId::Pipeline( + registry + .find::(bc) + .active_pipeline + .get(), + ), + Root::DedicatedWorker(w) => UniqueId::Worker(registry.find::(w).id), + } + } + fn evaluateJS( &self, registry: &ActorRegistry, msg: &Map, ) -> Result { - let browsing_context = registry.find::(&self.browsing_context); let input = msg.get("text").unwrap().as_str().unwrap().to_owned(); let (chan, port) = ipc::channel().unwrap(); - browsing_context - .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( - browsing_context.active_pipeline.get(), + pipeline, input.clone(), chan, )) @@ -196,21 +241,21 @@ impl ConsoleActor { pub(crate) fn handle_page_error( &self, page_error: PageError, - pipeline: PipelineId, - browsing_context: &BrowsingContextActor, + id: UniqueId, + registry: &ActorRegistry, ) { self.cached_events .borrow_mut() - .entry(pipeline) + .entry(id.clone()) .or_insert(vec![]) .push(CachedConsoleMessage::PageError(page_error.clone())); - if browsing_context.active_pipeline.get() == pipeline { + if id == self.current_unique_id(registry) { let msg = PageErrorMsg { from: self.name(), type_: "pageError".to_owned(), pageError: page_error, }; - for stream in &mut *browsing_context.streams.borrow_mut() { + for stream in &mut *self.streams_mut(registry) { stream.write_json_packet(&msg); } } @@ -219,8 +264,8 @@ impl ConsoleActor { pub(crate) fn handle_console_api( &self, console_message: ConsoleMessage, - pipeline: PipelineId, - browsing_context: &BrowsingContextActor, + id: UniqueId, + registry: &ActorRegistry, ) { let level = match console_message.logLevel { LogLevel::Debug => "debug", @@ -232,7 +277,7 @@ impl ConsoleActor { .to_owned(); self.cached_events .borrow_mut() - .entry(pipeline) + .entry(id.clone()) .or_insert(vec![]) .push(CachedConsoleMessage::ConsoleAPI(ConsoleAPI { type_: "ConsoleAPI".to_owned(), @@ -244,7 +289,7 @@ impl ConsoleActor { private: false, arguments: vec![console_message.message.clone()], })); - if browsing_context.active_pipeline.get() == pipeline { + if id == self.current_unique_id(registry) { let msg = ConsoleAPICall { from: self.name(), type_: "consoleAPICall".to_owned(), @@ -257,7 +302,7 @@ impl ConsoleActor { columnNumber: console_message.columnNumber, }, }; - for stream in &mut *browsing_context.streams.borrow_mut() { + for stream in &mut *self.streams_mut(registry) { stream.write_json_packet(&msg); } } @@ -278,11 +323,11 @@ impl Actor for ConsoleActor { ) -> Result { Ok(match msg_type { "clearMessagesCache" => { - let browsing_context = - registry.find::(&self.browsing_context); - self.cached_events.borrow_mut().remove(&browsing_context.active_pipeline.get()); + self.cached_events + .borrow_mut() + .remove(&self.current_unique_id(registry)); ActorMessageStatus::Processed - } + }, "getCachedMessages" => { let str_types = msg @@ -302,13 +347,11 @@ impl Actor for ConsoleActor { s => debug!("unrecognized message type requested: \"{}\"", s), }; } - let browsing_context = - registry.find::(&self.browsing_context); let mut messages = vec![]; for event in self .cached_events .borrow() - .get(&browsing_context.active_pipeline.get()) + .get(&self.current_unique_id(registry)) .unwrap_or(&vec![]) .iter() { 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; @@ -72,11 +73,6 @@ struct ListWorkersReply { workers: Vec, } -#[derive(Serialize)] -struct WorkerMsg { - id: u32, -} - #[derive(Serialize)] struct ListServiceWorkerRegistrationsReply { from: String, @@ -110,6 +106,7 @@ struct GetProcessResponse { pub struct RootActor { pub tabs: Vec, + pub workers: Vec, 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::(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, + pub streams: RefCell>, +} + +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, - _: &mut TcpStream, + _registry: &ActorRegistry, + msg_type: &str, + _msg: &Map, + stream: &mut TcpStream, ) -> Result { - 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 6d9bd938e14..477e44494ee 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -19,7 +19,7 @@ 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::framerate::FramerateActor; use crate::actors::network_event::{EventActor, NetworkEventActor, ResponseStartMsg}; @@ -27,7 +27,8 @@ use crate::actors::performance::PerformanceActor; use crate::actors::preference::PreferenceActor; use crate::actors::process::ProcessActor; use crate::actors::root::RootActor; -use crate::actors::worker::WorkerActor; +use crate::actors::thread::ThreadActor; +use crate::actors::worker::{WorkerActor, WorkerType}; use crate::protocol::JsonPacketStream; use crossbeam_channel::{unbounded, Receiver, Sender}; use devtools_traits::{ChromeToDevtoolsControlMsg, ConsoleMessage, DevtoolsControlMsg}; @@ -69,6 +70,12 @@ mod actors { } mod protocol; +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +enum UniqueId { + Pipeline(PipelineId), + Worker(WorkerId), +} + #[derive(Serialize)] struct ConsoleAPICall { from: String, @@ -176,6 +183,7 @@ fn run_server( let root = Box::new(RootActor { tabs: vec![], + workers: vec![], device: device.name(), performance: performance.name(), preference: preference.name(), @@ -197,7 +205,7 @@ fn run_server( let mut pipelines: HashMap = HashMap::new(); let mut actor_requests: HashMap = HashMap::new(); - let mut actor_workers: HashMap<(PipelineId, WorkerId), String> = HashMap::new(); + let mut actor_workers: HashMap = HashMap::new(); /// Process the input from a single devtools client until EOF. fn handle_client(actors: Arc>, mut stream: TcpStream) { @@ -258,11 +266,11 @@ fn run_server( // TODO: move this into the root or target modules? fn handle_new_global( actors: Arc>, - ids: (Option, PipelineId, Option), + ids: (BrowsingContextId, PipelineId, Option), script_sender: IpcSender, browsing_contexts: &mut HashMap, pipelines: &mut HashMap, - actor_workers: &mut HashMap<(PipelineId, WorkerId), String>, + actor_workers: &mut HashMap, page_info: DevtoolsPageInfo, ) { let mut actors = actors.lock().unwrap(); @@ -271,83 +279,70 @@ fn run_server( let console_name = actors.new_name("console"); - let browsing_context_name = if let Some(browsing_context) = browsing_context { - pipelines.insert(pipeline, browsing_context); - if let Some(actor) = browsing_contexts.get(&browsing_context) { - actor.to_owned() - } else { - let browsing_context_actor = BrowsingContextActor::new( - console_name.clone(), - browsing_context, - page_info, - pipeline, - script_sender.clone(), - &mut *actors, - ); - let name = browsing_context_actor.name(); - browsing_contexts.insert(browsing_context, name.clone()); - actors.register(Box::new(browsing_context_actor)); - name - } + let parent_actor = if let Some(id) = worker_id { + assert!(pipelines.get(&pipeline).is_some()); + assert!(browsing_contexts.get(&browsing_context).is_some()); + + let thread = ThreadActor::new(actors.new_name("context")); + let thread_name = thread.name(); + actors.register(Box::new(thread)); + + 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::("root"); + root.workers.push(worker.name.clone()); + + actor_workers.insert(id, worker_name.clone()); + actors.register(Box::new(worker)); + + Root::DedicatedWorker(worker_name) } else { - "".to_owned() + 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 + }, + ) }; - // XXXjdm this new actor is useless if it's not a new worker global let console = ConsoleActor { name: console_name, cached_events: Default::default(), - browsing_context: browsing_context_name, + root: parent_actor, }; - if let Some(id) = worker_id { - let worker = WorkerActor { - name: actors.new_name("worker"), - console: console.name(), - id: id, - }; - let root = actors.find_mut::("root"); - root.tabs.push(worker.name.clone()); - - actor_workers.insert((pipeline, id), worker.name.clone()); - actors.register(Box::new(worker)); - } - actors.register(Box::new(console)); } fn handle_page_error( actors: Arc>, id: PipelineId, + worker_id: Option, page_error: PageError, browsing_contexts: &HashMap, - pipelines: &HashMap, - ) { - let console_actor_name = match find_console_actor( - actors.clone(), - id, - None, - &HashMap::new(), - browsing_contexts, - pipelines, - ) { - Some(name) => name, - None => return, - }; - let actors = actors.lock().unwrap(); - let console_actor = actors.find::(&console_actor_name); - let browsing_context_actor = - actors.find::(&console_actor.browsing_context); - console_actor.handle_page_error(page_error, id, &browsing_context_actor); - } - - fn handle_console_message( - actors: Arc>, - id: PipelineId, - worker_id: Option, - console_message: ConsoleMessage, - browsing_contexts: &HashMap, - actor_workers: &HashMap<(PipelineId, WorkerId), String>, + actor_workers: &HashMap, pipelines: &HashMap, ) { let console_actor_name = match find_console_actor( @@ -363,22 +358,47 @@ fn run_server( }; let actors = actors.lock().unwrap(); let console_actor = actors.find::(&console_actor_name); - let browsing_context_actor = - actors.find::(&console_actor.browsing_context); - console_actor.handle_console_api(console_message, id, &browsing_context_actor); + let id = worker_id.map_or(UniqueId::Pipeline(id), UniqueId::Worker); + console_actor.handle_page_error(page_error, id, &*actors); + } + + fn handle_console_message( + actors: Arc>, + id: PipelineId, + worker_id: Option, + console_message: ConsoleMessage, + browsing_contexts: &HashMap, + actor_workers: &HashMap, + pipelines: &HashMap, + ) { + 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::(&console_actor_name); + 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>, pipeline: PipelineId, worker_id: Option, - actor_workers: &HashMap<(PipelineId, WorkerId), String>, + actor_workers: &HashMap, browsing_contexts: &HashMap, pipelines: &HashMap, ) -> Option { let actors = actors.lock().unwrap(); if let Some(worker_id) = worker_id { - let actor_name = (*actor_workers).get(&(pipeline, worker_id))?; + let actor_name = actor_workers.get(&worker_id)?; Some(actors.find::(actor_name).console.clone()) } else { let id = pipelines.get(&pipeline)?; @@ -397,7 +417,7 @@ fn run_server( mut connections: Vec, browsing_contexts: &HashMap, actor_requests: &mut HashMap, - actor_workers: &HashMap<(PipelineId, WorkerId), String>, + actor_workers: &HashMap, pipelines: &HashMap, pipeline_id: PipelineId, request_id: String, @@ -570,6 +590,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(); @@ -619,8 +640,10 @@ fn run_server( )) => handle_page_error( actors.clone(), id, + None, page_error, &browsing_contexts, + &actor_workers, &pipelines, ), DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportCSSError( diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs index f55e0990588..2ac1cae598a 100644 --- a/components/devtools_traits/lib.rs +++ b/components/devtools_traits/lib.rs @@ -80,7 +80,7 @@ pub enum ScriptToDevtoolsControlMsg { /// A new global object was created, associated with a particular pipeline. /// The means of communicating directly with it are provided. NewGlobal( - (Option, PipelineId, Option), + (BrowsingContextId, PipelineId, Option), IpcSender, DevtoolsPageInfo, ), 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, #[ignore_malloc_size_of = "Arc"] image_cache: Arc, + browsing_context: Option, } impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope { @@ -221,6 +222,7 @@ impl DedicatedWorkerGlobalScope { receiver: Receiver, closing: Arc, image_cache: Arc, + browsing_context: Option, ) -> 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, closing: Arc, image_cache: Arc, + browsing_context: Option, ) -> DomRoot { 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, image_cache: Arc, + browsing_context: Option, ) { 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 { + self.browsing_context + } } #[allow(unsafe_code)] 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/worker.rs b/components/script/dom/worker.rs index 9c0cc91273a..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::() + .map(|w| w.window_proxy().browsing_context_id()) + .or_else(|| { + global + .downcast::() + .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( - (None, 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>, + worker_id: Option, ) -> 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 7431bcf1032..01d8e7873e2 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -3384,7 +3384,7 @@ impl ScriptThread { url: url, }; chan.send(ScriptToDevtoolsControlMsg::NewGlobal( - (Some(bc), p, w), + (bc, p, w), self.devtools_sender.clone(), page_info.clone(), )) diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs index 0383d7bf6da..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,23 +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( - ( - None, - 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(), From b9bf9f873b22952a1f21d2d23c16cf833d020d37 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sun, 26 Apr 2020 17:49:06 -0400 Subject: [PATCH 4/7] Move some console-related message types into the console code. --- components/devtools/actors/console.rs | 28 ++++++++++++++++++++++++++- components/devtools/lib.rs | 26 ------------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index 6a6ef1243d4..505256d970c 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -13,8 +13,8 @@ use crate::actors::object::ObjectActor; use crate::actors::worker::WorkerActor; use crate::protocol::JsonPacketStream; use crate::UniqueId; -use crate::{ConsoleAPICall, ConsoleMessage, ConsoleMsg, PageErrorMsg}; 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::{ @@ -473,3 +473,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, + filename: String, + lineNumber: usize, + columnNumber: usize, +} + +#[derive(Serialize)] +struct PageErrorMsg { + from: String, + #[serde(rename = "type")] + type_: String, + pageError: PageError, +} diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 477e44494ee..a12205d4724 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -76,32 +76,6 @@ enum UniqueId { Worker(WorkerId), } -#[derive(Serialize)] -struct ConsoleAPICall { - from: String, - #[serde(rename = "type")] - type_: String, - message: ConsoleMsg, -} - -#[derive(Serialize)] -struct ConsoleMsg { - level: String, - timeStamp: u64, - arguments: Vec, - filename: String, - lineNumber: usize, - columnNumber: usize, -} - -#[derive(Serialize)] -struct PageErrorMsg { - from: String, - #[serde(rename = "type")] - type_: String, - pageError: PageError, -} - #[derive(Serialize)] struct NetworkEventMsg { from: String, From 3dfec95268a713bf027711a73eb2bf4888a79705 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Tue, 28 Apr 2020 12:23:34 -0400 Subject: [PATCH 5/7] Update browsing context title/url when navigating. --- .../devtools/actors/browsing_context.rs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index 01c153a0d1b..5ebe014cdad 100644 --- a/components/devtools/actors/browsing_context.rs +++ b/components/devtools/actors/browsing_context.rs @@ -120,8 +120,8 @@ pub struct BrowsingContextActorMsg { pub struct BrowsingContextActor { pub name: String, - pub title: String, - pub url: String, + pub title: RefCell, + pub url: RefCell, pub console: String, pub emulation: String, pub inspector: String, @@ -174,7 +174,7 @@ impl Actor for BrowsingContextActor { javascriptEnabled: true, traits: AttachedTraits { reconfigure: false, - frames: false, + frames: true, logInPage: false, canRewind: false, watchpoints: false, @@ -207,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); @@ -268,8 +269,8 @@ impl BrowsingContextActor { let target = BrowsingContextActor { name: name, script_chan: script_sender, - title: String::from(title), - url: url.into_string(), + title: RefCell::new(String::from(title)), + url: RefCell::new(url.into_string()), console: console, emulation: emulation.name(), inspector: inspector.name(), @@ -302,8 +303,8 @@ impl BrowsingContextActor { traits: BrowsingContextTraits { isBrowsingContext: true, }, - title: self.title.clone(), - url: self.url.clone(), + 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 @@ -328,6 +329,11 @@ impl BrowsingContextActor { 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(), From b53ce5c79a6744d24002467e1ff7b7b80e169f36 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Tue, 28 Apr 2020 15:40:02 -0400 Subject: [PATCH 6/7] Ignore eager JS evaluation in devtools. --- components/devtools/actors/console.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index 505256d970c..2a8b016b93d 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -443,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(), From 02ce6188aa967ef1722155f9b170183a01a14064 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Tue, 28 Apr 2020 15:40:16 -0400 Subject: [PATCH 7/7] Update devtools page titles. --- .../devtools/actors/browsing_context.rs | 7 +++++ components/devtools/lib.rs | 30 +++++++++++++++++++ components/devtools_traits/lib.rs | 3 ++ components/script/dom/document.rs | 8 +++++ 4 files changed, 48 insertions(+) diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index 5ebe014cdad..b90d84d04bd 100644 --- a/components/devtools/actors/browsing_context.rs +++ b/components/devtools/actors/browsing_context.rs @@ -347,6 +347,13 @@ impl BrowsingContextActor { 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)] diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index a12205d4724..2227be98549 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -235,6 +235,26 @@ fn run_server( .navigate(state); } + fn handle_title_changed( + actors: Arc>, + pipelines: &HashMap, + browsing_contexts: &HashMap, + 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::(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? @@ -578,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, diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs index 2ac1cae598a..7196c3a5b32 100644 --- a/components/devtools_traits/lib.rs +++ b/components/devtools_traits/lib.rs @@ -97,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/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::(); + if let Some(ref chan) = global.devtools_chan() { + let title = String::from(self.Title()); + let _ = chan.send(ScriptToDevtoolsControlMsg::TitleChanged( + global.pipeline_id(), + title, + )); + } } }