Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using IPC to handle navigations #222

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
3 changes: 3 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
10 changes: 10 additions & 0 deletions src/verso.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,16 @@ impl Verso {
);
}
}
ControllerMessage::OnNavigationStarting(sender) => {
if let Some((window, _)) = self.windows.values().next() {
window
.event_listeners
.on_navigation_starting
.lock()
.unwrap()
.replace(sender);
}
}
_ => {}
}
}
Expand Down
26 changes: 23 additions & 3 deletions src/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,15 @@ 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 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) => {
let contents = clipboard
Expand Down Expand Up @@ -286,4 +292,18 @@ impl Window {
}
false
}

fn allow_navigation(&self, url: ServoUrl) -> crate::Result<bool> {
if let Some(ref sender) = *self.event_listeners.on_navigation_starting.lock().unwrap() {
let (result_sender, receiver) =
ipc::channel::<bool>().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)
}
Comment on lines +297 to +307
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still believe there needs to be a dedicated channel for communicating asynchronously. The lock and recv here are strong deadlock material.

Copy link
Contributor Author

@Legend-Master Legend-Master Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lock and recv here are strong deadlock material.

I can clone the sender out here to prevent potential dead lock

for communicating asynchronously.

We can do this asynchronously though if you want it that way

I still believe there needs to be a dedicated channel

About a dedicated channel, do you mean an unified sender for all info we need to send to the core? I don't think it works well here to be honest

}
}
12 changes: 11 additions & 1 deletion src/window.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::cell::Cell;
use std::{cell::Cell, sync::Mutex};

use base::id::WebViewId;
use compositing_traits::ConstellationMsg;
Expand All @@ -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,
Expand All @@ -36,6 +37,11 @@ use crate::{

use arboard::Clipboard;

#[derive(Default)]
pub(crate) struct EventListeners {
pub(crate) on_navigation_starting: Mutex<Option<OnNavigationStartingPayload>>,
}

/// A Verso window is a Winit window containing several web views.
pub struct Window {
/// Access to Winit window
Expand All @@ -46,6 +52,8 @@ pub struct Window {
pub(crate) panel: Option<Panel>,
/// The WebView of this window.
pub(crate) webview: Option<WebView>,
/// Event listeners registered from the webview controller
pub(crate) event_listeners: EventListeners,
/// The mouse physical position in the web view.
mouse_position: Cell<Option<PhysicalPosition<f64>>>,
/// Modifiers state of the keyboard.
Expand Down Expand Up @@ -94,6 +102,7 @@ impl Window {
surface,
panel: None,
webview: None,
event_listeners: Default::default(),
mouse_position: Default::default(),
modifiers_state: Cell::new(ModifiersState::default()),
resizing: false,
Expand Down Expand Up @@ -131,6 +140,7 @@ impl Window {
surface,
panel: None,
webview: None,
event_listeners: Default::default(),
mouse_position: Default::default(),
modifiers_state: Cell::new(ModifiersState::default()),
resizing: false,
Expand Down
1 change: 1 addition & 0 deletions verso/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ edition = "2021"
ipc-channel = { workspace = true }
serde = { workspace = true }
url = { workspace = true }
log = { workspace = true }
versoview_messages = { path = "../versoview_messages" }
63 changes: 58 additions & 5 deletions verso/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
use std::{path::Path, process::Command};
use log::error;
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},
router::ROUTER,
};

pub struct VersoviewController(IpcSender<ControllerMessage>);
#[derive(Default)]
struct EventListeners {
on_navigation_starting: Arc<Mutex<Option<Box<dyn Fn(url::Url) -> bool + Send + 'static>>>>,
}

pub struct VersoviewController {
sender: IpcSender<ControllerMessage>,
event_listeners: EventListeners,
}

impl VersoviewController {
/// Create a new verso instance and get the controller to it
Expand All @@ -17,11 +33,48 @@ 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<ipc_channel::ErrorKind>> {
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<ipc_channel::ErrorKind>> {
if self
.event_listeners
.on_navigation_starting
.lock()
.unwrap()
.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<bool>)>()?;
self.sender
.send(ControllerMessage::OnNavigationStarting(sender))?;
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(())
}
}
6 changes: 6 additions & 0 deletions verso/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
4 changes: 4 additions & 0 deletions versoview_messages/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use ipc_channel::ipc;
use serde::{Deserialize, Serialize};

pub type OnNavigationStartingPayload = ipc::IpcSender<(url::Url, ipc::IpcSender<bool>)>;

#[derive(Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ControllerMessage {
NavigateTo(url::Url),
OnNavigationStarting(OnNavigationStartingPayload),
}