From fdaee9a5ce988c448dd035c2050c339d275e8d15 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 12 Jul 2023 22:49:32 +0300 Subject: [PATCH] feat(core/plugin): add `register_uri_scheme_protocol`, closes #7330 (#7350) Co-authored-by: Lucas Nogueira closes #7330 --- .changes/plugin-custom-protocol.md | 5 +++ core/tauri/src/app.rs | 9 ++++ core/tauri/src/manager.rs | 20 +++++++-- core/tauri/src/plugin.rs | 68 ++++++++++++++++++++++++++++-- 4 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 .changes/plugin-custom-protocol.md diff --git a/.changes/plugin-custom-protocol.md b/.changes/plugin-custom-protocol.md new file mode 100644 index 000000000..8841c6730 --- /dev/null +++ b/.changes/plugin-custom-protocol.md @@ -0,0 +1,5 @@ +--- +'tauri': 'minor:feat' +--- + +Add `tauri::plugin::Builder::register_uri_scheme_protocol` diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 97652556f..7bc7bd497 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -1462,6 +1462,15 @@ impl Builder { /// /// * `uri_scheme` The URI scheme to register, such as `example`. /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`. + /// + /// # Examples + /// + /// ```rust + /// tauri::Builder::default() + /// .register_uri_scheme_protocol("myscheme", |app, req| { + /// tauri::http::ResponseBuilder::new().body(Vec::new()) + /// }); + /// ``` #[must_use] pub fn register_uri_scheme_protocol< N: Into, diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 7a574ab00..f41581bbe 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -219,7 +219,7 @@ pub struct InnerWindowManager { package_info: PackageInfo, /// The webview protocols available to all windows. - uri_scheme_protocols: HashMap>>, + uri_scheme_protocols: Mutex>>>, /// The menu set to all windows. menu: Option, /// Menu event listeners to all windows. @@ -321,7 +321,7 @@ impl WindowManager { tray_icon: context.system_tray_icon, package_info: context.package_info, pattern: context.pattern, - uri_scheme_protocols, + uri_scheme_protocols: Mutex::new(uri_scheme_protocols), menu, menu_event_listeners: Arc::new(menu_event_listeners), window_event_listeners: Arc::new(window_event_listeners), @@ -350,6 +350,20 @@ impl WindowManager { self.inner.invoke_responder.clone() } + pub(crate) fn register_uri_scheme_protocol>( + &self, + uri_scheme: N, + protocol: Arc>, + ) { + let uri_scheme = uri_scheme.into(); + self + .inner + .uri_scheme_protocols + .lock() + .unwrap() + .insert(uri_scheme, protocol); + } + /// Get the base path to serve data from. /// /// * In dev mode, this will be based on the `devPath` configuration value. @@ -461,7 +475,7 @@ impl WindowManager { let mut registered_scheme_protocols = Vec::new(); - for (uri_scheme, protocol) in &self.inner.uri_scheme_protocols { + for (uri_scheme, protocol) in &*self.inner.uri_scheme_protocols.lock().unwrap() { registered_scheme_protocols.push(uri_scheme.clone()); let protocol = protocol.clone(); let app_handle = Mutex::new(app_handle.clone()); diff --git a/core/tauri/src/plugin.rs b/core/tauri/src/plugin.rs index d27fa5f42..f04890c58 100644 --- a/core/tauri/src/plugin.rs +++ b/core/tauri/src/plugin.rs @@ -5,14 +5,16 @@ //! The Tauri plugin extension to expand Tauri functionality. use crate::{ - utils::config::PluginConfig, AppHandle, Invoke, InvokeHandler, PageLoadPayload, RunEvent, - Runtime, Window, + http::{Request as HttpRequest, Response as HttpResponse}, + manager::CustomProtocol, + utils::config::PluginConfig, + AppHandle, Invoke, InvokeHandler, PageLoadPayload, RunEvent, Runtime, Window, }; use serde::de::DeserializeOwned; use serde_json::Value as JsonValue; use tauri_macros::default_runtime; -use std::{collections::HashMap, fmt}; +use std::{collections::HashMap, fmt, sync::Arc}; /// The result type of Tauri plugin module. pub type Result = std::result::Result>; @@ -146,6 +148,7 @@ pub struct Builder { on_webview_ready: Box>, on_event: Box>, on_drop: Option>>, + uri_scheme_protocols: HashMap>>, } impl Builder { @@ -161,6 +164,7 @@ impl Builder { on_webview_ready: Box::new(|_| ()), on_event: Box::new(|_, _| ()), on_drop: None, + uri_scheme_protocols: Default::default(), } } @@ -411,6 +415,57 @@ impl Builder { self } + /// Registers a URI scheme protocol available to all webviews. + /// Leverages [setURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler) on macOS, + /// [AddWebResourceRequestedFilter](https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addwebresourcerequestedfilter?view=webview2-dotnet-1.0.774.44) on Windows + /// and [webkit-web-context-register-uri-scheme](https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebContext.html#webkit-web-context-register-uri-scheme) on Linux. + /// + /// # Known limitations + /// + /// URI scheme protocols are registered when the webview is created. Due to this limitation, if the plugin is registed after a webview has been created, this protocol won't be available. + /// + /// # Arguments + /// + /// * `uri_scheme` The URI scheme to register, such as `example`. + /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`. + /// + /// # Examples + /// + /// ```rust + /// use tauri::{plugin::{Builder, TauriPlugin}, Runtime}; + /// + /// fn init() -> TauriPlugin { + /// Builder::new("myplugin") + /// .register_uri_scheme_protocol("myscheme", |app, req| { + /// tauri::http::ResponseBuilder::new().body(Vec::new()) + /// }) + /// .build() + /// } + /// ``` + #[must_use] + pub fn register_uri_scheme_protocol< + N: Into, + H: Fn( + &AppHandle, + &HttpRequest, + ) -> std::result::Result> + + Send + + Sync + + 'static, + >( + mut self, + uri_scheme: N, + protocol: H, + ) -> Self { + self.uri_scheme_protocols.insert( + uri_scheme.into(), + Arc::new(CustomProtocol { + protocol: Box::new(protocol), + }), + ); + self + } + /// Builds the [TauriPlugin]. pub fn build(self) -> TauriPlugin { TauriPlugin { @@ -424,6 +479,7 @@ impl Builder { on_webview_ready: self.on_webview_ready, on_event: self.on_event, on_drop: self.on_drop, + uri_scheme_protocols: self.uri_scheme_protocols, } } } @@ -440,6 +496,7 @@ pub struct TauriPlugin { on_webview_ready: Box>, on_event: Box>, on_drop: Option>>, + uri_scheme_protocols: HashMap>>, } impl Drop for TauriPlugin { @@ -463,6 +520,11 @@ impl Plugin for TauriPlugin { if let Some(s) = self.setup_with_config.take() { (s)(app, serde_json::from_value(config)?)?; } + for (uri_scheme, protocol) in &self.uri_scheme_protocols { + app + .manager + .register_uri_scheme_protocol(uri_scheme, protocol.clone()) + } Ok(()) }