From 3843d4e7e0cc1aadcf43ecaa931e6012c7c5a268 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:57:41 +0800 Subject: [PATCH] Initial effort to use IPC calls to control verso (#207) * Initial effort to use IPC calls to control verso * Move functions to a struct * Change simple example to main.rs * Rename to `ipc_channel` * Rename and give the thread a name --- Cargo.lock | 2 ++ Cargo.toml | 3 +- src/config.rs | 11 ++++++- src/main.rs | 22 ++++++++++--- src/verso.rs | 61 ++++++++++++++++++++++++++++++++--- verso/Cargo.toml | 1 + verso/src/lib.rs | 26 +++++++++++++++ verso/src/main.rs | 16 +++++++++ versoview_messages/Cargo.toml | 1 + versoview_messages/src/lib.rs | 6 ++++ 10 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 verso/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 1e59eb1b..509435fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6220,6 +6220,7 @@ version = "0.0.1" dependencies = [ "ipc-channel", "serde", + "url", "versoview_messages", ] @@ -6288,6 +6289,7 @@ version = "0.0.1" dependencies = [ "ipc-channel", "serde", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index de3751c9..aa142a24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = ["verso", "versoview_messages"] [workspace.dependencies] ipc-channel = "0.19" serde = { version = "1.0", features = ["derive"] } +url = { version = "2.5.2", features = ["serde"] } [package] name = "versoview" @@ -104,7 +105,7 @@ webxr-api = { git = "https://github.com/servo/webxr" } cargo-packager-resource-resolver = { version = "0.1.1", features = [ "auto-detect-format", ], optional = true } -url = "2.5.2" +url = { workspace = true } headers = "0.3" versoview_messages = { path = "./versoview_messages" } diff --git a/src/config.rs b/src/config.rs index 16eaad0c..6c641ed1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,6 +15,8 @@ use servo_config::opts::{default_opts, set_options, Opts}; pub struct CliArgs { /// URL to load initially. pub url: Option, + /// URL to load initially. + pub ipc_channel: Option, } /// Configuration of Verso instance. @@ -33,6 +35,12 @@ fn parse_cli_args() -> Result { let mut opts = getopts::Options::new(); opts.optopt("", "url", "URL to load on start", "URL"); + opts.optopt( + "", + "ipc-channel", + "IPC channel name to communicate and control verso", + "", + ); let matches: getopts::Matches = opts.parse(&args[1..])?; let url = matches @@ -49,8 +57,9 @@ fn parse_cli_args() -> Result { None } }); + let ipc_channel = matches.opt_str("ipc-channel"); - Ok(CliArgs { url }) + Ok(CliArgs { url, ipc_channel }) } impl Config { diff --git a/src/main.rs b/src/main.rs index c30c4bca..50a2ddf8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use versoview::config::Config; +use versoview::verso::EventLoopProxyMessage; use versoview::{Result, Verso}; use winit::application::ApplicationHandler; use winit::event_loop::{self, DeviceEvents}; @@ -9,10 +10,10 @@ use winit::event_loop::{EventLoop, EventLoopProxy}; struct App { verso: Option, - proxy: EventLoopProxy<()>, + proxy: EventLoopProxy, } -impl ApplicationHandler for App { +impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { let config = Config::new(resources_dir_path().unwrap()); self.verso = Some(Verso::new(event_loop, self.proxy.clone(), config)); @@ -30,15 +31,26 @@ impl ApplicationHandler for App { } } - fn user_event(&mut self, event_loop: &event_loop::ActiveEventLoop, _: ()) { + fn user_event( + &mut self, + event_loop: &event_loop::ActiveEventLoop, + event: EventLoopProxyMessage, + ) { if let Some(v) = self.verso.as_mut() { - v.handle_servo_messages(event_loop); + match event { + EventLoopProxyMessage::Wake => { + v.handle_servo_messages(event_loop); + } + EventLoopProxyMessage::IpcMessage(message) => { + v.handle_incoming_webview_message(message); + } + } } } } fn main() -> Result<()> { - let event_loop = EventLoop::new()?; + let event_loop = EventLoop::::with_user_event().build()?; event_loop.listen_device_events(DeviceEvents::Never); let proxy = event_loop.create_proxy(); let mut app = App { verso: None, proxy }; diff --git a/src/verso.rs b/src/verso.rs index 5480442d..3a43ead5 100644 --- a/src/verso.rs +++ b/src/verso.rs @@ -26,7 +26,9 @@ use profile; use script::{self, JSEngineSetup}; use script_traits::WindowSizeData; use servo_config::{opts, pref}; +use servo_url::ServoUrl; use style; +use versoview_messages::ControllerMessage; use webgpu; use webrender::{create_webrender_instance, ShaderPrecacheFlags, WebRenderOptions}; use webrender_api::*; @@ -75,10 +77,36 @@ impl Verso { /// - Canvas: Enabled /// - Constellation: Enabled /// - Image Cache: Enabled - pub fn new(evl: &ActiveEventLoop, proxy: EventLoopProxy<()>, config: Config) -> Self { + pub fn new( + evl: &ActiveEventLoop, + proxy: EventLoopProxy, + config: Config, + ) -> Self { + if let Some(ipc_channel) = &config.args.ipc_channel { + let sender = + IpcSender::>::connect(ipc_channel.to_string()) + .unwrap(); + let (controller_sender, receiver) = ipc::channel::().unwrap(); + sender.send(controller_sender).unwrap(); + let proxy_clone = proxy.clone(); + std::thread::Builder::new() + .name("IpcMessageRelay".to_owned()) + .spawn(move || { + while let Ok(message) = receiver.recv() { + if let Err(e) = + proxy_clone.send_event(EventLoopProxyMessage::IpcMessage(message)) + { + log::error!("Failed to send controller message to Verso: {e}"); + } + } + }) + .unwrap(); + }; + // Initialize configurations and Verso window let protocols = config.create_protocols(); let initial_url = config.args.url.clone(); + config.init(); // Reserving a namespace to create TopLevelBrowsingContextId. PipelineNamespace::install(PipelineNamespaceId(0)); @@ -494,6 +522,23 @@ impl Verso { } } + /// Handle message came from webview controller. + pub fn handle_incoming_webview_message(&self, message: ControllerMessage) { + match message { + ControllerMessage::NavigateTo(to_url) => { + if let Some(webview_id) = self.windows.values().next().and_then(|(window, _)| { + window.webview.as_ref().map(|webview| webview.webview_id) + }) { + send_to_constellation( + &self.constellation_sender, + ConstellationMsg::LoadUrl(webview_id, ServoUrl::from_url(to_url)), + ); + } + } + _ => {} + } + } + /// Return true if one of the Verso windows is animating. pub fn is_animating(&self) -> bool { self.compositor @@ -516,8 +561,16 @@ impl Verso { } } +/// Message send to the event loop +pub enum EventLoopProxyMessage { + /// Wake + Wake, + /// Message coming from the webview controller + IpcMessage(ControllerMessage), +} + #[derive(Debug, Clone)] -struct Waker(pub EventLoopProxy<()>); +struct Waker(pub EventLoopProxy); impl EventLoopWaker for Waker { fn clone_box(&self) -> Box { @@ -525,8 +578,8 @@ impl EventLoopWaker for Waker { } fn wake(&self) { - if let Err(e) = self.0.send_event(()) { - log::error!("Servo failed to send wake up event to Verso: {}", e); + if let Err(e) = self.0.send_event(EventLoopProxyMessage::Wake) { + log::error!("Servo failed to send wake up event to Verso: {e}"); } } } diff --git a/verso/Cargo.toml b/verso/Cargo.toml index f7f82154..807eaadc 100644 --- a/verso/Cargo.toml +++ b/verso/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] ipc-channel = { workspace = true } serde = { workspace = true } +url = { workspace = true } versoview_messages = { path = "../versoview_messages" } diff --git a/verso/src/lib.rs b/verso/src/lib.rs index 8b137891..6ab21f86 100644 --- a/verso/src/lib.rs +++ b/verso/src/lib.rs @@ -1 +1,27 @@ +use std::{path::Path, process::Command}; +use versoview_messages::ControllerMessage; +use ipc_channel::ipc::{IpcOneShotServer, IpcSender}; + +pub struct VersoviewController(IpcSender); + +impl VersoviewController { + /// Create a new verso instance and get the controller to it + pub fn new(verso_path: impl AsRef, initial_url: url::Url) -> Self { + let path = verso_path.as_ref(); + let (server, server_name) = + IpcOneShotServer::>::new().unwrap(); + Command::new(path) + .arg(format!("--ipc-channel={server_name}")) + .arg(format!("--url={initial_url}")) + .spawn() + .unwrap(); + let (_, sender) = server.accept().unwrap(); + Self(sender) + } + + /// Navigate to url + pub fn navigate(&self, url: url::Url) -> Result<(), Box> { + self.0.send(ControllerMessage::NavigateTo(url)) + } +} diff --git a/verso/src/main.rs b/verso/src/main.rs new file mode 100644 index 00000000..188191d5 --- /dev/null +++ b/verso/src/main.rs @@ -0,0 +1,16 @@ +use std::{env::current_exe, thread::sleep, time::Duration}; + +fn main() { + let versoview_path = current_exe().unwrap().parent().unwrap().join("versoview"); + let controller = verso::VersoviewController::new( + versoview_path, + url::Url::parse("https://example.com").unwrap(), + ); + sleep(Duration::from_secs(10)); + dbg!(controller + .navigate(url::Url::parse("https://docs.rs").unwrap()) + .unwrap()); + loop { + sleep(Duration::MAX); + } +} diff --git a/versoview_messages/Cargo.toml b/versoview_messages/Cargo.toml index dc7c2e4a..58e0828b 100644 --- a/versoview_messages/Cargo.toml +++ b/versoview_messages/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] ipc-channel = { workspace = true } serde = { workspace = true } +url = { workspace = true } diff --git a/versoview_messages/src/lib.rs b/versoview_messages/src/lib.rs index 8b137891..f099afb0 100644 --- a/versoview_messages/src/lib.rs +++ b/versoview_messages/src/lib.rs @@ -1 +1,7 @@ +use serde::{Deserialize, Serialize}; +#[derive(Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum ControllerMessage { + NavigateTo(url::Url), +}