mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-02-06 15:16:46 +00:00
feat: check if webview runtime is accessible when creating a webview (#13406)
This commit is contained in:
parent
0e616dbbcb
commit
96ecfca428
7
.changes/webview-runtime-check.md
Normal file
7
.changes/webview-runtime-check.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
"tauri": "patch:enhance"
|
||||
"tauri-runtime-wry": "patch:enhance"
|
||||
---
|
||||
|
||||
Check if the webview runtime is accessible when creating a webview, returning an error if it doesn't.
|
||||
|
||||
@ -76,3 +76,4 @@ objc-exception = []
|
||||
tracing = ["dep:tracing", "wry/tracing"]
|
||||
macos-proxy = ["wry/mac-proxy"]
|
||||
unstable = []
|
||||
common-controls-v6 = []
|
||||
|
||||
16
crates/tauri-runtime-wry/src/dialog/mod.rs
Normal file
16
crates/tauri-runtime-wry/src/dialog/mod.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
pub fn error<S: AsRef<str>>(err: S) {
|
||||
#[cfg(windows)]
|
||||
windows::error(err);
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
unimplemented!("Error dialog is not implemented for this platform");
|
||||
}
|
||||
}
|
||||
124
crates/tauri-runtime-wry/src/dialog/windows.rs
Normal file
124
crates/tauri-runtime-wry/src/dialog/windows.rs
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use windows::core::{w, HSTRING, PCWSTR};
|
||||
|
||||
enum Level {
|
||||
Error,
|
||||
#[allow(unused)]
|
||||
Warning,
|
||||
#[allow(unused)]
|
||||
Info,
|
||||
}
|
||||
|
||||
pub fn error<S: AsRef<str>>(err: S) {
|
||||
dialog_inner(err.as_ref(), Level::Error);
|
||||
}
|
||||
|
||||
fn dialog_inner(err: &str, level: Level) {
|
||||
let title = match level {
|
||||
Level::Warning => w!("Warning"),
|
||||
Level::Error => w!("Error"),
|
||||
Level::Info => w!("Info"),
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "common-controls-v6"))]
|
||||
{
|
||||
use windows::Win32::UI::WindowsAndMessaging::*;
|
||||
|
||||
let err = remove_hyperlink(err);
|
||||
let err = HSTRING::from(err);
|
||||
|
||||
unsafe {
|
||||
MessageBoxW(
|
||||
None,
|
||||
err,
|
||||
title,
|
||||
match level {
|
||||
Level::Warning => MB_ICONWARNING,
|
||||
Level::Error => MB_ICONERROR,
|
||||
Level::Info => MB_ICONINFORMATION,
|
||||
},
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "common-controls-v6")]
|
||||
{
|
||||
use windows::core::HRESULT;
|
||||
use windows::Win32::Foundation::*;
|
||||
use windows::Win32::UI::Controls::*;
|
||||
use windows::Win32::UI::Shell::*;
|
||||
use windows::Win32::UI::WindowsAndMessaging::*;
|
||||
|
||||
extern "system" fn task_dialog_callback(
|
||||
_hwnd: HWND,
|
||||
msg: TASKDIALOG_NOTIFICATIONS,
|
||||
_wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
_data: isize,
|
||||
) -> HRESULT {
|
||||
if msg == TDN_HYPERLINK_CLICKED {
|
||||
let link = PCWSTR(lparam.0 as _);
|
||||
let _ = unsafe { ShellExecuteW(None, None, link, None, None, SW_SHOWNORMAL) };
|
||||
}
|
||||
|
||||
S_OK
|
||||
}
|
||||
|
||||
let err = HSTRING::from(err);
|
||||
let err = PCWSTR(err.as_ptr());
|
||||
|
||||
let task_dialog_config = TASKDIALOGCONFIG {
|
||||
cbSize: std::mem::size_of::<TASKDIALOGCONFIG>() as u32,
|
||||
dwFlags: TDF_ALLOW_DIALOG_CANCELLATION | TDF_ENABLE_HYPERLINKS,
|
||||
pszWindowTitle: title,
|
||||
pszContent: err,
|
||||
Anonymous1: TASKDIALOGCONFIG_0 {
|
||||
pszMainIcon: match level {
|
||||
Level::Warning => TD_WARNING_ICON,
|
||||
Level::Error => TD_ERROR_ICON,
|
||||
Level::Info => TD_INFORMATION_ICON,
|
||||
},
|
||||
},
|
||||
dwCommonButtons: TDCBF_OK_BUTTON,
|
||||
pfCallback: Some(task_dialog_callback),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let _ = unsafe { TaskDialogIndirect(&task_dialog_config, None, None, None) };
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "common-controls-v6"))]
|
||||
fn remove_hyperlink(str: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut in_hyperlink = false;
|
||||
|
||||
for c in str.chars() {
|
||||
if c == '<' {
|
||||
in_hyperlink = true;
|
||||
} else if c == '>' {
|
||||
in_hyperlink = false;
|
||||
} else if !in_hyperlink {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(not(feature = "common-controls-v6"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_remove_hyperlink() {
|
||||
let input = "This is a <A href=\"some link\">test</A> string.";
|
||||
let expected = "This is a test string.";
|
||||
let result = remove_hyperlink(input);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
@ -130,6 +130,8 @@ use std::{
|
||||
pub type WebviewId = u32;
|
||||
type IpcHandler = dyn Fn(Request<String>) + 'static;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
mod dialog;
|
||||
mod monitor;
|
||||
#[cfg(any(
|
||||
windows,
|
||||
@ -248,6 +250,7 @@ pub struct Context<T: UserEvent> {
|
||||
next_webview_id: Arc<AtomicU32>,
|
||||
next_window_event_id: Arc<AtomicU32>,
|
||||
next_webview_event_id: Arc<AtomicU32>,
|
||||
webview_runtime_installed: bool,
|
||||
}
|
||||
|
||||
impl<T: UserEvent> Context<T> {
|
||||
@ -2712,6 +2715,7 @@ impl<T: UserEvent> Wry<T> {
|
||||
next_webview_id: Default::default(),
|
||||
next_window_event_id: Default::default(),
|
||||
next_webview_event_id: Default::default(),
|
||||
webview_runtime_installed: wry::webview_version().is_ok(),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
@ -4421,6 +4425,20 @@ fn create_webview<T: UserEvent>(
|
||||
pending: PendingWebview<T, Wry<T>>,
|
||||
#[allow(unused_variables)] focused_webview: Arc<Mutex<Option<String>>>,
|
||||
) -> Result<WebviewWrapper> {
|
||||
if !context.webview_runtime_installed {
|
||||
#[cfg(all(not(debug_assertions), windows))]
|
||||
dialog::error(
|
||||
r#"Could not find the WebView2 Runtime.
|
||||
|
||||
Make sure it is installed or download it from <A href="https://developer.microsoft.com/en-us/microsoft-edge/webview2">https://developer.microsoft.com/en-us/microsoft-edge/webview2</A>
|
||||
|
||||
You may have it installed on another user account, but it is not available for this one.
|
||||
"#,
|
||||
);
|
||||
|
||||
return Err(Error::WebviewRuntimeNotInstalled);
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let PendingWebview {
|
||||
webview_attributes,
|
||||
|
||||
@ -169,6 +169,8 @@ pub enum Error {
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[error("failed to remove data store")]
|
||||
FailedToRemoveDataStore,
|
||||
#[error("Could not find the webview runtime, make sure it is installed")]
|
||||
WebviewRuntimeNotInstalled,
|
||||
}
|
||||
|
||||
/// Result type.
|
||||
|
||||
@ -185,6 +185,7 @@ unstable = ["tauri-runtime-wry?/unstable"]
|
||||
common-controls-v6 = [
|
||||
"tray-icon?/common-controls-v6",
|
||||
"muda/common-controls-v6",
|
||||
"tauri-runtime-wry?/common-controls-v6",
|
||||
]
|
||||
tray-icon = ["dep:tray-icon"]
|
||||
tracing = ["dep:tracing", "tauri-macros/tracing", "tauri-runtime-wry?/tracing"]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user