Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add use_https_scheme for Windows and Android #11477

Merged
merged 11 commits into from
Nov 5, 2024
9 changes: 9 additions & 0 deletions .changes/use_https_windows-and-android-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"tauri": "minor:feat"
"tauri-utils": "minor:feat"
---

Add `app > windows > useHttpsScheme` config option to choose whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android



10 changes: 10 additions & 0 deletions .changes/use_https_windows-and-android.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"tauri": "minor:feat"
"tauri-runtime": "minor:feat"
"tauri-runtime-wry": "minor:feat"
---

Add `WebviewWindowBuilder/WebviewBuilder::use_https_scheme` to choose whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android



5 changes: 5 additions & 0 deletions crates/tauri-cli/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,11 @@
"description": "Whether browser extensions can be installed for the webview process\n\n ## Platform-specific:\n\n - **Windows**: Enables the WebView2 environment's [`AreBrowserExtensionsEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2environmentoptions?view=webview2-winrt-1.0.2739.15#arebrowserextensionsenabled)\n - **MacOS / Linux / iOS / Android** - Unsupported.",
"default": false,
"type": "boolean"
},
"useHttpsScheme": {
"description": "Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.\n\n ## Note\n\n Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.\n\n ## Warning\n\n Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
14 changes: 8 additions & 6 deletions crates/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,8 @@ impl WindowBuilder for WindowBuilderWrapper {
builder = builder.title_bar_style(TitleBarStyle::Visible);
}

builder = builder.title("Tauri App");

builder
}

Expand Down Expand Up @@ -4026,6 +4028,11 @@ fn create_webview<T: UserEvent>(
.with_clipboard(webview_attributes.clipboard)
.with_hotkeys_zoom(webview_attributes.zoom_hotkeys_enabled);

#[cfg(any(target_os = "windows", target_os = "android"))]
{
webview_builder = webview_builder.with_https_scheme(webview_attributes.use_https_scheme);
}

if webview_attributes.drag_drop_handler_enabled {
let proxy = context.proxy.clone();
let window_id_ = window_id.clone();
Expand Down Expand Up @@ -4168,11 +4175,6 @@ fn create_webview<T: UserEvent>(
});
}

#[cfg(windows)]
{
webview_builder = webview_builder.with_https_scheme(false);
}

#[cfg(windows)]
{
webview_builder = webview_builder
Expand Down Expand Up @@ -4282,7 +4284,7 @@ fn create_webview<T: UserEvent>(
builder
}
}
.map_err(|e| Error::CreateWebview(Box::new(dbg!(e))))?;
.map_err(|e| Error::CreateWebview(Box::new(e)))?;

if kind == WebviewKind::WindowContent {
#[cfg(any(
Expand Down
18 changes: 18 additions & 0 deletions crates/tauri-runtime/src/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ pub struct WebviewAttributes {
pub proxy_url: Option<Url>,
pub zoom_hotkeys_enabled: bool,
pub browser_extensions_enabled: bool,
pub use_https_scheme: bool,
}

impl From<&WindowConfig> for WebviewAttributes {
Expand All @@ -218,6 +219,7 @@ impl From<&WindowConfig> for WebviewAttributes {
.incognito(config.incognito)
.focused(config.focus)
.zoom_hotkeys_enabled(config.zoom_hotkeys_enabled)
.use_https_scheme(config.use_https_scheme)
.browser_extensions_enabled(config.browser_extensions_enabled);
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
{
Expand Down Expand Up @@ -264,6 +266,7 @@ impl WebviewAttributes {
proxy_url: None,
zoom_hotkeys_enabled: false,
browser_extensions_enabled: false,
use_https_scheme: false,
}
}

Expand Down Expand Up @@ -388,6 +391,21 @@ impl WebviewAttributes {
self.browser_extensions_enabled = enabled;
self
}

/// Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.
///
/// ## Note
///
/// Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
///
/// ## Warning
///
/// Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data.
#[must_use]
pub fn use_https_scheme(mut self, enabled: bool) -> Self {
self.use_https_scheme = enabled;
self
}
}

/// IPC handler.
Expand Down
5 changes: 5 additions & 0 deletions crates/tauri-schema-generator/schemas/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,11 @@
"description": "Whether browser extensions can be installed for the webview process\n\n ## Platform-specific:\n\n - **Windows**: Enables the WebView2 environment's [`AreBrowserExtensionsEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2environmentoptions?view=webview2-winrt-1.0.2739.15#arebrowserextensionsenabled)\n - **MacOS / Linux / iOS / Android** - Unsupported.",
"default": false,
"type": "boolean"
},
"useHttpsScheme": {
"description": "Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.\n\n ## Note\n\n Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.\n\n ## Warning\n\n Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
17 changes: 16 additions & 1 deletion crates/tauri-utils/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,18 @@ pub struct WindowConfig {
/// - **MacOS / Linux / iOS / Android** - Unsupported.
#[serde(default)]
pub browser_extensions_enabled: bool,

/// Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.
///
/// ## Note
///
/// Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
///
/// ## Warning
///
/// Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data.
#[serde(default, alias = "use-https-scheme")]
pub use_https_scheme: bool,
}

impl Default for WindowConfig {
Expand Down Expand Up @@ -1534,6 +1546,7 @@ impl Default for WindowConfig {
proxy_url: None,
zoom_hotkeys_enabled: false,
browser_extensions_enabled: false,
use_https_scheme: false,
}
}
}
Expand Down Expand Up @@ -2505,6 +2518,7 @@ mod build {
let parent = opt_str_lit(self.parent.as_ref());
let zoom_hotkeys_enabled = self.zoom_hotkeys_enabled;
let browser_extensions_enabled = self.browser_extensions_enabled;
let use_https_scheme = self.use_https_scheme;

literal_struct!(
tokens,
Expand Down Expand Up @@ -2551,7 +2565,8 @@ mod build {
incognito,
parent,
zoom_hotkeys_enabled,
browser_extensions_enabled
browser_extensions_enabled,
use_https_scheme
);
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/tauri/scripts/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
}

const osName = __TEMPLATE_os_name__
const protocolScheme = __TEMPLATE_protocol_scheme__

Object.defineProperty(window.__TAURI_INTERNALS__, 'convertFileSrc', {
value: function (filePath, protocol = 'asset') {
const path = encodeURIComponent(filePath)
return osName === 'windows' || osName === 'android'
? `http://${protocol}.localhost/${path}`
? `${protocolScheme}://${protocol}.localhost/${path}`
: `${protocol}://localhost/${path}`
}
})
Expand Down
12 changes: 10 additions & 2 deletions crates/tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ pub struct AssetResolver<R: Runtime> {
}

impl<R: Runtime> AssetResolver<R> {
/// Same as [AssetResolver::get_for_scheme] but always resolves with csp header for `http` scheme.
pub fn get(&self, path: String) -> Option<Asset> {
self.get_for_scheme(path, false)
}

/// Gets the app asset associated with the given path.
///
/// Resolves to the embedded asset that is part of the app
Expand All @@ -275,7 +280,10 @@ impl<R: Runtime> AssetResolver<R> {
///
/// Fallbacks to reading the asset from the [distDir] folder so the behavior is consistent in development.
/// Note that the dist directory must exist so you might need to build your frontend assets first.
pub fn get(&self, path: String) -> Option<Asset> {
///
/// - `use_https_scheme`: If `true` when using [`Pattern::Isolation`](tauri::Pattern::Isolation),
/// the csp header will contain `https://tauri.localhost` instead of `http://tauri.localhost`
pub fn get_for_scheme(&self, path: String, use_https_scheme: bool) -> Option<Asset> {
#[cfg(dev)]
{
// on dev if the devPath is a path to a directory we have the embedded assets
Expand Down Expand Up @@ -306,7 +314,7 @@ impl<R: Runtime> AssetResolver<R> {
}
}

self.manager.get_asset(path).ok()
self.manager.get_asset(path, use_https_scheme).ok()
}

/// Iterate on all assets.
Expand Down
29 changes: 21 additions & 8 deletions crates/tauri/src/manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,10 @@ impl<R: Runtime> AppManager<R> {
self.config.build.dev_url.as_ref()
}

pub(crate) fn protocol_url(&self) -> Cow<'_, Url> {
pub(crate) fn protocol_url(&self, https: bool) -> Cow<'_, Url> {
if cfg!(windows) || cfg!(target_os = "android") {
Cow::Owned(Url::parse("http://tauri.localhost").unwrap())
let https = if https { "https" } else { "http" };
Cow::Owned(Url::parse(&format!("{https}://tauri.localhost")).unwrap())
} else {
Cow::Owned(Url::parse("tauri://localhost").unwrap())
}
Expand All @@ -351,10 +352,10 @@ impl<R: Runtime> AppManager<R> {
/// Get the base URL to use for webview requests.
///
/// In dev mode, this will be based on the `devUrl` configuration value.
pub(crate) fn get_url(&self) -> Cow<'_, Url> {
pub(crate) fn get_url(&self, https: bool) -> Cow<'_, Url> {
match self.base_path() {
Some(url) => Cow::Borrowed(url),
_ => self.protocol_url(),
_ => self.protocol_url(https),
}
}

Expand All @@ -372,7 +373,11 @@ impl<R: Runtime> AppManager<R> {
}
}

pub fn get_asset(&self, mut path: String) -> Result<Asset, Box<dyn std::error::Error>> {
pub fn get_asset(
&self,
mut path: String,
use_https_schema: bool,
) -> Result<Asset, Box<dyn std::error::Error>> {
let assets = &self.assets;
if path.ends_with('/') {
path.pop();
Expand Down Expand Up @@ -435,7 +440,7 @@ impl<R: Runtime> AppManager<R> {
let default_src = csp_map
.entry("default-src".into())
.or_insert_with(Default::default);
default_src.push(crate::pattern::format_real_schema(schema));
default_src.push(crate::pattern::format_real_schema(schema, use_https_schema));
}

csp_header.replace(Csp::DirectiveMap(csp_map).to_string());
Expand Down Expand Up @@ -771,17 +776,25 @@ mod test {
#[cfg(custom_protocol)]
{
assert_eq!(
manager.get_url().to_string(),
manager.get_url(false).to_string(),
if cfg!(windows) || cfg!(target_os = "android") {
"http://tauri.localhost/"
} else {
"tauri://localhost"
}
);
assert_eq!(
manager.get_url(true).to_string(),
if cfg!(windows) || cfg!(target_os = "android") {
"https://tauri.localhost/"
} else {
"tauri://localhost"
}
);
}

#[cfg(dev)]
assert_eq!(manager.get_url().to_string(), "http://localhost:4000/");
assert_eq!(manager.get_url(false).to_string(), "http://localhost:4000/");
}

struct EventSetup {
Expand Down
20 changes: 15 additions & 5 deletions crates/tauri/src/manager/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,14 @@ impl<R: Runtime> WebviewManager<R> {

let mut webview_attributes = pending.webview_attributes;

let use_https_scheme = webview_attributes.use_https_scheme;

let ipc_init = IpcJavascript {
isolation_origin: &match &*app_manager.pattern {
#[cfg(feature = "isolation")]
crate::Pattern::Isolation { schema, .. } => crate::pattern::format_real_schema(schema),
crate::Pattern::Isolation { schema, .. } => {
crate::pattern::format_real_schema(schema, use_https_scheme)
}
_ => "".to_string(),
},
}
Expand Down Expand Up @@ -180,6 +184,7 @@ impl<R: Runtime> WebviewManager<R> {
&ipc_init.into_string(),
&pattern_init.into_string(),
is_init_global,
use_https_scheme,
)?);

for plugin_init_script in plugin_init_scripts {
Expand All @@ -190,7 +195,7 @@ impl<R: Runtime> WebviewManager<R> {
if let crate::Pattern::Isolation { schema, .. } = &*app_manager.pattern {
webview_attributes = webview_attributes.initialization_script(
&IsolationJavascript {
isolation_src: &crate::pattern::format_real_schema(schema),
isolation_src: &crate::pattern::format_real_schema(schema, use_https_scheme),
style: tauri_utils::pattern::isolation::IFRAME_STYLE,
}
.render_default(&Default::default())?
Expand Down Expand Up @@ -232,7 +237,8 @@ impl<R: Runtime> WebviewManager<R> {
&& window_url.scheme() != "http"
&& window_url.scheme() != "https"
{
format!("http://{}.localhost", window_url.scheme())
let https = if use_https_scheme { "https" } else { "http" };
format!("{https}://{}.localhost", window_url.scheme())
} else if let Some(host) = window_url.host() {
format!(
"{}://{}{}",
Expand Down Expand Up @@ -320,6 +326,7 @@ impl<R: Runtime> WebviewManager<R> {
assets.clone(),
*crypto_keys.aes_gcm().raw(),
window_origin,
use_https_scheme,
);
pending.register_uri_scheme_protocol(schema, move |webview_id, request, responder| {
protocol(webview_id, request, UriSchemeResponder(responder))
Expand All @@ -335,6 +342,7 @@ impl<R: Runtime> WebviewManager<R> {
ipc_script: &str,
pattern_script: &str,
with_global_tauri: bool,
use_https_scheme: bool,
) -> crate::Result<String> {
#[derive(Template)]
#[default_template("../../scripts/init.js")]
Expand All @@ -357,6 +365,7 @@ impl<R: Runtime> WebviewManager<R> {
#[default_template("../../scripts/core.js")]
struct CoreJavascript<'a> {
os_name: &'a str,
protocol_scheme: &'a str,
invoke_key: &'a str,
}

Expand All @@ -378,6 +387,7 @@ impl<R: Runtime> WebviewManager<R> {
bundle_script,
core_script: &CoreJavascript {
os_name: std::env::consts::OS,
protocol_scheme: if use_https_scheme { "https" } else { "http" },
invoke_key: self.invoke_key(),
}
.render_default(&Default::default())?
Expand Down Expand Up @@ -411,7 +421,7 @@ impl<R: Runtime> WebviewManager<R> {
let url = if PROXY_DEV_SERVER {
Cow::Owned(Url::parse("tauri://localhost").unwrap())
} else {
app_manager.get_url()
app_manager.get_url(pending.webview_attributes.use_https_scheme)
};
// ignore "index.html" just to simplify the url
if path.to_str() != Some("index.html") {
Expand All @@ -425,7 +435,7 @@ impl<R: Runtime> WebviewManager<R> {
}
}
WebviewUrl::External(url) => {
let config_url = app_manager.get_url();
let config_url = app_manager.get_url(pending.webview_attributes.use_https_scheme);
let is_local = config_url.make_relative(url).is_some();
let mut url = url.clone();
if is_local && PROXY_DEV_SERVER {
Expand Down
Loading
Loading