From 6033a745aa51bfafa174fc4bda9396919b5f906d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 17 Feb 2026 15:38:04 +1100 Subject: [PATCH 01/33] test: add more unit-tests for `html` module --- Cargo.lock | 1 + crates/tauri-utils/Cargo.toml | 1 + crates/tauri-utils/src/html.rs | 106 ++++++++++++++++++++++++++++++--- 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ffa7c32f7962..c74d920cb418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8895,6 +8895,7 @@ dependencies = [ "serialize-to-javascript", "swift-rs", "tauri", + "tempfile", "thiserror 2.0.12", "toml 0.9.10+spec-1.1.0", "url", diff --git a/crates/tauri-utils/Cargo.toml b/crates/tauri-utils/Cargo.toml index 9a1d6637e558..7e1d181ecbe5 100644 --- a/crates/tauri-utils/Cargo.toml +++ b/crates/tauri-utils/Cargo.toml @@ -58,6 +58,7 @@ swift-rs = { version = "1", optional = true, features = ["build"] } getrandom = { version = "0.3", features = ["std"] } serial_test = "3" tauri = { path = "../tauri" } +tempfile = "3.15.0" [features] build = [ diff --git a/crates/tauri-utils/src/html.rs b/crates/tauri-utils/src/html.rs index 958ce597832f..3335743dd2d6 100644 --- a/crates/tauri-utils/src/html.rs +++ b/crates/tauri-utils/src/html.rs @@ -315,6 +315,13 @@ pub fn normalize_script_for_csp(input: &[u8]) -> Vec { #[cfg(test)] mod tests { + use std::io::Write; + + use super::*; + use crate::{ + assets::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN}, + config, + }; #[test] fn csp() { @@ -322,12 +329,14 @@ mod tests { "".to_string(), "".to_string(), ]; + for html in htmls { - let document = super::parse(html); + let document = parse(html); let csp = "csp-string"; - super::inject_csp(&document, csp); + inject_csp(&document, csp); + assert_eq!( - document.to_string(), + String::from_utf8(serialize_node(&document)).unwrap(), format!( r#""#, ) @@ -336,12 +345,95 @@ mod tests { } #[test] - fn normalize_script_for_csp() { + fn normalize_script_for_csp_test() { let js = "// Copyright 2019-2024 Tauri Programme within The Commons Conservancy\r// SPDX-License-Identifier: Apache-2.0\n// SPDX-License-Identifier: MIT\r\n\r\nwindow.__TAURI_ISOLATION_HOOK__ = (payload, options) => {\r\n return payload\r\n}\r\n"; let expected = "// Copyright 2019-2024 Tauri Programme within The Commons Conservancy\n// SPDX-License-Identifier: Apache-2.0\n// SPDX-License-Identifier: MIT\n\nwindow.__TAURI_ISOLATION_HOOK__ = (payload, options) => {\n return payload\n}\n"; + + assert_eq!(normalize_script_for_csp(js.as_bytes()), expected.as_bytes()) + } + + #[test] + fn parse_and_serialize_roundtrips() { + let htmls = [ + "Test

Hello

", + "", + ]; + + for html in htmls { + let parsed = parse(html.to_string()); + let serialized = serialize_node(&parsed); + let result = String::from_utf8(serialized).unwrap(); + + assert_eq!(result, html); + } + } + + #[test] + fn inject_nonce_to_scripts() { + let html = r#""#; + + let document = parse(html.to_string()); + inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(false)); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + format!( + r#""# + ) + ); + } + + #[test] + fn inject_nonce_to_styles() { + let html = r#""#; + + let document = parse(html.to_string()); + inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(false)); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + format!( + r#""# + ) + ); + } + + #[test] + fn inject_nonce_skips_existing() { + let html = r#""#; + + let document = parse(html.to_string()); + inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(false)); + + assert_eq!(String::from_utf8(serialize_node(&document)).unwrap(), html); + } + + #[test] + fn inject_nonce_respects_disabled_modification() { + let html = r#""#; + + let document = parse(html.to_string()); + inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(true)); + assert_eq!( - super::normalize_script_for_csp(js.as_bytes()), - expected.as_bytes() - ) + String::from_utf8(serialize_node(&document)).unwrap(), + r#""# + ); + } + + #[test] + fn inline_isolation_replaces_src_with_content() { + let temp_dir = tempfile::tempdir().unwrap(); + let mut file = tempfile::tempfile_in(&temp_dir).unwrap(); + file.write_all(b"console.log('test');").unwrap(); + + let html = r#""#; + let document = parse(html.to_string()); + inline_isolation(&document, temp_dir.path()); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + r#""# + ); } } From 65ffbe2fe97c478f6ece45efb31b64e40366b210 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 17 Feb 2026 16:06:12 +1100 Subject: [PATCH 02/33] refactor: remove html dependencies from `tauri-cli` --- Cargo.lock | 2 -- crates/tauri-cli/Cargo.toml | 2 -- .../tauri-cli/src/dev/builtin_dev_server.rs | 28 ++++--------------- crates/tauri-utils/src/html.rs | 22 +++++++++++++++ 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c74d920cb418..915fc34c847d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8570,7 +8570,6 @@ dependencies = [ "glob", "handlebars", "heck 0.5.0", - "html5ever", "ignore", "image", "include_dir", @@ -8583,7 +8582,6 @@ dependencies = [ "jsonrpsee-core", "jsonrpsee-ws-client", "jsonschema", - "kuchikiki", "libc", "local-ip-address", "log", diff --git a/crates/tauri-cli/Cargo.toml b/crates/tauri-cli/Cargo.toml index 370cdc623c7e..e7f9a362ff37 100644 --- a/crates/tauri-cli/Cargo.toml +++ b/crates/tauri-cli/Cargo.toml @@ -89,8 +89,6 @@ env_logger = "0.11" icns = { package = "tauri-icns", version = "0.1" } image = { version = "0.25", default-features = false, features = ["ico"] } axum = { version = "0.8", features = ["ws"] } -html5ever = "0.29" -kuchiki = { package = "kuchikiki", version = "=0.8.8-speedreader" } tokio = { version = "1", features = ["macros", "sync"] } common-path = "1" serde-value = "0.7" diff --git a/crates/tauri-cli/src/dev/builtin_dev_server.rs b/crates/tauri-cli/src/dev/builtin_dev_server.rs index 1b49058ed224..0492bf408d8c 100644 --- a/crates/tauri-cli/src/dev/builtin_dev_server.rs +++ b/crates/tauri-cli/src/dev/builtin_dev_server.rs @@ -7,8 +7,6 @@ use axum::{ http::{header, StatusCode, Uri}, response::{IntoResponse, Response}, }; -use html5ever::{namespace_url, ns, LocalName, QualName}; -use kuchiki::{traits::TendrilSink, NodeRef}; use std::{ net::{IpAddr, SocketAddr}, path::{Path, PathBuf}, @@ -128,28 +126,12 @@ async fn ws_handler(ws: WebSocketUpgrade, state: State) -> Response } fn inject_address(html_bytes: Vec, address: &SocketAddr) -> Vec { - fn with_html_head(document: &mut NodeRef, f: F) { - if let Ok(ref node) = document.select_first("head") { - f(node.as_node()) - } else { - let node = NodeRef::new_element( - QualName::new(None, ns!(html), LocalName::from("head")), - None, - ); - f(&node); - document.prepend(node) - } - } + let document = tauri_utils::html::parse(String::from_utf8_lossy(&html_bytes).into_owned()); - let mut document = kuchiki::parse_html() - .one(String::from_utf8_lossy(&html_bytes).into_owned()) - .document_node; - with_html_head(&mut document, |head| { - let script = RELOAD_SCRIPT.replace("{{reload_url}}", &format!("ws://{address}/__tauri_cli")); - let script_el = NodeRef::new_element(QualName::new(None, ns!(html), "script".into()), None); - script_el.append(NodeRef::new_text(script)); - head.prepend(script_el); - }); + tauri_utils::html::append_script_to_head( + &document, + &RELOAD_SCRIPT.replace("{{reload_url}}", &format!("ws://{address}/__tauri_cli")), + ); tauri_utils::html::serialize_node(&document) } diff --git a/crates/tauri-utils/src/html.rs b/crates/tauri-utils/src/html.rs index 3335743dd2d6..91ddd0b3bb1a 100644 --- a/crates/tauri-utils/src/html.rs +++ b/crates/tauri-utils/src/html.rs @@ -164,6 +164,15 @@ pub fn inject_csp(document: &NodeRef, csp: &str) { }); } +/// Injects a content security policy to the HTML. +pub fn append_script_to_head(document: &NodeRef, script: &str) { + with_head(document, |head| { + let script_el = NodeRef::new_element(QualName::new(None, ns!(html), "script".into()), None); + script_el.append(NodeRef::new_text(script)); + head.prepend(script_el); + }); +} + fn create_csp_meta_tag(csp: &str) -> NodeRef { NodeRef::new_element( QualName::new(None, ns!(html), LocalName::from("meta")), @@ -398,6 +407,19 @@ mod tests { ); } + #[test] + fn append_script_to_head_test() { + let html = r#""#; + + let document = parse(html.to_string()); + append_script_to_head(&document, r#"console.log('Test')"#); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + format!(r#""#) + ); + } + #[test] fn inject_nonce_skips_existing() { let html = r#""#; From ff39ab7ca90a3df2cc89af0905011b324fe02052 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 23 Feb 2026 17:23:14 +1100 Subject: [PATCH 03/33] feat: introduce `html-manipulation-2` feature --- Cargo.lock | 219 +++++++++++- crates/tauri-cli/Cargo.toml | 2 +- .../tauri-cli/src/dev/builtin_dev_server.rs | 6 +- crates/tauri-codegen/Cargo.toml | 7 +- crates/tauri-codegen/src/context.rs | 21 +- crates/tauri-codegen/src/embedded_assets.rs | 2 +- crates/tauri-utils/Cargo.toml | 2 + crates/tauri-utils/src/html.rs | 1 + crates/tauri-utils/src/html2.rs | 338 ++++++++++++++++++ crates/tauri-utils/src/lib.rs | 3 + crates/tauri/Cargo.toml | 6 +- crates/tauri/src/manager/webview.rs | 6 +- crates/tests/acl/Cargo.toml | 8 +- 13 files changed, 588 insertions(+), 33 deletions(-) create mode 100644 crates/tauri-utils/src/html2.rs diff --git a/Cargo.lock b/Cargo.lock index 915fc34c847d..4a562551d628 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1632,6 +1632,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.13.1", + "smallvec", +] + [[package]] name = "cssparser-macros" version = "0.6.1" @@ -1867,7 +1880,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl", + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", ] [[package]] @@ -1882,6 +1904,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "des" version = "0.8.1" @@ -2022,6 +2055,21 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dom_query" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d9c2e7f1d22d0f2ce07626d259b8a55f4a47cb0938d4006dd8ae037f17d585e" +dependencies = [ + "bit-set", + "cssparser 0.36.0", + "foldhash", + "html5ever 0.36.1", + "precomputed-hash", + "selectors 0.35.0", + "tendril", +] + [[package]] name = "dpi" version = "0.1.1" @@ -2504,6 +2552,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "fontconfig-parser" version = "0.5.7" @@ -3244,10 +3298,20 @@ checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.14.1", "match_token", ] +[[package]] +name = "html5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" +dependencies = [ + "log", + "markup5ever 0.36.1", +] + [[package]] name = "http" version = "0.2.12" @@ -4231,10 +4295,10 @@ version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ - "cssparser", - "html5ever", + "cssparser 0.29.6", + "html5ever 0.29.1", "indexmap 2.11.4", - "selectors", + "selectors 0.24.0", ] [[package]] @@ -4482,11 +4546,22 @@ dependencies = [ "log", "phf 0.11.3", "phf_codegen 0.11.3", - "string_cache", - "string_cache_codegen", + "string_cache 0.8.7", + "string_cache_codegen 0.5.2", "tendril", ] +[[package]] +name = "markup5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + [[package]] name = "match_token" version = "0.1.0" @@ -5841,6 +5916,17 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + [[package]] name = "phf_codegen" version = "0.8.0" @@ -5861,6 +5947,16 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", +] + [[package]] name = "phf_generator" version = "0.8.0" @@ -5891,6 +5987,16 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.10.0" @@ -5918,6 +6024,19 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "phf_shared" version = "0.8.0" @@ -5945,6 +6064,15 @@ dependencies = [ "siphasher 1.0.1", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -6154,9 +6282,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -7405,14 +7533,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", - "cssparser", + "cssparser 0.29.6", "derive_more 0.99.18", "fxhash", "log", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", - "servo_arc", + "servo_arc 0.2.0", + "smallvec", +] + +[[package]] +name = "selectors" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fdfed56cd634f04fe8b9ddf947ae3dc493483e819593d2ba17df9ad05db8b2" +dependencies = [ + "bitflags 2.7.0", + "cssparser 0.36.0", + "derive_more 2.0.1", + "log", + "new_debug_unreachable", + "phf 0.13.1", + "phf_codegen 0.13.1", + "precomputed-hash", + "rustc-hash", + "servo_arc 0.4.3", "smallvec", ] @@ -7690,6 +7837,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.10.6" @@ -8108,6 +8264,18 @@ dependencies = [ "serde", ] +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.13.1", + "precomputed-hash", +] + [[package]] name = "string_cache_codegen" version = "0.5.2" @@ -8120,6 +8288,18 @@ dependencies = [ "quote", ] +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", +] + [[package]] name = "strsim" version = "0.11.1" @@ -8868,10 +9048,11 @@ dependencies = [ "brotli", "cargo_metadata", "ctor 0.2.9", + "dom_query", "dunce", "getrandom 0.3.3", "glob", - "html5ever", + "html5ever 0.29.1", "http 1.3.1", "infer", "json-patch", @@ -10085,6 +10266,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" +dependencies = [ + "phf 0.13.1", + "phf_codegen 0.13.1", + "string_cache 0.9.0", + "string_cache_codegen 0.6.1", +] + [[package]] name = "webkit2gtk" version = "2.0.2" @@ -10843,7 +11036,7 @@ dependencies = [ "dunce", "gdkx11", "gtk", - "html5ever", + "html5ever 0.29.1", "http 1.3.1", "javascriptcore-rs", "jni", diff --git a/crates/tauri-cli/Cargo.toml b/crates/tauri-cli/Cargo.toml index e7f9a362ff37..d697c001a5db 100644 --- a/crates/tauri-cli/Cargo.toml +++ b/crates/tauri-cli/Cargo.toml @@ -63,7 +63,7 @@ tauri-utils = { version = "2.8.3", path = "../tauri-utils", features = [ "schema", "config-json5", "config-toml", - "html-manipulation", + "html-manipulation-2", ] } toml = "0.9" jsonschema = { version = "0.33", default-features = false } diff --git a/crates/tauri-cli/src/dev/builtin_dev_server.rs b/crates/tauri-cli/src/dev/builtin_dev_server.rs index 0492bf408d8c..414719fc2ae8 100644 --- a/crates/tauri-cli/src/dev/builtin_dev_server.rs +++ b/crates/tauri-cli/src/dev/builtin_dev_server.rs @@ -126,14 +126,14 @@ async fn ws_handler(ws: WebSocketUpgrade, state: State) -> Response } fn inject_address(html_bytes: Vec, address: &SocketAddr) -> Vec { - let document = tauri_utils::html::parse(String::from_utf8_lossy(&html_bytes).into_owned()); + let document = tauri_utils::html2::parse(String::from_utf8_lossy(&html_bytes).into_owned()); - tauri_utils::html::append_script_to_head( + tauri_utils::html2::append_script_to_head( &document, &RELOAD_SCRIPT.replace("{{reload_url}}", &format!("ws://{address}/__tauri_cli")), ); - tauri_utils::html::serialize_node(&document) + tauri_utils::html2::serialize_node(&document) } fn fs_read_scoped(path: PathBuf, scope: &Path) -> crate::Result> { diff --git a/crates/tauri-codegen/Cargo.toml b/crates/tauri-codegen/Cargo.toml index e1154e67687c..ef06b79fff3e 100644 --- a/crates/tauri-codegen/Cargo.toml +++ b/crates/tauri-codegen/Cargo.toml @@ -21,7 +21,12 @@ syn = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tauri-utils = { version = "2.8.3", path = "../tauri-utils", features = [ - "build", + "proc-macro2", + "quote", + "cargo_metadata", + "schema", + "swift-rs", + "html-manipulation-2", ] } thiserror = "2" walkdir = "2" diff --git a/crates/tauri-codegen/src/context.rs b/crates/tauri-codegen/src/context.rs index bc889bd8d4fc..0c7a25455a06 100644 --- a/crates/tauri-codegen/src/context.rs +++ b/crates/tauri-codegen/src/context.rs @@ -25,7 +25,9 @@ use tauri_utils::{ }, assets::AssetKey, config::{Config, FrontendDist, PatternKind}, - html::{inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node, NodeRef}, + html2::{ + inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node, Document, + }, platform::Target, tokens::{map_lit, str_lit}, }; @@ -44,13 +46,14 @@ pub struct ContextData { pub test: bool, } -fn inject_script_hashes(document: &NodeRef, key: &AssetKey, csp_hashes: &mut CspHashes) { - if let Ok(inline_script_elements) = document.select("script:not(:empty)") { +fn inject_script_hashes(document: &Document, key: &AssetKey, csp_hashes: &mut CspHashes) { + let inline_script_elements = document.select("script:not(:empty)"); + if !inline_script_elements.is_empty() { let mut scripts = Vec::new(); - for inline_script_el in inline_script_elements { - let script = inline_script_el.as_node().text_contents(); + for inline_script_el in inline_script_elements.iter() { + let script = inline_script_el.text(); let mut hasher = Sha256::new(); - hasher.update(tauri_utils::html::normalize_script_for_csp( + hasher.update(tauri_utils::html2::normalize_script_for_csp( script.as_bytes(), )); let hash = hasher.finalize(); @@ -111,10 +114,10 @@ fn map_isolation( let isolation_html = parse_html(String::from_utf8_lossy(input).into_owned()); // this is appended, so no need to reverse order it - tauri_utils::html::inject_codegen_isolation_script(&isolation_html); + tauri_utils::html2::inject_codegen_isolation_script(&isolation_html); // temporary workaround for windows not loading assets - tauri_utils::html::inline_isolation(&isolation_html, &dir); + tauri_utils::html2::inline_isolation(&isolation_html, &dir); inject_nonce_token( &isolation_html, @@ -125,7 +128,7 @@ fn map_isolation( csp_hashes.styles.push(iframe_style_csp_hash.clone()); - *input = isolation_html.to_string().as_bytes().to_vec() + *input = serialize_html_node(&isolation_html) } Ok(()) diff --git a/crates/tauri-codegen/src/embedded_assets.rs b/crates/tauri-codegen/src/embedded_assets.rs index ede7ee6d9310..f3d1bd2757cb 100644 --- a/crates/tauri-codegen/src/embedded_assets.rs +++ b/crates/tauri-codegen/src/embedded_assets.rs @@ -181,7 +181,7 @@ impl CspHashes { let mut hasher = Sha256::new(); hasher.update( &std::fs::read(path) - .map(|b| tauri_utils::html::normalize_script_for_csp(&b)) + .map(|b| tauri_utils::html2::normalize_script_for_csp(&b)) .map_err(|error| EmbeddedAssetsError::AssetRead { path: path.to_path_buf(), error, diff --git a/crates/tauri-utils/Cargo.toml b/crates/tauri-utils/Cargo.toml index 7e1d181ecbe5..92cb47b62ffa 100644 --- a/crates/tauri-utils/Cargo.toml +++ b/crates/tauri-utils/Cargo.toml @@ -24,6 +24,7 @@ brotli = { version = "8", optional = true, default-features = false, features = url = { version = "2", features = ["serde"] } html5ever = { version = "0.29", optional = true } kuchiki = { package = "kuchikiki", version = "0.8.8-speedreader", optional = true } +dom_query = { version = "0.25", optional = true, default-features = false } proc-macro2 = { version = "1", optional = true } quote = { version = "1", optional = true } # Our code requires at least 0.8.21 so don't change this to 0.8 @@ -77,3 +78,4 @@ config-json5 = ["json5"] config-toml = [] resources = ["walkdir"] html-manipulation = ["dep:html5ever", "dep:kuchiki"] +html-manipulation-2 = ["dep:dom_query"] diff --git a/crates/tauri-utils/src/html.rs b/crates/tauri-utils/src/html.rs index 91ddd0b3bb1a..98243f29628b 100644 --- a/crates/tauri-utils/src/html.rs +++ b/crates/tauri-utils/src/html.rs @@ -323,6 +323,7 @@ pub fn normalize_script_for_csp(input: &[u8]) -> Vec { } #[cfg(test)] +#[allow(deprecated)] mod tests { use std::io::Write; diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs new file mode 100644 index 000000000000..91f0b5a17b91 --- /dev/null +++ b/crates/tauri-utils/src/html2.rs @@ -0,0 +1,338 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +//! The module to process HTML in Tauri. + +use std::path::{Path, PathBuf}; + +use dom_query::NodeRef; +use serde::Serialize; + +#[cfg(feature = "isolation")] +use serialize_to_javascript::DefaultTemplate; + +#[cfg(feature = "isolation")] +use crate::pattern::isolation::IsolationJavascriptCodegen; +use crate::{ + assets::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN}, + config::{DisabledCspModificationKind, PatternKind}, +}; + +pub use dom_query::Document; + +/// Serializes the document to HTML. +pub fn serialize_node(document: &Document) -> Vec { + document.html().as_bytes().to_vec() +} + +/// Parses the given HTML string. +pub fn parse(html: String) -> Document { + Document::from(html.as_str()) +} + +fn with_head)>(document: &Document, f: F) { + if let Some(head) = document.head() { + f(head) + } else { + let html = document.html_root(); + html.append_html(""); + + f(document.head().unwrap()) + } +} + +fn inject_nonce(document: &Document, selector: &str, token: &str) { + let elements = document.select(selector); + for elem in elements.iter() { + // if the node already has the `nonce` attribute, skip it + if elem.attr("nonce").is_some() { + continue; + } + elem.set_attr("nonce", token); + } +} + +/// Inject nonce tokens to all scripts and styles. +pub fn inject_nonce_token( + document: &Document, + dangerous_disable_asset_csp_modification: &DisabledCspModificationKind, +) { + if dangerous_disable_asset_csp_modification.can_modify("script-src") { + inject_nonce(document, "script[src^='http']", SCRIPT_NONCE_TOKEN); + } + if dangerous_disable_asset_csp_modification.can_modify("style-src") { + inject_nonce(document, "style", STYLE_NONCE_TOKEN); + } +} + +/// Injects a content security policy to the HTML. +pub fn inject_csp(document: &Document, csp: &str) { + with_head(document, |head| { + let meta_tag = format!(r#""#); + + head.prepend_html(meta_tag.as_str()); + }); +} + +/// Injects a content security policy to the HTML. +pub fn append_script_to_head(document: &Document, script: &str) { + with_head(document, |head| { + let script_tag = format!(r#""#); + + head.prepend_html(script_tag.as_str()); + }); +} + +/// The shape of the JavaScript Pattern config +#[derive(Debug, Serialize)] +#[serde(rename_all = "lowercase", tag = "pattern")] +pub enum PatternObject { + /// Brownfield pattern. + Brownfield, + /// Isolation pattern. Recommended for security purposes. + Isolation { + /// Which `IsolationSide` this `PatternObject` is getting injected into + side: IsolationSide, + }, +} + +impl From<&PatternKind> for PatternObject { + fn from(pattern_kind: &PatternKind) -> Self { + match pattern_kind { + PatternKind::Brownfield => Self::Brownfield, + PatternKind::Isolation { .. } => Self::Isolation { + side: IsolationSide::default(), + }, + } + } +} + +/// Where the JavaScript is injected to +#[derive(Debug, Serialize, Default)] +#[serde(rename_all = "lowercase")] +pub enum IsolationSide { + /// Original frame, the Brownfield application + #[default] + Original, + /// Secure frame, the isolation security application + Secure, +} + +/// Injects the Isolation JavaScript to a codegen time document. +/// +/// Note: This function is not considered part of the stable API. +#[cfg(feature = "isolation")] +pub fn inject_codegen_isolation_script(document: &Document) { + with_head(document, |head| { + let script_content = IsolationJavascriptCodegen {} + .render_default(&Default::default()) + .expect("unable to render codegen isolation script template") + .into_string(); + + let script_tag = format!( + r#""#, + SCRIPT_NONCE_TOKEN, script_content + ); + + head.prepend_html(script_tag.as_str()); + }); +} + +/// Temporary workaround for Windows not allowing requests +/// +/// Note: this does not prevent path traversal due to the isolation application expectation that it +/// is secure. +pub fn inline_isolation(document: &Document, dir: &Path) { + let scripts = document.select("script[src]"); + + for script in scripts.iter() { + let src = match script.attr("src") { + Some(s) => s.to_string(), + None => continue, + }; + + let mut path = PathBuf::from(src); + if path.has_root() { + path = path + .strip_prefix("/") + .expect("Tauri \"Isolation\" Pattern only supports relative or absolute (`/`) paths.") + .into(); + } + + let file = std::fs::read_to_string(dir.join(path)).expect("unable to find isolation file"); + + script.set_html(file.as_str()); + script.remove_attr("src"); + } +} + +/// Normalize line endings in script content to match what the browser uses for CSP hashing. +/// +/// According to the HTML spec, browsers normalize: +/// - `\r\n` → `\n` +/// - `\r` → `\n` +pub fn normalize_script_for_csp(input: &[u8]) -> Vec { + let mut output = Vec::with_capacity(input.len()); + + let mut i = 0; + while i < input.len() { + match input[i] { + b'\r' => { + if i + 1 < input.len() && input[i + 1] == b'\n' { + // CRLF → LF + output.push(b'\n'); + i += 2; + } else { + // Lone CR → LF + output.push(b'\n'); + i += 1; + } + } + _ => { + output.push(input[i]); + i += 1; + } + } + } + + output +} + +#[cfg(test)] +mod tests { + use std::io::Write; + + use super::*; + use crate::{ + assets::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN}, + config, + }; + + #[test] + fn csp() { + let htmls = vec![ + "".to_string(), + "".to_string(), + ]; + + for html in htmls { + let document = parse(html); + let csp = "csp-string"; + inject_csp(&document, csp); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + format!( + r#""# + ) + ); + } + } + + #[test] + fn normalize_script_for_csp_test() { + let js = "// Copyright 2019-2024 Tauri Programme within The Commons Conservancy\r// SPDX-License-Identifier: Apache-2.0\n// SPDX-License-Identifier: MIT\r\n\r\nwindow.__TAURI_ISOLATION_HOOK__ = (payload, options) => {\r\n return payload\r\n}\r\n"; + let expected = "// Copyright 2019-2024 Tauri Programme within The Commons Conservancy\n// SPDX-License-Identifier: Apache-2.0\n// SPDX-License-Identifier: MIT\n\nwindow.__TAURI_ISOLATION_HOOK__ = (payload, options) => {\n return payload\n}\n"; + + assert_eq!(normalize_script_for_csp(js.as_bytes()), expected.as_bytes()) + } + + #[test] + fn parse_and_serialize_roundtrips() { + let htmls = [ + "Test

Hello

", + "", + ]; + + for html in htmls { + let parsed = parse(html.to_string()); + let serialized = serialize_node(&parsed); + let result = String::from_utf8(serialized).unwrap(); + + assert_eq!(result, html); + } + } + + #[test] + fn inject_nonce_to_scripts() { + let html = r#""#; + + let document = parse(html.to_string()); + inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(false)); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + format!( + r#""# + ) + ); + } + + #[test] + fn inject_nonce_to_styles() { + let html = r#""#; + + let document = parse(html.to_string()); + inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(false)); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + format!( + r#""# + ) + ); + } + + #[test] + fn append_script_to_head_test() { + let html = r#""#; + + let document = parse(html.to_string()); + append_script_to_head(&document, r#"console.log('Test')"#); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + format!(r#""#) + ); + } + + #[test] + fn inject_nonce_skips_existing() { + let html = r#""#; + + let document = parse(html.to_string()); + inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(false)); + + assert_eq!(String::from_utf8(serialize_node(&document)).unwrap(), html); + } + + #[test] + fn inject_nonce_respects_disabled_modification() { + let html = r#""#; + + let document = parse(html.to_string()); + inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(true)); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + r#""# + ); + } + + #[test] + fn inline_isolation_replaces_src_with_content() { + let temp_dir = tempfile::tempdir().unwrap(); + let mut file = tempfile::tempfile_in(&temp_dir).unwrap(); + file.write_all(b"console.log('test');").unwrap(); + + let html = r#""#; + let document = parse(html.to_string()); + inline_isolation(&document, temp_dir.path()); + + assert_eq!( + String::from_utf8(serialize_node(&document)).unwrap(), + r#""# + ); + } +} diff --git a/crates/tauri-utils/src/lib.rs b/crates/tauri-utils/src/lib.rs index 25e5a4f97db7..c925561babbd 100644 --- a/crates/tauri-utils/src/lib.rs +++ b/crates/tauri-utils/src/lib.rs @@ -25,7 +25,10 @@ pub mod assets; pub mod config; pub mod config_v1; #[cfg(feature = "html-manipulation")] +#[deprecated] pub mod html; +#[cfg(feature = "html-manipulation-2")] +pub mod html2; pub mod io; pub mod mime_type; pub mod platform; diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml index cdc4bc697197..6cb3211ca49c 100644 --- a/crates/tauri/Cargo.toml +++ b/crates/tauri/Cargo.toml @@ -167,7 +167,11 @@ glob = "0.3" heck = "0.5" tauri-build = { path = "../tauri-build/", default-features = false, version = "2.5.6" } tauri-utils = { path = "../tauri-utils/", version = "2.8.3", features = [ - "build", + "isolation", + "schema", + "config-json5", + "config-toml", + "html-manipulation-2", ] } [dev-dependencies] diff --git a/crates/tauri/src/manager/webview.rs b/crates/tauri/src/manager/webview.rs index 98c470b76e60..5aefbdcbff64 100644 --- a/crates/tauri/src/manager/webview.rs +++ b/crates/tauri/src/manager/webview.rs @@ -460,9 +460,9 @@ impl WebviewManager { let html = String::from_utf8_lossy(&body).into_owned(); // naive way to check if it's an html if html.contains('<') && html.contains('>') { - let document = tauri_utils::html::parse(html); - tauri_utils::html::inject_csp(&document, &csp.to_string()); - url.set_path(&format!("{},{document}", mime::TEXT_HTML)); + let document = tauri_utils::html2::parse(html); + tauri_utils::html2::inject_csp(&document, &csp.to_string()); + url.set_path(&format!("{},{}", mime::TEXT_HTML, document.html())); } } } diff --git a/crates/tests/acl/Cargo.toml b/crates/tests/acl/Cargo.toml index 0fe970b9990d..8481ee6e2c61 100644 --- a/crates/tests/acl/Cargo.toml +++ b/crates/tests/acl/Cargo.toml @@ -11,6 +11,12 @@ rust-version.workspace = true publish = false [dev-dependencies] -tauri-utils = { path = "../../tauri-utils/", features = ["build"] } +tauri-utils = { path = "../../tauri-utils/", features = [ + "isolation", + "schema", + "config-json5", + "config-toml", + "html-manipulation-2", +] } serde_json = "1" insta = "1" From d38e3a404a7837dda02734f2aadb8a210973ba21 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 23 Feb 2026 22:11:49 +1100 Subject: [PATCH 04/33] Remove deprecation --- crates/tauri-utils/src/html.rs | 1 - crates/tauri-utils/src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/crates/tauri-utils/src/html.rs b/crates/tauri-utils/src/html.rs index 98243f29628b..91ddd0b3bb1a 100644 --- a/crates/tauri-utils/src/html.rs +++ b/crates/tauri-utils/src/html.rs @@ -323,7 +323,6 @@ pub fn normalize_script_for_csp(input: &[u8]) -> Vec { } #[cfg(test)] -#[allow(deprecated)] mod tests { use std::io::Write; diff --git a/crates/tauri-utils/src/lib.rs b/crates/tauri-utils/src/lib.rs index c925561babbd..970a829c00f9 100644 --- a/crates/tauri-utils/src/lib.rs +++ b/crates/tauri-utils/src/lib.rs @@ -25,7 +25,6 @@ pub mod assets; pub mod config; pub mod config_v1; #[cfg(feature = "html-manipulation")] -#[deprecated] pub mod html; #[cfg(feature = "html-manipulation-2")] pub mod html2; From 0a16744b50678bc8a84bdd01bfd27353ceefa3a2 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 23 Feb 2026 22:11:55 +1100 Subject: [PATCH 05/33] Use new feature flag --- crates/tauri/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml index 6cb3211ca49c..76290973c3a2 100644 --- a/crates/tauri/Cargo.toml +++ b/crates/tauri/Cargo.toml @@ -216,7 +216,7 @@ macos-private-api = [ "tauri-runtime/macos-private-api", "tauri-runtime-wry?/macos-private-api", ] -webview-data-url = ["data-url", "tauri-utils/html-manipulation"] +webview-data-url = ["data-url", "tauri-utils/html-manipulation-2"] protocol-asset = ["http-range"] config-json5 = ["tauri-macros/config-json5"] config-toml = ["tauri-macros/config-toml"] From 01d3b1e5a71eba66843ad411e42548ba2f0230bf Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 23 Feb 2026 22:33:51 +1100 Subject: [PATCH 06/33] Unroll `build` feature --- crates/tauri-build/Cargo.toml | 7 ++++++- crates/tauri-plugin/Cargo.toml | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/tauri-build/Cargo.toml b/crates/tauri-build/Cargo.toml index e12c041471b9..bfff9b4db9d1 100644 --- a/crates/tauri-build/Cargo.toml +++ b/crates/tauri-build/Cargo.toml @@ -28,7 +28,12 @@ anyhow = "1" quote = { version = "1", optional = true } tauri-codegen = { version = "2.5.5", path = "../tauri-codegen", optional = true } tauri-utils = { version = "2.8.3", path = "../tauri-utils", features = [ - "build", + "proc-macro2", + "quote", + "cargo_metadata", + "schema", + "swift-rs", + "html-manipulation-2", "resources", ] } cargo_toml = "0.22" diff --git a/crates/tauri-plugin/Cargo.toml b/crates/tauri-plugin/Cargo.toml index c14998107a9e..bc0154c85613 100644 --- a/crates/tauri-plugin/Cargo.toml +++ b/crates/tauri-plugin/Cargo.toml @@ -29,7 +29,12 @@ runtime = [] anyhow = { version = "1", optional = true } serde = { version = "1", optional = true } tauri-utils = { version = "2.8.3", default-features = false, features = [ - "build", + "proc-macro2", + "quote", + "cargo_metadata", + "schema", + "swift-rs", + "html-manipulation-2", ], path = "../tauri-utils" } serde_json = { version = "1", optional = true } glob = { version = "0.3", optional = true } From b34aa50eb3479e6cf826e85bcd055649d538131d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 24 Feb 2026 15:18:47 +1100 Subject: [PATCH 07/33] Introduce `build-2` feature --- crates/tauri-build/Cargo.toml | 9 ++------- crates/tauri-codegen/Cargo.toml | 7 +------ crates/tauri-plugin/Cargo.toml | 7 +------ crates/tauri-utils/Cargo.toml | 8 ++++++++ crates/tauri-utils/src/acl/mod.rs | 2 +- crates/tests/acl/Cargo.toml | 6 +----- 6 files changed, 14 insertions(+), 25 deletions(-) diff --git a/crates/tauri-build/Cargo.toml b/crates/tauri-build/Cargo.toml index bfff9b4db9d1..b31e0dc542d2 100644 --- a/crates/tauri-build/Cargo.toml +++ b/crates/tauri-build/Cargo.toml @@ -28,13 +28,8 @@ anyhow = "1" quote = { version = "1", optional = true } tauri-codegen = { version = "2.5.5", path = "../tauri-codegen", optional = true } tauri-utils = { version = "2.8.3", path = "../tauri-utils", features = [ - "proc-macro2", - "quote", - "cargo_metadata", - "schema", - "swift-rs", - "html-manipulation-2", - "resources", + "build-2", + "resources" ] } cargo_toml = "0.22" serde = "1" diff --git a/crates/tauri-codegen/Cargo.toml b/crates/tauri-codegen/Cargo.toml index ef06b79fff3e..78de83cf0791 100644 --- a/crates/tauri-codegen/Cargo.toml +++ b/crates/tauri-codegen/Cargo.toml @@ -21,12 +21,7 @@ syn = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tauri-utils = { version = "2.8.3", path = "../tauri-utils", features = [ - "proc-macro2", - "quote", - "cargo_metadata", - "schema", - "swift-rs", - "html-manipulation-2", + "build-2", ] } thiserror = "2" walkdir = "2" diff --git a/crates/tauri-plugin/Cargo.toml b/crates/tauri-plugin/Cargo.toml index bc0154c85613..3c4077647dba 100644 --- a/crates/tauri-plugin/Cargo.toml +++ b/crates/tauri-plugin/Cargo.toml @@ -29,12 +29,7 @@ runtime = [] anyhow = { version = "1", optional = true } serde = { version = "1", optional = true } tauri-utils = { version = "2.8.3", default-features = false, features = [ - "proc-macro2", - "quote", - "cargo_metadata", - "schema", - "swift-rs", - "html-manipulation-2", + "build-2", ], path = "../tauri-utils" } serde_json = { version = "1", optional = true } glob = { version = "0.3", optional = true } diff --git a/crates/tauri-utils/Cargo.toml b/crates/tauri-utils/Cargo.toml index 92cb47b62ffa..fc57862d4d4d 100644 --- a/crates/tauri-utils/Cargo.toml +++ b/crates/tauri-utils/Cargo.toml @@ -70,6 +70,14 @@ build = [ "swift-rs", "html-manipulation", ] +build-2 = [ + "proc-macro2", + "quote", + "cargo_metadata", + "schema", + "swift-rs", + "html-manipulation-2", +] compression = ["brotli"] schema = ["schemars"] isolation = ["aes-gcm", "getrandom", "serialize-to-javascript"] diff --git a/crates/tauri-utils/src/acl/mod.rs b/crates/tauri-utils/src/acl/mod.rs index 03c33d4fdc4c..5bd3964aa8bb 100644 --- a/crates/tauri-utils/src/acl/mod.rs +++ b/crates/tauri-utils/src/acl/mod.rs @@ -58,7 +58,7 @@ pub const ALLOWED_COMMANDS_FILE_NAME: &str = "allowed-commands.json"; /// the value is set to the config's directory pub const REMOVE_UNUSED_COMMANDS_ENV_VAR: &str = "REMOVE_UNUSED_COMMANDS"; -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] pub mod build; pub mod capability; pub mod identifier; diff --git a/crates/tests/acl/Cargo.toml b/crates/tests/acl/Cargo.toml index 8481ee6e2c61..28d49af9bac7 100644 --- a/crates/tests/acl/Cargo.toml +++ b/crates/tests/acl/Cargo.toml @@ -12,11 +12,7 @@ publish = false [dev-dependencies] tauri-utils = { path = "../../tauri-utils/", features = [ - "isolation", - "schema", - "config-json5", - "config-toml", - "html-manipulation-2", + "build-2", ] } serde_json = "1" insta = "1" From 36790ad4ab7662f04a8f30ed345044bd5e39abac Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 24 Feb 2026 15:20:57 +1100 Subject: [PATCH 08/33] Reduce diff --- crates/tauri-build/Cargo.toml | 2 +- crates/tauri/Cargo.toml | 6 +----- crates/tests/acl/Cargo.toml | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/crates/tauri-build/Cargo.toml b/crates/tauri-build/Cargo.toml index b31e0dc542d2..700dc6f028d9 100644 --- a/crates/tauri-build/Cargo.toml +++ b/crates/tauri-build/Cargo.toml @@ -29,7 +29,7 @@ quote = { version = "1", optional = true } tauri-codegen = { version = "2.5.5", path = "../tauri-codegen", optional = true } tauri-utils = { version = "2.8.3", path = "../tauri-utils", features = [ "build-2", - "resources" + "resources", ] } cargo_toml = "0.22" serde = "1" diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml index 76290973c3a2..669a92ece21a 100644 --- a/crates/tauri/Cargo.toml +++ b/crates/tauri/Cargo.toml @@ -167,11 +167,7 @@ glob = "0.3" heck = "0.5" tauri-build = { path = "../tauri-build/", default-features = false, version = "2.5.6" } tauri-utils = { path = "../tauri-utils/", version = "2.8.3", features = [ - "isolation", - "schema", - "config-json5", - "config-toml", - "html-manipulation-2", + "build-2", ] } [dev-dependencies] diff --git a/crates/tests/acl/Cargo.toml b/crates/tests/acl/Cargo.toml index 28d49af9bac7..9e4f45b903b7 100644 --- a/crates/tests/acl/Cargo.toml +++ b/crates/tests/acl/Cargo.toml @@ -11,8 +11,6 @@ rust-version.workspace = true publish = false [dev-dependencies] -tauri-utils = { path = "../../tauri-utils/", features = [ - "build-2", -] } +tauri-utils = { path = "../../tauri-utils/", features = ["build-2"] } serde_json = "1" insta = "1" From 8de523820aa74da298e7cb739fed194809ebdd68 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 24 Feb 2026 15:29:17 +1100 Subject: [PATCH 09/33] Use `build-2` in more places --- crates/tauri-utils/src/acl/capability.rs | 2 +- crates/tauri-utils/src/acl/identifier.rs | 2 +- crates/tauri-utils/src/acl/manifest.rs | 2 +- crates/tauri-utils/src/acl/mod.rs | 4 ++-- crates/tauri-utils/src/acl/resolved.rs | 2 +- crates/tauri-utils/src/acl/value.rs | 2 +- crates/tauri-utils/src/config.rs | 2 +- crates/tauri-utils/src/lib.rs | 4 ++-- crates/tauri-utils/src/platform.rs | 2 +- crates/tauri-utils/src/plugin.rs | 4 ++-- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/tauri-utils/src/acl/capability.rs b/crates/tauri-utils/src/acl/capability.rs index 4e7417d11c4e..33a2c60c78ac 100644 --- a/crates/tauri-utils/src/acl/capability.rs +++ b/crates/tauri-utils/src/acl/capability.rs @@ -322,7 +322,7 @@ impl FromStr for CapabilityFile { } } -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] mod build { use std::convert::identity; diff --git a/crates/tauri-utils/src/acl/identifier.rs b/crates/tauri-utils/src/acl/identifier.rs index bb571c9fd326..26c7326d2c6d 100644 --- a/crates/tauri-utils/src/acl/identifier.rs +++ b/crates/tauri-utils/src/acl/identifier.rs @@ -283,7 +283,7 @@ mod tests { } } -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] mod build { use proc_macro2::TokenStream; use quote::{quote, ToTokens, TokenStreamExt}; diff --git a/crates/tauri-utils/src/acl/manifest.rs b/crates/tauri-utils/src/acl/manifest.rs index 8c4eed0fda92..ff18df825d3a 100644 --- a/crates/tauri-utils/src/acl/manifest.rs +++ b/crates/tauri-utils/src/acl/manifest.rs @@ -126,7 +126,7 @@ impl Manifest { } } -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] mod build { use proc_macro2::TokenStream; use quote::{quote, ToTokens, TokenStreamExt}; diff --git a/crates/tauri-utils/src/acl/mod.rs b/crates/tauri-utils/src/acl/mod.rs index 5bd3964aa8bb..b9bb6a2d0ebc 100644 --- a/crates/tauri-utils/src/acl/mod.rs +++ b/crates/tauri-utils/src/acl/mod.rs @@ -104,7 +104,7 @@ pub enum Error { CreateDir(std::io::Error, PathBuf), /// [`cargo_metadata`] was not able to complete successfully - #[cfg(feature = "build")] + #[cfg(any(feature = "build", feature = "build-2"))] #[error("failed to execute: {0}")] Metadata(#[from] ::cargo_metadata::Error), @@ -460,7 +460,7 @@ mod tests { } } -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] mod build_ { use std::convert::identity; diff --git a/crates/tauri-utils/src/acl/resolved.rs b/crates/tauri-utils/src/acl/resolved.rs index 941019822a00..8aa4322f5640 100644 --- a/crates/tauri-utils/src/acl/resolved.rs +++ b/crates/tauri-utils/src/acl/resolved.rs @@ -438,7 +438,7 @@ fn display_perm_key(prefix: &str) -> &str { } } -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] mod build { use proc_macro2::TokenStream; use quote::{quote, ToTokens, TokenStreamExt}; diff --git a/crates/tauri-utils/src/acl/value.rs b/crates/tauri-utils/src/acl/value.rs index 34c7efc488a6..24ac91eb7382 100644 --- a/crates/tauri-utils/src/acl/value.rs +++ b/crates/tauri-utils/src/acl/value.rs @@ -145,7 +145,7 @@ impl From for Value { } } -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] mod build { use std::convert::identity; diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index 78f0ed3eceda..e2b0d32f9bb6 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -3336,7 +3336,7 @@ pub struct PluginConfig(pub HashMap); /// This allows for a build script to output the values in a `Config` to a `TokenStream`, which can /// then be consumed by another crate. Useful for passing a config to both the build script and the /// application using tauri while only parsing it once (in the build script). -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] mod build { use super::*; use crate::{literal_struct, tokens::*}; diff --git a/crates/tauri-utils/src/lib.rs b/crates/tauri-utils/src/lib.rs index 970a829c00f9..3b2a31ef59ac 100644 --- a/crates/tauri-utils/src/lib.rs +++ b/crates/tauri-utils/src/lib.rs @@ -35,10 +35,10 @@ pub mod plugin; /// Prepare application resources and sidecars. #[cfg(feature = "resources")] pub mod resources; -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] pub mod tokens; -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] pub mod build; /// Application pattern. diff --git a/crates/tauri-utils/src/platform.rs b/crates/tauri-utils/src/platform.rs index 4ce3d2fac403..a83201bdc4a5 100644 --- a/crates/tauri-utils/src/platform.rs +++ b/crates/tauri-utils/src/platform.rs @@ -369,7 +369,7 @@ pub fn bundle_type() -> Option { } } -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] mod build { use proc_macro2::TokenStream; use quote::{quote, ToTokens, TokenStreamExt}; diff --git a/crates/tauri-utils/src/plugin.rs b/crates/tauri-utils/src/plugin.rs index 8f178ca4163a..9fe35be2dfc4 100644 --- a/crates/tauri-utils/src/plugin.rs +++ b/crates/tauri-utils/src/plugin.rs @@ -3,10 +3,10 @@ // SPDX-License-Identifier: MIT //! Compile-time and runtime types for Tauri plugins. -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] pub use build::*; -#[cfg(feature = "build")] +#[cfg(any(feature = "build", feature = "build-2"))] mod build { use std::{ env::vars_os, From 25499ab8fc1d3f8a655577a5c1b2d9b2912f5672 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sun, 1 Mar 2026 16:57:33 +1100 Subject: [PATCH 10/33] Add docs --- crates/tauri-utils/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/tauri-utils/Cargo.toml b/crates/tauri-utils/Cargo.toml index fc57862d4d4d..58fd355a353b 100644 --- a/crates/tauri-utils/Cargo.toml +++ b/crates/tauri-utils/Cargo.toml @@ -70,6 +70,7 @@ build = [ "swift-rs", "html-manipulation", ] +# Same as `build` but uses `html-manipulation-2` to avoid the `kuchikiki` dependency. build-2 = [ "proc-macro2", "quote", From 2f125902101afafe0ad09ba69c9e83865e12a2e3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sun, 1 Mar 2026 17:02:29 +1100 Subject: [PATCH 11/33] Refactor `inject_script_hashes` --- crates/tauri-codegen/src/context.rs | 39 +++++++++++++---------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/crates/tauri-codegen/src/context.rs b/crates/tauri-codegen/src/context.rs index 0c7a25455a06..9532a42e7f17 100644 --- a/crates/tauri-codegen/src/context.rs +++ b/crates/tauri-codegen/src/context.rs @@ -47,27 +47,24 @@ pub struct ContextData { } fn inject_script_hashes(document: &Document, key: &AssetKey, csp_hashes: &mut CspHashes) { - let inline_script_elements = document.select("script:not(:empty)"); - if !inline_script_elements.is_empty() { - let mut scripts = Vec::new(); - for inline_script_el in inline_script_elements.iter() { - let script = inline_script_el.text(); - let mut hasher = Sha256::new(); - hasher.update(tauri_utils::html2::normalize_script_for_csp( - script.as_bytes(), - )); - let hash = hasher.finalize(); - scripts.push(format!( - "'sha256-{}'", - base64::engine::general_purpose::STANDARD.encode(hash) - )); - } - csp_hashes - .inline_scripts - .entry(key.clone().into()) - .or_default() - .append(&mut scripts); - } + let script_elements = document.select("script:not(:empty)"); + + let scripts = script_elements + .iter() + .map(|element| { + let script = tauri_utils::html2::normalize_script_for_csp(element.text().as_bytes()); + let script_hash = Sha256::digest(script); + let hash_base64 = base64::engine::general_purpose::STANDARD.encode(script_hash); + + format!("'sha256-{hash_base64}'") + }) + .collect::>(); + + csp_hashes + .inline_scripts + .entry(key.clone().into()) + .or_default() + .extend(scripts); } fn map_core_assets( From 3cfdf396662541664bc28d6f98e5d473cc1015db Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sun, 1 Mar 2026 17:03:56 +1100 Subject: [PATCH 12/33] Refactor `with_head` --- crates/tauri-utils/src/html2.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index 91f0b5a17b91..ae1cc9191b6c 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -32,14 +32,14 @@ pub fn parse(html: String) -> Document { } fn with_head)>(document: &Document, f: F) { - if let Some(head) = document.head() { - f(head) - } else { + let head = document.head().unwrap_or_else(|| { let html = document.html_root(); - html.append_html(""); + let head = document.tree.new_element("head"); + html.prepend_child(&head); + head + }); - f(document.head().unwrap()) - } + f(head) } fn inject_nonce(document: &Document, selector: &str, token: &str) { From 395048cc4544824c7e3701f412df3e61fed4615a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sun, 1 Mar 2026 17:05:11 +1100 Subject: [PATCH 13/33] Rename serialize and parse functions --- .../tauri-cli/src/dev/builtin_dev_server.rs | 4 +-- crates/tauri-codegen/src/context.rs | 2 +- crates/tauri-utils/src/html2.rs | 36 +++++++++---------- crates/tauri/src/manager/webview.rs | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/crates/tauri-cli/src/dev/builtin_dev_server.rs b/crates/tauri-cli/src/dev/builtin_dev_server.rs index 414719fc2ae8..292b7459dd10 100644 --- a/crates/tauri-cli/src/dev/builtin_dev_server.rs +++ b/crates/tauri-cli/src/dev/builtin_dev_server.rs @@ -126,14 +126,14 @@ async fn ws_handler(ws: WebSocketUpgrade, state: State) -> Response } fn inject_address(html_bytes: Vec, address: &SocketAddr) -> Vec { - let document = tauri_utils::html2::parse(String::from_utf8_lossy(&html_bytes).into_owned()); + let document = tauri_utils::html2::parse_doc(String::from_utf8_lossy(&html_bytes).into_owned()); tauri_utils::html2::append_script_to_head( &document, &RELOAD_SCRIPT.replace("{{reload_url}}", &format!("ws://{address}/__tauri_cli")), ); - tauri_utils::html2::serialize_node(&document) + tauri_utils::html2::serialize_doc(&document) } fn fs_read_scoped(path: PathBuf, scope: &Path) -> crate::Result> { diff --git a/crates/tauri-codegen/src/context.rs b/crates/tauri-codegen/src/context.rs index 9532a42e7f17..8674ea37cdeb 100644 --- a/crates/tauri-codegen/src/context.rs +++ b/crates/tauri-codegen/src/context.rs @@ -26,7 +26,7 @@ use tauri_utils::{ assets::AssetKey, config::{Config, FrontendDist, PatternKind}, html2::{ - inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node, Document, + inject_nonce_token, parse_doc as parse_html, serialize_doc as serialize_html_node, Document, }, platform::Target, tokens::{map_lit, str_lit}, diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index ae1cc9191b6c..f073d0920d77 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -22,12 +22,12 @@ use crate::{ pub use dom_query::Document; /// Serializes the document to HTML. -pub fn serialize_node(document: &Document) -> Vec { +pub fn serialize_doc(document: &Document) -> Vec { document.html().as_bytes().to_vec() } /// Parses the given HTML string. -pub fn parse(html: String) -> Document { +pub fn parse_doc(html: String) -> Document { Document::from(html.as_str()) } @@ -217,12 +217,12 @@ mod tests { ]; for html in htmls { - let document = parse(html); + let document = parse_doc(html); let csp = "csp-string"; inject_csp(&document, csp); assert_eq!( - String::from_utf8(serialize_node(&document)).unwrap(), + String::from_utf8(serialize_doc(&document)).unwrap(), format!( r#""# ) @@ -246,8 +246,8 @@ mod tests { ]; for html in htmls { - let parsed = parse(html.to_string()); - let serialized = serialize_node(&parsed); + let parsed = parse_doc(html.to_string()); + let serialized = serialize_doc(&parsed); let result = String::from_utf8(serialized).unwrap(); assert_eq!(result, html); @@ -258,11 +258,11 @@ mod tests { fn inject_nonce_to_scripts() { let html = r#""#; - let document = parse(html.to_string()); + let document = parse_doc(html.to_string()); inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(false)); assert_eq!( - String::from_utf8(serialize_node(&document)).unwrap(), + String::from_utf8(serialize_doc(&document)).unwrap(), format!( r#""# ) @@ -273,11 +273,11 @@ mod tests { fn inject_nonce_to_styles() { let html = r#""#; - let document = parse(html.to_string()); + let document = parse_doc(html.to_string()); inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(false)); assert_eq!( - String::from_utf8(serialize_node(&document)).unwrap(), + String::from_utf8(serialize_doc(&document)).unwrap(), format!( r#""# ) @@ -288,11 +288,11 @@ mod tests { fn append_script_to_head_test() { let html = r#""#; - let document = parse(html.to_string()); + let document = parse_doc(html.to_string()); append_script_to_head(&document, r#"console.log('Test')"#); assert_eq!( - String::from_utf8(serialize_node(&document)).unwrap(), + String::from_utf8(serialize_doc(&document)).unwrap(), format!(r#""#) ); } @@ -301,21 +301,21 @@ mod tests { fn inject_nonce_skips_existing() { let html = r#""#; - let document = parse(html.to_string()); + let document = parse_doc(html.to_string()); inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(false)); - assert_eq!(String::from_utf8(serialize_node(&document)).unwrap(), html); + assert_eq!(String::from_utf8(serialize_doc(&document)).unwrap(), html); } #[test] fn inject_nonce_respects_disabled_modification() { let html = r#""#; - let document = parse(html.to_string()); + let document = parse_doc(html.to_string()); inject_nonce_token(&document, &config::DisabledCspModificationKind::Flag(true)); assert_eq!( - String::from_utf8(serialize_node(&document)).unwrap(), + String::from_utf8(serialize_doc(&document)).unwrap(), r#""# ); } @@ -327,11 +327,11 @@ mod tests { file.write_all(b"console.log('test');").unwrap(); let html = r#""#; - let document = parse(html.to_string()); + let document = parse_doc(html.to_string()); inline_isolation(&document, temp_dir.path()); assert_eq!( - String::from_utf8(serialize_node(&document)).unwrap(), + String::from_utf8(serialize_doc(&document)).unwrap(), r#""# ); } diff --git a/crates/tauri/src/manager/webview.rs b/crates/tauri/src/manager/webview.rs index 5aefbdcbff64..4845f4fa6e99 100644 --- a/crates/tauri/src/manager/webview.rs +++ b/crates/tauri/src/manager/webview.rs @@ -460,7 +460,7 @@ impl WebviewManager { let html = String::from_utf8_lossy(&body).into_owned(); // naive way to check if it's an html if html.contains('<') && html.contains('>') { - let document = tauri_utils::html2::parse(html); + let document = tauri_utils::html2::parse_doc(html); tauri_utils::html2::inject_csp(&document, &csp.to_string()); url.set_path(&format!("{},{}", mime::TEXT_HTML, document.html())); } From b7ebae8fc9d5cc454a4f8102182453dea65c5d07 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 2 Mar 2026 10:26:57 +1100 Subject: [PATCH 14/33] Add changes file --- .changes/supersede-kuchikiki.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/supersede-kuchikiki.md diff --git a/.changes/supersede-kuchikiki.md b/.changes/supersede-kuchikiki.md new file mode 100644 index 000000000000..8ad0de57c1bb --- /dev/null +++ b/.changes/supersede-kuchikiki.md @@ -0,0 +1,6 @@ +--- +"tauri-utils": minor:enhance +--- + +Add new `html-manipulation-2` and `build-2` feature flags that use `dom_query` instead of `kuchikiki` for HTML parsing / manipulation. +This allows downstream users to remove `kuchikiki` and its dependencies from their dependency tree. From f86f8744d0bd5bfb2c3d99c7036374ad804e88b9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 10 Mar 2026 15:03:24 +1100 Subject: [PATCH 15/33] Remove unused function --- crates/tauri-utils/src/html.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/crates/tauri-utils/src/html.rs b/crates/tauri-utils/src/html.rs index 91ddd0b3bb1a..8e7c962843ab 100644 --- a/crates/tauri-utils/src/html.rs +++ b/crates/tauri-utils/src/html.rs @@ -164,15 +164,6 @@ pub fn inject_csp(document: &NodeRef, csp: &str) { }); } -/// Injects a content security policy to the HTML. -pub fn append_script_to_head(document: &NodeRef, script: &str) { - with_head(document, |head| { - let script_el = NodeRef::new_element(QualName::new(None, ns!(html), "script".into()), None); - script_el.append(NodeRef::new_text(script)); - head.prepend(script_el); - }); -} - fn create_csp_meta_tag(csp: &str) -> NodeRef { NodeRef::new_element( QualName::new(None, ns!(html), LocalName::from("meta")), From 6f2ce5cc4095388abc58db9aa1a0cf253480be6b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 10 Mar 2026 15:04:15 +1100 Subject: [PATCH 16/33] Update changelog --- .changes/supersede-kuchikiki.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/supersede-kuchikiki.md b/.changes/supersede-kuchikiki.md index 8ad0de57c1bb..18974ff48ab8 100644 --- a/.changes/supersede-kuchikiki.md +++ b/.changes/supersede-kuchikiki.md @@ -1,5 +1,5 @@ --- -"tauri-utils": minor:enhance +"tauri-utils": minor:deps --- Add new `html-manipulation-2` and `build-2` feature flags that use `dom_query` instead of `kuchikiki` for HTML parsing / manipulation. From 445394aa3d6f7c2e0b700a91cdf7d4bb111b1216 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 10 Mar 2026 15:20:41 +1100 Subject: [PATCH 17/33] Remove test --- crates/tauri-utils/src/html.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/crates/tauri-utils/src/html.rs b/crates/tauri-utils/src/html.rs index 8e7c962843ab..3335743dd2d6 100644 --- a/crates/tauri-utils/src/html.rs +++ b/crates/tauri-utils/src/html.rs @@ -398,19 +398,6 @@ mod tests { ); } - #[test] - fn append_script_to_head_test() { - let html = r#""#; - - let document = parse(html.to_string()); - append_script_to_head(&document, r#"console.log('Test')"#); - - assert_eq!( - String::from_utf8(serialize_node(&document)).unwrap(), - format!(r#""#) - ); - } - #[test] fn inject_nonce_skips_existing() { let html = r#""#; From c25c7671dd98d806336dfa9f5d70fb7b8c1cd689 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 14 Mar 2026 22:19:42 +0800 Subject: [PATCH 18/33] Update wry --- Cargo.lock | 53 ++++++++++++++--------------- crates/tauri-runtime-wry/Cargo.toml | 2 +- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a562551d628..f0fabdeea5dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -819,7 +819,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.4", ] [[package]] @@ -1320,7 +1320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2006,7 +2006,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -4383,7 +4383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -4717,7 +4717,7 @@ dependencies = [ "gtk", "keyboard-types", "libxdo", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", @@ -5122,9 +5122,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", "objc2-exception-helper", @@ -5138,7 +5138,7 @@ checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" dependencies = [ "bitflags 2.7.0", "block2 0.6.0", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.0", ] @@ -5150,7 +5150,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" dependencies = [ "bitflags 2.7.0", - "objc2 0.6.0", + "objc2 0.6.4", ] [[package]] @@ -5198,7 +5198,7 @@ checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" dependencies = [ "bitflags 2.7.0", "block2 0.6.0", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-core-foundation", ] @@ -5234,7 +5234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1" dependencies = [ "bitflags 2.7.0", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.0", ] @@ -5247,7 +5247,7 @@ checksum = "b717127e4014b0f9f3e8bba3d3f2acec81f1bde01f656823036e823ed2c94dce" dependencies = [ "bitflags 2.7.0", "block2 0.6.0", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", @@ -5829,7 +5829,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.22.1", + "base64 0.21.7", "bitfield", "block-padding", "blowfish", @@ -7261,7 +7261,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -8548,7 +8548,7 @@ dependencies = [ "ndk", "ndk-context", "ndk-sys", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-foundation 0.3.0", "once_cell", @@ -8622,7 +8622,7 @@ dependencies = [ "log", "mime", "muda", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-foundation 0.3.0", "objc2-ui-kit", @@ -8943,7 +8943,7 @@ dependencies = [ "byte-unit", "fern", "log", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-foundation 0.3.0", "serde", "serde_json", @@ -8975,7 +8975,7 @@ dependencies = [ "gtk", "http 1.3.1", "jni", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-ui-kit", "objc2-web-kit", "raw-window-handle", @@ -8997,7 +8997,7 @@ dependencies = [ "http 1.3.1", "jni", "log", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "once_cell", "percent-encoding", @@ -9619,7 +9619,7 @@ dependencies = [ "dirs 6.0.0", "libappindicator", "muda", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", @@ -10437,7 +10437,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -10452,7 +10452,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", @@ -11023,27 +11023,26 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.54.0" +version = "0.54.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e456eeaf7f09413fdc16799782879b2b9f1d264dfdbce4cf7e924df0ef36afb9" +checksum = "a24eda84b5d488f99344e54b807138896cee8df0b2d16c793f1f6b80e6d8df1f" dependencies = [ "base64 0.22.1", "block2 0.6.0", "cookie", "crossbeam-channel", "dirs 6.0.0", + "dom_query", "dpi", "dunce", "gdkx11", "gtk", - "html5ever 0.29.1", "http 1.3.1", "javascriptcore-rs", "jni", - "kuchikiki", "libc", "ndk", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", diff --git a/crates/tauri-runtime-wry/Cargo.toml b/crates/tauri-runtime-wry/Cargo.toml index 10ad16be189c..79f82da8b834 100644 --- a/crates/tauri-runtime-wry/Cargo.toml +++ b/crates/tauri-runtime-wry/Cargo.toml @@ -13,7 +13,7 @@ edition.workspace = true rust-version.workspace = true [dependencies] -wry = { version = "0.54.0", default-features = false, features = [ +wry = { version = "0.54.3", default-features = false, features = [ "drag-drop", "protocol", "os-webview", From abd555a5ec3814e8aa7c6df1e608c924df010ec6 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 14 Mar 2026 22:37:06 +0800 Subject: [PATCH 19/33] Add todo comments we don't have the git blame data in html2, better do it now or never find it again --- crates/tauri-utils/src/html.rs | 1 + crates/tauri-utils/src/html2.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/tauri-utils/src/html.rs b/crates/tauri-utils/src/html.rs index 3335743dd2d6..7a3dd0703038 100644 --- a/crates/tauri-utils/src/html.rs +++ b/crates/tauri-utils/src/html.rs @@ -281,6 +281,7 @@ pub fn inline_isolation(document: &NodeRef, dir: &Path) { } } +// TODO: Verify this, this is not found in the HTML spec, see https://github.com/tauri-apps/tauri/pull/14265#discussion_r2415396842 /// Normalize line endings in script content to match what the browser uses for CSP hashing. /// /// According to the HTML spec, browsers normalize: diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index f073d0920d77..f51b899dda6d 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -167,6 +167,7 @@ pub fn inline_isolation(document: &Document, dir: &Path) { } } +// TODO: Verify this, this is not found in the HTML spec, see https://github.com/tauri-apps/tauri/pull/14265#discussion_r2415396842 /// Normalize line endings in script content to match what the browser uses for CSP hashing. /// /// According to the HTML spec, browsers normalize: From d5127322513cd47dd7b824a2d42ec212bb5c8f63 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 14 Mar 2026 22:41:12 +0800 Subject: [PATCH 20/33] refactor `with_head` to `ensure_head` --- crates/tauri-utils/src/html2.rs | 48 +++++++++++++++------------------ 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index f51b899dda6d..1a4d5efd5c5c 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -31,15 +31,13 @@ pub fn parse_doc(html: String) -> Document { Document::from(html.as_str()) } -fn with_head)>(document: &Document, f: F) { - let head = document.head().unwrap_or_else(|| { +fn ensure_head(document: &Document) -> NodeRef<'_> { + document.head().unwrap_or_else(|| { let html = document.html_root(); let head = document.tree.new_element("head"); html.prepend_child(&head); head - }); - - f(head) + }) } fn inject_nonce(document: &Document, selector: &str, token: &str) { @@ -68,20 +66,16 @@ pub fn inject_nonce_token( /// Injects a content security policy to the HTML. pub fn inject_csp(document: &Document, csp: &str) { - with_head(document, |head| { - let meta_tag = format!(r#""#); - - head.prepend_html(meta_tag.as_str()); - }); + let head = ensure_head(document); + let meta_tag = format!(r#""#); + head.prepend_html(meta_tag.as_str()); } /// Injects a content security policy to the HTML. pub fn append_script_to_head(document: &Document, script: &str) { - with_head(document, |head| { - let script_tag = format!(r#""#); - - head.prepend_html(script_tag.as_str()); - }); + let head = ensure_head(document); + let script_tag = format!(r#""#); + head.prepend_html(script_tag.as_str()); } /// The shape of the JavaScript Pattern config @@ -124,19 +118,19 @@ pub enum IsolationSide { /// Note: This function is not considered part of the stable API. #[cfg(feature = "isolation")] pub fn inject_codegen_isolation_script(document: &Document) { - with_head(document, |head| { - let script_content = IsolationJavascriptCodegen {} - .render_default(&Default::default()) - .expect("unable to render codegen isolation script template") - .into_string(); - - let script_tag = format!( - r#""#, - SCRIPT_NONCE_TOKEN, script_content - ); + let head = ensure_head(document); + + let script_content = IsolationJavascriptCodegen {} + .render_default(&Default::default()) + .expect("unable to render codegen isolation script template") + .into_string(); + + let script_tag = format!( + r#""#, + SCRIPT_NONCE_TOKEN, script_content + ); - head.prepend_html(script_tag.as_str()); - }); + head.prepend_html(script_tag.as_str()); } /// Temporary workaround for Windows not allowing requests From 999de1eebe0586beec9aaa71f8e51ea1b2bbe7ed Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 14 Mar 2026 22:43:15 +0800 Subject: [PATCH 21/33] Remove unused casts --- crates/tauri-utils/src/html2.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index 1a4d5efd5c5c..05cd5727c1cd 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -28,7 +28,7 @@ pub fn serialize_doc(document: &Document) -> Vec { /// Parses the given HTML string. pub fn parse_doc(html: String) -> Document { - Document::from(html.as_str()) + Document::from(html) } fn ensure_head(document: &Document) -> NodeRef<'_> { @@ -68,14 +68,14 @@ pub fn inject_nonce_token( pub fn inject_csp(document: &Document, csp: &str) { let head = ensure_head(document); let meta_tag = format!(r#""#); - head.prepend_html(meta_tag.as_str()); + head.prepend_html(meta_tag); } /// Injects a content security policy to the HTML. pub fn append_script_to_head(document: &Document, script: &str) { let head = ensure_head(document); let script_tag = format!(r#""#); - head.prepend_html(script_tag.as_str()); + head.prepend_html(script_tag); } /// The shape of the JavaScript Pattern config @@ -125,12 +125,9 @@ pub fn inject_codegen_isolation_script(document: &Document) { .expect("unable to render codegen isolation script template") .into_string(); - let script_tag = format!( - r#""#, - SCRIPT_NONCE_TOKEN, script_content - ); + let script_tag = format!(r#""#,); - head.prepend_html(script_tag.as_str()); + head.prepend_html(script_tag); } /// Temporary workaround for Windows not allowing requests @@ -156,7 +153,7 @@ pub fn inline_isolation(document: &Document, dir: &Path) { let file = std::fs::read_to_string(dir.join(path)).expect("unable to find isolation file"); - script.set_html(file.as_str()); + script.set_html(file); script.remove_attr("src"); } } From 16d480899a6644861da9d1c5b0276e3b7abfb181 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 14 Mar 2026 23:30:03 +0800 Subject: [PATCH 22/33] Avoid using format to construct html elements which has the potential to get injected --- crates/tauri-codegen/src/context.rs | 12 +++++------- crates/tauri-utils/src/html2.rs | 17 +++++++++++------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/tauri-codegen/src/context.rs b/crates/tauri-codegen/src/context.rs index 8674ea37cdeb..415b7413fcf3 100644 --- a/crates/tauri-codegen/src/context.rs +++ b/crates/tauri-codegen/src/context.rs @@ -25,9 +25,7 @@ use tauri_utils::{ }, assets::AssetKey, config::{Config, FrontendDist, PatternKind}, - html2::{ - inject_nonce_token, parse_doc as parse_html, serialize_doc as serialize_html_node, Document, - }, + html2::{inject_nonce_token, parse_doc, serialize_doc, Document}, platform::Target, tokens::{map_lit, str_lit}, }; @@ -77,7 +75,7 @@ fn map_core_assets( if path.extension() == Some(OsStr::new("html")) { #[allow(clippy::collapsible_if)] if csp { - let document = parse_html(String::from_utf8_lossy(input).into_owned()); + let document = parse_doc(String::from_utf8_lossy(input).into_owned()); inject_nonce_token(&document, &dangerous_disable_asset_csp_modification); @@ -85,7 +83,7 @@ fn map_core_assets( inject_script_hashes(&document, key, csp_hashes); } - *input = serialize_html_node(&document); + *input = serialize_doc(&document); } } Ok(()) @@ -108,7 +106,7 @@ fn map_isolation( move |key, path, input, csp_hashes| { if path.extension() == Some(OsStr::new("html")) { - let isolation_html = parse_html(String::from_utf8_lossy(input).into_owned()); + let isolation_html = parse_doc(String::from_utf8_lossy(input).into_owned()); // this is appended, so no need to reverse order it tauri_utils::html2::inject_codegen_isolation_script(&isolation_html); @@ -125,7 +123,7 @@ fn map_isolation( csp_hashes.styles.push(iframe_style_csp_hash.clone()); - *input = serialize_html_node(&isolation_html) + *input = serialize_doc(&isolation_html) } Ok(()) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index 05cd5727c1cd..5b509759e58b 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -67,15 +67,18 @@ pub fn inject_nonce_token( /// Injects a content security policy to the HTML. pub fn inject_csp(document: &Document, csp: &str) { let head = ensure_head(document); - let meta_tag = format!(r#""#); - head.prepend_html(meta_tag); + let meta_tag = document.tree.new_element("meta"); + meta_tag.set_attr("http-equiv", "Content-Security-Policy"); + meta_tag.set_attr("content", csp); + head.prepend_child(&meta_tag); } /// Injects a content security policy to the HTML. pub fn append_script_to_head(document: &Document, script: &str) { let head = ensure_head(document); - let script_tag = format!(r#""#); - head.prepend_html(script_tag); + let script_tag = document.tree.new_element("script"); + script_tag.set_text(script); + head.prepend_child(&script_tag); } /// The shape of the JavaScript Pattern config @@ -125,9 +128,11 @@ pub fn inject_codegen_isolation_script(document: &Document) { .expect("unable to render codegen isolation script template") .into_string(); - let script_tag = format!(r#""#,); + let script_tag = document.tree.new_element("script"); + script_tag.set_attr("nonce", SCRIPT_NONCE_TOKEN); + script_tag.set_text(script_content); - head.prepend_html(script_tag); + head.prepend_child(&script_tag); } /// Temporary workaround for Windows not allowing requests From 55c56cb1c745aa710832fdbafeab7db667e63467 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 14 Mar 2026 23:55:14 +0800 Subject: [PATCH 23/33] Feature gate `inline_isolation` --- crates/tauri-utils/src/html2.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index 5b509759e58b..ef968cd572f6 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -9,11 +9,6 @@ use std::path::{Path, PathBuf}; use dom_query::NodeRef; use serde::Serialize; -#[cfg(feature = "isolation")] -use serialize_to_javascript::DefaultTemplate; - -#[cfg(feature = "isolation")] -use crate::pattern::isolation::IsolationJavascriptCodegen; use crate::{ assets::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN}, config::{DisabledCspModificationKind, PatternKind}, @@ -121,6 +116,9 @@ pub enum IsolationSide { /// Note: This function is not considered part of the stable API. #[cfg(feature = "isolation")] pub fn inject_codegen_isolation_script(document: &Document) { + use crate::pattern::isolation::IsolationJavascriptCodegen; + use serialize_to_javascript::DefaultTemplate; + let head = ensure_head(document); let script_content = IsolationJavascriptCodegen {} @@ -139,6 +137,7 @@ pub fn inject_codegen_isolation_script(document: &Document) { /// /// Note: this does not prevent path traversal due to the isolation application expectation that it /// is secure. +#[cfg(feature = "isolation")] pub fn inline_isolation(document: &Document, dir: &Path) { let scripts = document.select("script[src]"); From f49131cbeec0ba00fc032a81dddba0afe5d2910c Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 14 Mar 2026 23:55:34 +0800 Subject: [PATCH 24/33] Keep old prepends appends --- crates/tauri-utils/src/html2.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index ef968cd572f6..a2daba663265 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -65,7 +65,7 @@ pub fn inject_csp(document: &Document, csp: &str) { let meta_tag = document.tree.new_element("meta"); meta_tag.set_attr("http-equiv", "Content-Security-Policy"); meta_tag.set_attr("content", csp); - head.prepend_child(&meta_tag); + head.append_child(&meta_tag); } /// Injects a content security policy to the HTML. @@ -141,7 +141,7 @@ pub fn inject_codegen_isolation_script(document: &Document) { pub fn inline_isolation(document: &Document, dir: &Path) { let scripts = document.select("script[src]"); - for script in scripts.iter() { + for script in scripts.nodes() { let src = match script.attr("src") { Some(s) => s.to_string(), None => continue, @@ -157,7 +157,7 @@ pub fn inline_isolation(document: &Document, dir: &Path) { let file = std::fs::read_to_string(dir.join(path)).expect("unable to find isolation file"); - script.set_html(file); + script.set_text(file); script.remove_attr("src"); } } From 70ad66632dee7c9a61623b48d1cb9cfad9d98917 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 15 Mar 2026 00:05:27 +0800 Subject: [PATCH 25/33] Fix `inline_isolation_replaces_src_with_content` test --- crates/tauri-utils/src/html2.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index a2daba663265..d50b967ff5b1 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -4,8 +4,6 @@ //! The module to process HTML in Tauri. -use std::path::{Path, PathBuf}; - use dom_query::NodeRef; use serde::Serialize; @@ -138,7 +136,7 @@ pub fn inject_codegen_isolation_script(document: &Document) { /// Note: this does not prevent path traversal due to the isolation application expectation that it /// is secure. #[cfg(feature = "isolation")] -pub fn inline_isolation(document: &Document, dir: &Path) { +pub fn inline_isolation(document: &Document, dir: &std::path::Path) { let scripts = document.select("script[src]"); for script in scripts.nodes() { @@ -147,7 +145,7 @@ pub fn inline_isolation(document: &Document, dir: &Path) { None => continue, }; - let mut path = PathBuf::from(src); + let mut path = std::path::PathBuf::from(src); if path.has_root() { path = path .strip_prefix("/") @@ -317,13 +315,16 @@ mod tests { } #[test] + #[cfg(feature = "isolation")] fn inline_isolation_replaces_src_with_content() { let temp_dir = tempfile::tempdir().unwrap(); - let mut file = tempfile::tempfile_in(&temp_dir).unwrap(); + let mut file = tempfile::NamedTempFile::with_suffix_in(".js", &temp_dir).unwrap(); file.write_all(b"console.log('test');").unwrap(); + let file_name = file.path().file_name().unwrap().to_str().unwrap(); - let html = r#""#; - let document = parse_doc(html.to_string()); + let html = + format!(r#""#); + let document = parse_doc(html); inline_isolation(&document, temp_dir.path()); assert_eq!( From 7c96a7f181a0941db0de18ff0de93292dff1474a Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 15 Mar 2026 00:28:41 +0800 Subject: [PATCH 26/33] End meta tag --- crates/tauri-utils/src/html2.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index d50b967ff5b1..a7c4ff8f90ff 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -195,8 +195,6 @@ pub fn normalize_script_for_csp(input: &[u8]) -> Vec { #[cfg(test)] mod tests { - use std::io::Write; - use super::*; use crate::{ assets::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN}, @@ -218,7 +216,7 @@ mod tests { assert_eq!( String::from_utf8(serialize_doc(&document)).unwrap(), format!( - r#""# + r#""# ) ); } @@ -317,6 +315,8 @@ mod tests { #[test] #[cfg(feature = "isolation")] fn inline_isolation_replaces_src_with_content() { + use std::io::Write; + let temp_dir = tempfile::tempdir().unwrap(); let mut file = tempfile::NamedTempFile::with_suffix_in(".js", &temp_dir).unwrap(); file.write_all(b"console.log('test');").unwrap(); From a7742bf6eca015f3fd879e2f80dc933d02909108 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 15 Mar 2026 00:45:24 +0800 Subject: [PATCH 27/33] Mirror test to old html module --- crates/tauri-utils/src/html.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/tauri-utils/src/html.rs b/crates/tauri-utils/src/html.rs index 7a3dd0703038..bc2aaf373c10 100644 --- a/crates/tauri-utils/src/html.rs +++ b/crates/tauri-utils/src/html.rs @@ -425,11 +425,13 @@ mod tests { #[test] fn inline_isolation_replaces_src_with_content() { let temp_dir = tempfile::tempdir().unwrap(); - let mut file = tempfile::tempfile_in(&temp_dir).unwrap(); + let mut file = tempfile::NamedTempFile::with_suffix_in(".js", &temp_dir).unwrap(); file.write_all(b"console.log('test');").unwrap(); + let file_name = file.path().file_name().unwrap().to_str().unwrap(); - let html = r#""#; - let document = parse(html.to_string()); + let html = + format!(r#""#); + let document = parse(html); inline_isolation(&document, temp_dir.path()); assert_eq!( From 8bd11a8bd0cf9139b1bd89e7effc698cf342f516 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 15 Mar 2026 00:48:40 +0800 Subject: [PATCH 28/33] Use back to `append_html` for csp and link issue --- crates/tauri-utils/src/html2.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index a7c4ff8f90ff..7e173945131d 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -60,10 +60,13 @@ pub fn inject_nonce_token( /// Injects a content security policy to the HTML. pub fn inject_csp(document: &Document, csp: &str) { let head = ensure_head(document); - let meta_tag = document.tree.new_element("meta"); - meta_tag.set_attr("http-equiv", "Content-Security-Policy"); - meta_tag.set_attr("content", csp); - head.append_child(&meta_tag); + // TODO: Switch to this after https://github.com/niklak/dom_query/issues/178 is fixed + // let meta_tag = document.tree.new_element("meta"); + // meta_tag.set_attr("http-equiv", "Content-Security-Policy"); + // meta_tag.set_attr("content", csp); + // head.append_child(&meta_tag); + let meta_tag = format!(r#""#); + head.append_html(meta_tag); } /// Injects a content security policy to the HTML. @@ -216,7 +219,7 @@ mod tests { assert_eq!( String::from_utf8(serialize_doc(&document)).unwrap(), format!( - r#""# + r#""# ) ); } From 80ee2303c1b07e6b096c2c81df25efb53f694233 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 15 Mar 2026 11:53:15 +0800 Subject: [PATCH 29/33] Try out dom query main branch --- Cargo.lock | 84 +++++++++++++++++++++++++++++---- Cargo.toml | 1 + crates/tauri-utils/Cargo.toml | 2 +- crates/tauri-utils/src/html2.rs | 11 ++--- 4 files changed, 80 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15166fb4f321..0356163ce57d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1320,7 +1320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2006,7 +2006,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2067,7 +2067,21 @@ dependencies = [ "html5ever 0.36.1", "precomputed-hash", "selectors 0.35.0", - "tendril", + "tendril 0.4.3", +] + +[[package]] +name = "dom_query" +version = "0.26.0" +source = "git+https://github.com/niklak/dom_query.git#c166febc5a3c90c178c954318bd84d593557851e" +dependencies = [ + "bit-set", + "cssparser 0.36.0", + "foldhash", + "html5ever 0.38.0", + "precomputed-hash", + "selectors 0.36.0", + "tendril 0.5.0", ] [[package]] @@ -3312,6 +3326,16 @@ dependencies = [ "markup5ever 0.36.1", ] +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever 0.38.0", +] + [[package]] name = "http" version = "0.2.12" @@ -4548,7 +4572,7 @@ dependencies = [ "phf_codegen 0.11.3", "string_cache 0.8.7", "string_cache_codegen 0.5.2", - "tendril", + "tendril 0.4.3", ] [[package]] @@ -4558,7 +4582,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" dependencies = [ "log", - "tendril", + "tendril 0.4.3", + "web_atoms", +] + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril 0.5.0", "web_atoms", ] @@ -5829,7 +5864,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.21.7", + "base64 0.22.1", "bitfield", "block-padding", "blowfish", @@ -7261,7 +7296,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -7563,6 +7598,25 @@ dependencies = [ "smallvec", ] +[[package]] +name = "selectors" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223b960be86f7d302b7168cdbab137f9bdbf7e04a90c185312fab14dff49dc5f" +dependencies = [ + "bitflags 2.7.0", + "cssparser 0.36.0", + "derive_more 2.0.1", + "log", + "new_debug_unreachable", + "phf 0.13.1", + "phf_codegen 0.13.1", + "precomputed-hash", + "rustc-hash", + "servo_arc 0.4.3", + "smallvec", +] + [[package]] name = "semver" version = "1.0.24" @@ -9049,7 +9103,7 @@ dependencies = [ "brotli", "cargo_metadata", "ctor 0.2.9", - "dom_query", + "dom_query 0.26.0", "dunce", "getrandom 0.3.3", "glob", @@ -9131,6 +9185,16 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tendril" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" +dependencies = [ + "new_debug_unreachable", + "utf-8", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -10438,7 +10502,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -11033,7 +11097,7 @@ dependencies = [ "cookie", "crossbeam-channel", "dirs 6.0.0", - "dom_query", + "dom_query 0.25.1", "dpi", "dunce", "gdkx11", diff --git a/Cargo.toml b/Cargo.toml index 670a647672c2..7a6805f62c8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,3 +72,4 @@ schemars_derive = { git = 'https://github.com/tauri-apps/schemars.git', branch = tauri = { path = "./crates/tauri" } tauri-plugin = { path = "./crates/tauri-plugin" } tauri-utils = { path = "./crates/tauri-utils" } +dom_query = { git = "https://github.com/niklak/dom_query.git" } diff --git a/crates/tauri-utils/Cargo.toml b/crates/tauri-utils/Cargo.toml index 58fd355a353b..d03c2afa9983 100644 --- a/crates/tauri-utils/Cargo.toml +++ b/crates/tauri-utils/Cargo.toml @@ -24,7 +24,7 @@ brotli = { version = "8", optional = true, default-features = false, features = url = { version = "2", features = ["serde"] } html5ever = { version = "0.29", optional = true } kuchiki = { package = "kuchikiki", version = "0.8.8-speedreader", optional = true } -dom_query = { version = "0.25", optional = true, default-features = false } +dom_query = { version = "0.26", optional = true, default-features = false } proc-macro2 = { version = "1", optional = true } quote = { version = "1", optional = true } # Our code requires at least 0.8.21 so don't change this to 0.8 diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index 7e173945131d..125351806dc4 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -60,13 +60,10 @@ pub fn inject_nonce_token( /// Injects a content security policy to the HTML. pub fn inject_csp(document: &Document, csp: &str) { let head = ensure_head(document); - // TODO: Switch to this after https://github.com/niklak/dom_query/issues/178 is fixed - // let meta_tag = document.tree.new_element("meta"); - // meta_tag.set_attr("http-equiv", "Content-Security-Policy"); - // meta_tag.set_attr("content", csp); - // head.append_child(&meta_tag); - let meta_tag = format!(r#""#); - head.append_html(meta_tag); + let meta_tag = document.tree.new_element("meta"); + meta_tag.set_attr("http-equiv", "Content-Security-Policy"); + meta_tag.set_attr("content", csp); + head.append_child(&meta_tag); } /// Injects a content security policy to the HTML. From 3190747eb75c34484b2f5848176be3f464221206 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 17 Mar 2026 12:00:07 +0800 Subject: [PATCH 30/33] Use nodes instead to avoid an extra clone --- crates/tauri-utils/src/html2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index 125351806dc4..5e84c1246d3e 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -35,7 +35,7 @@ fn ensure_head(document: &Document) -> NodeRef<'_> { fn inject_nonce(document: &Document, selector: &str, token: &str) { let elements = document.select(selector); - for elem in elements.iter() { + for elem in elements.nodes() { // if the node already has the `nonce` attribute, skip it if elem.attr("nonce").is_some() { continue; From 6f1f9ca9e0895b988d89ba102bc2cef47129433e Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 17 Mar 2026 23:47:21 +0800 Subject: [PATCH 31/33] Use wry 0.54.4 and dom_query 0.27 --- Cargo.lock | 84 ++++++----------------------- Cargo.toml | 1 - crates/tauri-runtime-wry/Cargo.toml | 2 +- crates/tauri-utils/Cargo.toml | 2 +- 4 files changed, 17 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0356163ce57d..33060bd0e544 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1320,7 +1320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -2057,23 +2057,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dom_query" -version = "0.25.1" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d9c2e7f1d22d0f2ce07626d259b8a55f4a47cb0938d4006dd8ae037f17d585e" -dependencies = [ - "bit-set", - "cssparser 0.36.0", - "foldhash", - "html5ever 0.36.1", - "precomputed-hash", - "selectors 0.35.0", - "tendril 0.4.3", -] - -[[package]] -name = "dom_query" -version = "0.26.0" -source = "git+https://github.com/niklak/dom_query.git#c166febc5a3c90c178c954318bd84d593557851e" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" dependencies = [ "bit-set", "cssparser 0.36.0", @@ -2406,7 +2392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3316,16 +3302,6 @@ dependencies = [ "match_token", ] -[[package]] -name = "html5ever" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" -dependencies = [ - "log", - "markup5ever 0.36.1", -] - [[package]] name = "html5ever" version = "0.38.0" @@ -4407,7 +4383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -4575,17 +4551,6 @@ dependencies = [ "tendril 0.4.3", ] -[[package]] -name = "markup5ever" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" -dependencies = [ - "log", - "tendril 0.4.3", - "web_atoms", -] - [[package]] name = "markup5ever" version = "0.38.0" @@ -5864,7 +5829,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.22.1", + "base64 0.21.7", "bitfield", "block-padding", "blowfish", @@ -7157,7 +7122,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7170,7 +7135,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7296,7 +7261,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -7579,25 +7544,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "selectors" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fdfed56cd634f04fe8b9ddf947ae3dc493483e819593d2ba17df9ad05db8b2" -dependencies = [ - "bitflags 2.7.0", - "cssparser 0.36.0", - "derive_more 2.0.1", - "log", - "new_debug_unreachable", - "phf 0.13.1", - "phf_codegen 0.13.1", - "precomputed-hash", - "rustc-hash", - "servo_arc 0.4.3", - "smallvec", -] - [[package]] name = "selectors" version = "0.36.0" @@ -9103,7 +9049,7 @@ dependencies = [ "brotli", "cargo_metadata", "ctor 0.2.9", - "dom_query 0.26.0", + "dom_query", "dunce", "getrandom 0.3.3", "glob", @@ -9171,7 +9117,7 @@ dependencies = [ "getrandom 0.2.15", "once_cell", "rustix 0.38.43", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -10502,7 +10448,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -11088,16 +11034,16 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.54.3" +version = "0.54.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a24eda84b5d488f99344e54b807138896cee8df0b2d16c793f1f6b80e6d8df1f" +checksum = "e5a8135d8676225e5744de000d4dff5a082501bf7db6a1c1495034f8c314edbc" dependencies = [ "base64 0.22.1", "block2 0.6.0", "cookie", "crossbeam-channel", "dirs 6.0.0", - "dom_query 0.25.1", + "dom_query", "dpi", "dunce", "gdkx11", diff --git a/Cargo.toml b/Cargo.toml index 7a6805f62c8f..670a647672c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,4 +72,3 @@ schemars_derive = { git = 'https://github.com/tauri-apps/schemars.git', branch = tauri = { path = "./crates/tauri" } tauri-plugin = { path = "./crates/tauri-plugin" } tauri-utils = { path = "./crates/tauri-utils" } -dom_query = { git = "https://github.com/niklak/dom_query.git" } diff --git a/crates/tauri-runtime-wry/Cargo.toml b/crates/tauri-runtime-wry/Cargo.toml index 79f82da8b834..dcd921d1a029 100644 --- a/crates/tauri-runtime-wry/Cargo.toml +++ b/crates/tauri-runtime-wry/Cargo.toml @@ -13,7 +13,7 @@ edition.workspace = true rust-version.workspace = true [dependencies] -wry = { version = "0.54.3", default-features = false, features = [ +wry = { version = "0.54.4", default-features = false, features = [ "drag-drop", "protocol", "os-webview", diff --git a/crates/tauri-utils/Cargo.toml b/crates/tauri-utils/Cargo.toml index d03c2afa9983..493f8c4d9214 100644 --- a/crates/tauri-utils/Cargo.toml +++ b/crates/tauri-utils/Cargo.toml @@ -24,7 +24,7 @@ brotli = { version = "8", optional = true, default-features = false, features = url = { version = "2", features = ["serde"] } html5ever = { version = "0.29", optional = true } kuchiki = { package = "kuchikiki", version = "0.8.8-speedreader", optional = true } -dom_query = { version = "0.26", optional = true, default-features = false } +dom_query = { version = "0.27", optional = true, default-features = false } proc-macro2 = { version = "1", optional = true } quote = { version = "1", optional = true } # Our code requires at least 0.8.21 so don't change this to 0.8 From 19afa091a205abfe249ffa9d4ca0e12023d40f30 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 25 Mar 2026 20:38:50 +0800 Subject: [PATCH 32/33] Mark stability --- crates/tauri-utils/src/html2.rs | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index 5e84c1246d3e..b37e1bf387d1 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -3,6 +3,11 @@ // SPDX-License-Identifier: MIT //! The module to process HTML in Tauri. +//! +//! # Stability +//! +//! This is utility used in Tauri internally and not considered part of the stable API. +//! If you use it, note that it may include breaking changes in the future. use dom_query::NodeRef; use serde::Serialize; @@ -12,14 +17,25 @@ use crate::{ config::{DisabledCspModificationKind, PatternKind}, }; +/// # Stability +/// +/// This dependency might receive updates in minor releases. pub use dom_query::Document; /// Serializes the document to HTML. +/// +/// # Stability +/// +/// This dependency [`dom_query`] for [`Document`] might receive updates in minor releases. pub fn serialize_doc(document: &Document) -> Vec { document.html().as_bytes().to_vec() } /// Parses the given HTML string. +/// +/// # Stability +/// +/// This dependency [`dom_query`] for [`Document`] might receive updates in minor releases. pub fn parse_doc(html: String) -> Document { Document::from(html) } @@ -45,6 +61,10 @@ fn inject_nonce(document: &Document, selector: &str, token: &str) { } /// Inject nonce tokens to all scripts and styles. +/// +/// # Stability +/// +/// This dependency [`dom_query`] for [`Document`] might receive updates in minor releases. pub fn inject_nonce_token( document: &Document, dangerous_disable_asset_csp_modification: &DisabledCspModificationKind, @@ -58,6 +78,10 @@ pub fn inject_nonce_token( } /// Injects a content security policy to the HTML. +/// +/// # Stability +/// +/// This dependency [`dom_query`] for [`Document`] might receive updates in minor releases. pub fn inject_csp(document: &Document, csp: &str) { let head = ensure_head(document); let meta_tag = document.tree.new_element("meta"); @@ -67,6 +91,10 @@ pub fn inject_csp(document: &Document, csp: &str) { } /// Injects a content security policy to the HTML. +/// +/// # Stability +/// +/// This dependency [`dom_query`] for [`Document`] might receive updates in minor releases. pub fn append_script_to_head(document: &Document, script: &str) { let head = ensure_head(document); let script_tag = document.tree.new_element("script"); @@ -112,6 +140,10 @@ pub enum IsolationSide { /// Injects the Isolation JavaScript to a codegen time document. /// /// Note: This function is not considered part of the stable API. +/// +/// # Stability +/// +/// This dependency [`dom_query`] for [`Document`] might receive updates in minor releases. #[cfg(feature = "isolation")] pub fn inject_codegen_isolation_script(document: &Document) { use crate::pattern::isolation::IsolationJavascriptCodegen; @@ -135,6 +167,10 @@ pub fn inject_codegen_isolation_script(document: &Document) { /// /// Note: this does not prevent path traversal due to the isolation application expectation that it /// is secure. +/// +/// # Stability +/// +/// This dependency [`dom_query`] for [`Document`] might receive updates in minor releases. #[cfg(feature = "isolation")] pub fn inline_isolation(document: &Document, dir: &std::path::Path) { let scripts = document.select("script[src]"); From 91888fb6b240b803a528952ab739cfce0cd29e45 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 25 Mar 2026 20:39:38 +0800 Subject: [PATCH 33/33] Remove `PatternObject` --- crates/tauri-utils/src/html2.rs | 38 +-------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/crates/tauri-utils/src/html2.rs b/crates/tauri-utils/src/html2.rs index b37e1bf387d1..7f591dc2788d 100644 --- a/crates/tauri-utils/src/html2.rs +++ b/crates/tauri-utils/src/html2.rs @@ -10,11 +10,10 @@ //! If you use it, note that it may include breaking changes in the future. use dom_query::NodeRef; -use serde::Serialize; use crate::{ assets::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN}, - config::{DisabledCspModificationKind, PatternKind}, + config::DisabledCspModificationKind, }; /// # Stability @@ -102,41 +101,6 @@ pub fn append_script_to_head(document: &Document, script: &str) { head.prepend_child(&script_tag); } -/// The shape of the JavaScript Pattern config -#[derive(Debug, Serialize)] -#[serde(rename_all = "lowercase", tag = "pattern")] -pub enum PatternObject { - /// Brownfield pattern. - Brownfield, - /// Isolation pattern. Recommended for security purposes. - Isolation { - /// Which `IsolationSide` this `PatternObject` is getting injected into - side: IsolationSide, - }, -} - -impl From<&PatternKind> for PatternObject { - fn from(pattern_kind: &PatternKind) -> Self { - match pattern_kind { - PatternKind::Brownfield => Self::Brownfield, - PatternKind::Isolation { .. } => Self::Isolation { - side: IsolationSide::default(), - }, - } - } -} - -/// Where the JavaScript is injected to -#[derive(Debug, Serialize, Default)] -#[serde(rename_all = "lowercase")] -pub enum IsolationSide { - /// Original frame, the Brownfield application - #[default] - Original, - /// Secure frame, the isolation security application - Secure, -} - /// Injects the Isolation JavaScript to a codegen time document. /// /// Note: This function is not considered part of the stable API.