mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-02-06 13:57:16 +00:00
feat: add use_https_scheme for Windows and Android (#11477)
* feat: add `use_https_scheme` for Windows and Android closes #11252 * fix compilation * Apply suggestions from code review Co-authored-by: Fabian-Lars <github@fabianlars.de> * change wording * add migrations * migrate `dangerousUseHttpScheme` * fmt * infer AssetResolver::get https scheme config * fix tests --------- Co-authored-by: Fabian-Lars <github@fabianlars.de> Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
parent
058c0db72f
commit
f37e97d410
6
.changes/use_https_windows-and-android-config.md
Normal file
6
.changes/use_https_windows-and-android-config.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"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
|
||||
7
.changes/use_https_windows-and-android.md
Normal file
7
.changes/use_https_windows-and-android.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
"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
|
||||
@ -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
|
||||
|
||||
@ -87,6 +87,28 @@ fn migrate_config(config: &mut Value) -> Result<MigratedConfig> {
|
||||
migrated.permissions = permissions;
|
||||
}
|
||||
|
||||
// dangerousUseHttpScheme/useHttpsScheme
|
||||
let dangerouse_use_http = tauri_config
|
||||
.get("security")
|
||||
.and_then(|w| w.as_object())
|
||||
.and_then(|w| {
|
||||
w.get("dangerousUseHttpScheme")
|
||||
.or_else(|| w.get("dangerous-use-http-scheme"))
|
||||
})
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(windows) = tauri_config
|
||||
.get_mut("windows")
|
||||
.and_then(|w| w.as_array_mut())
|
||||
{
|
||||
for window in windows {
|
||||
if let Some(window) = window.as_object_mut() {
|
||||
window.insert("useHttpsScheme".to_string(), (!dangerouse_use_http).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// security
|
||||
if let Some(security) = tauri_config
|
||||
.get_mut("security")
|
||||
@ -802,7 +824,8 @@ mod test {
|
||||
"pattern": { "use": "brownfield" },
|
||||
"security": {
|
||||
"csp": "default-src 'self' tauri:"
|
||||
}
|
||||
},
|
||||
"windows": [{}]
|
||||
}
|
||||
});
|
||||
|
||||
@ -907,6 +930,8 @@ mod test {
|
||||
migrated["app"]["withGlobalTauri"],
|
||||
original["build"]["withGlobalTauri"]
|
||||
);
|
||||
|
||||
assert_eq!(migrated["app"]["windows"][0]["useHttpsScheme"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -941,6 +966,28 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migrate_dangerous_use_http_scheme() {
|
||||
let original = serde_json::json!({
|
||||
"tauri": {
|
||||
"windows": [{}],
|
||||
"security": {
|
||||
"dangerousUseHttpScheme": true,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let migrated = migrate(&original);
|
||||
assert_eq!(
|
||||
!migrated["app"]["windows"][0]["useHttpsScheme"]
|
||||
.as_bool()
|
||||
.unwrap(),
|
||||
original["tauri"]["security"]["dangerousUseHttpScheme"]
|
||||
.as_bool()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_migrate_default_config() {
|
||||
let original = serde_json::to_value(tauri_utils_v1::config::Config::default()).unwrap();
|
||||
|
||||
@ -22,8 +22,8 @@ use tauri_runtime::{
|
||||
monitor::Monitor,
|
||||
webview::{DetachedWebview, DownloadEvent, PendingWebview, WebviewIpcHandler},
|
||||
window::{
|
||||
CursorIcon, DetachedWindow, DragDropEvent, PendingWindow, RawWindow, WebviewEvent,
|
||||
WindowBuilder, WindowBuilderBase, WindowEvent, WindowId, WindowSizeConstraints,
|
||||
CursorIcon, DetachedWindow, DetachedWindowWebview, DragDropEvent, PendingWindow, RawWindow,
|
||||
WebviewEvent, WindowBuilder, WindowBuilderBase, WindowEvent, WindowId, WindowSizeConstraints,
|
||||
},
|
||||
DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, ProgressBarState,
|
||||
ProgressBarStatus, Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType,
|
||||
@ -276,7 +276,16 @@ impl<T: UserEvent> Context<T> {
|
||||
let label = pending.label.clone();
|
||||
let context = self.clone();
|
||||
let window_id = self.next_window_id();
|
||||
let webview_id = pending.webview.as_ref().map(|_| context.next_webview_id());
|
||||
let (webview_id, use_https_scheme) = pending
|
||||
.webview
|
||||
.as_ref()
|
||||
.map(|w| {
|
||||
(
|
||||
Some(context.next_webview_id()),
|
||||
w.webview_attributes.use_https_scheme,
|
||||
)
|
||||
})
|
||||
.unwrap_or((None, false));
|
||||
|
||||
send_user_message(
|
||||
self,
|
||||
@ -300,13 +309,19 @@ impl<T: UserEvent> Context<T> {
|
||||
context: self.clone(),
|
||||
};
|
||||
|
||||
let detached_webview = webview_id.map(|id| DetachedWebview {
|
||||
label: label.clone(),
|
||||
dispatcher: WryWebviewDispatcher {
|
||||
window_id: Arc::new(Mutex::new(window_id)),
|
||||
webview_id: id,
|
||||
context: self.clone(),
|
||||
},
|
||||
let detached_webview = webview_id.map(|id| {
|
||||
let webview = DetachedWebview {
|
||||
label: label.clone(),
|
||||
dispatcher: WryWebviewDispatcher {
|
||||
window_id: Arc::new(Mutex::new(window_id)),
|
||||
webview_id: id,
|
||||
context: self.clone(),
|
||||
},
|
||||
};
|
||||
DetachedWindowWebview {
|
||||
webview,
|
||||
use_https_scheme,
|
||||
}
|
||||
});
|
||||
|
||||
Ok(DetachedWindow {
|
||||
@ -746,6 +761,8 @@ impl WindowBuilder for WindowBuilderWrapper {
|
||||
builder = builder.title_bar_style(TitleBarStyle::Visible);
|
||||
}
|
||||
|
||||
builder = builder.title("Tauri App");
|
||||
|
||||
builder
|
||||
}
|
||||
|
||||
@ -2497,10 +2514,16 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
) -> Result<DetachedWindow<T, Self>> {
|
||||
let label = pending.label.clone();
|
||||
let window_id = self.context.next_window_id();
|
||||
let webview_id = pending
|
||||
let (webview_id, use_https_scheme) = pending
|
||||
.webview
|
||||
.as_ref()
|
||||
.map(|_| self.context.next_webview_id());
|
||||
.map(|w| {
|
||||
(
|
||||
Some(self.context.next_webview_id()),
|
||||
w.webview_attributes.use_https_scheme,
|
||||
)
|
||||
})
|
||||
.unwrap_or((None, false));
|
||||
|
||||
let window = create_window(
|
||||
window_id,
|
||||
@ -2524,13 +2547,19 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
.borrow_mut()
|
||||
.insert(window_id, window);
|
||||
|
||||
let detached_webview = webview_id.map(|id| DetachedWebview {
|
||||
label: label.clone(),
|
||||
dispatcher: WryWebviewDispatcher {
|
||||
window_id: Arc::new(Mutex::new(window_id)),
|
||||
webview_id: id,
|
||||
context: self.context.clone(),
|
||||
},
|
||||
let detached_webview = webview_id.map(|id| {
|
||||
let webview = DetachedWebview {
|
||||
label: label.clone(),
|
||||
dispatcher: WryWebviewDispatcher {
|
||||
window_id: Arc::new(Mutex::new(window_id)),
|
||||
webview_id: id,
|
||||
context: self.context.clone(),
|
||||
},
|
||||
};
|
||||
DetachedWindowWebview {
|
||||
webview,
|
||||
use_https_scheme,
|
||||
}
|
||||
});
|
||||
|
||||
Ok(DetachedWindow {
|
||||
@ -4026,6 +4055,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();
|
||||
@ -4168,11 +4202,6 @@ fn create_webview<T: UserEvent>(
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
webview_builder = webview_builder.with_https_scheme(false);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
webview_builder = webview_builder
|
||||
@ -4282,7 +4311,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(
|
||||
|
||||
@ -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 {
|
||||
@ -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"))]
|
||||
{
|
||||
@ -264,6 +266,7 @@ impl WebviewAttributes {
|
||||
proxy_url: None,
|
||||
zoom_hotkeys_enabled: false,
|
||||
browser_extensions_enabled: false,
|
||||
use_https_scheme: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -512,7 +512,23 @@ pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
|
||||
pub dispatcher: R::WindowDispatcher,
|
||||
|
||||
/// The webview dispatcher in case this window has an attached webview.
|
||||
pub webview: Option<DetachedWebview<T, R>>,
|
||||
pub webview: Option<DetachedWindowWebview<T, R>>,
|
||||
}
|
||||
|
||||
/// A detached webview associated with a window.
|
||||
#[derive(Debug)]
|
||||
pub struct DetachedWindowWebview<T: UserEvent, R: Runtime<T>> {
|
||||
pub webview: DetachedWebview<T, R>,
|
||||
pub use_https_scheme: bool,
|
||||
}
|
||||
|
||||
impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWindowWebview<T, R> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
webview: self.webview.clone(),
|
||||
use_https_scheme: self.use_https_scheme,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWindow<T, R> {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1519,6 +1519,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 {
|
||||
@ -1567,6 +1579,7 @@ impl Default for WindowConfig {
|
||||
proxy_url: None,
|
||||
zoom_hotkeys_enabled: false,
|
||||
browser_extensions_enabled: false,
|
||||
use_https_scheme: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2538,6 +2551,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,
|
||||
@ -2584,7 +2598,8 @@ mod build {
|
||||
incognito,
|
||||
parent,
|
||||
zoom_hotkeys_enabled,
|
||||
browser_extensions_enabled
|
||||
browser_extensions_enabled,
|
||||
use_https_scheme
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}`
|
||||
}
|
||||
})
|
||||
|
||||
@ -268,6 +268,10 @@ pub struct AssetResolver<R: Runtime> {
|
||||
impl<R: Runtime> AssetResolver<R> {
|
||||
/// Gets the app asset associated with the given path.
|
||||
///
|
||||
/// By default it tries to infer your application's URL scheme in production by checking if all webviews
|
||||
/// were configured with [`crate::webview::WebviewBuilder::use_https_scheme`] or `tauri.conf.json > app > windows > useHttpsScheme`.
|
||||
/// If you are resolving an asset for a webview with a more dynamic configuration, see [`AssetResolver::get_for_scheme`].
|
||||
///
|
||||
/// Resolves to the embedded asset that is part of the app
|
||||
/// in dev when [`devUrl`](https://v2.tauri.app/reference/config/#devurl) points to a folder in your filesystem
|
||||
/// or in production when [`frontendDist`](https://v2.tauri.app/reference/config/#frontenddist)
|
||||
@ -276,6 +280,19 @@ 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> {
|
||||
let use_https_scheme = self
|
||||
.manager
|
||||
.webviews()
|
||||
.values()
|
||||
.all(|webview| webview.use_https_scheme());
|
||||
self.get_for_scheme(path, use_https_scheme)
|
||||
}
|
||||
|
||||
/// Same as [AssetResolver::get] but resolves the custom protocol scheme based on a parameter.
|
||||
///
|
||||
/// - `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
|
||||
@ -306,7 +323,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.
|
||||
|
||||
@ -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 scheme = if https { "https" } else { "http" };
|
||||
Cow::Owned(Url::parse(&format!("{scheme}://tauri.localhost")).unwrap())
|
||||
} else {
|
||||
Cow::Owned(Url::parse("tauri://localhost").unwrap())
|
||||
}
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
@ -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());
|
||||
@ -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 {
|
||||
|
||||
@ -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(),
|
||||
},
|
||||
}
|
||||
@ -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 {
|
||||
@ -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())?
|
||||
@ -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!(
|
||||
"{}://{}{}",
|
||||
@ -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))
|
||||
@ -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")]
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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())?
|
||||
@ -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") {
|
||||
@ -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 {
|
||||
@ -572,8 +582,9 @@ impl<R: Runtime> WebviewManager<R> {
|
||||
&self,
|
||||
window: Window<R>,
|
||||
webview: DetachedWebview<EventLoopMessage, R>,
|
||||
use_https_scheme: bool,
|
||||
) -> Webview<R> {
|
||||
let webview = Webview::new(window, webview);
|
||||
let webview = Webview::new(window, webview, use_https_scheme);
|
||||
|
||||
let webview_event_listeners = self.event_listeners.clone();
|
||||
let webview_ = webview.clone();
|
||||
|
||||
@ -85,9 +85,10 @@ pub(crate) struct PatternJavascript {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn format_real_schema(schema: &str) -> String {
|
||||
pub(crate) fn format_real_schema(schema: &str, https: bool) -> String {
|
||||
if cfg!(windows) || cfg!(target_os = "android") {
|
||||
format!("http://{schema}.{ISOLATION_IFRAME_SRC_DOMAIN}")
|
||||
let scheme = if https { "https" } else { "http" };
|
||||
format!("{scheme}://{schema}.{ISOLATION_IFRAME_SRC_DOMAIN}")
|
||||
} else {
|
||||
format!("{schema}://{ISOLATION_IFRAME_SRC_DOMAIN}")
|
||||
}
|
||||
|
||||
@ -21,9 +21,11 @@ pub fn get<R: Runtime>(
|
||||
assets: Arc<EmbeddedAssets>,
|
||||
aes_gcm_key: [u8; 32],
|
||||
window_origin: String,
|
||||
use_https_scheme: bool,
|
||||
) -> UriSchemeProtocolHandler {
|
||||
let frame_src = if cfg!(any(windows, target_os = "android")) {
|
||||
format!("http://{schema}.localhost")
|
||||
let https = if use_https_scheme { "https" } else { "http" };
|
||||
format!("{https}://{schema}.localhost")
|
||||
} else {
|
||||
format!("{schema}:")
|
||||
};
|
||||
|
||||
@ -30,7 +30,10 @@ pub fn get<R: Runtime>(
|
||||
) -> UriSchemeProtocolHandler {
|
||||
#[cfg(all(dev, mobile))]
|
||||
let url = {
|
||||
let mut url = manager.get_url().as_str().to_string();
|
||||
let mut url = manager
|
||||
.get_url(window_origin.starts_with("https"))
|
||||
.as_str()
|
||||
.to_string();
|
||||
if url.ends_with('/') {
|
||||
url.pop();
|
||||
}
|
||||
@ -152,7 +155,8 @@ fn get_response<R: Runtime>(
|
||||
|
||||
#[cfg(not(all(dev, mobile)))]
|
||||
let mut response = {
|
||||
let asset = manager.get_asset(path)?;
|
||||
let use_https_scheme = request.uri().scheme() == Some(&http::uri::Scheme::HTTPS);
|
||||
let asset = manager.get_asset(path, use_https_scheme)?;
|
||||
builder = builder.header(CONTENT_TYPE, &asset.mime_type);
|
||||
if let Some(csp) = &asset.csp_header {
|
||||
builder = builder.header("Content-Security-Policy", csp);
|
||||
|
||||
@ -9,8 +9,10 @@ use tauri_runtime::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
monitor::Monitor,
|
||||
webview::{DetachedWebview, PendingWebview},
|
||||
window::{CursorIcon, DetachedWindow, PendingWindow, RawWindow, WindowEvent, WindowId},
|
||||
window::{WindowBuilder, WindowBuilderBase},
|
||||
window::{
|
||||
CursorIcon, DetachedWindow, DetachedWindowWebview, PendingWindow, RawWindow, WindowBuilder,
|
||||
WindowBuilderBase, WindowEvent, WindowId,
|
||||
},
|
||||
DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, ProgressBarState,
|
||||
Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent,
|
||||
WebviewDispatch, WindowDispatch, WindowEventId,
|
||||
@ -158,14 +160,17 @@ impl<T: UserEvent> RuntimeHandle<T> for MockRuntimeHandle {
|
||||
},
|
||||
);
|
||||
|
||||
let webview = webview_id.map(|id| DetachedWebview {
|
||||
label: pending.label.clone(),
|
||||
dispatcher: MockWebviewDispatcher {
|
||||
id,
|
||||
context: self.context.clone(),
|
||||
url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
|
||||
last_evaluated_script: Default::default(),
|
||||
let webview = webview_id.map(|id| DetachedWindowWebview {
|
||||
webview: DetachedWebview {
|
||||
label: pending.label.clone(),
|
||||
dispatcher: MockWebviewDispatcher {
|
||||
id,
|
||||
context: self.context.clone(),
|
||||
url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
|
||||
last_evaluated_script: Default::default(),
|
||||
},
|
||||
},
|
||||
use_https_scheme: false,
|
||||
});
|
||||
|
||||
Ok(DetachedWindow {
|
||||
@ -773,14 +778,17 @@ impl<T: UserEvent> WindowDispatch<T> for MockWindowDispatcher {
|
||||
},
|
||||
);
|
||||
|
||||
let webview = webview_id.map(|id| DetachedWebview {
|
||||
label: pending.label.clone(),
|
||||
dispatcher: MockWebviewDispatcher {
|
||||
id,
|
||||
context: self.context.clone(),
|
||||
url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
|
||||
last_evaluated_script: Default::default(),
|
||||
let webview = webview_id.map(|id| DetachedWindowWebview {
|
||||
webview: DetachedWebview {
|
||||
label: pending.label.clone(),
|
||||
dispatcher: MockWebviewDispatcher {
|
||||
id,
|
||||
context: self.context.clone(),
|
||||
url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
|
||||
last_evaluated_script: Default::default(),
|
||||
},
|
||||
},
|
||||
use_https_scheme: false,
|
||||
});
|
||||
|
||||
Ok(DetachedWindow {
|
||||
@ -1065,14 +1073,17 @@ impl<T: UserEvent> Runtime<T> for MockRuntime {
|
||||
},
|
||||
);
|
||||
|
||||
let webview = webview_id.map(|id| DetachedWebview {
|
||||
label: pending.label.clone(),
|
||||
dispatcher: MockWebviewDispatcher {
|
||||
id,
|
||||
context: self.context.clone(),
|
||||
url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
|
||||
last_evaluated_script: Default::default(),
|
||||
let webview = webview_id.map(|id| DetachedWindowWebview {
|
||||
webview: DetachedWebview {
|
||||
label: pending.label.clone(),
|
||||
dispatcher: MockWebviewDispatcher {
|
||||
id,
|
||||
context: self.context.clone(),
|
||||
url: Arc::new(Mutex::new(pending.webview.unwrap().url)),
|
||||
last_evaluated_script: Default::default(),
|
||||
},
|
||||
},
|
||||
use_https_scheme: false,
|
||||
});
|
||||
|
||||
Ok(DetachedWindow {
|
||||
|
||||
@ -605,11 +605,17 @@ tauri::Builder::default()
|
||||
|
||||
pending.webview_attributes.bounds = Some(tauri_runtime::Rect { size, position });
|
||||
|
||||
let use_https_scheme = pending.webview_attributes.use_https_scheme;
|
||||
|
||||
let webview = match &mut window.runtime() {
|
||||
RuntimeOrDispatch::Dispatch(dispatcher) => dispatcher.create_webview(pending),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
.map(|webview| app_manager.webview.attach_webview(window.clone(), webview))?;
|
||||
.map(|webview| {
|
||||
app_manager
|
||||
.webview
|
||||
.attach_webview(window.clone(), webview, use_https_scheme)
|
||||
})?;
|
||||
|
||||
Ok(webview)
|
||||
}
|
||||
@ -794,6 +800,21 @@ fn main() {
|
||||
self.webview_attributes.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.webview_attributes.use_https_scheme = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Webview.
|
||||
@ -806,6 +827,7 @@ pub struct Webview<R: Runtime> {
|
||||
pub(crate) manager: Arc<AppManager<R>>,
|
||||
pub(crate) app_handle: AppHandle<R>,
|
||||
pub(crate) resources_table: Arc<Mutex<ResourceTable>>,
|
||||
use_https_scheme: bool,
|
||||
}
|
||||
|
||||
impl<R: Runtime> std::fmt::Debug for Webview<R> {
|
||||
@ -813,6 +835,7 @@ impl<R: Runtime> std::fmt::Debug for Webview<R> {
|
||||
f.debug_struct("Window")
|
||||
.field("window", &self.window.lock().unwrap())
|
||||
.field("webview", &self.webview)
|
||||
.field("use_https_scheme", &self.use_https_scheme)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@ -825,6 +848,7 @@ impl<R: Runtime> Clone for Webview<R> {
|
||||
manager: self.manager.clone(),
|
||||
app_handle: self.app_handle.clone(),
|
||||
resources_table: self.resources_table.clone(),
|
||||
use_https_scheme: self.use_https_scheme,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -847,13 +871,18 @@ impl<R: Runtime> PartialEq for Webview<R> {
|
||||
/// Base webview functions.
|
||||
impl<R: Runtime> Webview<R> {
|
||||
/// Create a new webview that is attached to the window.
|
||||
pub(crate) fn new(window: Window<R>, webview: DetachedWebview<EventLoopMessage, R>) -> Self {
|
||||
pub(crate) fn new(
|
||||
window: Window<R>,
|
||||
webview: DetachedWebview<EventLoopMessage, R>,
|
||||
use_https_scheme: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
manager: window.manager.clone(),
|
||||
app_handle: window.app_handle.clone(),
|
||||
window: Arc::new(Mutex::new(window)),
|
||||
webview,
|
||||
resources_table: Default::default(),
|
||||
use_https_scheme,
|
||||
}
|
||||
}
|
||||
|
||||
@ -880,6 +909,11 @@ impl<R: Runtime> Webview<R> {
|
||||
&self.webview.label
|
||||
}
|
||||
|
||||
/// Whether the webview was configured to use the HTTPS scheme or not.
|
||||
pub(crate) fn use_https_scheme(&self) -> bool {
|
||||
self.use_https_scheme
|
||||
}
|
||||
|
||||
/// Registers a window event listener.
|
||||
pub fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, f: F) {
|
||||
self
|
||||
@ -1180,9 +1214,11 @@ fn main() {
|
||||
}
|
||||
|
||||
fn is_local_url(&self, current_url: &Url) -> bool {
|
||||
let uses_https = current_url.scheme() == "https";
|
||||
|
||||
// if from `tauri://` custom protocol
|
||||
({
|
||||
let protocol_url = self.manager().protocol_url();
|
||||
let protocol_url = self.manager().protocol_url(uses_https);
|
||||
current_url.scheme() == protocol_url.scheme()
|
||||
&& current_url.domain() == protocol_url.domain()
|
||||
}) ||
|
||||
@ -1190,7 +1226,7 @@ fn main() {
|
||||
// or if relative to `devUrl` or `frontendDist`
|
||||
self
|
||||
.manager()
|
||||
.get_url()
|
||||
.get_url(uses_https)
|
||||
.make_relative(current_url)
|
||||
.is_some()
|
||||
|
||||
@ -1206,7 +1242,7 @@ fn main() {
|
||||
// so we check using the first part of the domain
|
||||
#[cfg(any(windows, target_os = "android"))]
|
||||
let local = {
|
||||
let protocol_url = self.manager().protocol_url();
|
||||
let protocol_url = self.manager().protocol_url(uses_https);
|
||||
let maybe_protocol = current_url
|
||||
.domain()
|
||||
.and_then(|d| d .split_once('.'))
|
||||
|
||||
@ -898,6 +898,21 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
|
||||
self.webview_builder = self.webview_builder.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.webview_builder = self.webview_builder.use_https_scheme(enabled);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that wraps a [`Window`] together with a [`Webview`].
|
||||
|
||||
@ -381,7 +381,11 @@ tauri::Builder::default()
|
||||
);
|
||||
|
||||
if let Some(webview) = detached_window.webview {
|
||||
app_manager.webview.attach_webview(window.clone(), webview);
|
||||
app_manager.webview.attach_webview(
|
||||
window.clone(),
|
||||
webview.webview,
|
||||
webview.use_https_scheme,
|
||||
);
|
||||
}
|
||||
|
||||
window
|
||||
|
||||
@ -66,7 +66,8 @@ pub fn run_app<R: Runtime, F: FnOnce(&App<R>) + Send + 'static>(
|
||||
.build()?,
|
||||
));
|
||||
|
||||
let mut window_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default());
|
||||
let mut window_builder =
|
||||
WebviewWindowBuilder::new(app, "main", WebviewUrl::default()).use_https_scheme(true);
|
||||
|
||||
#[cfg(all(desktop, not(test)))]
|
||||
{
|
||||
|
||||
@ -734,6 +734,21 @@ interface WebviewOptions {
|
||||
* - **Android / iOS**: Unsupported.
|
||||
*/
|
||||
zoomHotkeysEnabled?: boolean
|
||||
|
||||
/**
|
||||
* 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 them.
|
||||
*
|
||||
* @since 2.1.0
|
||||
*/
|
||||
useHttpsScheme?: boolean
|
||||
}
|
||||
|
||||
export { Webview, getCurrentWebview, getAllWebviews }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user