From bcf15e918812a731801c456fcfe1b7b758eb226e Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Wed, 6 May 2026 18:26:37 +0200 Subject: [PATCH] Fixed possibility of opening external links in system browser --- runtime/capabilities/default.json | 6 +++++ runtime/src/app_window.rs | 41 ++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/runtime/capabilities/default.json b/runtime/capabilities/default.json index 8ea2bc38..86f14897 100644 --- a/runtime/capabilities/default.json +++ b/runtime/capabilities/default.json @@ -2,12 +2,18 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Default capability for MindWork AI Studio", + "remote": { + "urls": [ + "http://localhost:*" + ] + }, "windows": [ "main" ], "permissions": [ "core:default", "updater:default", + "opener:default", "shell:allow-open", { "identifier": "shell:allow-spawn", diff --git a/runtime/src/app_window.rs b/runtime/src/app_window.rs index 7d1dc2f2..d33fdc52 100644 --- a/runtime/src/app_window.rs +++ b/runtime/src/app_window.rs @@ -38,6 +38,9 @@ static EVENT_BROADCAST: Lazy>>> = Lazy::ne /// Stores the currently registered global shortcuts (name -> shortcut string). static REGISTERED_SHORTCUTS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); +/// Stores the localhost origin of the Blazor app after the .NET server is ready. +static APPROVED_APP_URL: Lazy>> = Lazy::new(|| Mutex::new(None)); + /// Enum identifying global keyboard shortcuts. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display)] #[strum(serialize_all = "SCREAMING_SNAKE_CASE")] @@ -81,6 +84,7 @@ pub fn start_tauri() { let app = tauri::Builder::default() .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_opener::init()) .plugin( tauri::plugin::Builder::::new("external-link-handler") .on_navigation(|webview, url| { @@ -199,16 +203,35 @@ fn is_local_host(host: Option<&str>) -> bool { matches!(host, Some("localhost") | Some("127.0.0.1") | Some("::1") | Some("[::1]")) } +fn is_local_http_url(url: &tauri::Url) -> bool { + matches!(url.scheme(), "http" | "https") && is_local_host(url.host_str()) +} + +fn same_origin(left: &tauri::Url, right: &tauri::Url) -> bool { + left.scheme() == right.scheme() + && left.host_str() == right.host_str() + && left.port_or_known_default() == right.port_or_known_default() +} + fn should_open_in_system_browser(webview: &tauri::Webview, url: &tauri::Url) -> bool { - if !matches!(url.scheme(), "http" | "https") { - return false; + match url.scheme() { + "mailto" | "tel" => return true, + "http" | "https" => {}, + _ => return false, + } + + if let Some(approved_app_url) = APPROVED_APP_URL.lock().unwrap().as_ref() { + if same_origin(approved_app_url, url) { + return false; + } + + if is_local_http_url(url) { + return true; + } } if let Ok(current_url) = webview.url() { - let same_origin = current_url.scheme() == url.scheme() - && current_url.host_str() == url.host_str() - && current_url.port_or_known_default() == url.port_or_known_default(); - if same_origin { + if same_origin(¤t_url, url) { return false; } } @@ -377,6 +400,12 @@ pub async fn change_location_to(url: &str) { } } + if let Ok(parsed_url) = tauri::Url::parse(url) { + if is_local_http_url(&parsed_url) { + *APPROVED_APP_URL.lock().unwrap() = Some(parsed_url); + } + } + let js_location_change = format!("window.location = '{url}';"); let main_window = main_window_spawn_clone.lock().unwrap(); let location_change_result = main_window.as_ref().unwrap().eval(js_location_change.as_str());