Files
Genarrative/apps/desktop-shell/src-tauri/src/shell/lifecycle.rs
T
kdletters 6066a7be4f 收口桌面壳生命周期诊断日志
桌面壳生命周期事件失败只记录固定阶段标签

扩展桌面壳配置门禁禁止生命周期日志输出 Tauri 错误细节

补充宿主壳方案和共享决策中的生命周期诊断边界
2026-06-21 02:35:24 +08:00

244 lines
6.6 KiB
Rust

use crate::shell::events::host_bridge_event_script;
use crate::shell::navigation::register_desktop_navigation_events;
use serde_json::json;
use tauri::webview::PageLoadEvent;
use tauri::{WebviewWindow, WindowEvent};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct DesktopLifecyclePayload {
state: &'static str,
focused: bool,
native_state: &'static str,
}
pub(crate) fn resolve_desktop_lifecycle_payload(
focused: bool,
visible: bool,
minimized: bool,
) -> DesktopLifecyclePayload {
if !visible {
return DesktopLifecyclePayload {
state: "background",
focused: false,
native_state: "hidden",
};
}
if minimized {
return DesktopLifecyclePayload {
state: "background",
focused: false,
native_state: "minimized",
};
}
if focused {
DesktopLifecyclePayload {
state: "active",
focused: true,
native_state: "focused",
}
} else {
DesktopLifecyclePayload {
state: "inactive",
focused: false,
native_state: "blurred",
}
}
}
pub(crate) fn emit_desktop_lifecycle_event(
window: &WebviewWindow,
state: &'static str,
focused: bool,
native_state: &'static str,
) -> tauri::Result<()> {
let script = host_bridge_event_script(
"app.lifecycle",
json!({
"state": state,
"focused": focused,
"nativeState": native_state,
}),
)
.map_err(tauri::Error::Json)?;
window.eval(script)
}
pub(crate) fn emit_desktop_lifecycle_payload(
window: &WebviewWindow,
payload: DesktopLifecyclePayload,
) -> tauri::Result<()> {
emit_desktop_lifecycle_event(window, payload.state, payload.focused, payload.native_state)
}
fn resolve_desktop_lifecycle_window_flag(
label: &'static str,
result: tauri::Result<bool>,
default_value: bool,
) -> bool {
match result {
Ok(value) => value,
Err(_error) => {
eprintln!("desktop host event failed for app.lifecycle.{label}");
default_value
}
}
}
pub(crate) fn emit_current_desktop_lifecycle_event(window: &WebviewWindow) -> tauri::Result<()> {
let visible =
resolve_desktop_lifecycle_window_flag("visible", window.is_visible(), true);
let minimized =
resolve_desktop_lifecycle_window_flag("minimized", window.is_minimized(), false);
let focused = resolve_desktop_lifecycle_window_flag(
"focused",
window.is_focused(),
visible && !minimized,
);
emit_desktop_lifecycle_payload(
window,
resolve_desktop_lifecycle_payload(focused, visible, minimized),
)
}
pub(crate) fn register_desktop_lifecycle_events(window: &WebviewWindow) {
let lifecycle_window = window.clone();
window.on_window_event(move |event| {
if matches!(event, WindowEvent::Focused(_) | WindowEvent::Resized(_)) {
log_desktop_host_event_result(
"app.lifecycle",
emit_current_desktop_lifecycle_event(&lifecycle_window),
);
}
});
}
pub(crate) fn should_replay_desktop_webview_state_on_page_load(event: PageLoadEvent) -> bool {
event == PageLoadEvent::Finished
}
pub(crate) fn log_desktop_host_event_result(
label: &'static str,
result: tauri::Result<()>,
) -> bool {
match result {
Ok(()) => true,
Err(_error) => {
eprintln!("desktop host event failed for {label}");
false
}
}
}
pub(crate) fn replay_desktop_webview_state(window: &WebviewWindow) {
log_desktop_host_event_result(
"app.lifecycle",
emit_current_desktop_lifecycle_event(window),
);
log_desktop_host_event_result(
"navigation.canGoBack",
register_desktop_navigation_events(window),
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn desktop_page_load_replays_state_only_after_finished_load() {
assert!(should_replay_desktop_webview_state_on_page_load(
PageLoadEvent::Finished
));
assert!(!should_replay_desktop_webview_state_on_page_load(
PageLoadEvent::Started
));
}
#[test]
fn desktop_host_event_result_reports_success_and_failure() {
assert!(log_desktop_host_event_result("network", Ok(())));
assert!(!log_desktop_host_event_result(
"navigation",
Err(tauri::Error::AssetNotFound("navigation".to_string()))
));
}
#[test]
fn desktop_host_event_result_logs_stable_label_only() {
assert!(!log_desktop_host_event_result(
"navigation.canGoBack",
Err(tauri::Error::AssetNotFound(
"navigation?token=private".to_string()
))
));
}
#[test]
fn desktop_lifecycle_window_flag_logs_failure_and_uses_default() {
assert!(resolve_desktop_lifecycle_window_flag(
"visible",
Ok(true),
false
));
assert!(!resolve_desktop_lifecycle_window_flag(
"focused",
Err(tauri::Error::WindowNotFound),
false
));
}
#[test]
fn desktop_lifecycle_window_flag_logs_stable_label_only() {
assert!(!resolve_desktop_lifecycle_window_flag(
"focused",
Err(tauri::Error::AssetNotFound(
"window?token=private".to_string()
)),
false
));
}
#[test]
fn desktop_lifecycle_payload_maps_hidden_and_minimized_to_background() {
assert_eq!(
resolve_desktop_lifecycle_payload(true, false, false),
DesktopLifecyclePayload {
state: "background",
focused: false,
native_state: "hidden",
}
);
assert_eq!(
resolve_desktop_lifecycle_payload(true, true, true),
DesktopLifecyclePayload {
state: "background",
focused: false,
native_state: "minimized",
}
);
}
#[test]
fn desktop_lifecycle_payload_maps_visible_focus_state() {
assert_eq!(
resolve_desktop_lifecycle_payload(true, true, false),
DesktopLifecyclePayload {
state: "active",
focused: true,
native_state: "focused",
}
);
assert_eq!(
resolve_desktop_lifecycle_payload(false, true, false),
DesktopLifecyclePayload {
state: "inactive",
focused: false,
native_state: "blurred",
}
);
}
}