diff --git a/.changes/permission-handler.md b/.changes/permission-handler.md new file mode 100644 index 000000000000..4489dd1a1ddd --- /dev/null +++ b/.changes/permission-handler.md @@ -0,0 +1,11 @@ +--- +"tauri": patch +"tauri-runtime": patch +"tauri-runtime-wry": patch +--- + +Expand the `PermissionKind` and `PermissionResponse` enums to match the updated `wry` API. +This includes support for permission types such as `DisplayCapture`, `Midi`, `Sensors`, `MediaKeySystemAccess`, `LocalFonts`, `WindowManagement`, `PointerLock`, `AutomaticDownloads`, `FileSystemAccess`, and `Autoplay`. +Added `PermissionResponse::Prompt` to explicitly trigger system dialogs. +Added Android support for geolocation, microphone, camera, protected media, and MIDI requests via JNI. +Updated Linux DisplayCapture support for WebKitGTK versions older than 2.42. diff --git a/Cargo.lock b/Cargo.lock index 3f2544cd1385..c7a10ddbea8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1321,7 +1321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -2021,7 +2021,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2413,7 +2413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5520,7 +5520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -5937,7 +5937,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.22.1", + "base64 0.21.7", "bitfield", "block-padding", "blowfish", @@ -7243,7 +7243,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7256,7 +7256,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -9259,7 +9259,7 @@ dependencies = [ "getrandom 0.2.15", "once_cell", "rustix 0.38.43", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -10614,7 +10614,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -11207,8 +11207,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" version = "0.55.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3013fd6116aac351dd2e18f349b28b2cfef3a5ff3253a9d0ce2d7193bb1b4429" +source = "git+https://github.com/F0RLE/wry?rev=88472aedf318287eddd832cc3e2217cde426f766#88472aedf318287eddd832cc3e2217cde426f766" dependencies = [ "base64 0.22.1", "block2 0.6.2", diff --git a/crates/tauri-runtime-wry/Cargo.toml b/crates/tauri-runtime-wry/Cargo.toml index 675f7fd8c876..f2b10aa5d999 100644 --- a/crates/tauri-runtime-wry/Cargo.toml +++ b/crates/tauri-runtime-wry/Cargo.toml @@ -13,7 +13,7 @@ edition.workspace = true rust-version.workspace = true [dependencies] -wry = { version = "0.55.0", default-features = false, features = [ +wry = { git = "https://github.com/F0RLE/wry", rev = "88472aedf318287eddd832cc3e2217cde426f766", default-features = false, features = [ "protocol", "os-webview", "linux-body", diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index da4cb6a637c0..7f132ea22819 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -4907,6 +4907,49 @@ You may have it installed on another user account, but it is not available for t webview_builder.with_document_title_changed_handler(document_title_changed_handler) } + if let Some(permission_request_handler) = pending.permission_request_handler { + webview_builder = webview_builder.with_permission_handler(move |kind| { + let kind = match kind { + wry::PermissionKind::Microphone => tauri_runtime::webview::PermissionKind::Microphone, + wry::PermissionKind::Camera => tauri_runtime::webview::PermissionKind::Camera, + wry::PermissionKind::Geolocation => tauri_runtime::webview::PermissionKind::Geolocation, + wry::PermissionKind::Notifications => tauri_runtime::webview::PermissionKind::Notifications, + wry::PermissionKind::ClipboardRead => tauri_runtime::webview::PermissionKind::ClipboardRead, + wry::PermissionKind::DisplayCapture => { + tauri_runtime::webview::PermissionKind::DisplayCapture + } + wry::PermissionKind::Midi => tauri_runtime::webview::PermissionKind::Midi, + wry::PermissionKind::Sensors => tauri_runtime::webview::PermissionKind::Sensors, + wry::PermissionKind::MediaKeySystemAccess => { + tauri_runtime::webview::PermissionKind::MediaKeySystemAccess + } + wry::PermissionKind::LocalFonts => tauri_runtime::webview::PermissionKind::LocalFonts, + wry::PermissionKind::WindowManagement => { + tauri_runtime::webview::PermissionKind::WindowManagement + } + wry::PermissionKind::PointerLock => tauri_runtime::webview::PermissionKind::PointerLock, + wry::PermissionKind::AutomaticDownloads => { + tauri_runtime::webview::PermissionKind::AutomaticDownloads + } + wry::PermissionKind::FileSystemAccess => { + tauri_runtime::webview::PermissionKind::FileSystemAccess + } + wry::PermissionKind::Autoplay => tauri_runtime::webview::PermissionKind::Autoplay, + wry::PermissionKind::Other => tauri_runtime::webview::PermissionKind::Other, + _ => tauri_runtime::webview::PermissionKind::Other, + }; + + let response = permission_request_handler(kind); + + match response { + tauri_runtime::webview::PermissionResponse::Allow => wry::PermissionResponse::Allow, + tauri_runtime::webview::PermissionResponse::Deny => wry::PermissionResponse::Deny, + tauri_runtime::webview::PermissionResponse::Default => wry::PermissionResponse::Default, + tauri_runtime::webview::PermissionResponse::Prompt => wry::PermissionResponse::Prompt, + } + }); + } + let webview_bounds = if let Some(bounds) = webview_attributes.bounds { let bounds: RectWrapper = bounds.into(); let bounds = bounds.0; diff --git a/crates/tauri-runtime/src/webview.rs b/crates/tauri-runtime/src/webview.rs index d4f7e0145313..b713831bd93b 100644 --- a/crates/tauri-runtime/src/webview.rs +++ b/crates/tauri-runtime/src/webview.rs @@ -41,6 +41,8 @@ type DocumentTitleChangedHandler = dyn Fn(String) + Send + 'static; type DownloadHandler = dyn Fn(DownloadEvent) -> bool + Send + Sync; +type PermissionRequestHandler = dyn Fn(PermissionKind) -> PermissionResponse + Send + Sync; + #[cfg(any(target_os = "macos", target_os = "ios"))] type OnWebContentProcessTerminateHandler = dyn Fn() + Send; @@ -72,6 +74,100 @@ pub enum DownloadEvent<'a> { }, } +/// Permission request response. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum PermissionResponse { + /// Permission allowed. + Allow, + /// Permission denied. + Deny, + /// Use default behavior (show system prompt). + #[default] + Default, + /// Explicitly prompt the user (system dialog). + Prompt, +} + +impl std::fmt::Display for PermissionResponse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Allow => write!(f, "allow"), + Self::Deny => write!(f, "deny"), + Self::Default => write!(f, "default"), + Self::Prompt => write!(f, "prompt"), + } + } +} + +/// Permission request kind. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum PermissionKind { + /// Microphone access permission. + Microphone, + /// Camera access permission. + Camera, + /// Geolocation access permission. + Geolocation, + /// Notifications permission. + Notifications, + /// Clipboard read permission. + ClipboardRead, + /// Display capture permission (for getDisplayMedia). + DisplayCapture, + /// Midi access permission. + Midi, + /// Sensors (accelerometer, gyroscope, etc.) access permission. + Sensors, + /// Media key system access permission. + MediaKeySystemAccess, + /// Local fonts access permission. + LocalFonts, + /// Window management permission. + WindowManagement, + /// Pointer lock permission. + PointerLock, + /// Automatic downloads permission (multiple downloads without user interaction). + AutomaticDownloads, + /// File system access permission (read/write via File System Access API). + /// + /// ## Platform-specific + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_FILE_READ_WRITE`. + /// - **macOS / Linux / Android / iOS**: Not yet supported by platform backends. + FileSystemAccess, + /// Media autoplay permission. + /// + /// ## Platform-specific + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_AUTOPLAY`. + /// - **macOS / Linux / Android / iOS**: Not yet supported by platform backends. + Autoplay, + /// Other unrecognized permission type. + Other, +} + +impl std::fmt::Display for PermissionKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Microphone => write!(f, "microphone"), + Self::Camera => write!(f, "camera"), + Self::Geolocation => write!(f, "geolocation"), + Self::Notifications => write!(f, "notifications"), + Self::ClipboardRead => write!(f, "clipboard-read"), + Self::DisplayCapture => write!(f, "display-capture"), + Self::Midi => write!(f, "midi"), + Self::Sensors => write!(f, "sensors"), + Self::MediaKeySystemAccess => write!(f, "media-key-system-access"), + Self::LocalFonts => write!(f, "local-fonts"), + Self::WindowManagement => write!(f, "window-management"), + Self::PointerLock => write!(f, "pointer-lock"), + Self::AutomaticDownloads => write!(f, "automatic-downloads"), + Self::FileSystemAccess => write!(f, "file-system-access"), + Self::Autoplay => write!(f, "autoplay"), + Self::Other => write!(f, "other"), + } + } +} + #[cfg(target_os = "android")] pub struct CreationContext<'a, 'b> { pub env: &'a mut jni::JNIEnv<'b>, @@ -229,6 +325,8 @@ pub struct PendingWebview> { pub download_handler: Option>, + pub permission_request_handler: Option>, + #[cfg(any(target_os = "macos", target_os = "ios"))] pub on_web_content_process_terminate_handler: Option>, } @@ -257,6 +355,7 @@ impl> PendingWebview { web_resource_request_handler: None, on_page_load_handler: None, download_handler: None, + permission_request_handler: None, #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate_handler: None, }) diff --git a/crates/tauri/src/app.rs b/crates/tauri/src/app.rs index 8bd9df66ee8c..e3c79ea81591 100644 --- a/crates/tauri/src/app.rs +++ b/crates/tauri/src/app.rs @@ -1486,6 +1486,9 @@ pub struct Builder { /// Page load hook. on_page_load: Option>>, + /// Permission request hook. + on_permission_request: Option>>, + /// Web content process termination hook. #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate: Option>>, @@ -1576,6 +1579,7 @@ impl Builder { .into_string(), channel_interceptor: None, on_page_load: None, + on_permission_request: None, #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate: None, plugins: PluginStore::default(), @@ -1758,6 +1762,24 @@ tauri::Builder::default() self } + /// Defines a closure to be executed when a permission is requested. + /// + /// The handler receives the [`crate::webview::PermissionKind`] and should return + /// the desired [`crate::webview::PermissionResponse`]. + #[must_use] + pub fn on_permission_request(mut self, on_permission_request: F) -> Self + where + F: Fn(Webview, crate::webview::PermissionKind) -> crate::webview::PermissionResponse + + Send + + Sync + + 'static, + { + self + .on_permission_request + .replace(Arc::new(on_permission_request)); + self + } + /// Defines the web content process termination hook. /// /// ## Platform-specific @@ -2223,6 +2245,7 @@ tauri::Builder::default() self.plugins, self.invoke_handler, self.on_page_load, + self.on_permission_request, #[cfg(any(target_os = "macos", target_os = "ios"))] self.on_web_content_process_terminate, self.uri_scheme_protocols, diff --git a/crates/tauri/src/manager/mod.rs b/crates/tauri/src/manager/mod.rs index 8215cf1335c6..c9fcd4a7ff99 100644 --- a/crates/tauri/src/manager/mod.rs +++ b/crates/tauri/src/manager/mod.rs @@ -254,6 +254,7 @@ impl AppManager { plugins: PluginStore, invoke_handler: Box>, on_page_load: Option>>, + on_permission_request: Option>>, #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate: Option< Arc>, >, @@ -290,6 +291,7 @@ impl AppManager { webviews: Mutex::default(), invoke_handler, on_page_load, + on_permission_request, #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate, uri_scheme_protocols: Mutex::new(uri_scheme_protocols), diff --git a/crates/tauri/src/manager/webview.rs b/crates/tauri/src/manager/webview.rs index 57e617e71a9e..22f960a7911b 100644 --- a/crates/tauri/src/manager/webview.rs +++ b/crates/tauri/src/manager/webview.rs @@ -73,6 +73,8 @@ pub struct WebviewManager { pub invoke_handler: Box>, /// The page load hook, invoked when the webview performs a navigation. pub on_page_load: Option>>, + /// The permission request hook, invoked when the webview requests a permission. + pub on_permission_request: Option>>, /// The web content process termination hook. #[cfg(any(target_os = "macos", target_os = "ios"))] pub on_web_content_process_terminate: Option>>, @@ -310,6 +312,37 @@ impl WebviewManager { } })); + let label_ = pending.label.clone(); + let app_manager_ = manager.manager_owned(); + let permission_request_handler = pending.permission_request_handler.take(); + pending + .permission_request_handler + .replace(Box::new(move |kind| { + let mut response = crate::webview::PermissionResponse::Default; + + if let Some(handler) = &permission_request_handler { + response = handler(kind); + } + + if let Some(w) = app_manager_.get_webview(&label_) { + if response == crate::webview::PermissionResponse::Default { + if let Some(on_permission_request) = &app_manager_.webview.on_permission_request { + response = on_permission_request(w.clone(), kind); + } + } + + if response == crate::webview::PermissionResponse::Default { + response = app_manager_ + .plugins + .lock() + .unwrap() + .on_permission_request(&w, kind); + } + } + + response + })); + #[cfg(any(target_os = "macos", target_os = "ios"))] if pending.on_web_content_process_terminate_handler.is_none() { let app_manager_ = manager.manager_owned(); diff --git a/crates/tauri/src/plugin.rs b/crates/tauri/src/plugin.rs index 3a22de7bc700..f01f94ff0669 100644 --- a/crates/tauri/src/plugin.rs +++ b/crates/tauri/src/plugin.rs @@ -100,6 +100,16 @@ pub trait Plugin: Send { #[allow(unused_variables)] fn on_page_load(&mut self, webview: &Webview, payload: &PageLoadPayload<'_>) {} + /// Callback invoked when the webview requests a permission. + #[allow(unused_variables)] + fn on_permission_request( + &mut self, + webview: &Webview, + kind: crate::webview::PermissionKind, + ) -> crate::webview::PermissionResponse { + crate::webview::PermissionResponse::Default + } + /// Callback invoked when the event loop receives a new event. #[allow(unused_variables)] fn on_event(&mut self, app: &AppHandle, event: &RunEvent) {} @@ -118,6 +128,8 @@ type OnWebviewReady = dyn FnMut(Webview) + Send; type OnEvent = dyn FnMut(&AppHandle, &RunEvent) + Send; type OnNavigation = dyn Fn(&Webview, &Url) -> bool + Send; type OnPageLoad = dyn FnMut(&Webview, &PageLoadPayload<'_>) + Send; +type OnPermissionRequest = dyn FnMut(&Webview, crate::webview::PermissionKind) -> crate::webview::PermissionResponse + + Send; type OnDrop = dyn FnOnce(AppHandle) + Send; /// A handle to a plugin. @@ -268,6 +280,7 @@ pub struct Builder { js_init_script: Option, on_navigation: Box>, on_page_load: Box>, + on_permission_request: Box>, on_window_ready: Box>, on_webview_ready: Box>, on_event: Box>, @@ -285,6 +298,7 @@ impl Builder { invoke_handler: Box::new(|_| false), on_navigation: Box::new(|_, _| true), on_page_load: Box::new(|_, _| ()), + on_permission_request: Box::new(|_, _| crate::webview::PermissionResponse::Default), on_window_ready: Box::new(|_| ()), on_webview_ready: Box::new(|_| ()), on_event: Box::new(|_, _| ()), @@ -487,6 +501,33 @@ impl Builder { self } + /// Callback invoked when the webview requests a permission. + /// + /// # Examples + /// + /// ```rust + /// use tauri::{plugin::{Builder, TauriPlugin}, Runtime}; + /// + /// fn init() -> TauriPlugin { + /// Builder::new("example") + /// .on_permission_request(|webview, kind| { + /// println!("Permission {} requested in webview {}", kind, webview.label()); + /// tauri::webview::PermissionResponse::Prompt + /// }) + /// .build() + /// } + /// ``` + #[must_use] + pub fn on_permission_request(mut self, on_permission_request: F) -> Self + where + F: FnMut(&Webview, crate::webview::PermissionKind) -> crate::webview::PermissionResponse + + Send + + 'static, + { + self.on_permission_request = Box::new(on_permission_request); + self + } + /// Callback invoked when the window is created. /// /// # Examples @@ -737,6 +778,7 @@ impl Builder { js_init_script: self.js_init_script, on_navigation: self.on_navigation, on_page_load: self.on_page_load, + on_permission_request: self.on_permission_request, on_window_ready: self.on_window_ready, on_webview_ready: self.on_webview_ready, on_event: self.on_event, @@ -764,6 +806,7 @@ pub struct TauriPlugin { js_init_script: Option, on_navigation: Box>, on_page_load: Box>, + on_permission_request: Box>, on_window_ready: Box>, on_webview_ready: Box>, on_event: Box>, @@ -843,6 +886,14 @@ impl Plugin for TauriPlugin { (self.on_page_load)(webview, payload) } + fn on_permission_request( + &mut self, + webview: &Webview, + kind: crate::webview::PermissionKind, + ) -> crate::webview::PermissionResponse { + (self.on_permission_request)(webview, kind) + } + fn on_event(&mut self, app: &AppHandle, event: &RunEvent) { (self.on_event)(app, event) } @@ -971,6 +1022,25 @@ impl PluginStore { }) } + /// Runs the on_permission_request hook for all plugins in the store. + pub(crate) fn on_permission_request( + &mut self, + webview: &Webview, + kind: crate::webview::PermissionKind, + ) -> crate::webview::PermissionResponse { + for plugin in self.store.iter_mut() { + #[cfg(feature = "tracing")] + let _span = + tracing::trace_span!("plugin::hooks::on_permission_request", name = plugin.name()) + .entered(); + let res = plugin.on_permission_request(webview, kind); + if res != crate::webview::PermissionResponse::Default { + return res; + } + } + crate::webview::PermissionResponse::Default + } + /// Runs the on_event hook for all plugins in the store. pub(crate) fn on_event(&mut self, app: &AppHandle, event: &RunEvent) { self diff --git a/crates/tauri/src/webview/mod.rs b/crates/tauri/src/webview/mod.rs index b86eb01a223c..eca325288453 100644 --- a/crates/tauri/src/webview/mod.rs +++ b/crates/tauri/src/webview/mod.rs @@ -18,7 +18,9 @@ pub use cookie; use http::HeaderMap; use serde::Serialize; use tauri_macros::default_runtime; -pub use tauri_runtime::webview::{NewWindowFeatures, PageLoadEvent, ScrollBarStyle}; +pub use tauri_runtime::webview::{ + NewWindowFeatures, PageLoadEvent, PermissionKind, PermissionResponse, ScrollBarStyle, +}; // Remove this re-export in v3 pub use tauri_runtime::Cookie; #[cfg(desktop)] @@ -64,6 +66,8 @@ pub(crate) type UriSchemeProtocolHandler = pub(crate) type OnPageLoad = dyn Fn(Webview, PageLoadPayload<'_>) + Send + Sync + 'static; pub(crate) type OnDocumentTitleChanged = dyn Fn(Webview, String) + Send + 'static; pub(crate) type DownloadHandler = dyn Fn(Webview, DownloadEvent<'_>) -> bool + Send + Sync; +pub(crate) type PermissionRequestHandler = + dyn Fn(Webview, PermissionKind) -> PermissionResponse + Send + Sync + 'static; #[derive(Clone, Serialize)] pub(crate) struct CreatedEvent { @@ -277,6 +281,7 @@ unstable_struct!( pub(crate) on_page_load_handler: Option>>, pub(crate) document_title_changed_handler: Option>>, pub(crate) download_handler: Option>>, + pub(crate) permission_request_handler: Option>>, } ); @@ -355,6 +360,7 @@ async fn create_window(app: tauri::AppHandle) { on_page_load_handler: None, document_title_changed_handler: None, download_handler: None, + permission_request_handler: None, } } @@ -434,6 +440,7 @@ async fn create_window(app: tauri::AppHandle) { on_page_load_handler: None, document_title_changed_handler: None, download_handler: None, + permission_request_handler: None, } } @@ -693,6 +700,52 @@ tauri::Builder::default() self } + /// Defines a closure to be executed when a permission is requested. + /// + /// The handler receives the [`PermissionKind`] and should return + /// the desired [`PermissionResponse`]. + /// + /// > [!NOTE] + /// > This handler only triggers for new permission requests. If the user has already + /// > allowed or denied a permission persistently within the webview, the browser + /// > will use the saved preference instead of calling this handler. + /// + /// ## Platform-specific: + /// + /// - **Windows**: Fully supported via WebView2's PermissionRequested event. + /// - **macOS / iOS**: Fully supported via WKUIDelegate's requestMediaCapturePermission. + /// - **Linux**: Fully supported via WebKitGTK's permission-request signal. + /// - **Android**: Experimental support implemented via JNI. + /// + /// # Examples + /// + /// ```rust,no_run + /// use tauri::webview::{WebviewBuilder, PermissionKind, PermissionResponse}; + /// tauri::Builder::default() + /// .setup(|app| { + /// let window = tauri::window::WindowBuilder::new(app, "label").build()?; + /// let webview_builder = WebviewBuilder::new("core", tauri::WebviewUrl::App("index.html".into())) + /// .on_permission_request(|webview, kind| { + /// match kind { + /// PermissionKind::Geolocation => PermissionResponse::Allow, + /// PermissionKind::Notifications => PermissionResponse::Allow, + /// _ => PermissionResponse::Default, + /// } + /// }); + /// let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?; + /// Ok(()) + /// }); + /// ``` + pub fn on_permission_request< + F: Fn(Webview, PermissionKind) -> PermissionResponse + Send + Sync + 'static, + >( + mut self, + f: F, + ) -> Self { + self.permission_request_handler.replace(Box::new(f)); + self + } + pub(crate) fn into_pending_webview>( mut self, manager: &M, @@ -771,6 +824,18 @@ tauri::Builder::default() } })); + let label_ = pending.label.clone(); + let manager_ = manager.manager_owned(); + if let Some(handler) = self.permission_request_handler { + pending.permission_request_handler = Some(Box::new(move |kind| { + if let Some(w) = manager_.get_webview(&label_) { + handler(w, kind) + } else { + PermissionResponse::Default + } + })); + } + manager .manager() .webview diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs index 95bf67719399..3823c9ec6488 100644 --- a/crates/tauri/src/webview/webview_window.rs +++ b/crates/tauri/src/webview/webview_window.rs @@ -434,6 +434,20 @@ tauri::Builder::default() self } + /// Defines a closure to be executed when a permission is requested. + pub fn on_permission_request< + F: Fn(Webview, crate::webview::PermissionKind) -> crate::webview::PermissionResponse + + Send + + Sync + + 'static, + >( + mut self, + f: F, + ) -> Self { + self.webview_builder = self.webview_builder.on_permission_request(f); + self + } + /// Creates a new window. pub fn build(self) -> crate::Result> { let (window, webview) = self.window_builder.with_webview(self.webview_builder)?;