16a5d28ed3
release 客户端关闭 WebView 默认上下文菜单 保留 contextmenu 事件供业务组件处理 补充 release/debug 行为测试与技术方案约定
85 lines
2.7 KiB
Rust
85 lines
2.7 KiB
Rust
use tauri::{
|
||
plugin::{Builder, TauriPlugin},
|
||
Runtime,
|
||
};
|
||
|
||
/// Release 客户端只取消浏览器的默认上下文菜单。
|
||
///
|
||
/// 这里刻意只调用 `preventDefault`:`contextmenu` 事件仍会继续沿 DOM
|
||
/// 传播,业务组件可以照常接收并打开自己的菜单。捕获阶段用于覆盖没有
|
||
/// 自己右键处理的页面空白区域,但不会调用 `stopPropagation`。
|
||
const RELEASE_CONTEXT_MENU_INIT_SCRIPT: &str = r#"
|
||
document.addEventListener(
|
||
'contextmenu',
|
||
(event) => {
|
||
event.preventDefault();
|
||
},
|
||
true,
|
||
);
|
||
"#;
|
||
|
||
fn context_menu_init_script(is_release: bool) -> Option<&'static str> {
|
||
is_release.then_some(RELEASE_CONTEXT_MENU_INIT_SCRIPT)
|
||
}
|
||
|
||
#[cfg(all(windows, not(debug_assertions)))]
|
||
fn disable_windows_default_context_menus<R: Runtime>(webview: tauri::Webview<R>) {
|
||
let _ = webview.with_webview(|platform_webview| {
|
||
// WebView2 的原生设置只关闭浏览器菜单,不会取消 DOM 的
|
||
// `contextmenu` 事件,因此业务右键菜单仍然可以正常工作。
|
||
// SAFETY: Tauri 在 WebView2 UI 线程上提供当前存活的 controller,
|
||
// 这些 COM 接口调用只使用该闭包期间有效的句柄。
|
||
let _ = unsafe {
|
||
platform_webview
|
||
.controller()
|
||
.CoreWebView2()
|
||
.and_then(|webview| webview.Settings())
|
||
.and_then(|settings| settings.SetAreDefaultContextMenusEnabled(false))
|
||
};
|
||
});
|
||
}
|
||
|
||
/// 注册 release-only 的默认网页上下文菜单策略。
|
||
pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
|
||
let mut builder = Builder::new("release-context-menu");
|
||
|
||
if let Some(script) = context_menu_init_script(!cfg!(debug_assertions)) {
|
||
builder = builder.js_init_script_on_all_frames(script);
|
||
}
|
||
|
||
#[cfg(all(windows, not(debug_assertions)))]
|
||
{
|
||
builder = builder.on_webview_ready(disable_windows_default_context_menus);
|
||
}
|
||
|
||
builder.build()
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn release_build_gets_a_context_menu_init_script() {
|
||
assert_eq!(
|
||
context_menu_init_script(true),
|
||
Some(RELEASE_CONTEXT_MENU_INIT_SCRIPT)
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn debug_build_keeps_the_default_context_menu_strategy_untouched() {
|
||
assert_eq!(context_menu_init_script(false), None);
|
||
}
|
||
|
||
#[test]
|
||
fn init_script_prevents_only_the_default_action() {
|
||
let script = RELEASE_CONTEXT_MENU_INIT_SCRIPT;
|
||
assert!(script.contains("contextmenu"));
|
||
assert!(script.contains("event.preventDefault()"));
|
||
assert!(script.contains("true"));
|
||
assert!(!script.contains("stopPropagation"));
|
||
assert!(!script.contains("stopImmediatePropagation"));
|
||
}
|
||
}
|