Browse Source

feat(core/plugin): add `register_uri_scheme_protocol`, closes #7330 (#7350)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
closes #7330
Amr Bashir 2 years ago
parent
commit
fdaee9a5ce
4 changed files with 96 additions and 6 deletions
  1. 5 0
      .changes/plugin-custom-protocol.md
  2. 9 0
      core/tauri/src/app.rs
  3. 17 3
      core/tauri/src/manager.rs
  4. 65 3
      core/tauri/src/plugin.rs

+ 5 - 0
.changes/plugin-custom-protocol.md

@@ -0,0 +1,5 @@
+---
+'tauri': 'minor:feat'
+---
+
+Add `tauri::plugin::Builder::register_uri_scheme_protocol`

+ 9 - 0
core/tauri/src/app.rs

@@ -1462,6 +1462,15 @@ impl<R: Runtime> Builder<R> {
   ///
   /// * `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<String>,

+ 17 - 3
core/tauri/src/manager.rs

@@ -219,7 +219,7 @@ pub struct InnerWindowManager<R: Runtime> {
 
   package_info: PackageInfo,
   /// The webview protocols available to all windows.
-  uri_scheme_protocols: HashMap<String, Arc<CustomProtocol<R>>>,
+  uri_scheme_protocols: Mutex<HashMap<String, Arc<CustomProtocol<R>>>>,
   /// The menu set to all windows.
   menu: Option<Menu>,
   /// Menu event listeners to all windows.
@@ -321,7 +321,7 @@ impl<R: Runtime> WindowManager<R> {
         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<R: Runtime> WindowManager<R> {
     self.inner.invoke_responder.clone()
   }
 
+  pub(crate) fn register_uri_scheme_protocol<N: Into<String>>(
+    &self,
+    uri_scheme: N,
+    protocol: Arc<CustomProtocol<R>>,
+  ) {
+    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<R: Runtime> WindowManager<R> {
 
     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());

+ 65 - 3
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<T> = std::result::Result<T, Box<dyn std::error::Error>>;
@@ -146,6 +148,7 @@ pub struct Builder<R: Runtime, C: DeserializeOwned = ()> {
   on_webview_ready: Box<OnWebviewReady<R>>,
   on_event: Box<OnEvent<R>>,
   on_drop: Option<Box<OnDrop<R>>>,
+  uri_scheme_protocols: HashMap<String, Arc<CustomProtocol<R>>>,
 }
 
 impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
@@ -161,6 +164,7 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
       on_webview_ready: Box::new(|_| ()),
       on_event: Box::new(|_, _| ()),
       on_drop: None,
+      uri_scheme_protocols: Default::default(),
     }
   }
 
@@ -411,6 +415,57 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
     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<R: Runtime>() -> TauriPlugin<R> {
+  ///   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<String>,
+    H: Fn(
+        &AppHandle<R>,
+        &HttpRequest,
+      ) -> std::result::Result<HttpResponse, Box<dyn std::error::Error>>
+      + 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<R, C> {
     TauriPlugin {
@@ -424,6 +479,7 @@ impl<R: Runtime, C: DeserializeOwned> Builder<R, C> {
       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<R: Runtime, C: DeserializeOwned = ()> {
   on_webview_ready: Box<OnWebviewReady<R>>,
   on_event: Box<OnEvent<R>>,
   on_drop: Option<Box<OnDrop<R>>>,
+  uri_scheme_protocols: HashMap<String, Arc<CustomProtocol<R>>>,
 }
 
 impl<R: Runtime, C: DeserializeOwned> Drop for TauriPlugin<R, C> {
@@ -463,6 +520,11 @@ impl<R: Runtime, C: DeserializeOwned> Plugin<R> for TauriPlugin<R, C> {
     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(())
   }