fix(desktop): Downloads aus der Seite (blob/data) in der App speichern
Der Client reichte jeden Download an den System-Browser weiter. Dateien, die die Seite selbst erzeugt (blob:/data:, z. B. alle Downloads im Zertifikat-Manager), kann der Browser nicht abrufen - Windows zeigte nur "Holen Sie sich eine App, um diesen blob-Link zu oeffnen" (VM 8233). Solche Downloads speichert die App jetzt selbst (Ordner Downloads) und meldet Dateiname und Ordner; http/https-Downloads gehen weiter an den Browser. Dazu CHANGELOG und Quick-Doku zu 261001-l4q. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -939,6 +939,41 @@ fn external_target(url: &tauri::Url) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Downloads, die die App selbst speichert statt sie an den System-Browser
|
||||
/// zu geben: Inhalte, die die Seite im Speicher erzeugt hat (`blob:`,
|
||||
/// `data:`) -- der Browser kann sie nicht abrufen.
|
||||
fn saves_in_app(url: &tauri::Url) -> bool {
|
||||
matches!(url.scheme(), "blob" | "data")
|
||||
}
|
||||
|
||||
/// Titel und Text der Meldung nach einem in der App gespeicherten Download.
|
||||
fn download_notice(path: Option<&std::path::Path>, success: bool) -> (String, String) {
|
||||
let name = path
|
||||
.and_then(|p| p.file_name())
|
||||
.map(|n| n.to_string_lossy().into_owned());
|
||||
let folder = path
|
||||
.and_then(|p| p.parent())
|
||||
.and_then(|p| p.file_name())
|
||||
.map(|n| n.to_string_lossy().into_owned());
|
||||
match (success, name) {
|
||||
(true, Some(name)) => (
|
||||
"Download gespeichert".to_string(),
|
||||
match folder {
|
||||
Some(folder) => format!("„{name}“ liegt im Ordner {folder}."),
|
||||
None => format!("„{name}“ wurde gespeichert."),
|
||||
},
|
||||
),
|
||||
(true, None) => (
|
||||
"Download gespeichert".to_string(),
|
||||
"Die Datei wurde gespeichert.".to_string(),
|
||||
),
|
||||
(false, _) => (
|
||||
"Download fehlgeschlagen".to_string(),
|
||||
"Die Datei konnte nicht gespeichert werden.".to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Hauptfenster nach vorne holen. Ein minimiertes Fenster (z. B. per Win+D)
|
||||
/// wird zuerst wiederhergestellt.
|
||||
fn show_main_window(app: &AppHandle) {
|
||||
@@ -1009,15 +1044,36 @@ pub fn run() {
|
||||
tauri::webview::NewWindowResponse::Deny
|
||||
}
|
||||
})
|
||||
.on_download(|webview, event| {
|
||||
if let tauri::webview::DownloadEvent::Requested { url, .. } = event {
|
||||
.on_download(|webview, event| match event {
|
||||
// Dateien, die die Seite selbst erzeugt (blob:/data:, z. B.
|
||||
// Zertifikats-Downloads), kennt der System-Browser nicht --
|
||||
// Windows meldete „Holen Sie sich eine App, um diesen
|
||||
// ‚blob‘-Link zu öffnen“ (VM 8233, 01.10.2026). Die speichert
|
||||
// die App selbst im Ordner Downloads und meldet es danach.
|
||||
tauri::webview::DownloadEvent::Requested { url, .. } => {
|
||||
if saves_in_app(&url) {
|
||||
return true;
|
||||
}
|
||||
let _ = webview
|
||||
.app_handle()
|
||||
.opener()
|
||||
.open_url(url.to_string(), None::<&str>);
|
||||
return false;
|
||||
false
|
||||
}
|
||||
true
|
||||
tauri::webview::DownloadEvent::Finished { url, path, success } => {
|
||||
if saves_in_app(&url) {
|
||||
let (title, body) = download_notice(path.as_deref(), success);
|
||||
let _ = webview
|
||||
.app_handle()
|
||||
.notification()
|
||||
.builder()
|
||||
.title(title)
|
||||
.body(body)
|
||||
.show();
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => true,
|
||||
})
|
||||
.build()?;
|
||||
|
||||
@@ -1500,6 +1556,33 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saves_in_app_nur_blob_und_data() {
|
||||
for (url, expected) in [
|
||||
("blob:http://localhost:3000/0c1d-11", true),
|
||||
("data:application/x-pem-file;base64,QUJD", true),
|
||||
("https://alpha.tessera.ctl.de/desktop/download/x.exe", false),
|
||||
("http://intranet.local/datei.pdf", false),
|
||||
] {
|
||||
let parsed = tauri::Url::parse(url).unwrap();
|
||||
assert_eq!(saves_in_app(&parsed), expected, "{url}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn download_notice_nennt_datei_und_ordner() {
|
||||
let path = std::path::Path::new("/home/tessera/Downloads/www.example.de.crt");
|
||||
let (title, body) = download_notice(Some(path), true);
|
||||
assert_eq!(title, "Download gespeichert");
|
||||
assert_eq!(body, "„www.example.de.crt“ liegt im Ordner Downloads.");
|
||||
|
||||
let (title, _) = download_notice(None, false);
|
||||
assert_eq!(title, "Download fehlgeschlagen");
|
||||
|
||||
let (_, body) = download_notice(None, true);
|
||||
assert_eq!(body, "Die Datei wurde gespeichert.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_target_nur_http_und_https() {
|
||||
let https = tauri::Url::parse("https://www.google.com/search?q=tessera").unwrap();
|
||||
|
||||
Reference in New Issue
Block a user