From 0f2018adc9784057ae33ef8fd769cf7eae8a82ce Mon Sep 17 00:00:00 2001 From: huangminggg Date: Mon, 5 Feb 2024 11:22:07 +0800 Subject: [PATCH 1/5] fix(core) Fix HTML encoding in webview rendered via data url --- Cargo.lock | 13 +++++-- core/tauri-runtime-wry/Cargo.toml | 1 + core/tauri-runtime-wry/src/lib.rs | 36 +++++++++++++++---- core/tauri-runtime/Cargo.toml | 1 + core/tauri-runtime/src/window.rs | 10 +++--- core/tauri-utils/Cargo.toml | 2 ++ core/tauri-utils/src/config.rs | 13 ++++++- core/tauri-utils/src/html.rs | 14 ++++++++ core/tauri/Cargo.toml | 10 ++++-- core/tauri/src/manager.rs | 45 ++++++++++++----------- core/tauri/src/test/mock_runtime.rs | 4 +-- examples/data-url/README.md | 3 ++ examples/data-url/index.html | 11 ++++++ examples/data-url/main.rs | 46 ++++++++++++++++++++++++ examples/data-url/tauri.conf.json | 55 +++++++++++++++++++++++++++++ 15 files changed, 226 insertions(+), 38 deletions(-) create mode 100644 examples/data-url/README.md create mode 100644 examples/data-url/index.html create mode 100644 examples/data-url/main.rs create mode 100644 examples/data-url/tauri.conf.json diff --git a/Cargo.lock b/Cargo.lock index 7d62264ae4d5..ef2b36084a33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -896,9 +896,9 @@ dependencies = [ [[package]] name = "data-url" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "derivative" @@ -4083,6 +4083,7 @@ dependencies = [ "tokio-test", "tracing", "url", + "urlencoding", "uuid", "webkit2gtk", "webview2-com", @@ -4189,6 +4190,7 @@ dependencies = [ "tauri-runtime", "tauri-utils", "tracing", + "urlencoding", "uuid", "webkit2gtk", "webview2-com", @@ -4203,6 +4205,7 @@ dependencies = [ "aes-gcm", "brotli", "ctor", + "data-url", "dunce", "getrandom 0.2.12", "glob", @@ -4689,6 +4692,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 136e6df35e04..5cb093ed6ef3 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -21,6 +21,7 @@ rand = "0.8" raw-window-handle = "0.5" tracing = { version = "0.1", optional = true } arboard = { version = "3", optional = true } +urlencoding = "2.1.3" [target."cfg(windows)".dependencies] webview2-com = "0.19.1" diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index acbf1a10095c..2f85bf66739c 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -3246,13 +3246,35 @@ fn create_webview( if window_builder.center { let _ = center_window(&window, window.inner_size()); } - let mut webview_builder = WebViewBuilder::new(window) - .map_err(|e| Error::CreateWebview(Box::new(e)))? - .with_focused(focused) - .with_url(&url) - .unwrap() // safe to unwrap because we validate the URL beforehand - .with_transparent(is_window_transparent) - .with_accept_first_mouse(webview_attributes.accept_first_mouse); + + let url_str = url.to_string(); + + let mut webview_builder = + WebViewBuilder::new(window).map_err(|e| Error::CreateWebview(Box::new(e)))?; + + // use with_html method if html content can be extracted from url. + // else defaults to with_url method + webview_builder = if let Some(html_string) = tauri_utils::html::extract_html_content(&url_str) { + // we run a decode here on the html_string for sanity check + // the content should be fully decoded when passing to WRY, which + // will be responsible for handling the encoding based on the OS. + let html = + urlencoding::decode(html_string).map_err(|e| Error::CreateWebview(Box::new(e)))?; + + webview_builder + .with_html(html) + .map_err(|e| Error::CreateWebview(Box::new(e)))? + } else { + webview_builder + .with_url(&url_str) + .map_err(|e| Error::CreateWebview(Box::new(e)))? + }; + + webview_builder = webview_builder + .with_focused(focused) + .with_transparent(is_window_transparent) + .with_accept_first_mouse(webview_attributes.accept_first_mouse); + if webview_attributes.file_drop_handler_enabled { webview_builder = webview_builder .with_file_drop_handler(create_file_drop_handler(window_event_listeners.clone())); diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml index cccd75221dae..f6b9bcfe0c69 100644 --- a/core/tauri-runtime/Cargo.toml +++ b/core/tauri-runtime/Cargo.toml @@ -50,3 +50,4 @@ system-tray = [ ] macos-private-api = [ ] global-shortcut = [ ] clipboard = [ ] +window-data-url = [ "tauri-utils/window-data-url" ] diff --git a/core/tauri-runtime/src/window.rs b/core/tauri-runtime/src/window.rs index 34a5f5118427..528e91fe0f3b 100644 --- a/core/tauri-runtime/src/window.rs +++ b/core/tauri-runtime/src/window.rs @@ -241,8 +241,8 @@ pub struct PendingWindow> { pub web_resource_request_handler: Option>, - /// The resolved URL to load on the webview. - pub url: String, + /// The URL to load on the webview. + pub url: Url, } pub fn is_label_valid(label: &str) -> bool { @@ -283,7 +283,8 @@ impl> PendingWindow { js_event_listeners: Default::default(), navigation_handler: Default::default(), web_resource_request_handler: Default::default(), - url: "tauri://localhost".to_string(), + // This will never fail + url: Url::parse("tauri://localhost").unwrap(), http_scheme: false, }) } @@ -315,7 +316,8 @@ impl> PendingWindow { js_event_listeners: Default::default(), navigation_handler: Default::default(), web_resource_request_handler: Default::default(), - url: "tauri://localhost".to_string(), + // This will never fail + url: Url::parse("tauri://localhost").unwrap(), http_scheme: false, }) } diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml index a7c2748fa3e9..32532adcb64e 100644 --- a/core/tauri-utils/Cargo.toml +++ b/core/tauri-utils/Cargo.toml @@ -38,6 +38,7 @@ semver = "1" infer = "0.13" dunce = "1" log = "0.4.20" +data-url = { version = "0.3.1", optional = true } [target."cfg(target_os = \"linux\")".dependencies] heck = "0.4" @@ -54,3 +55,4 @@ process-relaunch-dangerous-allow-symlink-macos = [ ] config-json5 = [ "json5" ] config-toml = [ "toml" ] resources = [ "glob", "walkdir" ] +window-data-url = [ "data-url" ] diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index c9485884df5d..0a820e5b4ec1 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -54,12 +54,18 @@ pub enum WindowUrl { /// For instance, to load `tauri://localhost/users/john`, /// you can simply provide `users/john` in this configuration. App(PathBuf), + #[cfg(feature = "window-data-url")] + /// A data url, for example data:text/html,

Hello world

+ /// Data url should not be encoded + DataUrl(Url), } impl fmt::Display for WindowUrl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::External(url) => write!(f, "{url}"), + Self::External(url)=> write!(f, "{url}"), + #[cfg(feature = "window-data-url")] + Self::DataUrl(url) => write!(f, "{url}"), Self::App(path) => write!(f, "{}", path.display()), } } @@ -3281,6 +3287,11 @@ mod build { let url = url_lit(url); quote! { #prefix::External(#url) } } + #[cfg(feature = "window-data-url")] + Self::DataUrl(url) => { + let url = url_lit(url); + quote! { #prefix::DataUrl(#url) } + } }) } } diff --git a/core/tauri-utils/src/html.rs b/core/tauri-utils/src/html.rs index 5b8af5772cac..e066e09e447e 100644 --- a/core/tauri-utils/src/html.rs +++ b/core/tauri-utils/src/html.rs @@ -286,6 +286,20 @@ pub fn inline_isolation(document: &mut NodeRef, dir: &Path) { } } +/// Temporary naive method to check if a string is a html +pub fn is_html(data_string: &str) -> bool { + data_string.contains('<') && data_string.contains('>') +} + +/// Temporary naive method to extract data from html data string +pub fn extract_html_content(input: &str) -> Option<&str> { + if input.starts_with("data:text/html,") { + Some(&input[15..]) + } else { + None + } +} + #[cfg(test)] mod tests { use kuchiki::traits::*; diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 516968ecd2dd..0877460d759a 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -87,7 +87,7 @@ time = { version = "0.3", features = [ "parsing", "formatting" ], optional = tru os_info = { version = "3", optional = true } regex = { version = "1", optional = true } glob = "0.3" -data-url = { version = "0.2", optional = true } +data-url = { version = "0.3.1", optional = true } serialize-to-javascript = "=0.1.1" infer = { version = "0.9", optional = true } png = { version = "0.17", optional = true } @@ -96,6 +96,7 @@ encoding_rs = "0.8.31" sys-locale = { version = "0.2.3", optional = true } tracing = { version = "0.1", optional = true } indexmap = { version = "1", features = [ "std", "serde" ], optional = true } +urlencoding = "2.1.3" [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] rfd = { version = "0.10", optional = true, features = [ "gtk3", "common-controls-v6" ] } @@ -183,7 +184,7 @@ macos-private-api = [ "tauri-runtime-wry/macos-private-api" ] windows7-compat = [ "win7-notifications" ] -window-data-url = [ "data-url" ] +window-data-url = [ "tauri-utils/window-data-url", "data-url" ] api-all = [ "clipboard-all", "dialog-all", @@ -358,3 +359,8 @@ path = "../../examples/streaming/main.rs" name = "isolation" path = "../../examples/isolation/main.rs" required-features = [ "isolation" ] + +[[example]] +name = "data-url" +path = "../../examples/data-url/main.rs" +required-features = [ "window-data-url" ] \ No newline at end of file diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index fd19adcd17a4..508e844f4e40 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -499,18 +499,17 @@ impl WindowManager { }); } - let window_url = Url::parse(&pending.url).unwrap(); - let window_origin = if window_url.scheme() == "data" { + let window_origin = if pending.url.scheme() == "data" { "null".into() - } else if cfg!(windows) && window_url.scheme() != "http" && window_url.scheme() != "https" { + } else if cfg!(windows) && pending.url.scheme() != "http" && pending.url.scheme() != "https" { let scheme = if pending.http_scheme { "http" } else { "https" }; - format!("{scheme}://{}.localhost", window_url.scheme()) + format!("{scheme}://{}.localhost", pending.url.scheme()) } else { format!( "{}://{}{}", - window_url.scheme(), - window_url.host().unwrap(), - window_url + pending.url.scheme(), + pending.url.host().unwrap(), + pending.url .port() .map(|p| format!(":{p}")) .unwrap_or_default() @@ -995,6 +994,8 @@ impl WindowManager { } } WindowUrl::External(url) => url.clone(), + #[cfg(feature = "window-data-url")] + WindowUrl::DataUrl(url) => url.clone(), _ => unimplemented!(), }; @@ -1006,22 +1007,26 @@ impl WindowManager { } #[cfg(feature = "window-data-url")] - if let Some(csp) = self.csp() { - if url.scheme() == "data" { - if let Ok(data_url) = data_url::DataUrl::process(url.as_str()) { - let (body, _) = data_url.decode_to_vec().unwrap(); - let html = String::from_utf8_lossy(&body).into_owned(); - // naive way to check if it's an html - if html.contains('<') && html.contains('>') { - let mut document = tauri_utils::html::parse(html); - tauri_utils::html::inject_csp(&mut document, &csp.to_string()); - url.set_path(&format!("text/html,{}", document.to_string())); + match (url.scheme(), tauri_utils::html::extract_html_content(url.as_str())) { + ("data", Some(html_string)) => { + // There is an issue with the external DataUrl where HTML containing special characters + // are not correctly processed. A workaround is to first percent encode the html string, + // before it processed by DataUrl. + if let Ok(data_url) = data_url::DataUrl::process(&urlencoding::encode(&html_string)) { + if let Ok((body, _)) = data_url.decode_to_vec() { + let html = String::from_utf8_lossy(&body).into_owned(); + let mut document = tauri_utils::html::parse(html); + if let Some(csp) = self.csp() { + tauri_utils::html::inject_csp(&mut document, &csp.to_string()); + } + url.set_path(&format!("text/html,{}", document.to_string())); + } } - } } - } + _ => {} + }; - pending.url = url.to_string(); + pending.url = url; if !pending.window_builder.has_icon() { if let Some(default_window_icon) = self.inner.default_window_icon.clone() { diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index 8fe083dfd49e..afc1f36a2abe 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -174,7 +174,7 @@ impl RuntimeHandle for MockRuntimeHandle { pub struct MockDispatcher { id: WindowId, context: RuntimeContext, - url: String, + url: url::Url, last_evaluated_script: Arc>>, } @@ -417,7 +417,7 @@ impl Dispatch for MockDispatcher { } fn url(&self) -> Result { - self.url.parse().map_err(|_| Error::FailedToReceiveMessage) + Ok(self.url.clone()) } fn scale_factor(&self) -> Result { diff --git a/examples/data-url/README.md b/examples/data-url/README.md new file mode 100644 index 000000000000..5ce92661ce53 --- /dev/null +++ b/examples/data-url/README.md @@ -0,0 +1,3 @@ +# Data Url Example + +To execute run the following on the root directory of the repository: `cargo run --example data-url --features window-data-url`. diff --git a/examples/data-url/index.html b/examples/data-url/index.html new file mode 100644 index 000000000000..30db0bd4eb99 --- /dev/null +++ b/examples/data-url/index.html @@ -0,0 +1,11 @@ + + + + + + Welcome to Tauri! + + +

Welcome to Tauri!

+ + diff --git a/examples/data-url/main.rs b/examples/data-url/main.rs new file mode 100644 index 000000000000..5094dd97b7ed --- /dev/null +++ b/examples/data-url/main.rs @@ -0,0 +1,46 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use tauri::WindowBuilder; + +#[cfg(not(feature = "window-data-url"))] +fn main() { + compile_error!("Feature `window-data-url` is required to run this example"); +} + +#[cfg(feature = "window-data-url")] +fn main() { + + tauri::Builder::default() + .setup(|app| { + let html = r#" + + + /+&'=#%?\^`{}|[]~
+ Hello World
+ สวัสดีชาวโลก!
+ 你好世界!
+ + "#; + let data = format!("data:text/html,{}", html); + #[allow(unused_mut)] + let mut builder = + WindowBuilder::new( + app, + "Rust".to_string(), + tauri::WindowUrl::DataUrl(data.parse().unwrap())); + #[cfg(target_os = "macos")] + { + builder = builder.tabbing_identifier("Rust"); + } + let _window = builder.title("Tauri - Rust").build()?; + + Ok(()) + }) + .run(tauri::generate_context!( + "../../examples/data-url/tauri.conf.json" + )) + .expect("error while running tauri application"); +} diff --git a/examples/data-url/tauri.conf.json b/examples/data-url/tauri.conf.json new file mode 100644 index 000000000000..1931e5066a5d --- /dev/null +++ b/examples/data-url/tauri.conf.json @@ -0,0 +1,55 @@ +{ + "$schema": "../../core/tauri-config-schema/schema.json", + "build": { + "distDir": ["index.html"], + "devPath": ["index.html"], + "beforeDevCommand": "", + "beforeBuildCommand": "" + }, + "package": { + "productName": "Data Url", + "version": "0.1.0" + }, + "tauri": { + "bundle": { + "active": true, + "targets": "all", + "identifier": "com.tauri.dev", + "icon": [ + "../.icons/32x32.png", + "../.icons/128x128.png", + "../.icons/128x128@2x.png", + "../.icons/icon.icns", + "../.icons/icon.ico" + ], + "resources": [], + "externalBin": [], + "copyright": "", + "category": "DeveloperTool", + "shortDescription": "", + "longDescription": "", + "deb": { + "depends": [] + }, + "macOS": { + "frameworks": [], + "exceptionDomain": "" + } + }, + "allowlist": { + "all": false + }, + "windows": [ + { + "title": "Welcome to Tauri!", + "width": 800, + "height": 600, + "resizable": true, + "fullscreen": false + } + ], + "security": { + "csp": "default-src 'self'" + } + } +} From 2f825633518decf73d7f6c70d9bbd71edd1306b5 Mon Sep 17 00:00:00 2001 From: huangmingg Date: Sat, 10 Feb 2024 19:08:35 +0800 Subject: [PATCH 2/5] fix(core) Addressed PR comments --- Cargo.lock | 1 - core/tauri-runtime-wry/Cargo.toml | 3 +-- core/tauri-runtime-wry/src/lib.rs | 4 +++- core/tauri/src/manager.rs | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef2b36084a33..cfb8e87bf142 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4190,7 +4190,6 @@ dependencies = [ "tauri-runtime", "tauri-utils", "tracing", - "urlencoding", "uuid", "webkit2gtk", "webview2-com", diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 5cb093ed6ef3..e36d1d4bbc48 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -21,7 +21,7 @@ rand = "0.8" raw-window-handle = "0.5" tracing = { version = "0.1", optional = true } arboard = { version = "3", optional = true } -urlencoding = "2.1.3" +percent-encoding = "2.1" [target."cfg(windows)".dependencies] webview2-com = "0.19.1" @@ -33,7 +33,6 @@ webview2-com = "0.19.1" [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] gtk = { version = "0.15", features = [ "v3_20" ] } webkit2gtk = { version = "0.18.2", features = [ "v2_22" ] } -percent-encoding = "2.1" [target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies] cocoa = "0.24" diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 2f85bf66739c..6dab56e637ee 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -3259,7 +3259,9 @@ fn create_webview( // the content should be fully decoded when passing to WRY, which // will be responsible for handling the encoding based on the OS. let html = - urlencoding::decode(html_string).map_err(|e| Error::CreateWebview(Box::new(e)))?; + percent_encoding::percent_decode_str(html_string) + .decode_utf8() + .map_err(|e| Error::CreateWebview(Box::new(e)))?; webview_builder .with_html(html) diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 508e844f4e40..fd6aa0af1517 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -1012,7 +1012,8 @@ impl WindowManager { // There is an issue with the external DataUrl where HTML containing special characters // are not correctly processed. A workaround is to first percent encode the html string, // before it processed by DataUrl. - if let Ok(data_url) = data_url::DataUrl::process(&urlencoding::encode(&html_string)) { + let encoded_string = percent_encoding::utf8_percent_encode(&html_string, percent_encoding::NON_ALPHANUMERIC).to_string(); + if let Ok(data_url) = data_url::DataUrl::process(&encoded_string) { if let Ok((body, _)) = data_url.decode_to_vec() { let html = String::from_utf8_lossy(&body).into_owned(); let mut document = tauri_utils::html::parse(html); From b14ebac885983ac47913d98e313f9be8f4320139 Mon Sep 17 00:00:00 2001 From: huangmingg Date: Sat, 10 Feb 2024 21:45:23 +0800 Subject: [PATCH 3/5] fix(core) Addressed PR comments --- core/tauri-runtime-wry/src/lib.rs | 16 +++--------- core/tauri-runtime/src/window.rs | 10 +++----- core/tauri/src/manager.rs | 41 ++++++++++++++++++------------- 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 6dab56e637ee..08a0b4d043e7 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -3247,28 +3247,18 @@ fn create_webview( let _ = center_window(&window, window.inner_size()); } - let url_str = url.to_string(); - let mut webview_builder = WebViewBuilder::new(window).map_err(|e| Error::CreateWebview(Box::new(e)))?; // use with_html method if html content can be extracted from url. // else defaults to with_url method - webview_builder = if let Some(html_string) = tauri_utils::html::extract_html_content(&url_str) { - // we run a decode here on the html_string for sanity check - // the content should be fully decoded when passing to WRY, which - // will be responsible for handling the encoding based on the OS. - let html = - percent_encoding::percent_decode_str(html_string) - .decode_utf8() - .map_err(|e| Error::CreateWebview(Box::new(e)))?; - + webview_builder = if let Some(html_string) = tauri_utils::html::extract_html_content(&url) { webview_builder - .with_html(html) + .with_html(html_string) .map_err(|e| Error::CreateWebview(Box::new(e)))? } else { webview_builder - .with_url(&url_str) + .with_url(&url) .map_err(|e| Error::CreateWebview(Box::new(e)))? }; diff --git a/core/tauri-runtime/src/window.rs b/core/tauri-runtime/src/window.rs index 528e91fe0f3b..34a5f5118427 100644 --- a/core/tauri-runtime/src/window.rs +++ b/core/tauri-runtime/src/window.rs @@ -241,8 +241,8 @@ pub struct PendingWindow> { pub web_resource_request_handler: Option>, - /// The URL to load on the webview. - pub url: Url, + /// The resolved URL to load on the webview. + pub url: String, } pub fn is_label_valid(label: &str) -> bool { @@ -283,8 +283,7 @@ impl> PendingWindow { js_event_listeners: Default::default(), navigation_handler: Default::default(), web_resource_request_handler: Default::default(), - // This will never fail - url: Url::parse("tauri://localhost").unwrap(), + url: "tauri://localhost".to_string(), http_scheme: false, }) } @@ -316,8 +315,7 @@ impl> PendingWindow { js_event_listeners: Default::default(), navigation_handler: Default::default(), web_resource_request_handler: Default::default(), - // This will never fail - url: Url::parse("tauri://localhost").unwrap(), + url: "tauri://localhost".to_string(), http_scheme: false, }) } diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index fd6aa0af1517..7624da7cb7a5 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -499,17 +499,18 @@ impl WindowManager { }); } - let window_origin = if pending.url.scheme() == "data" { + let window_url = Url::parse(&pending.url).unwrap(); + let window_origin = if window_url.scheme() == "data" { "null".into() - } else if cfg!(windows) && pending.url.scheme() != "http" && pending.url.scheme() != "https" { + } else if cfg!(windows) && window_url.scheme() != "http" && window_url.scheme() != "https" { let scheme = if pending.http_scheme { "http" } else { "https" }; - format!("{scheme}://{}.localhost", pending.url.scheme()) + format!("{scheme}://{}.localhost", window_url.scheme()) } else { format!( "{}://{}{}", - pending.url.scheme(), - pending.url.host().unwrap(), - pending.url + window_url.scheme(), + window_url.host().unwrap(), + window_url .port() .map(|p| format!(":{p}")) .unwrap_or_default() @@ -1006,29 +1007,35 @@ impl WindowManager { )); } - #[cfg(feature = "window-data-url")] match (url.scheme(), tauri_utils::html::extract_html_content(url.as_str())) { + #[cfg(feature = "window-data-url")] ("data", Some(html_string)) => { // There is an issue with the external DataUrl where HTML containing special characters // are not correctly processed. A workaround is to first percent encode the html string, // before it processed by DataUrl. - let encoded_string = percent_encoding::utf8_percent_encode(&html_string, percent_encoding::NON_ALPHANUMERIC).to_string(); - if let Ok(data_url) = data_url::DataUrl::process(&encoded_string) { - if let Ok((body, _)) = data_url.decode_to_vec() { - let html = String::from_utf8_lossy(&body).into_owned(); + let encoded_string = urlencoding::encode(html_string); + let url = data_url::DataUrl::process(&format!("data:text/html,{}", encoded_string)) + .map_err(|_| crate::Error::InvalidWindowUrl("Failed to process data url")) + .and_then(|data_url| data_url.decode_to_vec().map_err(|_| crate::Error::InvalidWindowUrl("Failed to decode processed data url"))) + .and_then(|(body, _)| { + let html = String::from_utf8_lossy(&body).into_owned(); let mut document = tauri_utils::html::parse(html); if let Some(csp) = self.csp() { tauri_utils::html::inject_csp(&mut document, &csp.to_string()); } - url.set_path(&format!("text/html,{}", document.to_string())); - } - } + // decode back to raw html, as the content should be fully decoded + // when passing to wry / tauri-runtime-wry, which will be responsible + // for handling the encoding based on the OS. + let encoded_html = document.to_string(); + Ok(percent_encoding::percent_decode_str(encoded_html.as_str()).decode_utf8_lossy().to_string()) + }).unwrap_or(html_string.to_string()); + pending.url = format!("data:text/html,{}", url); + } + _ => { + pending.url = url.to_string(); } - _ => {} }; - pending.url = url; - if !pending.window_builder.has_icon() { if let Some(default_window_icon) = self.inner.default_window_icon.clone() { pending.window_builder = pending From f91ab78e76282b27b4213b304a3657713f77b9f4 Mon Sep 17 00:00:00 2001 From: huangmingg Date: Sat, 10 Feb 2024 22:24:16 +0800 Subject: [PATCH 4/5] fix(core) Remove data url example --- core/tauri-runtime-wry/src/lib.rs | 20 +++++------ core/tauri-utils/src/config.rs | 2 +- core/tauri-utils/src/html.rs | 4 +-- core/tauri/Cargo.toml | 5 --- core/tauri/src/manager.rs | 56 +++++++++++++++++++------------ examples/data-url/README.md | 3 -- examples/data-url/index.html | 11 ------ examples/data-url/main.rs | 46 ------------------------- examples/data-url/tauri.conf.json | 55 ------------------------------ 9 files changed, 47 insertions(+), 155 deletions(-) delete mode 100644 examples/data-url/README.md delete mode 100644 examples/data-url/index.html delete mode 100644 examples/data-url/main.rs delete mode 100644 examples/data-url/tauri.conf.json diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 08a0b4d043e7..0c72affba7ac 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -3248,24 +3248,24 @@ fn create_webview( } let mut webview_builder = - WebViewBuilder::new(window).map_err(|e| Error::CreateWebview(Box::new(e)))?; + WebViewBuilder::new(window).map_err(|e| Error::CreateWebview(Box::new(e)))?; // use with_html method if html content can be extracted from url. // else defaults to with_url method webview_builder = if let Some(html_string) = tauri_utils::html::extract_html_content(&url) { - webview_builder - .with_html(html_string) - .map_err(|e| Error::CreateWebview(Box::new(e)))? + webview_builder + .with_html(html_string) + .map_err(|e| Error::CreateWebview(Box::new(e)))? } else { - webview_builder - .with_url(&url) - .map_err(|e| Error::CreateWebview(Box::new(e)))? + webview_builder + .with_url(&url) + .map_err(|e| Error::CreateWebview(Box::new(e)))? }; webview_builder = webview_builder - .with_focused(focused) - .with_transparent(is_window_transparent) - .with_accept_first_mouse(webview_attributes.accept_first_mouse); + .with_focused(focused) + .with_transparent(is_window_transparent) + .with_accept_first_mouse(webview_attributes.accept_first_mouse); if webview_attributes.file_drop_handler_enabled { webview_builder = webview_builder diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 0a820e5b4ec1..29908fee0c84 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -63,7 +63,7 @@ pub enum WindowUrl { impl fmt::Display for WindowUrl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::External(url)=> write!(f, "{url}"), + Self::External(url) => write!(f, "{url}"), #[cfg(feature = "window-data-url")] Self::DataUrl(url) => write!(f, "{url}"), Self::App(path) => write!(f, "{}", path.display()), diff --git a/core/tauri-utils/src/html.rs b/core/tauri-utils/src/html.rs index e066e09e447e..6fd356983327 100644 --- a/core/tauri-utils/src/html.rs +++ b/core/tauri-utils/src/html.rs @@ -294,9 +294,9 @@ pub fn is_html(data_string: &str) -> bool { /// Temporary naive method to extract data from html data string pub fn extract_html_content(input: &str) -> Option<&str> { if input.starts_with("data:text/html,") { - Some(&input[15..]) + Some(&input[15..]) } else { - None + None } } diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 0877460d759a..4e1e1a279dd5 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -359,8 +359,3 @@ path = "../../examples/streaming/main.rs" name = "isolation" path = "../../examples/isolation/main.rs" required-features = [ "isolation" ] - -[[example]] -name = "data-url" -path = "../../examples/data-url/main.rs" -required-features = [ "window-data-url" ] \ No newline at end of file diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 7624da7cb7a5..7f696774dffc 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -1007,34 +1007,46 @@ impl WindowManager { )); } - match (url.scheme(), tauri_utils::html::extract_html_content(url.as_str())) { + match ( + url.scheme(), + tauri_utils::html::extract_html_content(url.as_str()), + ) { #[cfg(feature = "window-data-url")] ("data", Some(html_string)) => { - // There is an issue with the external DataUrl where HTML containing special characters - // are not correctly processed. A workaround is to first percent encode the html string, - // before it processed by DataUrl. - let encoded_string = urlencoding::encode(html_string); - let url = data_url::DataUrl::process(&format!("data:text/html,{}", encoded_string)) - .map_err(|_| crate::Error::InvalidWindowUrl("Failed to process data url")) - .and_then(|data_url| data_url.decode_to_vec().map_err(|_| crate::Error::InvalidWindowUrl("Failed to decode processed data url"))) - .and_then(|(body, _)| { - let html = String::from_utf8_lossy(&body).into_owned(); - let mut document = tauri_utils::html::parse(html); - if let Some(csp) = self.csp() { - tauri_utils::html::inject_csp(&mut document, &csp.to_string()); - } - // decode back to raw html, as the content should be fully decoded - // when passing to wry / tauri-runtime-wry, which will be responsible - // for handling the encoding based on the OS. - let encoded_html = document.to_string(); - Ok(percent_encoding::percent_decode_str(encoded_html.as_str()).decode_utf8_lossy().to_string()) - }).unwrap_or(html_string.to_string()); - pending.url = format!("data:text/html,{}", url); + // There is an issue with the external DataUrl where HTML containing special characters + // are not correctly processed. A workaround is to first percent encode the html string, + // before it processed by DataUrl. + let encoded_string = urlencoding::encode(html_string); + let url = data_url::DataUrl::process(&format!("data:text/html,{}", encoded_string)) + .map_err(|_| crate::Error::InvalidWindowUrl("Failed to process data url")) + .and_then(|data_url| { + data_url + .decode_to_vec() + .map_err(|_| crate::Error::InvalidWindowUrl("Failed to decode processed data url")) + }) + .and_then(|(body, _)| { + let html = String::from_utf8_lossy(&body).into_owned(); + let mut document = tauri_utils::html::parse(html); + if let Some(csp) = self.csp() { + tauri_utils::html::inject_csp(&mut document, &csp.to_string()); + } + // decode back to raw html, as the content should be fully decoded + // when passing to wry / tauri-runtime-wry, which will be responsible + // for handling the encoding based on the OS. + let encoded_html = document.to_string(); + Ok( + percent_encoding::percent_decode_str(encoded_html.as_str()) + .decode_utf8_lossy() + .to_string(), + ) + }) + .unwrap_or(html_string.to_string()); + pending.url = format!("data:text/html,{}", url); } _ => { pending.url = url.to_string(); } - }; + }; if !pending.window_builder.has_icon() { if let Some(default_window_icon) = self.inner.default_window_icon.clone() { diff --git a/examples/data-url/README.md b/examples/data-url/README.md deleted file mode 100644 index 5ce92661ce53..000000000000 --- a/examples/data-url/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Data Url Example - -To execute run the following on the root directory of the repository: `cargo run --example data-url --features window-data-url`. diff --git a/examples/data-url/index.html b/examples/data-url/index.html deleted file mode 100644 index 30db0bd4eb99..000000000000 --- a/examples/data-url/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Welcome to Tauri! - - -

Welcome to Tauri!

- - diff --git a/examples/data-url/main.rs b/examples/data-url/main.rs deleted file mode 100644 index 5094dd97b7ed..000000000000 --- a/examples/data-url/main.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use tauri::WindowBuilder; - -#[cfg(not(feature = "window-data-url"))] -fn main() { - compile_error!("Feature `window-data-url` is required to run this example"); -} - -#[cfg(feature = "window-data-url")] -fn main() { - - tauri::Builder::default() - .setup(|app| { - let html = r#" - - - /+&'=#%?\^`{}|[]~
- Hello World
- สวัสดีชาวโลก!
- 你好世界!
- - "#; - let data = format!("data:text/html,{}", html); - #[allow(unused_mut)] - let mut builder = - WindowBuilder::new( - app, - "Rust".to_string(), - tauri::WindowUrl::DataUrl(data.parse().unwrap())); - #[cfg(target_os = "macos")] - { - builder = builder.tabbing_identifier("Rust"); - } - let _window = builder.title("Tauri - Rust").build()?; - - Ok(()) - }) - .run(tauri::generate_context!( - "../../examples/data-url/tauri.conf.json" - )) - .expect("error while running tauri application"); -} diff --git a/examples/data-url/tauri.conf.json b/examples/data-url/tauri.conf.json deleted file mode 100644 index 1931e5066a5d..000000000000 --- a/examples/data-url/tauri.conf.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$schema": "../../core/tauri-config-schema/schema.json", - "build": { - "distDir": ["index.html"], - "devPath": ["index.html"], - "beforeDevCommand": "", - "beforeBuildCommand": "" - }, - "package": { - "productName": "Data Url", - "version": "0.1.0" - }, - "tauri": { - "bundle": { - "active": true, - "targets": "all", - "identifier": "com.tauri.dev", - "icon": [ - "../.icons/32x32.png", - "../.icons/128x128.png", - "../.icons/128x128@2x.png", - "../.icons/icon.icns", - "../.icons/icon.ico" - ], - "resources": [], - "externalBin": [], - "copyright": "", - "category": "DeveloperTool", - "shortDescription": "", - "longDescription": "", - "deb": { - "depends": [] - }, - "macOS": { - "frameworks": [], - "exceptionDomain": "" - } - }, - "allowlist": { - "all": false - }, - "windows": [ - { - "title": "Welcome to Tauri!", - "width": 800, - "height": 600, - "resizable": true, - "fullscreen": false - } - ], - "security": { - "csp": "default-src 'self'" - } - } -} From 7cdf2418850f04304d7e087117c13d2edcc76fd1 Mon Sep 17 00:00:00 2001 From: huangmingg Date: Thu, 15 Feb 2024 17:14:52 +0800 Subject: [PATCH 5/5] fix(windows) Addressed PR comments --- Cargo.lock | 7 ------- core/tauri/Cargo.toml | 1 - core/tauri/src/manager.rs | 2 +- core/tauri/src/test/mock_runtime.rs | 4 ++-- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c542483e1209..aa3f9849b280 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4083,7 +4083,6 @@ dependencies = [ "tokio-test", "tracing", "url", - "urlencoding", "uuid", "webkit2gtk", "webview2-com", @@ -4691,12 +4690,6 @@ dependencies = [ "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf-8" version = "0.7.6" diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index e5a01d119052..35d93d533e3a 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -96,7 +96,6 @@ encoding_rs = "0.8.31" sys-locale = { version = "0.2.3", optional = true } tracing = { version = "0.1", optional = true } indexmap = { version = "1", features = [ "std", "serde" ], optional = true } -urlencoding = "2.1.3" [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] rfd = { version = "0.10", optional = true, features = [ "gtk3", "common-controls-v6" ] } diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 7f696774dffc..ecb21296d313 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -1016,7 +1016,7 @@ impl WindowManager { // There is an issue with the external DataUrl where HTML containing special characters // are not correctly processed. A workaround is to first percent encode the html string, // before it processed by DataUrl. - let encoded_string = urlencoding::encode(html_string); + let encoded_string = percent_encoding::utf8_percent_encode(html_string, percent_encoding::NON_ALPHANUMERIC).to_string(); let url = data_url::DataUrl::process(&format!("data:text/html,{}", encoded_string)) .map_err(|_| crate::Error::InvalidWindowUrl("Failed to process data url")) .and_then(|data_url| { diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index afc1f36a2abe..8fe083dfd49e 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -174,7 +174,7 @@ impl RuntimeHandle for MockRuntimeHandle { pub struct MockDispatcher { id: WindowId, context: RuntimeContext, - url: url::Url, + url: String, last_evaluated_script: Arc>>, } @@ -417,7 +417,7 @@ impl Dispatch for MockDispatcher { } fn url(&self) -> Result { - Ok(self.url.clone()) + self.url.parse().map_err(|_| Error::FailedToReceiveMessage) } fn scale_factor(&self) -> Result {