Skip to content

Commit

Permalink
refactor(ipc): use http::Request on with_ipc_handler (#1183)
Browse files Browse the repository at this point in the history
* refactor(ipc): introduce IpcRequest type to get source URL on supported platforms

* fix linux

* use http::Request

* leftover config

* url from webview as fallback

* docs

* docs
  • Loading branch information
lucasfernog authored Mar 6, 2024
1 parent 549c663 commit b8fea39
Show file tree
Hide file tree
Showing 15 changed files with 100 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .changes/ipc-handler-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": minor
---

Changed `WebViewBuilder::with_ipc_handler` closure to take `http::Request` instead of `String` so the request URL is available.
5 changes: 3 additions & 2 deletions bench/tests/src/cpu_intensive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use std::process::exit;

fn main() -> wry::Result<()> {
use http::Request;
use tao::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
Expand All @@ -18,8 +19,8 @@ fn main() -> wry::Result<()> {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();

let handler = |req: String| {
if &req == "process-complete" {
let handler = |req: Request<String>| {
if req.body() == "process-complete" {
exit(0);
}
};
Expand Down
5 changes: 3 additions & 2 deletions bench/tests/src/custom_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct MessageParameters {
}

fn main() -> wry::Result<()> {
use http::Request;
use tao::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
Expand All @@ -42,8 +43,8 @@ fn main() -> wry::Result<()> {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();

let handler = |req: String| {
if &req == "dom-loaded" {
let handler = |req: Request<String>| {
if req.body() == "dom-loaded" {
exit(0);
}
};
Expand Down
5 changes: 3 additions & 2 deletions bench/tests/src/hello_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct MessageParameters {
}

fn main() -> wry::Result<()> {
use http::Request;
use tao::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
Expand All @@ -29,8 +30,8 @@ fn main() -> wry::Result<()> {
</script>
"#;

let handler = |req: String| {
if &req == "dom-loaded" {
let handler = |req: Request<String>| {
if req.body() == "dom-loaded" {
exit(0);
}
};
Expand Down
6 changes: 4 additions & 2 deletions examples/custom_titlebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use http::Request;
use tao::{
dpi::PhysicalSize,
event::{Event, StartCause, WindowEvent},
Expand Down Expand Up @@ -207,8 +208,9 @@ fn main() -> wry::Result<()> {
"#;

let proxy = event_loop.create_proxy();
let handler = move |req: String| {
let mut req = req.split([':', ',']);
let handler = move |req: Request<String>| {
let body = req.body();
let mut req = body.split([':', ',']);
match req.next().unwrap() {
"minimize" => {
let _ = proxy.send_event(UserEvent::Minimize);
Expand Down
26 changes: 15 additions & 11 deletions examples/multiwindow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use http::Request;
use std::collections::HashMap;
use tao::{
event::{Event, WindowEvent},
Expand Down Expand Up @@ -75,18 +76,21 @@ fn create_new_window(
.build(event_loop)
.unwrap();
let window_id = window.id();
let handler = move |req: String| match req.as_str() {
"new-window" => {
let _ = proxy.send_event(UserEvent::NewWindow);
}
"close" => {
let _ = proxy.send_event(UserEvent::CloseWindow(window_id));
}
_ if req.starts_with("change-title") => {
let title = req.replace("change-title:", "");
let _ = proxy.send_event(UserEvent::NewTitle(window_id, title));
let handler = move |req: Request<String>| {
let body = req.body();
match body.as_str() {
"new-window" => {
let _ = proxy.send_event(UserEvent::NewWindow);
}
"close" => {
let _ = proxy.send_event(UserEvent::CloseWindow(window_id));
}
_ if body.starts_with("change-title") => {
let title = body.replace("change-title:", "");
let _ = proxy.send_event(UserEvent::NewTitle(window_id, title));
}
_ => {}
}
_ => {}
};

#[cfg(any(
Expand Down
15 changes: 8 additions & 7 deletions src/android/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ macro_rules! android_binding {
onPageLoaded,
[JString]
);
android_fn!($domain, $package, Ipc, ipc, [JString]);
android_fn!($domain, $package, Ipc, ipc, [JString, JString]);
android_fn!(
$domain,
$package,
Expand Down Expand Up @@ -300,18 +300,19 @@ pub unsafe fn onEval(mut env: JNIEnv, _: JClass, id: jint, result: JString) {
}
}

pub unsafe fn ipc(mut env: JNIEnv, _: JClass, arg: JString) {
match env.get_string(&arg) {
Ok(arg) => {
pub unsafe fn ipc(mut env: JNIEnv, _: JClass, url: JString, body: JString) {
match (env.get_string(&url), env.get_string(&body)) {
(Ok(url), Ok(body)) => {
#[cfg(feature = "tracing")]
let _span = tracing::info_span!("wry::ipc::handle").entered();

let arg = arg.to_string_lossy().to_string();
let url = url.to_string_lossy().to_string();
let body = body.to_string_lossy().to_string();
if let Some(ipc) = IPC.get() {
(ipc.handler)(arg)
(ipc.handler)(Request::builder().uri(url).body(body).unwrap())
}
}
Err(e) => log::warn!("Failed to parse JString: {}", e),
(Err(e), _) | (_, Err(e)) => log::warn!("Failed to parse JString: {}", e),
}
}

Expand Down
9 changes: 6 additions & 3 deletions src/android/kotlin/Ipc.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ package {{package}}

import android.webkit.*

class Ipc {
class Ipc(val webViewClient: RustWebViewClient) {
@JavascriptInterface
fun postMessage(message: String?) {
message?.let {m ->
this.ipc(m)
// we're not using WebView::getUrl() here because it needs to be executed on the main thread
// and it would slow down the Ipc
// so instead we track the current URL on the webview client
this.ipc(webViewClient.currentUrl, m)
}
}

Expand All @@ -22,7 +25,7 @@ class Ipc {
}
}

private external fun ipc(message: String)
private external fun ipc(url: String, message: String)

{{class-extension}}
}
2 changes: 2 additions & 0 deletions src/android/kotlin/RustWebViewClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.webkit.WebViewAssetLoader

class RustWebViewClient(context: Context): WebViewClient() {
private val interceptedState = mutableMapOf<String, Boolean>()
var currentUrl: String = "about:blank"

private val assetLoader = WebViewAssetLoader.Builder()
.setDomain(assetLoaderDomain())
Expand Down Expand Up @@ -48,6 +49,7 @@ class RustWebViewClient(context: Context): WebViewClient() {
}

override fun onPageFinished(view: WebView, url: String) {
currentUrl = url
return onPageLoaded(url)
}

Expand Down
14 changes: 8 additions & 6 deletions src/android/main_pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,9 @@ impl<'a> MainPipe<'a> {
}

// Create and set webview client
let rust_webview_client_class = find_class(
&mut self.env,
activity,
format!("{}/RustWebViewClient", PACKAGE.get().unwrap()),
)?;
let client_class_name = format!("{}/RustWebViewClient", PACKAGE.get().unwrap());
let rust_webview_client_class =
find_class(&mut self.env, activity, client_class_name.clone())?;
let webview_client = self.env.new_object(
&rust_webview_client_class,
"(Landroid/content/Context;)V",
Expand All @@ -163,7 +161,11 @@ impl<'a> MainPipe<'a> {
activity,
format!("{}/Ipc", PACKAGE.get().unwrap()),
)?;
let ipc = self.env.new_object(ipc_class, "()V", &[])?;
let ipc = self.env.new_object(
ipc_class,
format!("(L{client_class_name};)V"),
&[(&webview_client).into()],
)?;
let ipc_str = self.env.new_string("ipc")?;
self.env.call_method(
&webview,
Expand Down
2 changes: 1 addition & 1 deletion src/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ macro_rules! define_static_handlers {
}

define_static_handlers! {
IPC = UnsafeIpc { handler: Box<dyn Fn(String)> };
IPC = UnsafeIpc { handler: Box<dyn Fn(Request<String>)> };
REQUEST_HANDLER = UnsafeRequestHandler { handler: Box<dyn Fn(Request<Vec<u8>>, bool) -> Option<HttpResponse<Cow<'static, [u8]>>>> };
TITLE_CHANGE_HANDLER = UnsafeTitleHandler { handler: Box<dyn Fn(String)> };
URL_LOADING_OVERRIDE = UnsafeUrlLoadingOverride { handler: Box<dyn Fn(String) -> bool> };
Expand Down
8 changes: 6 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ pub struct WebViewAttributes {

/// The IPC handler to receive the message from Javascript on webview
/// using `window.ipc.postMessage("insert_message_here")` to host Rust code.
pub ipc_handler: Option<Box<dyn Fn(String)>>,
pub ipc_handler: Option<Box<dyn Fn(Request<String>)>>,

/// A handler closure to process incoming [`FileDropEvent`] of the webview.
///
Expand Down Expand Up @@ -756,9 +756,13 @@ impl<'a> WebViewBuilder<'a> {

/// Set the IPC handler to receive the message from Javascript on webview
/// using `window.ipc.postMessage("insert_message_here")` to host Rust code.
///
/// ## Platform-specific
///
/// - **Linux / Android**: The request URL is not supported on iframes and the main frame URL is used instead.
pub fn with_ipc_handler<F>(mut self, handler: F) -> Self
where
F: Fn(String) + 'static,
F: Fn(Request<String>) + 'static,
{
self.attrs.ipc_handler = Some(Box::new(handler));
self
Expand Down
16 changes: 13 additions & 3 deletions src/webkitgtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use gtk::{
glib::{self, translate::FromGlibPtrFull},
prelude::*,
};
use http::Request;
use javascriptcore::ValueExt;
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
#[cfg(any(debug_assertions, feature = "devtools"))]
Expand Down Expand Up @@ -257,7 +258,7 @@ impl InnerWebView {
Self::attach_handlers(&webview, web_context, &mut attributes);

// IPC handler
Self::attach_ipc_handler(web_context, &mut attributes);
Self::attach_ipc_handler(webview.clone(), web_context, &mut attributes);

// File drop handler
if let Some(file_drop_handler) = attributes.file_drop_handler.take() {
Expand Down Expand Up @@ -492,7 +493,11 @@ impl InnerWebView {
is_in_fixed_parent
}

fn attach_ipc_handler(web_context: &WebContext, attributes: &mut WebViewAttributes) {
fn attach_ipc_handler(
webview: WebView,
web_context: &WebContext,
attributes: &mut WebViewAttributes,
) {
// Message handler
let ipc_handler = attributes.ipc_handler.take();
let manager = web_context.manager();
Expand All @@ -504,7 +509,12 @@ impl InnerWebView {

if let Some(js) = msg.js_value() {
if let Some(ipc_handler) = &ipc_handler {
ipc_handler(js.to_string());
ipc_handler(
Request::builder()
.uri(webview.uri().unwrap().to_string())
.body(js.to_string())
.unwrap(),
);
}
}
});
Expand Down
8 changes: 7 additions & 1 deletion src/webview2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,12 @@ impl InnerWebView {
return Ok(());
};

let url = {
let mut url = PWSTR::null();
args.Source(&mut url)?;
take_pwstr(url)
};

let js = {
let mut js = PWSTR::null();
args.TryGetWebMessageAsString(&mut js)?;
Expand All @@ -744,7 +750,7 @@ impl InnerWebView {

#[cfg(feature = "tracing")]
let _span = tracing::info_span!("wry::ipc::handle").entered();
ipc_handler(js);
ipc_handler(Request::builder().uri(url).body(js).unwrap());

Ok(())
})),
Expand Down
21 changes: 16 additions & 5 deletions src/wkwebview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub(crate) struct InnerWebView {
pending_scripts: Arc<Mutex<Option<Vec<String>>>>,
// Note that if following functions signatures are changed in the future,
// all functions pointer declarations in objc callbacks below all need to get updated.
ipc_handler_ptr: *mut Box<dyn Fn(String)>,
ipc_handler_ptr: *mut Box<dyn Fn(Request<String>)>,
document_title_changed_handler: *mut Box<dyn Fn(String)>,
navigation_decide_policy_ptr: *mut Box<dyn Fn(String, bool) -> bool>,
page_load_handler: *mut Box<dyn Fn(PageLoadEvent)>,
Expand Down Expand Up @@ -138,13 +138,23 @@ impl InnerWebView {

let function = this.get_ivar::<*mut c_void>("function");
if !function.is_null() {
let function = &mut *(*function as *mut Box<dyn Fn(String)>);
let function = &mut *(*function as *mut Box<dyn Fn(Request<String>)>);
let body: id = msg_send![msg, body];
let is_string: bool = msg_send![body, isKindOfClass: class!(NSString)];
if is_string {
let utf8: *const c_char = msg_send![body, UTF8String];
if let Ok(js) = CStr::from_ptr(utf8).to_str() {
(function)(js.to_string());
let js_utf8: *const c_char = msg_send![body, UTF8String];

let frame_info: id = msg_send![msg, frameInfo];
let request: id = msg_send![frame_info, request];
let url: id = msg_send![request, URL];
let absolute_url: id = msg_send![url, absoluteString];
let url_utf8: *const c_char = msg_send![absolute_url, UTF8String];

if let (Ok(url), Ok(js)) = (
CStr::from_ptr(url_utf8).to_str(),
CStr::from_ptr(js_utf8).to_str(),
) {
(function)(Request::builder().uri(url).body(js.to_string()).unwrap());
return;
}
}
Expand Down Expand Up @@ -1166,6 +1176,7 @@ r#"Object.defineProperty(window, 'ipc', {
Ok(())
}

#[cfg(target_os = "macos")]
pub(crate) fn reparent(&self, window: id) -> crate::Result<()> {
unsafe {
let content_view: id = msg_send![window, contentView];
Expand Down

0 comments on commit b8fea39

Please sign in to comment.