From 6b87f8f3337bda540f9f052d0fca26e627cdb465 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 6 Nov 2025 09:07:37 -0300 Subject: [PATCH] move custom_scheme_url to runtime --- crates/tauri-runtime-cef/src/lib.rs | 8 ++++++++ crates/tauri-runtime-wry/src/lib.rs | 15 +++++++++++++++ crates/tauri-runtime/src/lib.rs | 3 +++ crates/tauri/src/manager/mod.rs | 5 ++--- crates/tauri/src/manager/webview.rs | 21 ++++++++++----------- crates/tauri/src/protocol/isolation.rs | 4 ++-- crates/tauri/src/test/mock_runtime.rs | 4 ++++ crates/tauri/src/webview/mod.rs | 15 ++------------- 8 files changed, 46 insertions(+), 29 deletions(-) diff --git a/crates/tauri-runtime-cef/src/lib.rs b/crates/tauri-runtime-cef/src/lib.rs index 36cdfb633..361ea2433 100644 --- a/crates/tauri-runtime-cef/src/lib.rs +++ b/crates/tauri-runtime-cef/src/lib.rs @@ -2080,6 +2080,14 @@ impl Runtime for CefRuntime { fn set_device_event_filter(&mut self, _filter: DeviceEventFilter) {} + fn custom_scheme_url(scheme: &str, https: bool) -> String { + // CEF always uses http/https format regardless of platform + format!( + "{}://{scheme}.localhost", + if https { "https" } else { "http" } + ) + } + #[cfg(any( target_os = "macos", windows, diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 5d91f9f1f..3c9cbf6d2 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -3027,6 +3027,21 @@ impl Runtime for Wry { .set_device_event_filter(DeviceEventFilterWrapper::from(filter).0); } + /// Returns the URL for a custom scheme. + /// + /// On Windows and Android, custom schemes use `http://.localhost` or `https://.localhost`. + /// On other platforms, custom schemes use `://localhost`. + fn custom_scheme_url(scheme: &str, _https: bool) -> String { + if cfg!(any(windows, target_os = "android")) { + format!( + "{}://{scheme}.localhost", + if _https { "https" } else { "http" } + ) + } else { + format!("{scheme}://localhost") + } + } + #[cfg(desktop)] fn run_iteration) + 'static>(&mut self, mut callback: F) { use tao::platform::run_return::EventLoopExtRunReturn; diff --git a/crates/tauri-runtime/src/lib.rs b/crates/tauri-runtime/src/lib.rs index 604354a5a..b37518604 100644 --- a/crates/tauri-runtime/src/lib.rs +++ b/crates/tauri-runtime/src/lib.rs @@ -490,6 +490,9 @@ pub trait Runtime: Debug + Sized + 'static { /// [`tao`]: https://crates.io/crates/tao fn set_device_event_filter(&mut self, filter: DeviceEventFilter); + /// Returns the URL for a custom scheme. + fn custom_scheme_url(scheme: &str, _https: bool) -> String; + /// Runs an iteration of the runtime event loop and returns control flow to the caller. #[cfg(desktop)] fn run_iteration) + 'static>(&mut self, callback: F); diff --git a/crates/tauri/src/manager/mod.rs b/crates/tauri/src/manager/mod.rs index aea0ed59e..126dcf7bc 100644 --- a/crates/tauri/src/manager/mod.rs +++ b/crates/tauri/src/manager/mod.rs @@ -28,7 +28,6 @@ use crate::{ plugin::PluginStore, resources::ResourceTable, utils::{config::Config, PackageInfo}, - webview::custom_scheme_url, Assets, Context, DebugAppIcon, EventName, Pattern, Runtime, StateManager, Webview, Window, }; @@ -357,7 +356,7 @@ impl AppManager { pub(crate) fn get_url(&self, https: bool) -> Cow<'_, Url> { match self.base_path() { Some(url) => Cow::Borrowed(url), - _ => Cow::Owned(Url::parse(&custom_scheme_url("tauri", https)).unwrap()), + _ => Cow::Owned(Url::parse(&R::custom_scheme_url("tauri", https)).unwrap()), } } @@ -438,7 +437,7 @@ impl AppManager { let default_src = csp_map .entry("default-src".into()) .or_insert_with(Default::default); - default_src.push(crate::webview::custom_scheme_url(schema, _use_https_schema)); + default_src.push(R::custom_scheme_url(schema, _use_https_schema)); } csp_header.replace(Csp::DirectiveMap(csp_map).to_string()); diff --git a/crates/tauri/src/manager/webview.rs b/crates/tauri/src/manager/webview.rs index 6abf7cf2b..186f4c59f 100644 --- a/crates/tauri/src/manager/webview.rs +++ b/crates/tauri/src/manager/webview.rs @@ -136,9 +136,7 @@ impl WebviewManager { let ipc_init = IpcJavascript { isolation_origin: &match &*app_manager.pattern { #[cfg(feature = "isolation")] - crate::Pattern::Isolation { schema, .. } => { - crate::webview::custom_scheme_url(schema, use_https_scheme) - } + crate::Pattern::Isolation { schema, .. } => R::custom_scheme_url(schema, use_https_scheme), _ => "".to_owned(), }, } @@ -195,7 +193,7 @@ impl WebviewManager { if let crate::Pattern::Isolation { schema, .. } = &*app_manager.pattern { all_initialization_scripts.push(main_frame_script( IsolationJavascript { - isolation_src: crate::webview::custom_scheme_url(schema, use_https_scheme).as_str(), + isolation_src: R::custom_scheme_url(schema, use_https_scheme).as_str(), style: tauri_utils::pattern::isolation::IFRAME_STYLE, } .render_default(&Default::default())? @@ -217,6 +215,7 @@ impl WebviewManager { let mut registered_scheme_protocols = Vec::new(); + let mut custom_uri_schemes = Vec::new(); for (uri_scheme, protocol) in &*self.uri_scheme_protocols.lock().unwrap() { registered_scheme_protocols.push(uri_scheme.clone()); let protocol = protocol.clone(); @@ -229,17 +228,17 @@ impl WebviewManager { }; (protocol.protocol)(context, request, UriSchemeResponder(responder)) }); + + custom_uri_schemes.push(uri_scheme.clone()); } let window_url = Url::parse(&pending.url).unwrap(); let window_origin = if window_url.scheme() == "data" { "null".into() - } else if (cfg!(windows) || cfg!(target_os = "android") || cfg!(feature = "cef")) - && window_url.scheme() != "http" - && window_url.scheme() != "https" - { - let https = if use_https_scheme { "https" } else { "http" }; - format!("{https}://{}.localhost", window_url.scheme()) + } else if custom_uri_schemes.contains(&window_url.scheme().to_string()) { + // when we're referencing a custom scheme, make sure it's using the actual window origin + // this will convert tauri://localhost to http://tauri.localhost (or any other scheme format) to match the runtime given URL + R::custom_scheme_url(window_url.scheme(), use_https_scheme) } else if let Some(host) = window_url.host() { format!( "{}://{}{}", @@ -556,7 +555,7 @@ impl WebviewManager { #[cfg(feature = "isolation")] let isolation_frame_url = if let crate::Pattern::Isolation { schema, .. } = &*pattern { Some( - Url::parse(&crate::webview::custom_scheme_url( + Url::parse(&R::custom_scheme_url( schema, pending.webview_attributes.use_https_scheme, )) diff --git a/crates/tauri/src/protocol/isolation.rs b/crates/tauri/src/protocol/isolation.rs index 2311ec8a2..1f57f6832 100644 --- a/crates/tauri/src/protocol/isolation.rs +++ b/crates/tauri/src/protocol/isolation.rs @@ -14,7 +14,7 @@ use std::sync::Arc; use crate::{ manager::{set_csp, webview::PROCESS_IPC_MESSAGE_FN, AppManager}, - webview::{UriSchemeProtocolHandler, custom_scheme_url}, + webview::UriSchemeProtocolHandler, Runtime, }; @@ -26,7 +26,7 @@ pub fn get( window_origin: String, use_https_scheme: bool, ) -> UriSchemeProtocolHandler { - let frame_src = custom_scheme_url(schema, use_https_scheme); + let frame_src = R::custom_scheme_url(schema, use_https_scheme); let assets = assets as Arc>; diff --git a/crates/tauri/src/test/mock_runtime.rs b/crates/tauri/src/test/mock_runtime.rs index 2414b2382..b4094d2ba 100644 --- a/crates/tauri/src/test/mock_runtime.rs +++ b/crates/tauri/src/test/mock_runtime.rs @@ -1264,6 +1264,10 @@ impl Runtime for MockRuntime { fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {} + fn custom_scheme_url(scheme: &str, _https: bool) -> String { + format!("{scheme}://localhost") + } + #[cfg(any( target_os = "macos", windows, diff --git a/crates/tauri/src/webview/mod.rs b/crates/tauri/src/webview/mod.rs index 08c94919d..70a26865e 100644 --- a/crates/tauri/src/webview/mod.rs +++ b/crates/tauri/src/webview/mod.rs @@ -1703,7 +1703,7 @@ tauri::Builder::default() // if from `tauri://` custom protocol ({ - let protocol_url = Url::parse(&custom_scheme_url("tauri", uses_https)).unwrap(); + let protocol_url = Url::parse(&R::custom_scheme_url("tauri", uses_https)).unwrap(); current_url.scheme() == protocol_url.scheme() && current_url.domain() == protocol_url.domain() }) || @@ -1727,7 +1727,7 @@ tauri::Builder::default() // so we check using the first part of the domain #[cfg(any(windows, target_os = "android"))] let local = { - let protocol_url = Url::parse(&custom_scheme_url("tauri", uses_https)).unwrap(); + let protocol_url = Url::parse(&R::custom_scheme_url("tauri", uses_https)).unwrap(); let maybe_protocol = current_url .domain() .and_then(|d| d .split_once('.')) @@ -2326,17 +2326,6 @@ impl ResolvedScope { } } -pub(crate) fn custom_scheme_url(scheme: &str, https: bool) -> String { - if cfg!(any(windows, target_os = "android", feature = "cef")) { - format!( - "{}://{scheme}.localhost", - if https { "https" } else { "http" } - ) - } else { - format!("{scheme}://localhost") - } -} - #[cfg(test)] mod tests { #[test]