From 32d41e7baa6317a838bef95168039d860cfd7dba Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Sat, 5 Mar 2022 17:05:36 +0000 Subject: [PATCH 01/20] Modify webcontext to take EventLoopProxy Adjust relevant structs/functions to take generic arguments due to new constraint. --- src/webview/mod.rs | 17 ++++++++++------- src/webview/web_context.rs | 21 ++++++++++++++++----- src/webview/webview2/mod.rs | 10 ++++++---- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 322289e5a..f7901ac16 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -30,6 +30,7 @@ pub(crate) mod wkwebview; use wkwebview::*; #[cfg(target_os = "windows")] pub(crate) mod webview2; +use self::web_context::WebContextGeneric; #[cfg(target_os = "windows")] use self::webview2::*; use crate::Result; @@ -150,17 +151,17 @@ impl Default for WebViewAttributes { /// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to constrcut WebView contents and /// scripts for those who prefer to control fine grained window creation and event handling. /// [`WebViewBuilder`] privides ability to setup initialization before web engine starts. -pub struct WebViewBuilder<'a> { +pub struct WebViewBuilder<'a, T: 'static> { pub webview: WebViewAttributes, - web_context: Option<&'a mut WebContext>, + web_context: Option<&'a mut WebContextGeneric>, window: Window, } -impl<'a> WebViewBuilder<'a> { +impl<'a> WebViewBuilder<'a, ()> { /// Create [`WebViewBuilder`] from provided [`Window`]. pub fn new(window: Window) -> Result { let webview = WebViewAttributes::default(); - let web_context = None; + let web_context: Option<&mut WebContextGeneric<()>> = None; Ok(Self { webview, @@ -168,7 +169,9 @@ impl<'a> WebViewBuilder<'a> { window, }) } - +} + +impl<'a, T: 'static> WebViewBuilder<'a, T> { /// Sets whether the WebView should be transparent. Not supported on Windows 7. pub fn with_transparent(mut self, transparent: bool) -> Self { self.webview.transparent = transparent; @@ -272,7 +275,7 @@ impl<'a> WebViewBuilder<'a> { } /// Set the web context that can share with multiple [`WebView`]s. - pub fn with_web_context(mut self, web_context: &'a mut WebContext) -> Self { + pub fn with_web_context(mut self, web_context: &'a mut WebContextGeneric) -> Self { self.web_context = Some(web_context); self } @@ -357,7 +360,7 @@ impl WebView { /// /// [`EventLoop`]: crate::application::event_loop::EventLoop pub fn new(window: Window) -> Result { - WebViewBuilder::new(window)?.build() + WebViewBuilder::<()>::new(window)?.build() } /// Get the [`Window`] associate with the [`WebView`]. This can let you perform window related diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index b50dc858b..e0c48e532 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -1,3 +1,5 @@ +use tao::event_loop::EventLoopProxy; + #[cfg(any( target_os = "linux", target_os = "dragonfly", @@ -23,13 +25,16 @@ use std::path::{Path, PathBuf}; /// /// [`WebView`]: crate::webview::WebView #[derive(Debug)] -pub struct WebContext { +pub struct WebContextGeneric { data: WebContextData, + event_loop_proxy: Option>, #[allow(dead_code)] // It's not needed on Windows and macOS. pub(crate) os: WebContextImpl, } -impl WebContext { +pub type WebContext = WebContextGeneric<()>; + +impl WebContextGeneric { /// Create a new [`WebContext`]. /// /// `data_directory`: @@ -38,7 +43,7 @@ impl WebContext { pub fn new(data_directory: Option) -> Self { let data = WebContextData { data_directory }; let os = WebContextImpl::new(&data); - Self { data, os } + Self { data, event_loop_proxy: None, os } } /// A reference to the data directory the context was created with. @@ -53,13 +58,19 @@ impl WebContext { pub fn set_allows_automation(&mut self, flag: bool) { self.os.set_allows_automation(flag); } + + pub fn with_event_loop_proxy(mut self, proxy: EventLoopProxy) -> Self { + self.event_loop_proxy = Some(proxy); + + self + } } -impl Default for WebContext { +impl Default for WebContextGeneric { fn default() -> Self { let data = WebContextData::default(); let os = WebContextImpl::new(&data); - Self { data, os } + Self { data, event_loop_proxy: None, os } } } diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index 042d89699..76a5169c0 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -36,6 +36,8 @@ use crate::{ http::RequestBuilder as HttpRequestBuilder, }; +use super::web_context::WebContextGeneric; + impl From for Error { fn from(err: webview2_com::Error) -> Self { Error::WebView2Error(err) @@ -52,10 +54,10 @@ pub struct InnerWebView { } impl InnerWebView { - pub fn new( + pub fn new( window: Rc, mut attributes: WebViewAttributes, - web_context: Option<&mut WebContext>, + web_context: Option<&mut WebContextGeneric>, ) -> Result { let hwnd = HWND(window.hwnd() as _); let file_drop_controller: Rc> = Rc::new(OnceCell::new()); @@ -79,8 +81,8 @@ impl InnerWebView { }) } - fn create_environment( - web_context: &Option<&mut WebContext>, + fn create_environment( + web_context: &Option<&mut WebContextGeneric>, ) -> webview2_com::Result { let (tx, rx) = mpsc::channel(); From 4b4ee4da8d6d180c29a99eea2ec51506591eb47b Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Sat, 5 Mar 2022 17:18:11 +0000 Subject: [PATCH 02/20] Attempt to make other platforms accept generic webcontext --- src/webview/webkitgtk/web_context.rs | 27 ++++++++++++++++++--------- src/webview/wkwebview/mod.rs | 6 +++--- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/webview/webkitgtk/web_context.rs b/src/webview/webkitgtk/web_context.rs index afa4d85eb..fc8fe8025 100644 --- a/src/webview/webkitgtk/web_context.rs +++ b/src/webview/webkitgtk/web_context.rs @@ -17,13 +17,13 @@ use std::{ use url::Url; //use webkit2gtk_sys::webkit_uri_request_get_http_headers; use webkit2gtk::{ - traits::*, ApplicationInfo, CookiePersistentStorage, LoadEvent, UserContentManager, WebContext, + traits::*, ApplicationInfo, CookiePersistentStorage, LoadEvent, UserContentManager, WebContextGeneric, WebContextBuilder, WebView, WebsiteDataManagerBuilder, }; #[derive(Debug)] -pub struct WebContextImpl { - context: WebContext, +pub struct WebContextImpl { + context: WebContextGeneric, manager: UserContentManager, webview_uri_loader: Rc, registered_protocols: HashSet, @@ -31,7 +31,7 @@ pub struct WebContextImpl { app_info: Option, } -impl WebContextImpl { +impl WebContextImpl<()> { pub fn new(data: &WebContextData) -> Self { use webkit2gtk::traits::*; @@ -84,18 +84,26 @@ impl WebContextImpl { app_info: Some(app_info), } } +} +impl WebContextImpl { pub fn set_allows_automation(&mut self, flag: bool) { use webkit2gtk::traits::*; self.automation = flag; self.context.set_automation_allowed(flag); } + + pub fn with_event_loop_proxy(mut self, proxy: EventLoopProxy) -> Self { + self.context.event_loop_proxy = Some(proxy); + + self + } } /// [`WebContext`](super::WebContext) items that only matter on unix. -pub trait WebContextExt { +pub trait WebContextExt { /// The GTK [`WebContext`] of all webviews in the context. - fn context(&self) -> &WebContext; + fn context(&self) -> &WebContextGeneric; /// The GTK [`UserContentManager`] of all webviews in the context. fn manager(&self) -> &UserContentManager; @@ -135,8 +143,8 @@ pub trait WebContextExt { fn register_automation(&mut self, webview: WebView); } -impl WebContextExt for super::WebContext { - fn context(&self) -> &WebContext { +impl WebContextExt for super::WebContextGeneric { + fn context(&self) -> &WebContextGeneric { &self.os.context } @@ -201,12 +209,13 @@ impl WebContextExt for super::WebContext { } fn actually_register_uri_scheme( - context: &mut super::WebContext, + context: &mut super::WebContextGeneric, name: &str, handler: F, ) -> crate::Result<()> where F: Fn(&HttpRequest) -> crate::Result + 'static, + T: 'static, { use webkit2gtk::traits::*; let context = &context.os.context; diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index b8f96c59a..e4601332c 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -46,7 +46,7 @@ use crate::{ dpi::{LogicalSize, PhysicalSize}, window::Window, }, - webview::{FileDropEvent, WebContext, WebViewAttributes}, + webview::{FileDropEvent, WebContextGeneric, WebViewAttributes}, Result, }; @@ -68,10 +68,10 @@ pub struct InnerWebView { } impl InnerWebView { - pub fn new( + pub fn new( window: Rc, attributes: WebViewAttributes, - mut web_context: Option<&mut WebContext>, + mut web_context: Option<&mut WebContextGeneric>, ) -> Result { // Function for ipc handler extern "C" fn did_receive(this: &Object, _: Sel, _: id, msg: id) { From f6ce724dbf203d3fca73c857e9b76e31c33b7610 Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Sun, 6 Mar 2022 15:29:54 +0000 Subject: [PATCH 03/20] Modify with_event_loop proxy to allow different event_loop types --- src/webview/web_context.rs | 10 ++++++---- src/webview/webkitgtk/web_context.rs | 11 +++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index e0c48e532..d8b450f33 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -59,10 +59,12 @@ impl WebContextGeneric { self.os.set_allows_automation(flag); } - pub fn with_event_loop_proxy(mut self, proxy: EventLoopProxy) -> Self { - self.event_loop_proxy = Some(proxy); - - self + pub fn with_event_loop_proxy(self, proxy: EventLoopProxy) -> WebContextGeneric { + WebContextGeneric { + event_loop_proxy: Some(proxy), + data: self.data, + os: self.os + } } } diff --git a/src/webview/webkitgtk/web_context.rs b/src/webview/webkitgtk/web_context.rs index fc8fe8025..a88b371bd 100644 --- a/src/webview/webkitgtk/web_context.rs +++ b/src/webview/webkitgtk/web_context.rs @@ -93,10 +93,17 @@ impl WebContextImpl { self.context.set_automation_allowed(flag); } - pub fn with_event_loop_proxy(mut self, proxy: EventLoopProxy) -> Self { + pub fn with_event_loop_proxy(self, proxy: EventLoopProxy) -> WebContextImpl { self.context.event_loop_proxy = Some(proxy); - self + WebContextImpl { + context: self.context.with_event_loop_proxy, + manager: self.manager, + webview_uri_loader: self.webview_uri_loader, + registered_protocols: self.registered_protocols, + automation: self.automation, + app_info: self.app_info, + } } } From f296b3deb9480849fae2182d6cda987d591951f0 Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Sun, 6 Mar 2022 22:12:24 +0000 Subject: [PATCH 04/20] Fix WebViewBuilder to be able to switch types --- src/webview/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/webview/mod.rs b/src/webview/mod.rs index f7901ac16..db9a49953 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -275,9 +275,12 @@ impl<'a, T: 'static> WebViewBuilder<'a, T> { } /// Set the web context that can share with multiple [`WebView`]s. - pub fn with_web_context(mut self, web_context: &'a mut WebContextGeneric) -> Self { - self.web_context = Some(web_context); - self + pub fn with_web_context(self, web_context: &'a mut WebContextGeneric) -> WebViewBuilder { + WebViewBuilder { + web_context: Some(web_context), + webview: self.webview, + window: self.window + } } /// Set a custom [user-agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) for the WebView. From 8f6197eb66ce99ad36c51b3b3ab5d9c395203d4b Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Sun, 6 Mar 2022 22:13:42 +0000 Subject: [PATCH 05/20] Add event loop proxy getter --- src/webview/web_context.rs | 5 +++++ src/webview/webkitgtk/web_context.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index d8b450f33..4630eada7 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -66,6 +66,11 @@ impl WebContextGeneric { os: self.os } } + + pub fn event_loop_proxy(&self) -> Option<&EventLoopProxy> { + self.event_loop_proxy.as_ref() + } + } impl Default for WebContextGeneric { diff --git a/src/webview/webkitgtk/web_context.rs b/src/webview/webkitgtk/web_context.rs index a88b371bd..9de87db0f 100644 --- a/src/webview/webkitgtk/web_context.rs +++ b/src/webview/webkitgtk/web_context.rs @@ -105,6 +105,10 @@ impl WebContextImpl { app_info: self.app_info, } } + + pub fn event_loop_proxy(&self) -> Option<&EventLoopProxy> { + self.context.event_loop_proxy.as_ref() + } } /// [`WebContext`](super::WebContext) items that only matter on unix. From a76bca4e5a63d3bb351af43039bcfc6860fda85a Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Sun, 6 Mar 2022 22:17:11 +0000 Subject: [PATCH 06/20] Add getter and setter for naviagtion event constructor callback --- src/webview/web_context.rs | 34 ++++++++++++++++++++++++---- src/webview/webkitgtk/web_context.rs | 11 +++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index 4630eada7..861723744 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -11,7 +11,7 @@ use crate::webview::webkitgtk::WebContextImpl; #[cfg(any(target_os = "macos", target_os = "ios"))] use crate::webview::wkwebview::WebContextImpl; -use std::path::{Path, PathBuf}; +use std::{path::{Path, PathBuf}, sync::Arc}; /// A context that is shared between multiple [`WebView`]s. /// @@ -24,10 +24,10 @@ use std::path::{Path, PathBuf}; /// interact with them. /// /// [`WebView`]: crate::webview::WebView -#[derive(Debug)] pub struct WebContextGeneric { data: WebContextData, event_loop_proxy: Option>, + navigation_event: Option T>>, #[allow(dead_code)] // It's not needed on Windows and macOS. pub(crate) os: WebContextImpl, } @@ -43,7 +43,12 @@ impl WebContextGeneric { pub fn new(data_directory: Option) -> Self { let data = WebContextData { data_directory }; let os = WebContextImpl::new(&data); - Self { data, event_loop_proxy: None, os } + Self { + data, + event_loop_proxy: None, + navigation_event: None, + os + } } /// A reference to the data directory the context was created with. @@ -62,6 +67,7 @@ impl WebContextGeneric { pub fn with_event_loop_proxy(self, proxy: EventLoopProxy) -> WebContextGeneric { WebContextGeneric { event_loop_proxy: Some(proxy), + navigation_event: None, data: self.data, os: self.os } @@ -71,13 +77,27 @@ impl WebContextGeneric { self.event_loop_proxy.as_ref() } + pub fn with_navigation_event(mut self, event: impl Fn(String) -> T + 'static) -> Self { + self.navigation_event = Some(Arc::new(event)); + + self + } + + pub fn navigation_event(&self) -> Option<&Arc T>> { + self.navigation_event.as_ref() + } } impl Default for WebContextGeneric { fn default() -> Self { let data = WebContextData::default(); let os = WebContextImpl::new(&data); - Self { data, event_loop_proxy: None, os } + Self { + data, + event_loop_proxy: None, + navigation_event: None, + os + } } } @@ -106,3 +126,9 @@ impl WebContextImpl { fn set_allows_automation(&mut self, _flag: bool) {} } + +impl std::fmt::Debug for WebContextGeneric { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WebContextGeneric").field("data", &self.data).field("event_loop_proxy", &self.event_loop_proxy).field("os", &self.os).finish() + } +} diff --git a/src/webview/webkitgtk/web_context.rs b/src/webview/webkitgtk/web_context.rs index 9de87db0f..4118ae8ec 100644 --- a/src/webview/webkitgtk/web_context.rs +++ b/src/webview/webkitgtk/web_context.rs @@ -10,6 +10,7 @@ use std::{ collections::{HashSet, VecDeque}, rc::Rc, sync::{ + Arc, atomic::{AtomicBool, Ordering::SeqCst}, Mutex, }, @@ -109,6 +110,16 @@ impl WebContextImpl { pub fn event_loop_proxy(&self) -> Option<&EventLoopProxy> { self.context.event_loop_proxy.as_ref() } + + pub fn with_navigation_event(mut self, event: impl Fn(String) -> T + 'static) -> Self { + self.context.navigation_event = Some(Arc::new(event)); + + self + } + + pub fn navigation_event(&self) -> Option<&Arc T>> { + self.context.navigation_event.as_ref() + } } /// [`WebContext`](super::WebContext) items that only matter on unix. From 460d8967df9d274e6e72e8a8bfc2b029c7806fbc Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Mon, 7 Mar 2022 11:33:49 +0000 Subject: [PATCH 07/20] Complete initial implementation of nav events, cancellation --- examples/navigation_event.rs | 54 ++++++++++++++++++++++++++++++++++++ src/webview/web_context.rs | 22 +++++++++++---- src/webview/webview2/mod.rs | 41 +++++++++++++++++++++++++-- 3 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 examples/navigation_event.rs diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs new file mode 100644 index 000000000..a25295e61 --- /dev/null +++ b/examples/navigation_event.rs @@ -0,0 +1,54 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +fn main() -> wry::Result<()> { + use wry::{ + application::{ + event::{Event, StartCause, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, + }, + webview::{WebViewBuilder, WebContext}, + }; + + enum UserEvent { + Navigation(String) + } + + let event_loop: EventLoop = EventLoop::with_user_event(); + let window = WindowBuilder::new() + .with_title("Hello World") + .build(&event_loop)?; + let mut web_context = WebContext::default() + .with_event_loop_proxy(event_loop.create_proxy()) + .with_navigation_event( + |uri| UserEvent::Navigation(uri.to_string()), + |uri| { + !uri.contains("neverssl") + } + ); + let webview = WebViewBuilder::new(window)? + .with_url("http://neverssl.com")? + .with_web_context(&mut web_context) + .build()?; + + #[cfg(debug_assertions)] + webview.devtool(); + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + + match event { + Event::NewEvents(StartCause::Init) => println!("Wry has started!"), + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => *control_flow = ControlFlow::Exit, + Event::UserEvent(UserEvent::Navigation(uri)) => { + println!("{}", uri); + } + _ => (), + } + }); +} diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index 861723744..cc5ff2e3d 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -27,7 +27,8 @@ use std::{path::{Path, PathBuf}, sync::Arc}; pub struct WebContextGeneric { data: WebContextData, event_loop_proxy: Option>, - navigation_event: Option T>>, + navigation_event: Option T>>, + should_cancel: Option bool>>, #[allow(dead_code)] // It's not needed on Windows and macOS. pub(crate) os: WebContextImpl, } @@ -47,6 +48,7 @@ impl WebContextGeneric { data, event_loop_proxy: None, navigation_event: None, + should_cancel: None, os } } @@ -68,6 +70,7 @@ impl WebContextGeneric { WebContextGeneric { event_loop_proxy: Some(proxy), navigation_event: None, + should_cancel: None, data: self.data, os: self.os } @@ -77,14 +80,22 @@ impl WebContextGeneric { self.event_loop_proxy.as_ref() } - pub fn with_navigation_event(mut self, event: impl Fn(String) -> T + 'static) -> Self { - self.navigation_event = Some(Arc::new(event)); + pub fn with_navigation_event( + mut self, + event_builder: impl Fn(&str) -> T + 'static, + should_cancel: impl Fn(&str) -> bool + 'static + ) -> Self { + self.navigation_event = Some(Arc::new(event_builder)); + self.should_cancel = Some(Arc::new(should_cancel)); self } - pub fn navigation_event(&self) -> Option<&Arc T>> { - self.navigation_event.as_ref() + pub fn navigation_event(&self) -> (Option<&Arc T>>, Option<&Arc bool>>) { + ( + self.navigation_event.as_ref(), + self.should_cancel.as_ref() + ) } } @@ -96,6 +107,7 @@ impl Default for WebContextGeneric { data, event_loop_proxy: None, navigation_event: None, + should_cancel: None, os } } diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index 76a5169c0..b32ce8afb 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -5,7 +5,7 @@ mod file_drop; use crate::{ - webview::{WebContext, WebViewAttributes}, + webview::WebViewAttributes, Error, Result, }; @@ -66,7 +66,7 @@ impl InnerWebView { let env = Self::create_environment(&web_context)?; let controller = Self::create_controller(hwnd, &env)?; - let webview = Self::init_webview(window, hwnd, attributes, &env, &controller)?; + let webview = Self::init_webview(window, hwnd, attributes, &env, &controller, &web_context)?; if let Some(file_drop_handler) = file_drop_handler { let mut controller = FileDropController::new(); @@ -154,12 +154,13 @@ impl InnerWebView { .map_err(webview2_com::Error::WindowsError) } - fn init_webview( + fn init_webview( window: Rc, hwnd: HWND, mut attributes: WebViewAttributes, env: &ICoreWebView2Environment, controller: &ICoreWebView2Controller, + context: &Option<&mut WebContextGeneric> ) -> webview2_com::Result { let webview = unsafe { controller.CoreWebView2() }.map_err(webview2_com::Error::WindowsError)?; @@ -301,6 +302,40 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ } .map_err(webview2_com::Error::WindowsError)?; + if let Some(( + Some(event_proxy), + ( + Some(event_builder), + Some(should_cancel) + ) + )) = context.as_ref().map(|c| (c.event_loop_proxy(), c.navigation_event())) { + unsafe { + let event_proxy = event_proxy.clone(); + let event_builder = event_builder.clone(); + let should_cancel = should_cancel.clone(); + webview + .NavigationStarting( + NavigationStartingEventHandler::create(Box::new(move |_, args| { + if let Some(args) = args { + let mut uri = PWSTR::default(); + args.Uri(&mut uri)?; + let uri = take_pwstr(uri); + + args.SetCancel(should_cancel(&uri))?; + + let event = event_builder(&uri); + if event_proxy.send_event(event).is_ok() { + } + } + + Ok(()) + })), + &mut token + ) + .map_err(webview2_com::Error::WindowsError)?; + } + } + let mut custom_protocol_names = HashSet::new(); if !attributes.custom_protocols.is_empty() { for (name, _) in &attributes.custom_protocols { From 9978b0797efcd10571d8bf3e0aff1ab657ef2dfd Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Mon, 7 Mar 2022 22:24:07 +0000 Subject: [PATCH 08/20] Move majority implementation into WebView/WebViewData No longer make WebContext generic over user event type. Only take single closure, which defines behaviour to execute when navigation occurs, and also whether to cancel navigation. If user wishes to submit an event, they should simply move an event proxy into the event closure and submit an event through it - see updated example. WebViewBuilder will now raise return an error if there is no WebContext to attach callback to. --- examples/navigation_event.rs | 15 +++---- src/lib.rs | 2 + src/webview/mod.rs | 39 +++++++++------- src/webview/web_context.rs | 87 +++++++++++------------------------- src/webview/webview2/mod.rs | 34 +++++--------- 5 files changed, 69 insertions(+), 108 deletions(-) diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs index a25295e61..ae5a6e72d 100644 --- a/examples/navigation_event.rs +++ b/examples/navigation_event.rs @@ -17,20 +17,19 @@ fn main() -> wry::Result<()> { } let event_loop: EventLoop = EventLoop::with_user_event(); + let proxy = event_loop.create_proxy(); let window = WindowBuilder::new() .with_title("Hello World") .build(&event_loop)?; - let mut web_context = WebContext::default() - .with_event_loop_proxy(event_loop.create_proxy()) - .with_navigation_event( - |uri| UserEvent::Navigation(uri.to_string()), - |uri| { - !uri.contains("neverssl") - } - ); + let mut web_context = WebContext::default(); let webview = WebViewBuilder::new(window)? .with_url("http://neverssl.com")? .with_web_context(&mut web_context) + .with_navigation_callback(move |uri: String| { + let submitted = proxy.send_event(UserEvent::Navigation(uri.clone())).is_ok(); + + !submitted || !uri.contains("neverssl") + })? .build()?; #[cfg(debug_assertions)] diff --git a/src/lib.rs b/src/lib.rs index 7308eda0f..de736d153 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,4 +180,6 @@ pub enum Error { InvalidMethod(#[from] InvalidMethod), #[error("Infallible error, something went really wrong: {0}")] Infallible(#[from] std::convert::Infallible), + #[error("No WebContext provided")] + MissingContext, } diff --git a/src/webview/mod.rs b/src/webview/mod.rs index db9a49953..555f0b21d 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -6,7 +6,7 @@ mod web_context; -pub use web_context::WebContext; +pub use web_context::{WebContext, NavCallback}; #[cfg(any( target_os = "linux", @@ -30,10 +30,9 @@ pub(crate) mod wkwebview; use wkwebview::*; #[cfg(target_os = "windows")] pub(crate) mod webview2; -use self::web_context::WebContextGeneric; #[cfg(target_os = "windows")] use self::webview2::*; -use crate::Result; +use crate::{Result, Error::MissingContext}; #[cfg(target_os = "windows")] use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; #[cfg(target_os = "windows")] @@ -151,17 +150,17 @@ impl Default for WebViewAttributes { /// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to constrcut WebView contents and /// scripts for those who prefer to control fine grained window creation and event handling. /// [`WebViewBuilder`] privides ability to setup initialization before web engine starts. -pub struct WebViewBuilder<'a, T: 'static> { +pub struct WebViewBuilder<'a> { pub webview: WebViewAttributes, - web_context: Option<&'a mut WebContextGeneric>, + web_context: Option<&'a mut WebContext>, window: Window, } -impl<'a> WebViewBuilder<'a, ()> { +impl<'a> WebViewBuilder<'a> { /// Create [`WebViewBuilder`] from provided [`Window`]. pub fn new(window: Window) -> Result { let webview = WebViewAttributes::default(); - let web_context: Option<&mut WebContextGeneric<()>> = None; + let web_context = None; Ok(Self { webview, @@ -169,9 +168,7 @@ impl<'a> WebViewBuilder<'a, ()> { window, }) } -} - -impl<'a, T: 'static> WebViewBuilder<'a, T> { + /// Sets whether the WebView should be transparent. Not supported on Windows 7. pub fn with_transparent(mut self, transparent: bool) -> Self { self.webview.transparent = transparent; @@ -275,12 +272,9 @@ impl<'a, T: 'static> WebViewBuilder<'a, T> { } /// Set the web context that can share with multiple [`WebView`]s. - pub fn with_web_context(self, web_context: &'a mut WebContextGeneric) -> WebViewBuilder { - WebViewBuilder { - web_context: Some(web_context), - webview: self.webview, - window: self.window - } + pub fn with_web_context(mut self, web_context: &'a mut WebContext) -> Self { + self.web_context = Some(web_context); + self } /// Set a custom [user-agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) for the WebView. @@ -302,6 +296,17 @@ impl<'a, T: 'static> WebViewBuilder<'a, T> { self } + pub fn with_navigation_callback(mut self, callback: impl NavCallback) -> Result { + match self.web_context { + None => Err(MissingContext), + Some(web_context) => { + web_context.set_navigation_callback(callback); + self.web_context = Some(web_context); + Ok(self) + } + } + } + /// Consume the builder and create the [`WebView`]. /// /// Platform-specific behavior: @@ -363,7 +368,7 @@ impl WebView { /// /// [`EventLoop`]: crate::application::event_loop::EventLoop pub fn new(window: Window) -> Result { - WebViewBuilder::<()>::new(window)?.build() + WebViewBuilder::new(window)?.build() } /// Get the [`Window`] associate with the [`WebView`]. This can let you perform window related diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index cc5ff2e3d..4001fd137 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -1,5 +1,3 @@ -use tao::event_loop::EventLoopProxy; - #[cfg(any( target_os = "linux", target_os = "dragonfly", @@ -24,33 +22,23 @@ use std::{path::{Path, PathBuf}, sync::Arc}; /// interact with them. /// /// [`WebView`]: crate::webview::WebView -pub struct WebContextGeneric { +#[derive(Debug)] +pub struct WebContext { data: WebContextData, - event_loop_proxy: Option>, - navigation_event: Option T>>, - should_cancel: Option bool>>, #[allow(dead_code)] // It's not needed on Windows and macOS. pub(crate) os: WebContextImpl, } -pub type WebContext = WebContextGeneric<()>; - -impl WebContextGeneric { +impl WebContext { /// Create a new [`WebContext`]. /// /// `data_directory`: /// * Whether the WebView window should have a custom user data path. This is useful in Windows /// when a bundled application can't have the webview data inside `Program Files`. pub fn new(data_directory: Option) -> Self { - let data = WebContextData { data_directory }; + let data = WebContextData { data_directory, nav_callback: None }; let os = WebContextImpl::new(&data); - Self { - data, - event_loop_proxy: None, - navigation_event: None, - should_cancel: None, - os - } + Self { data, os } } /// A reference to the data directory the context was created with. @@ -66,57 +54,32 @@ impl WebContextGeneric { self.os.set_allows_automation(flag); } - pub fn with_event_loop_proxy(self, proxy: EventLoopProxy) -> WebContextGeneric { - WebContextGeneric { - event_loop_proxy: Some(proxy), - navigation_event: None, - should_cancel: None, - data: self.data, - os: self.os - } - } - - pub fn event_loop_proxy(&self) -> Option<&EventLoopProxy> { - self.event_loop_proxy.as_ref() + pub fn set_navigation_callback(&mut self, callback: impl NavCallback) { + self.data.nav_callback = Some(Arc::new(callback)) } - pub fn with_navigation_event( - mut self, - event_builder: impl Fn(&str) -> T + 'static, - should_cancel: impl Fn(&str) -> bool + 'static - ) -> Self { - self.navigation_event = Some(Arc::new(event_builder)); - self.should_cancel = Some(Arc::new(should_cancel)); - - self - } - - pub fn navigation_event(&self) -> (Option<&Arc T>>, Option<&Arc bool>>) { - ( - self.navigation_event.as_ref(), - self.should_cancel.as_ref() - ) + pub fn navigation_callback(&self) -> Option<&Arc> { + self.data.nav_callback() } } -impl Default for WebContextGeneric { +impl Default for WebContext { fn default() -> Self { let data = WebContextData::default(); let os = WebContextImpl::new(&data); - Self { - data, - event_loop_proxy: None, - navigation_event: None, - should_cancel: None, - os - } + Self { data, os } } } +pub trait NavCallback: Fn(String) -> bool + 'static {} + +impl bool + 'static> NavCallback for T {} + /// Data that all [`WebContext`] share regardless of platform. -#[derive(Debug, Default)] +#[derive(Default)] pub struct WebContextData { data_directory: Option, + nav_callback: Option> } impl WebContextData { @@ -124,6 +87,16 @@ impl WebContextData { pub fn data_directory(&self) -> Option<&Path> { self.data_directory.as_deref() } + + pub fn nav_callback(&self) -> Option<&Arc> { + self.nav_callback.as_ref() + } +} + +impl std::fmt::Debug for WebContextData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WebContextData").field("data_directory", &self.data_directory).field("nav_callback", &self.nav_callback.is_some()).finish() + } } #[cfg(target_os = "windows")] @@ -138,9 +111,3 @@ impl WebContextImpl { fn set_allows_automation(&mut self, _flag: bool) {} } - -impl std::fmt::Debug for WebContextGeneric { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("WebContextGeneric").field("data", &self.data).field("event_loop_proxy", &self.event_loop_proxy).field("os", &self.os).finish() - } -} diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index b32ce8afb..0879115dc 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -5,7 +5,7 @@ mod file_drop; use crate::{ - webview::WebViewAttributes, + webview::{WebContext, WebViewAttributes}, Error, Result, }; @@ -36,8 +36,6 @@ use crate::{ http::RequestBuilder as HttpRequestBuilder, }; -use super::web_context::WebContextGeneric; - impl From for Error { fn from(err: webview2_com::Error) -> Self { Error::WebView2Error(err) @@ -54,10 +52,10 @@ pub struct InnerWebView { } impl InnerWebView { - pub fn new( + pub fn new( window: Rc, mut attributes: WebViewAttributes, - web_context: Option<&mut WebContextGeneric>, + web_context: Option<&mut WebContext>, ) -> Result { let hwnd = HWND(window.hwnd() as _); let file_drop_controller: Rc> = Rc::new(OnceCell::new()); @@ -81,8 +79,8 @@ impl InnerWebView { }) } - fn create_environment( - web_context: &Option<&mut WebContextGeneric>, + fn create_environment( + web_context: &Option<&mut WebContext>, ) -> webview2_com::Result { let (tx, rx) = mpsc::channel(); @@ -154,13 +152,13 @@ impl InnerWebView { .map_err(webview2_com::Error::WindowsError) } - fn init_webview( + fn init_webview( window: Rc, hwnd: HWND, mut attributes: WebViewAttributes, env: &ICoreWebView2Environment, controller: &ICoreWebView2Controller, - context: &Option<&mut WebContextGeneric> + context: &Option<&mut WebContext> ) -> webview2_com::Result { let webview = unsafe { controller.CoreWebView2() }.map_err(webview2_com::Error::WindowsError)?; @@ -302,17 +300,9 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ } .map_err(webview2_com::Error::WindowsError)?; - if let Some(( - Some(event_proxy), - ( - Some(event_builder), - Some(should_cancel) - ) - )) = context.as_ref().map(|c| (c.event_loop_proxy(), c.navigation_event())) { + if let Some(nav_callback) = context.as_ref().and_then(|c| c.navigation_callback()) { unsafe { - let event_proxy = event_proxy.clone(); - let event_builder = event_builder.clone(); - let should_cancel = should_cancel.clone(); + let nav_callback = nav_callback.clone(); webview .NavigationStarting( NavigationStartingEventHandler::create(Box::new(move |_, args| { @@ -321,11 +311,9 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ args.Uri(&mut uri)?; let uri = take_pwstr(uri); - args.SetCancel(should_cancel(&uri))?; + let cancel = nav_callback(uri); - let event = event_builder(&uri); - if event_proxy.send_event(event).is_ok() { - } + args.SetCancel(cancel)?; } Ok(()) From 6ae72f53920bfeb81b282c22ec9e81c9a3886b49 Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Mon, 7 Mar 2022 22:44:32 +0000 Subject: [PATCH 09/20] Revert changes to Gtk WebContext --- src/webview/webkitgtk/web_context.rs | 49 +++++----------------------- 1 file changed, 9 insertions(+), 40 deletions(-) diff --git a/src/webview/webkitgtk/web_context.rs b/src/webview/webkitgtk/web_context.rs index 4118ae8ec..afa4d85eb 100644 --- a/src/webview/webkitgtk/web_context.rs +++ b/src/webview/webkitgtk/web_context.rs @@ -10,7 +10,6 @@ use std::{ collections::{HashSet, VecDeque}, rc::Rc, sync::{ - Arc, atomic::{AtomicBool, Ordering::SeqCst}, Mutex, }, @@ -18,13 +17,13 @@ use std::{ use url::Url; //use webkit2gtk_sys::webkit_uri_request_get_http_headers; use webkit2gtk::{ - traits::*, ApplicationInfo, CookiePersistentStorage, LoadEvent, UserContentManager, WebContextGeneric, + traits::*, ApplicationInfo, CookiePersistentStorage, LoadEvent, UserContentManager, WebContext, WebContextBuilder, WebView, WebsiteDataManagerBuilder, }; #[derive(Debug)] -pub struct WebContextImpl { - context: WebContextGeneric, +pub struct WebContextImpl { + context: WebContext, manager: UserContentManager, webview_uri_loader: Rc, registered_protocols: HashSet, @@ -32,7 +31,7 @@ pub struct WebContextImpl { app_info: Option, } -impl WebContextImpl<()> { +impl WebContextImpl { pub fn new(data: &WebContextData) -> Self { use webkit2gtk::traits::*; @@ -85,47 +84,18 @@ impl WebContextImpl<()> { app_info: Some(app_info), } } -} -impl WebContextImpl { pub fn set_allows_automation(&mut self, flag: bool) { use webkit2gtk::traits::*; self.automation = flag; self.context.set_automation_allowed(flag); } - - pub fn with_event_loop_proxy(self, proxy: EventLoopProxy) -> WebContextImpl { - self.context.event_loop_proxy = Some(proxy); - - WebContextImpl { - context: self.context.with_event_loop_proxy, - manager: self.manager, - webview_uri_loader: self.webview_uri_loader, - registered_protocols: self.registered_protocols, - automation: self.automation, - app_info: self.app_info, - } - } - - pub fn event_loop_proxy(&self) -> Option<&EventLoopProxy> { - self.context.event_loop_proxy.as_ref() - } - - pub fn with_navigation_event(mut self, event: impl Fn(String) -> T + 'static) -> Self { - self.context.navigation_event = Some(Arc::new(event)); - - self - } - - pub fn navigation_event(&self) -> Option<&Arc T>> { - self.context.navigation_event.as_ref() - } } /// [`WebContext`](super::WebContext) items that only matter on unix. -pub trait WebContextExt { +pub trait WebContextExt { /// The GTK [`WebContext`] of all webviews in the context. - fn context(&self) -> &WebContextGeneric; + fn context(&self) -> &WebContext; /// The GTK [`UserContentManager`] of all webviews in the context. fn manager(&self) -> &UserContentManager; @@ -165,8 +135,8 @@ pub trait WebContextExt { fn register_automation(&mut self, webview: WebView); } -impl WebContextExt for super::WebContextGeneric { - fn context(&self) -> &WebContextGeneric { +impl WebContextExt for super::WebContext { + fn context(&self) -> &WebContext { &self.os.context } @@ -231,13 +201,12 @@ impl WebContextExt for super::WebContextGeneric { } fn actually_register_uri_scheme( - context: &mut super::WebContextGeneric, + context: &mut super::WebContext, name: &str, handler: F, ) -> crate::Result<()> where F: Fn(&HttpRequest) -> crate::Result + 'static, - T: 'static, { use webkit2gtk::traits::*; let context = &context.os.context; From 6893f742061a3c2ff84a2888dca50a7de133ea1c Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Mon, 7 Mar 2022 22:54:01 +0000 Subject: [PATCH 10/20] Revert changes to wkwebview --- src/webview/wkwebview/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index e4601332c..b8f96c59a 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -46,7 +46,7 @@ use crate::{ dpi::{LogicalSize, PhysicalSize}, window::Window, }, - webview::{FileDropEvent, WebContextGeneric, WebViewAttributes}, + webview::{FileDropEvent, WebContext, WebViewAttributes}, Result, }; @@ -68,10 +68,10 @@ pub struct InnerWebView { } impl InnerWebView { - pub fn new( + pub fn new( window: Rc, attributes: WebViewAttributes, - mut web_context: Option<&mut WebContextGeneric>, + mut web_context: Option<&mut WebContext>, ) -> Result { // Function for ipc handler extern "C" fn did_receive(this: &Object, _: Sel, _: id, msg: id) { From f0f5d96076774b8ebfd663eaf6795f92ff7d7e38 Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Tue, 8 Mar 2022 00:12:43 +0000 Subject: [PATCH 11/20] Extend event to cover new window/popup events --- examples/navigation_event.rs | 4 ++-- src/webview/web_context.rs | 4 ++-- src/webview/webview2/mod.rs | 23 +++++++++++++++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs index ae5a6e72d..b59b088df 100644 --- a/examples/navigation_event.rs +++ b/examples/navigation_event.rs @@ -25,10 +25,10 @@ fn main() -> wry::Result<()> { let webview = WebViewBuilder::new(window)? .with_url("http://neverssl.com")? .with_web_context(&mut web_context) - .with_navigation_callback(move |uri: String| { + .with_navigation_callback(move |uri: String, new_window: bool| { let submitted = proxy.send_event(UserEvent::Navigation(uri.clone())).is_ok(); - !submitted || !uri.contains("neverssl") + new_window || !submitted || !uri.contains("neverssl") })? .build()?; diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index 4001fd137..bd3aeb365 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -71,9 +71,9 @@ impl Default for WebContext { } } -pub trait NavCallback: Fn(String) -> bool + 'static {} +pub trait NavCallback: Fn(String, bool) -> bool + 'static {} -impl bool + 'static> NavCallback for T {} +impl bool + 'static> NavCallback for T {} /// Data that all [`WebContext`] share regardless of platform. #[derive(Default)] diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index 0879115dc..c58d34441 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -302,7 +302,7 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ if let Some(nav_callback) = context.as_ref().and_then(|c| c.navigation_callback()) { unsafe { - let nav_callback = nav_callback.clone(); + let nav_starting_callback = nav_callback.clone(); webview .NavigationStarting( NavigationStartingEventHandler::create(Box::new(move |_, args| { @@ -311,7 +311,7 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ args.Uri(&mut uri)?; let uri = take_pwstr(uri); - let cancel = nav_callback(uri); + let cancel = nav_starting_callback(uri, false); args.SetCancel(cancel)?; } @@ -321,6 +321,25 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ &mut token ) .map_err(webview2_com::Error::WindowsError)?; + let new_window_callback = nav_callback.clone(); + webview + .NewWindowRequested( + NewWindowRequestedEventHandler::create(Box::new(move |_, args| { + if let Some(args) = args { + let mut uri = PWSTR::default(); + args.Uri(&mut uri)?; + let uri = take_pwstr(uri); + + let cancel = new_window_callback(uri, false); + + args.SetHandled(cancel)?; + } + + Ok(()) + })), + &mut token + ) + .map_err(webview2_com::Error::WindowsError)?; } } From 011e61293108bb2ecd83c3a908d88e953eea8a95 Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Tue, 8 Mar 2022 15:38:43 +0000 Subject: [PATCH 12/20] Move navigation callback into WebViewAttributes --- examples/navigation_event.rs | 6 ++---- src/lib.rs | 2 -- src/webview/mod.rs | 23 ++++++++++++----------- src/webview/web_context.rs | 29 +++-------------------------- src/webview/webview2/mod.rs | 5 ++--- 5 files changed, 19 insertions(+), 46 deletions(-) diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs index b59b088df..cc77738d7 100644 --- a/examples/navigation_event.rs +++ b/examples/navigation_event.rs @@ -9,7 +9,7 @@ fn main() -> wry::Result<()> { event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }, - webview::{WebViewBuilder, WebContext}, + webview::WebViewBuilder, }; enum UserEvent { @@ -21,15 +21,13 @@ fn main() -> wry::Result<()> { let window = WindowBuilder::new() .with_title("Hello World") .build(&event_loop)?; - let mut web_context = WebContext::default(); let webview = WebViewBuilder::new(window)? .with_url("http://neverssl.com")? - .with_web_context(&mut web_context) .with_navigation_callback(move |uri: String, new_window: bool| { let submitted = proxy.send_event(UserEvent::Navigation(uri.clone())).is_ok(); new_window || !submitted || !uri.contains("neverssl") - })? + }) .build()?; #[cfg(debug_assertions)] diff --git a/src/lib.rs b/src/lib.rs index de736d153..7308eda0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,6 +180,4 @@ pub enum Error { InvalidMethod(#[from] InvalidMethod), #[error("Infallible error, something went really wrong: {0}")] Infallible(#[from] std::convert::Infallible), - #[error("No WebContext provided")] - MissingContext, } diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 555f0b21d..00d4586f1 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -6,7 +6,7 @@ mod web_context; -pub use web_context::{WebContext, NavCallback}; +pub use web_context::WebContext; #[cfg(any( target_os = "linux", @@ -32,7 +32,7 @@ use wkwebview::*; pub(crate) mod webview2; #[cfg(target_os = "windows")] use self::webview2::*; -use crate::{Result, Error::MissingContext}; +use crate::Result; #[cfg(target_os = "windows")] use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; #[cfg(target_os = "windows")] @@ -110,6 +110,8 @@ pub struct WebViewAttributes { #[cfg(not(feature = "file-drop"))] file_drop_handler: Option bool>>, + pub navigation_handler: Option>, + /// Enables clipboard access for the page rendered on **Linux** and **Windows**. /// /// macOS doesn't provide such method and is always enabled by default. But you still need to add menu @@ -139,6 +141,7 @@ impl Default for WebViewAttributes { custom_protocols: vec![], ipc_handler: None, file_drop_handler: None, + navigation_handler: None, clipboard: false, devtool: false, } @@ -296,15 +299,9 @@ impl<'a> WebViewBuilder<'a> { self } - pub fn with_navigation_callback(mut self, callback: impl NavCallback) -> Result { - match self.web_context { - None => Err(MissingContext), - Some(web_context) => { - web_context.set_navigation_callback(callback); - self.web_context = Some(web_context); - Ok(self) - } - } + pub fn with_navigation_callback(mut self, callback: impl NavCallback) -> Self { + self.webview.navigation_handler = Some(Rc::new(callback)); + self } /// Consume the builder and create the [`WebView`]. @@ -456,6 +453,10 @@ impl WebviewExtWindows for WebView { } } +pub trait NavCallback: Fn(String, bool) -> bool + 'static {} + +impl bool + 'static> NavCallback for T {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index bd3aeb365..577a9cc08 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -9,7 +9,7 @@ use crate::webview::webkitgtk::WebContextImpl; #[cfg(any(target_os = "macos", target_os = "ios"))] use crate::webview::wkwebview::WebContextImpl; -use std::{path::{Path, PathBuf}, sync::Arc}; +use std::{path::{Path, PathBuf}}; /// A context that is shared between multiple [`WebView`]s. /// @@ -36,7 +36,7 @@ impl WebContext { /// * Whether the WebView window should have a custom user data path. This is useful in Windows /// when a bundled application can't have the webview data inside `Program Files`. pub fn new(data_directory: Option) -> Self { - let data = WebContextData { data_directory, nav_callback: None }; + let data = WebContextData { data_directory }; let os = WebContextImpl::new(&data); Self { data, os } } @@ -53,14 +53,6 @@ impl WebContext { pub fn set_allows_automation(&mut self, flag: bool) { self.os.set_allows_automation(flag); } - - pub fn set_navigation_callback(&mut self, callback: impl NavCallback) { - self.data.nav_callback = Some(Arc::new(callback)) - } - - pub fn navigation_callback(&self) -> Option<&Arc> { - self.data.nav_callback() - } } impl Default for WebContext { @@ -71,15 +63,10 @@ impl Default for WebContext { } } -pub trait NavCallback: Fn(String, bool) -> bool + 'static {} - -impl bool + 'static> NavCallback for T {} - /// Data that all [`WebContext`] share regardless of platform. -#[derive(Default)] +#[derive(Default, Debug)] pub struct WebContextData { data_directory: Option, - nav_callback: Option> } impl WebContextData { @@ -87,16 +74,6 @@ impl WebContextData { pub fn data_directory(&self) -> Option<&Path> { self.data_directory.as_deref() } - - pub fn nav_callback(&self) -> Option<&Arc> { - self.nav_callback.as_ref() - } -} - -impl std::fmt::Debug for WebContextData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("WebContextData").field("data_directory", &self.data_directory).field("nav_callback", &self.nav_callback.is_some()).finish() - } } #[cfg(target_os = "windows")] diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index c58d34441..9e05c716a 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -64,7 +64,7 @@ impl InnerWebView { let env = Self::create_environment(&web_context)?; let controller = Self::create_controller(hwnd, &env)?; - let webview = Self::init_webview(window, hwnd, attributes, &env, &controller, &web_context)?; + let webview = Self::init_webview(window, hwnd, attributes, &env, &controller)?; if let Some(file_drop_handler) = file_drop_handler { let mut controller = FileDropController::new(); @@ -158,7 +158,6 @@ impl InnerWebView { mut attributes: WebViewAttributes, env: &ICoreWebView2Environment, controller: &ICoreWebView2Controller, - context: &Option<&mut WebContext> ) -> webview2_com::Result { let webview = unsafe { controller.CoreWebView2() }.map_err(webview2_com::Error::WindowsError)?; @@ -300,7 +299,7 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ } .map_err(webview2_com::Error::WindowsError)?; - if let Some(nav_callback) = context.as_ref().and_then(|c| c.navigation_callback()) { + if let Some(nav_callback) = attributes.navigation_handler { unsafe { let nav_starting_callback = nav_callback.clone(); webview From 7099b87ef20d63a9d1587c73ddb06441a34dc8d9 Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Tue, 8 Mar 2022 19:28:09 +0000 Subject: [PATCH 13/20] Remove unnecessary brackets in WebView Co-authored-by: Amr Bashir --- src/webview/web_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index 577a9cc08..07823a2ad 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -9,7 +9,7 @@ use crate::webview::webkitgtk::WebContextImpl; #[cfg(any(target_os = "macos", target_os = "ios"))] use crate::webview::wkwebview::WebContextImpl; -use std::{path::{Path, PathBuf}}; +use std::path::{Path, PathBuf}; /// A context that is shared between multiple [`WebView`]s. /// From 8c0f535001269bb204d065355408ed3e5c0a443e Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Tue, 8 Mar 2022 19:32:31 +0000 Subject: [PATCH 14/20] Remove new window handler Implement in a different PR --- src/webview/webview2/mod.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index 9e05c716a..41bac2cfd 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -320,25 +320,6 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ &mut token ) .map_err(webview2_com::Error::WindowsError)?; - let new_window_callback = nav_callback.clone(); - webview - .NewWindowRequested( - NewWindowRequestedEventHandler::create(Box::new(move |_, args| { - if let Some(args) = args { - let mut uri = PWSTR::default(); - args.Uri(&mut uri)?; - let uri = take_pwstr(uri); - - let cancel = new_window_callback(uri, false); - - args.SetHandled(cancel)?; - } - - Ok(()) - })), - &mut token - ) - .map_err(webview2_com::Error::WindowsError)?; } } From 8a5df2f4ad5db9ed025a44c957bbad5a8a5fa73b Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Tue, 8 Mar 2022 19:33:36 +0000 Subject: [PATCH 15/20] Remove references to new window values --- examples/navigation_event.rs | 4 ++-- src/webview/mod.rs | 4 ++-- src/webview/webview2/mod.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs index cc77738d7..18245bba8 100644 --- a/examples/navigation_event.rs +++ b/examples/navigation_event.rs @@ -23,10 +23,10 @@ fn main() -> wry::Result<()> { .build(&event_loop)?; let webview = WebViewBuilder::new(window)? .with_url("http://neverssl.com")? - .with_navigation_callback(move |uri: String, new_window: bool| { + .with_navigation_callback(move |uri: String| { let submitted = proxy.send_event(UserEvent::Navigation(uri.clone())).is_ok(); - new_window || !submitted || !uri.contains("neverssl") + !submitted || !uri.contains("neverssl") }) .build()?; diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 00d4586f1..2751fe09a 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -453,9 +453,9 @@ impl WebviewExtWindows for WebView { } } -pub trait NavCallback: Fn(String, bool) -> bool + 'static {} +pub trait NavCallback: Fn(String) -> bool + 'static {} -impl bool + 'static> NavCallback for T {} +impl bool + 'static> NavCallback for T {} #[cfg(test)] mod tests { diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index 41bac2cfd..099567315 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -310,7 +310,7 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ args.Uri(&mut uri)?; let uri = take_pwstr(uri); - let cancel = nav_starting_callback(uri, false); + let cancel = nav_starting_callback(uri); args.SetCancel(cancel)?; } From 0500e74cce6b324f1b3f691a83778ad6284565ed Mon Sep 17 00:00:00 2001 From: Iain Laird Date: Wed, 9 Mar 2022 13:01:59 +0000 Subject: [PATCH 16/20] Implement navigation event callback handler on gtk --- src/webview/webkitgtk/mod.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/webview/webkitgtk/mod.rs b/src/webview/webkitgtk/mod.rs index 86e34816e..1a976bb7d 100644 --- a/src/webview/webkitgtk/mod.rs +++ b/src/webview/webkitgtk/mod.rs @@ -14,10 +14,10 @@ use glib::signal::Inhibit; use gtk::prelude::*; use webkit2gtk::{ traits::*, UserContentInjectedFrames, UserScript, UserScriptInjectionTime, WebView, - WebViewBuilder, + WebViewBuilder, PolicyDecisionType, NavigationPolicyDecision, }; use webkit2gtk_sys::{ - webkit_get_major_version, webkit_get_micro_version, webkit_get_minor_version, + webkit_get_major_version, webkit_get_micro_version, webkit_get_minor_version, webkit_policy_decision_ignore, webkit_policy_decision_use, }; use web_context::WebContextExt; @@ -198,6 +198,31 @@ impl InnerWebView { Inhibit(false) }); + if let Some(nav_handler) = attributes.navigation_handler { + webview.connect_decide_policy(move |_webview, policy_decision, policy_type| { + if let PolicyDecisionType::NavigationAction = policy_type { + if let Some(policy) = policy_decision.dynamic_cast_ref::() { + if let Some(nav_action) = policy.navigation_action() { + if let Some(uri_req) = nav_action.request() { + if let Some(uri) = uri_req.uri() { + let cancel = nav_handler(uri.to_string()); + let pointer = policy_decision.as_ptr(); + unsafe { + if cancel { + webkit_policy_decision_ignore(pointer) + } else { + webkit_policy_decision_use(pointer) + } + } + } + } + } + } + } + false + }); + } + // Gtk application window can only contain one widget at a time. // In tao, we add a GtkBox to pack menu bar. So we check if // there's a box widget here. From e9a0929be88f3a139c80369dda999bc9207ee909 Mon Sep 17 00:00:00 2001 From: Yu-Wei Wu Date: Tue, 15 Mar 2022 14:42:17 +0800 Subject: [PATCH 17/20] Add navigation handler on macOS The function is still not called yet. But this is a prove it can work. --- Cargo.toml | 1 + src/webview/wkwebview/mod.rs | 40 +++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 78cc9daa8..1b6b8fddf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ sys-info = "0.9" ] [target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies] +block = "0.1" cocoa = "0.24" core-graphics = "0.22" objc = { version = "0.2", features = [ "exception" ] } diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index b8f96c59a..db466dcfe 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -15,7 +15,7 @@ use cocoa::{ }; use cocoa::{ base::id, - foundation::{NSDictionary, NSFastEnumeration}, + foundation::{NSDictionary, NSFastEnumeration, NSInteger}, }; use std::{ @@ -28,8 +28,8 @@ use std::{ use core_graphics::geometry::{CGPoint, CGRect, CGSize}; use objc::{ - declare::ClassDecl, - runtime::{Class, Object, Sel}, + declare::{ClassDecl, ProtocolDecl}, + runtime::{Class, Object, Sel, Protocol}, }; use objc_id::Id; @@ -308,6 +308,40 @@ impl InnerWebView { } else { null_mut() }; + + // Navigation handler + extern "C" fn navigation_policy(_this: &Object, _: Sel, _webview: id, action: id, handler:id) { + // TODO get the url from action and call the function + let handler = handler as *mut block::Block<(NSInteger,), c_void>; + unsafe { (*handler).call((0,)); } + } + let nav_handler_ptr = if let Some(nav_handler) = attributes.navigation_handler { + let protocol = match ProtocolDecl::new("WKNavigationDelegate") { + Some(p) => p.register(), + None => Protocol::get("WKNavigationDelegate").unwrap(), + }; + let cls = match ClassDecl::new("UIViewController", class!(NSObject)) { + Some(mut cls) => { + cls.add_ivar::<*mut c_void>("function"); + cls.add_protocol(protocol); + cls.add_method( + sel!(webView:decidePolicyForNavigationAction:decisionHandler:), + navigation_policy as extern "C" fn(&Object, Sel, id, id, id), + ); + cls.register() + }, + None => class!(UIViewController), + }; + + let handler: id = msg_send![cls, new]; + let nav_handler_ptr = Box::into_raw(Box::new(nav_handler)); + (*handler).set_ivar("function", ipc_handler_ptr as *mut _ as *mut c_void); + + let _: () = msg_send![webview, setNavigationDelegate:handler]; + nav_handler_ptr + } else { + null_mut() + }; // File drop handling #[cfg(target_os = "macos")] From 10ce4aadfaac58c3029f496c8bff63724d6d998b Mon Sep 17 00:00:00 2001 From: Yu-Wei Wu Date: Tue, 15 Mar 2022 15:19:51 +0800 Subject: [PATCH 18/20] Call the navigation handler on macOS --- examples/navigation_event.rs | 2 +- src/webview/webkitgtk/mod.rs | 7 +++-- src/webview/webview2/mod.rs | 8 ++--- src/webview/wkwebview/mod.rs | 61 +++++++++++++++++++++++------------- 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs index 18245bba8..ccecbd216 100644 --- a/examples/navigation_event.rs +++ b/examples/navigation_event.rs @@ -13,7 +13,7 @@ fn main() -> wry::Result<()> { }; enum UserEvent { - Navigation(String) + Navigation(String), } let event_loop: EventLoop = EventLoop::with_user_event(); diff --git a/src/webview/webkitgtk/mod.rs b/src/webview/webkitgtk/mod.rs index 1a976bb7d..61cca6847 100644 --- a/src/webview/webkitgtk/mod.rs +++ b/src/webview/webkitgtk/mod.rs @@ -13,11 +13,12 @@ use gio::Cancellable; use glib::signal::Inhibit; use gtk::prelude::*; use webkit2gtk::{ - traits::*, UserContentInjectedFrames, UserScript, UserScriptInjectionTime, WebView, - WebViewBuilder, PolicyDecisionType, NavigationPolicyDecision, + traits::*, NavigationPolicyDecision, PolicyDecisionType, UserContentInjectedFrames, UserScript, + UserScriptInjectionTime, WebView, WebViewBuilder, }; use webkit2gtk_sys::{ - webkit_get_major_version, webkit_get_micro_version, webkit_get_minor_version, webkit_policy_decision_ignore, webkit_policy_decision_use, + webkit_get_major_version, webkit_get_micro_version, webkit_get_minor_version, + webkit_policy_decision_ignore, webkit_policy_decision_use, }; use web_context::WebContextExt; diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index 099567315..2ddfc13ce 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -309,15 +309,15 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ let mut uri = PWSTR::default(); args.Uri(&mut uri)?; let uri = take_pwstr(uri); - + let cancel = nav_starting_callback(uri); - + args.SetCancel(cancel)?; } - + Ok(()) })), - &mut token + &mut token, ) .map_err(webview2_com::Error::WindowsError)?; } diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index db466dcfe..54dbf10cd 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -29,7 +29,7 @@ use std::{ use core_graphics::geometry::{CGPoint, CGRect, CGSize}; use objc::{ declare::{ClassDecl, ProtocolDecl}, - runtime::{Class, Object, Sel, Protocol}, + runtime::{Class, Object, Protocol, Sel}, }; use objc_id::Id; @@ -308,39 +308,58 @@ impl InnerWebView { } else { null_mut() }; - + // Navigation handler - extern "C" fn navigation_policy(_this: &Object, _: Sel, _webview: id, action: id, handler:id) { - // TODO get the url from action and call the function + extern "C" fn navigation_policy(this: &Object, _: Sel, _: id, action: id, handler: id) { + // TODO get the url from action and call the function + unsafe { + let request: id = msg_send![action, request]; + let url: id = msg_send![request, URL]; + let url: id = msg_send![url, absoluteString]; + let url = NSString(Id::from_ptr(url)); let handler = handler as *mut block::Block<(NSInteger,), c_void>; - unsafe { (*handler).call((0,)); } + + let function = this.get_ivar::<*mut c_void>("function"); + if !function.is_null() { + let function = &mut *(*function as *mut Box Fn(String) -> bool>); + match (function)(url.to_str().to_string()) { + true => (*handler).call((1,)), + false => (*handler).call((0,)), + }; + } else { + log::warn!("WebView instance is dropped! This navigation handler shouldn't be called."); + (*handler).call((1,)); + } + } } + // TODO free the ptr properly let nav_handler_ptr = if let Some(nav_handler) = attributes.navigation_handler { - let protocol = match ProtocolDecl::new("WKNavigationDelegate") { - Some(p) => p.register(), - None => Protocol::get("WKNavigationDelegate").unwrap(), - }; + // let protocol = match ProtocolDecl::new("WKNavigationDelegate") { + // Some(p) => p.register(), + // None => Protocol::get("WKNavigationDelegate").unwrap(), + // }; let cls = match ClassDecl::new("UIViewController", class!(NSObject)) { - Some(mut cls) => { - cls.add_ivar::<*mut c_void>("function"); - cls.add_protocol(protocol); - cls.add_method( - sel!(webView:decidePolicyForNavigationAction:decisionHandler:), - navigation_policy as extern "C" fn(&Object, Sel, id, id, id), - ); - cls.register() - }, - None => class!(UIViewController), + Some(mut cls) => { + cls.add_ivar::<*mut c_void>("function"); + // Doesn't seem to need this protocol. Add this back if objc runtime isn't happy. + // cls.add_protocol(protocol); + cls.add_method( + sel!(webView:decidePolicyForNavigationAction:decisionHandler:), + navigation_policy as extern "C" fn(&Object, Sel, id, id, id), + ); + cls.register() + } + None => class!(UIViewController), }; let handler: id = msg_send![cls, new]; let nav_handler_ptr = Box::into_raw(Box::new(nav_handler)); (*handler).set_ivar("function", ipc_handler_ptr as *mut _ as *mut c_void); - let _: () = msg_send![webview, setNavigationDelegate:handler]; + let _: () = msg_send![webview, setNavigationDelegate: handler]; nav_handler_ptr } else { - null_mut() + null_mut() }; // File drop handling From b03231f072fbb4a679e27db3a31bf2d0d0f07bb8 Mon Sep 17 00:00:00 2001 From: Yu-Wei Wu Date: Wed, 16 Mar 2022 14:20:22 +0800 Subject: [PATCH 19/20] Update navifation logic and type signature --- .changes/nav-handler.md | 5 +++++ examples/navigation_event.rs | 4 ++-- src/webview/mod.rs | 18 +++++++++++------- src/webview/webkitgtk/mod.rs | 10 +++++----- src/webview/webview2/mod.rs | 4 ++-- src/webview/wkwebview/mod.rs | 21 ++++++++++----------- 6 files changed, 35 insertions(+), 27 deletions(-) create mode 100644 .changes/nav-handler.md diff --git a/.changes/nav-handler.md b/.changes/nav-handler.md new file mode 100644 index 000000000..5b50940aa --- /dev/null +++ b/.changes/nav-handler.md @@ -0,0 +1,5 @@ +--- +"wry": patch +--- + +Add navigation handler to decide if a url is allowed to navigate. diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs index ccecbd216..2d326080e 100644 --- a/examples/navigation_event.rs +++ b/examples/navigation_event.rs @@ -23,10 +23,10 @@ fn main() -> wry::Result<()> { .build(&event_loop)?; let webview = WebViewBuilder::new(window)? .with_url("http://neverssl.com")? - .with_navigation_callback(move |uri: String| { + .with_navigation_handler(move |uri: String| { let submitted = proxy.send_event(UserEvent::Navigation(uri.clone())).is_ok(); - !submitted || !uri.contains("neverssl") + submitted && uri.contains("neverssl") }) .build()?; diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 2751fe09a..5ef1a4eea 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -110,7 +110,11 @@ pub struct WebViewAttributes { #[cfg(not(feature = "file-drop"))] file_drop_handler: Option bool>>, - pub navigation_handler: Option>, + /// Set a navigation handler to decide if incoming url is allowed to navigate. + /// + /// The closure take a `String` parameter as url and return `bool` to determine the url. True is + /// allow to nivagate and false is not. + pub navigation_handler: Option bool>>, /// Enables clipboard access for the page rendered on **Linux** and **Windows**. /// @@ -299,8 +303,12 @@ impl<'a> WebViewBuilder<'a> { self } - pub fn with_navigation_callback(mut self, callback: impl NavCallback) -> Self { - self.webview.navigation_handler = Some(Rc::new(callback)); + /// Set a navigation handler to decide if incoming url is allowed to navigate. + /// + /// The closure takes a `String` parameter as url and return `bool` to determine the url. True is + /// allowed to nivagate and false is not. + pub fn with_navigation_handler(mut self, callback: impl Fn(String) -> bool + 'static) -> Self { + self.webview.navigation_handler = Some(Box::new(callback)); self } @@ -453,10 +461,6 @@ impl WebviewExtWindows for WebView { } } -pub trait NavCallback: Fn(String) -> bool + 'static {} - -impl bool + 'static> NavCallback for T {} - #[cfg(test)] mod tests { use super::*; diff --git a/src/webview/webkitgtk/mod.rs b/src/webview/webkitgtk/mod.rs index 61cca6847..4e520a115 100644 --- a/src/webview/webkitgtk/mod.rs +++ b/src/webview/webkitgtk/mod.rs @@ -206,13 +206,13 @@ impl InnerWebView { if let Some(nav_action) = policy.navigation_action() { if let Some(uri_req) = nav_action.request() { if let Some(uri) = uri_req.uri() { - let cancel = nav_handler(uri.to_string()); + let allow = nav_handler(uri.to_string()); let pointer = policy_decision.as_ptr(); unsafe { - if cancel { - webkit_policy_decision_ignore(pointer) - } else { + if allow { webkit_policy_decision_use(pointer) + } else { + webkit_policy_decision_ignore(pointer) } } } @@ -220,7 +220,7 @@ impl InnerWebView { } } } - false + true }); } diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index 2ddfc13ce..ae56b7987 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -310,9 +310,9 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ args.Uri(&mut uri)?; let uri = take_pwstr(uri); - let cancel = nav_starting_callback(uri); + let allow = nav_starting_callback(uri); - args.SetCancel(cancel)?; + args.SetCancel(!allow)?; } Ok(()) diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index 54dbf10cd..b79e3b5ac 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -28,8 +28,8 @@ use std::{ use core_graphics::geometry::{CGPoint, CGRect, CGSize}; use objc::{ - declare::{ClassDecl, ProtocolDecl}, - runtime::{Class, Object, Protocol, Sel}, + declare::ClassDecl, + runtime::{Class, Object, Sel}, }; use objc_id::Id; @@ -62,6 +62,7 @@ pub struct InnerWebView { // Note that if following functions signatures are changed in the future, // all fucntions pointer declarations in objc callbacks below all need to get updated. ipc_handler_ptr: *mut (Box, Rc), + nav_handler_ptr: *mut Box bool>, #[cfg(target_os = "macos")] file_drop_ptr: *mut (Box bool>, Rc), protocol_ptrs: Vec<*mut Box Result>>, @@ -311,7 +312,6 @@ impl InnerWebView { // Navigation handler extern "C" fn navigation_policy(this: &Object, _: Sel, _: id, action: id, handler: id) { - // TODO get the url from action and call the function unsafe { let request: id = msg_send![action, request]; let url: id = msg_send![request, URL]; @@ -332,17 +332,11 @@ impl InnerWebView { } } } - // TODO free the ptr properly + let nav_handler_ptr = if let Some(nav_handler) = attributes.navigation_handler { - // let protocol = match ProtocolDecl::new("WKNavigationDelegate") { - // Some(p) => p.register(), - // None => Protocol::get("WKNavigationDelegate").unwrap(), - // }; let cls = match ClassDecl::new("UIViewController", class!(NSObject)) { Some(mut cls) => { cls.add_ivar::<*mut c_void>("function"); - // Doesn't seem to need this protocol. Add this back if objc runtime isn't happy. - // cls.add_protocol(protocol); cls.add_method( sel!(webView:decidePolicyForNavigationAction:decisionHandler:), navigation_policy as extern "C" fn(&Object, Sel, id, id, id), @@ -354,7 +348,7 @@ impl InnerWebView { let handler: id = msg_send![cls, new]; let nav_handler_ptr = Box::into_raw(Box::new(nav_handler)); - (*handler).set_ivar("function", ipc_handler_ptr as *mut _ as *mut c_void); + (*handler).set_ivar("function", nav_handler_ptr as *mut _ as *mut c_void); let _: () = msg_send![webview, setNavigationDelegate: handler]; nav_handler_ptr @@ -383,6 +377,7 @@ impl InnerWebView { ns_window, manager, ipc_handler_ptr, + nav_handler_ptr, #[cfg(target_os = "macos")] file_drop_ptr, protocol_ptrs, @@ -551,6 +546,10 @@ impl Drop for InnerWebView { let _ = Box::from_raw(self.ipc_handler_ptr); } + if !self.nav_handler_ptr.is_null() { + let _ = Box::from_raw(self.ipc_handler_ptr); + } + #[cfg(target_os = "macos")] if !self.file_drop_ptr.is_null() { let _ = Box::from_raw(self.file_drop_ptr); From eff4a6b8963f4782df58137b0463afa9648c3c9a Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Wed, 16 Mar 2022 19:44:46 +0800 Subject: [PATCH 20/20] Fix errors on windows --- src/webview/webview2/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index ae56b7987..1996ea158 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -301,7 +301,6 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ if let Some(nav_callback) = attributes.navigation_handler { unsafe { - let nav_starting_callback = nav_callback.clone(); webview .NavigationStarting( NavigationStartingEventHandler::create(Box::new(move |_, args| { @@ -310,7 +309,7 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ args.Uri(&mut uri)?; let uri = take_pwstr(uri); - let allow = nav_starting_callback(uri); + let allow = nav_callback(uri); args.SetCancel(!allow)?; }