From a1b0102c438e6515d4db426a01e464885fd2829e Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 8 Nov 2024 11:28:20 +0800 Subject: [PATCH 1/6] Allow using IPC to handle navigations --- src/verso.rs | 10 ++++++++ src/webview.rs | 18 ++++++++++--- src/window.rs | 12 ++++++++- verso/src/lib.rs | 48 +++++++++++++++++++++++++++++++---- verso/src/main.rs | 6 +++++ versoview_messages/src/lib.rs | 4 +++ 6 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/verso.rs b/src/verso.rs index 64463905..7e188ff3 100644 --- a/src/verso.rs +++ b/src/verso.rs @@ -531,6 +531,16 @@ impl Verso { ); } } + ControllerMessage::OnNavigationStarting(sender) => { + if let Some((window, _)) = self.windows.values().next() { + window + .event_listeners + .on_navigation_starting + .lock() + .unwrap() + .replace(sender); + } + } _ => {} } } diff --git a/src/webview.rs b/src/webview.rs index 796b47c4..3c564a1e 100644 --- a/src/webview.rs +++ b/src/webview.rs @@ -83,9 +83,21 @@ impl Window { self.window.request_redraw(); send_to_constellation(sender, ConstellationMsg::FocusWebView(webview_id)); } - EmbedderMsg::AllowNavigationRequest(id, _url) => { - // TODO should provide a API for users to check url - send_to_constellation(sender, ConstellationMsg::AllowNavigationResponse(id, true)); + EmbedderMsg::AllowNavigationRequest(id, url) => { + let mut allow = true; + if let Some(ref sender) = + *self.event_listeners.on_navigation_starting.lock().unwrap() + { + let (result_sender, receiver) = ipc::channel::().unwrap(); + if let Some(result) = sender + .send((url.into_url(), result_sender)) + .ok() + .and_then(|_| receiver.recv().ok()) + { + allow = result + } + } + send_to_constellation(sender, ConstellationMsg::AllowNavigationResponse(id, allow)); } EmbedderMsg::GetClipboardContents(sender) => { let contents = clipboard diff --git a/src/window.rs b/src/window.rs index a6b809b5..1d479e72 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,4 +1,4 @@ -use std::cell::Cell; +use std::{cell::Cell, sync::Mutex}; use base::id::WebViewId; use compositing_traits::ConstellationMsg; @@ -12,6 +12,7 @@ use glutin::{ use glutin_winit::DisplayBuilder; use script_traits::{TouchEventType, WheelDelta, WheelMode}; use servo_url::ServoUrl; +use versoview_messages::OnNavigationStartingPayload; use webrender_api::{ units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePoint, LayoutVector2D}, ScrollLocation, @@ -36,6 +37,11 @@ use crate::{ use arboard::Clipboard; +#[derive(Default)] +pub(crate) struct EventListeners { + pub(crate) on_navigation_starting: Mutex>, +} + /// A Verso window is a Winit window containing several web views. pub struct Window { /// Access to Winit window @@ -46,6 +52,8 @@ pub struct Window { pub(crate) panel: Option, /// The WebView of this window. pub(crate) webview: Option, + /// Event listners registered from the webview controller + pub(crate) event_listeners: EventListeners, /// The mouse physical position in the web view. mouse_position: Cell>>, /// Modifiers state of the keyboard. @@ -92,6 +100,7 @@ impl Window { surface, panel: None, webview: None, + event_listeners: Default::default(), mouse_position: Default::default(), modifiers_state: Cell::new(ModifiersState::default()), }, @@ -128,6 +137,7 @@ impl Window { surface, panel: None, webview: None, + event_listeners: Default::default(), mouse_position: Default::default(), modifiers_state: Cell::new(ModifiersState::default()), }; diff --git a/verso/src/lib.rs b/verso/src/lib.rs index 6ab21f86..2fcf0fca 100644 --- a/verso/src/lib.rs +++ b/verso/src/lib.rs @@ -1,9 +1,21 @@ -use std::{path::Path, process::Command}; +use std::{ + path::Path, + process::Command, + sync::{Arc, Mutex}, +}; use versoview_messages::ControllerMessage; -use ipc_channel::ipc::{IpcOneShotServer, IpcSender}; +use ipc_channel::ipc::{self, IpcOneShotServer, IpcSender}; -pub struct VersoviewController(IpcSender); +#[derive(Default)] +struct EventListeners { + on_navigation_starting: Arc bool + Send + 'static>>>>, +} + +pub struct VersoviewController { + sender: IpcSender, + event_listeners: EventListeners, +} impl VersoviewController { /// Create a new verso instance and get the controller to it @@ -17,11 +29,37 @@ impl VersoviewController { .spawn() .unwrap(); let (_, sender) = server.accept().unwrap(); - Self(sender) + Self { + sender, + event_listeners: EventListeners::default(), + } } /// Navigate to url pub fn navigate(&self, url: url::Url) -> Result<(), Box> { - self.0.send(ControllerMessage::NavigateTo(url)) + self.sender.send(ControllerMessage::NavigateTo(url)) + } + + /// Listen on navigation starting triggered by user click on a link, + /// return a boolean in the callback to decide whether or not allowing this navigation + pub fn on_navigation_starting( + &self, + callback: impl Fn(url::Url) -> bool + Send + 'static, + ) -> Result<(), Box> { + self.event_listeners + .on_navigation_starting + .lock() + .unwrap() + .replace(Box::new(callback)); + let cb = self.event_listeners.on_navigation_starting.clone(); + let (sender, receiver) = ipc::channel::<(url::Url, ipc::IpcSender)>().unwrap(); + self.sender + .send(ControllerMessage::OnNavigationStarting(sender))?; + std::thread::spawn(move || { + while let Ok((url, result_sender)) = receiver.recv() { + let _ = result_sender.send(cb.lock().unwrap().as_ref().unwrap()(url)); + } + }); + Ok(()) } } diff --git a/verso/src/main.rs b/verso/src/main.rs index 188191d5..ab8a56a3 100644 --- a/verso/src/main.rs +++ b/verso/src/main.rs @@ -6,6 +6,12 @@ fn main() { versoview_path, url::Url::parse("https://example.com").unwrap(), ); + controller + .on_navigation_starting(|url| { + dbg!(url); + true + }) + .unwrap(); sleep(Duration::from_secs(10)); dbg!(controller .navigate(url::Url::parse("https://docs.rs").unwrap()) diff --git a/versoview_messages/src/lib.rs b/versoview_messages/src/lib.rs index f099afb0..b828a22c 100644 --- a/versoview_messages/src/lib.rs +++ b/versoview_messages/src/lib.rs @@ -1,7 +1,11 @@ +use ipc_channel::ipc; use serde::{Deserialize, Serialize}; +pub type OnNavigationStartingPayload = ipc::IpcSender<(url::Url, ipc::IpcSender)>; + #[derive(Debug, Serialize, Deserialize)] #[non_exhaustive] pub enum ControllerMessage { NavigateTo(url::Url), + OnNavigationStarting(OnNavigationStartingPayload), } From 3a79e84287dfc4b09955506ec6bff957b11b174f Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 8 Nov 2024 13:07:57 +0800 Subject: [PATCH 2/6] Handle error instead of unwrap --- src/errors.rs | 3 +++ src/webview.rs | 32 ++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 248b404a..80005242 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -14,4 +14,7 @@ pub enum Error { /// Glutin errors. #[error(transparent)] GlutinError(#[from] glutin::error::Error), + /// IPC errors. + #[error(transparent)] + IpcError(#[from] ipc_channel::ipc::IpcError), } diff --git a/src/webview.rs b/src/webview.rs index 3c564a1e..d49277d2 100644 --- a/src/webview.rs +++ b/src/webview.rs @@ -84,19 +84,13 @@ impl Window { send_to_constellation(sender, ConstellationMsg::FocusWebView(webview_id)); } EmbedderMsg::AllowNavigationRequest(id, url) => { - let mut allow = true; - if let Some(ref sender) = - *self.event_listeners.on_navigation_starting.lock().unwrap() - { - let (result_sender, receiver) = ipc::channel::().unwrap(); - if let Some(result) = sender - .send((url.into_url(), result_sender)) - .ok() - .and_then(|_| receiver.recv().ok()) - { - allow = result + let allow = match self.allow_navigation(url) { + Ok(allow) => allow, + Err(e) => { + log::error!("Error when calling navigation handler: {e}"); + true } - } + }; send_to_constellation(sender, ConstellationMsg::AllowNavigationResponse(id, allow)); } EmbedderMsg::GetClipboardContents(sender) => { @@ -298,4 +292,18 @@ impl Window { } false } + + fn allow_navigation(&self, url: ServoUrl) -> crate::Result { + if let Some(ref sender) = *self.event_listeners.on_navigation_starting.lock().unwrap() { + let (result_sender, receiver) = + ipc::channel::().map_err(|e| ipc::IpcError::Io(e))?; + sender + .send((url.into_url(), result_sender)) + .map_err(|e| ipc::IpcError::Bincode(e))?; + let result = receiver.recv()?; + Ok(result) + } else { + Ok(true) + } + } } From 03570d174af521023829dd6223d976c455d9c839 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 8 Nov 2024 13:09:31 +0800 Subject: [PATCH 3/6] One more handle error instead of unwrap --- verso/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verso/src/lib.rs b/verso/src/lib.rs index 2fcf0fca..a6720fba 100644 --- a/verso/src/lib.rs +++ b/verso/src/lib.rs @@ -52,7 +52,7 @@ impl VersoviewController { .unwrap() .replace(Box::new(callback)); let cb = self.event_listeners.on_navigation_starting.clone(); - let (sender, receiver) = ipc::channel::<(url::Url, ipc::IpcSender)>().unwrap(); + let (sender, receiver) = ipc::channel::<(url::Url, ipc::IpcSender)>()?; self.sender .send(ControllerMessage::OnNavigationStarting(sender))?; std::thread::spawn(move || { From 4d42fdb0784660124cc028dc4eb48dcbb9be5bf6 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 8 Nov 2024 13:11:45 +0800 Subject: [PATCH 4/6] Typo --- src/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window.rs b/src/window.rs index 1d479e72..b5d83c47 100644 --- a/src/window.rs +++ b/src/window.rs @@ -52,7 +52,7 @@ pub struct Window { pub(crate) panel: Option, /// The WebView of this window. pub(crate) webview: Option, - /// Event listners registered from the webview controller + /// Event listeners registered from the webview controller pub(crate) event_listeners: EventListeners, /// The mouse physical position in the web view. mouse_position: Cell>>, From 559510cad15f9af544a395c6dea4694842113185 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 12 Nov 2024 10:18:50 +0800 Subject: [PATCH 5/6] Use ROUTER.add_typed_route --- Cargo.lock | 1 + Cargo.toml | 3 ++- verso/Cargo.toml | 1 + verso/src/lib.rs | 31 +++++++++++++++++++++++-------- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02de8026..0285b6e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6187,6 +6187,7 @@ name = "verso" version = "0.0.1" dependencies = [ "ipc-channel", + "log", "serde", "url", "versoview_messages", diff --git a/Cargo.toml b/Cargo.toml index cfe1e877..acfe2f1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = ["verso", "versoview_messages"] ipc-channel = "0.19" serde = { version = "1.0", features = ["derive"] } url = { version = "2.5.2", features = ["serde"] } +log = "0.4" [package] name = "versoview" @@ -63,7 +64,7 @@ glutin = "0.32.0" glutin-winit = "0.5.0" ipc-channel = { workspace = true } keyboard-types = "0.7" -log = "0.4" +log = { workspace = true } raw-window-handle = { version = "0.6", features = ["std"] } sparkle = "0.1.26" thiserror = "1.0" diff --git a/verso/Cargo.toml b/verso/Cargo.toml index 807eaadc..7461b781 100644 --- a/verso/Cargo.toml +++ b/verso/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" ipc-channel = { workspace = true } serde = { workspace = true } url = { workspace = true } +log = { workspace = true } versoview_messages = { path = "../versoview_messages" } diff --git a/verso/src/lib.rs b/verso/src/lib.rs index a6720fba..b2fe950f 100644 --- a/verso/src/lib.rs +++ b/verso/src/lib.rs @@ -1,3 +1,4 @@ +use log::error; use std::{ path::Path, process::Command, @@ -5,7 +6,10 @@ use std::{ }; use versoview_messages::ControllerMessage; -use ipc_channel::ipc::{self, IpcOneShotServer, IpcSender}; +use ipc_channel::{ + ipc::{self, IpcOneShotServer, IpcSender}, + router::ROUTER, +}; #[derive(Default)] struct EventListeners { @@ -46,20 +50,31 @@ impl VersoviewController { &self, callback: impl Fn(url::Url) -> bool + Send + 'static, ) -> Result<(), Box> { - self.event_listeners + if self + .event_listeners .on_navigation_starting .lock() .unwrap() - .replace(Box::new(callback)); + .replace(Box::new(callback)) + .is_some() + { + return Ok(()); + } let cb = self.event_listeners.on_navigation_starting.clone(); let (sender, receiver) = ipc::channel::<(url::Url, ipc::IpcSender)>()?; self.sender .send(ControllerMessage::OnNavigationStarting(sender))?; - std::thread::spawn(move || { - while let Ok((url, result_sender)) = receiver.recv() { - let _ = result_sender.send(cb.lock().unwrap().as_ref().unwrap()(url)); - } - }); + ROUTER.add_typed_route( + receiver, + Box::new(move |message| match message { + Ok((url, result_sender)) => { + if let Err(e) = result_sender.send(cb.lock().unwrap().as_ref().unwrap()(url)) { + error!("Error while sending back OnNavigationStarting result: {e}"); + } + } + Err(e) => error!("Error while receiving OnNavigationStarting message: {e}"), + }), + ); Ok(()) } } From 581fb5c089ecf39017eda3fcbd21cd4f148bb824 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 19 Jan 2025 11:43:51 +0800 Subject: [PATCH 6/6] Remove mutex in on_navigation_starting --- src/verso.rs | 6 ++---- src/webview/webview.rs | 2 +- src/window.rs | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/verso.rs b/src/verso.rs index 9884d55b..fc6dc6c9 100644 --- a/src/verso.rs +++ b/src/verso.rs @@ -582,7 +582,7 @@ impl Verso { } /// Handle message came from webview controller. - pub fn handle_incoming_webview_message(&self, message: ControllerMessage) { + pub fn handle_incoming_webview_message(&mut self, message: ControllerMessage) { match message { ControllerMessage::NavigateTo(to_url) => { if let Some(webview_id) = @@ -597,12 +597,10 @@ impl Verso { } } ControllerMessage::OnNavigationStarting(sender) => { - if let Some((window, _)) = self.windows.values().next() { + if let Some((window, _)) = self.windows.values_mut().next() { window .event_listeners .on_navigation_starting - .lock() - .unwrap() .replace(sender); } } diff --git a/src/webview/webview.rs b/src/webview/webview.rs index b400cd00..4c6d4692 100644 --- a/src/webview/webview.rs +++ b/src/webview/webview.rs @@ -607,7 +607,7 @@ impl Window { } fn allow_navigation(&self, url: ServoUrl) -> crate::Result { - if let Some(ref sender) = *self.event_listeners.on_navigation_starting.lock().unwrap() { + if let Some(ref sender) = self.event_listeners.on_navigation_starting { let (result_sender, receiver) = ipc::channel::().map_err(|e| ipc::IpcError::Io(e))?; sender diff --git a/src/window.rs b/src/window.rs index 11461807..54781e34 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,4 +1,4 @@ -use std::{cell::Cell, sync::Mutex}; +use std::cell::Cell; use base::id::WebViewId; use compositing_traits::ConstellationMsg; @@ -60,7 +60,7 @@ const PANEL_PADDING: f64 = 4.0; #[derive(Default)] pub(crate) struct EventListeners { - pub(crate) on_navigation_starting: Mutex>, + pub(crate) on_navigation_starting: Option, } /// A Verso window is a Winit window containing several web views.