Skip to content

Commit 96ecfca

Browse files
authored
feat: check if webview runtime is accessible when creating a webview (#13406)
1 parent 0e616db commit 96ecfca

File tree

7 files changed

+169
-0
lines changed

7 files changed

+169
-0
lines changed

.changes/webview-runtime-check.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri": "patch:enhance"
3+
"tauri-runtime-wry": "patch:enhance"
4+
---
5+
6+
Check if the webview runtime is accessible when creating a webview, returning an error if it doesn't.
7+

crates/tauri-runtime-wry/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,4 @@ objc-exception = []
7676
tracing = ["dep:tracing", "wry/tracing"]
7777
macos-proxy = ["wry/mac-proxy"]
7878
unstable = []
79+
common-controls-v6 = []
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
#[cfg(windows)]
6+
mod windows;
7+
8+
pub fn error<S: AsRef<str>>(err: S) {
9+
#[cfg(windows)]
10+
windows::error(err);
11+
12+
#[cfg(not(windows))]
13+
{
14+
unimplemented!("Error dialog is not implemented for this platform");
15+
}
16+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use windows::core::{w, HSTRING, PCWSTR};
6+
7+
enum Level {
8+
Error,
9+
#[allow(unused)]
10+
Warning,
11+
#[allow(unused)]
12+
Info,
13+
}
14+
15+
pub fn error<S: AsRef<str>>(err: S) {
16+
dialog_inner(err.as_ref(), Level::Error);
17+
}
18+
19+
fn dialog_inner(err: &str, level: Level) {
20+
let title = match level {
21+
Level::Warning => w!("Warning"),
22+
Level::Error => w!("Error"),
23+
Level::Info => w!("Info"),
24+
};
25+
26+
#[cfg(not(feature = "common-controls-v6"))]
27+
{
28+
use windows::Win32::UI::WindowsAndMessaging::*;
29+
30+
let err = remove_hyperlink(err);
31+
let err = HSTRING::from(err);
32+
33+
unsafe {
34+
MessageBoxW(
35+
None,
36+
err,
37+
title,
38+
match level {
39+
Level::Warning => MB_ICONWARNING,
40+
Level::Error => MB_ICONERROR,
41+
Level::Info => MB_ICONINFORMATION,
42+
},
43+
)
44+
};
45+
}
46+
47+
#[cfg(feature = "common-controls-v6")]
48+
{
49+
use windows::core::HRESULT;
50+
use windows::Win32::Foundation::*;
51+
use windows::Win32::UI::Controls::*;
52+
use windows::Win32::UI::Shell::*;
53+
use windows::Win32::UI::WindowsAndMessaging::*;
54+
55+
extern "system" fn task_dialog_callback(
56+
_hwnd: HWND,
57+
msg: TASKDIALOG_NOTIFICATIONS,
58+
_wparam: WPARAM,
59+
lparam: LPARAM,
60+
_data: isize,
61+
) -> HRESULT {
62+
if msg == TDN_HYPERLINK_CLICKED {
63+
let link = PCWSTR(lparam.0 as _);
64+
let _ = unsafe { ShellExecuteW(None, None, link, None, None, SW_SHOWNORMAL) };
65+
}
66+
67+
S_OK
68+
}
69+
70+
let err = HSTRING::from(err);
71+
let err = PCWSTR(err.as_ptr());
72+
73+
let task_dialog_config = TASKDIALOGCONFIG {
74+
cbSize: std::mem::size_of::<TASKDIALOGCONFIG>() as u32,
75+
dwFlags: TDF_ALLOW_DIALOG_CANCELLATION | TDF_ENABLE_HYPERLINKS,
76+
pszWindowTitle: title,
77+
pszContent: err,
78+
Anonymous1: TASKDIALOGCONFIG_0 {
79+
pszMainIcon: match level {
80+
Level::Warning => TD_WARNING_ICON,
81+
Level::Error => TD_ERROR_ICON,
82+
Level::Info => TD_INFORMATION_ICON,
83+
},
84+
},
85+
dwCommonButtons: TDCBF_OK_BUTTON,
86+
pfCallback: Some(task_dialog_callback),
87+
..Default::default()
88+
};
89+
90+
let _ = unsafe { TaskDialogIndirect(&task_dialog_config, None, None, None) };
91+
}
92+
}
93+
94+
#[cfg(not(feature = "common-controls-v6"))]
95+
fn remove_hyperlink(str: &str) -> String {
96+
let mut result = String::new();
97+
let mut in_hyperlink = false;
98+
99+
for c in str.chars() {
100+
if c == '<' {
101+
in_hyperlink = true;
102+
} else if c == '>' {
103+
in_hyperlink = false;
104+
} else if !in_hyperlink {
105+
result.push(c);
106+
}
107+
}
108+
109+
result
110+
}
111+
112+
#[cfg(test)]
113+
#[cfg(not(feature = "common-controls-v6"))]
114+
mod tests {
115+
use super::*;
116+
117+
#[test]
118+
fn test_remove_hyperlink() {
119+
let input = "This is a <A href=\"some link\">test</A> string.";
120+
let expected = "This is a test string.";
121+
let result = remove_hyperlink(input);
122+
assert_eq!(result, expected);
123+
}
124+
}

crates/tauri-runtime-wry/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ use std::{
130130
pub type WebviewId = u32;
131131
type IpcHandler = dyn Fn(Request<String>) + 'static;
132132

133+
#[cfg(not(debug_assertions))]
134+
mod dialog;
133135
mod monitor;
134136
#[cfg(any(
135137
windows,
@@ -248,6 +250,7 @@ pub struct Context<T: UserEvent> {
248250
next_webview_id: Arc<AtomicU32>,
249251
next_window_event_id: Arc<AtomicU32>,
250252
next_webview_event_id: Arc<AtomicU32>,
253+
webview_runtime_installed: bool,
251254
}
252255

253256
impl<T: UserEvent> Context<T> {
@@ -2712,6 +2715,7 @@ impl<T: UserEvent> Wry<T> {
27122715
next_webview_id: Default::default(),
27132716
next_window_event_id: Default::default(),
27142717
next_webview_event_id: Default::default(),
2718+
webview_runtime_installed: wry::webview_version().is_ok(),
27152719
};
27162720

27172721
Ok(Self {
@@ -4421,6 +4425,20 @@ fn create_webview<T: UserEvent>(
44214425
pending: PendingWebview<T, Wry<T>>,
44224426
#[allow(unused_variables)] focused_webview: Arc<Mutex<Option<String>>>,
44234427
) -> Result<WebviewWrapper> {
4428+
if !context.webview_runtime_installed {
4429+
#[cfg(all(not(debug_assertions), windows))]
4430+
dialog::error(
4431+
r#"Could not find the WebView2 Runtime.
4432+
4433+
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>
4434+
4435+
You may have it installed on another user account, but it is not available for this one.
4436+
"#,
4437+
);
4438+
4439+
return Err(Error::WebviewRuntimeNotInstalled);
4440+
}
4441+
44244442
#[allow(unused_mut)]
44254443
let PendingWebview {
44264444
webview_attributes,

crates/tauri-runtime/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ pub enum Error {
169169
#[cfg(any(target_os = "macos", target_os = "ios"))]
170170
#[error("failed to remove data store")]
171171
FailedToRemoveDataStore,
172+
#[error("Could not find the webview runtime, make sure it is installed")]
173+
WebviewRuntimeNotInstalled,
172174
}
173175

174176
/// Result type.

crates/tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ unstable = ["tauri-runtime-wry?/unstable"]
185185
common-controls-v6 = [
186186
"tray-icon?/common-controls-v6",
187187
"muda/common-controls-v6",
188+
"tauri-runtime-wry?/common-controls-v6",
188189
]
189190
tray-icon = ["dep:tray-icon"]
190191
tracing = ["dep:tracing", "tauri-macros/tracing", "tauri-runtime-wry?/tracing"]

0 commit comments

Comments
 (0)