Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 91 additions & 7 deletions app/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1450,6 +1450,31 @@ fn setup_tray(app: &AppHandle<AppRuntime>) -> tauri::Result<()> {

const CEF_PREWARM_LABEL: &str = "cef-prewarm";

/// Decide whether to spawn the CEF cold-start prewarm webview.
///
/// Testable pure function — callers pass the relevant env values directly.
///
/// Decision matrix:
/// - `env_override` = `Some("0"|"false"|"no"|"off")` → disabled (explicit)
/// - `env_override` = `Some(<other non-empty string>)` → enabled (explicit opt-in;
/// overrides even the Wayland guard so ops can re-enable if CEF subprocess
/// X handling improves)
/// - `env_override` = `None` (env var unset, default path):
/// - `wayland_display_set` = `true` → **disabled** — auto-guard against the
/// fatal `X_ConfigureWindow BadWindow` crash that fires in CEF render
/// subprocesses on Wayland/XWayland sessions (issue #2463). The main-process
/// silent X error handler (`install_silent_x_error_handler`) does not reach
/// CEF subprocesses; until subprocess-level coverage is available, skipping
/// the prewarm child webview is the safest mitigation.
/// - `wayland_display_set` = `false` → enabled
fn cef_prewarm_enabled(env_override: Option<&str>, wayland_display_set: bool) -> bool {
if let Some(v) = env_override {
let v = v.trim().to_ascii_lowercase();
return !(v == "0" || v == "false" || v == "no" || v == "off");
}
!wayland_display_set
}

/// Spawn a hidden 1×1 child webview at `about:blank` on the main window so
/// CEF's child-webview render path is hot before the user clicks an
/// account. The first `webview_account_open` then skips the cold
Expand Down Expand Up @@ -2825,13 +2850,12 @@ pub fn run() {
// tear it down in the shutdown sequence below. Disable at
// runtime with `OPENHUMAN_CEF_PREWARM=0` if it regresses.
{
let prewarm_enabled = std::env::var("OPENHUMAN_CEF_PREWARM")
.map(|v| {
let v = v.trim().to_ascii_lowercase();
!(v == "0" || v == "false" || v == "no" || v == "off")
})
.unwrap_or(true);
if prewarm_enabled {
#[cfg(target_os = "linux")]
let wayland_display_set = has_non_empty_env("WAYLAND_DISPLAY");
#[cfg(not(target_os = "linux"))]
let wayland_display_set = false;
let env_override = std::env::var("OPENHUMAN_CEF_PREWARM").ok();
if cef_prewarm_enabled(env_override.as_deref(), wayland_display_set) {
let app_handle = app.handle().clone();
tauri::async_runtime::spawn(async move {
// Defer one tick so the main window finishes its
Expand All @@ -2841,6 +2865,12 @@ pub fn run() {
log::warn!("[cef-prewarm] failed (non-fatal): {e}");
}
});
} else if wayland_display_set && env_override.is_none() {
log::info!(
"[cef-prewarm] auto-disabled: WAYLAND_DISPLAY is set (Wayland/XWayland \
session) — prevents X_ConfigureWindow BadWindow crash in CEF \
subprocesses (issue #2463); set OPENHUMAN_CEF_PREWARM=1 to override"
);
} else {
log::info!("[cef-prewarm] disabled via OPENHUMAN_CEF_PREWARM");
}
Expand Down Expand Up @@ -3800,6 +3830,60 @@ mod tests {
assert_eq!(std::env::consts::ARCH, "aarch64");
}

// -------------------------------------------------------------------------
// cef_prewarm_enabled (issue #2463 — Wayland/XWayland BadWindow guard)
// -------------------------------------------------------------------------

#[test]
fn prewarm_enabled_by_default_on_non_wayland() {
assert!(cef_prewarm_enabled(None, false));
}

#[test]
fn prewarm_auto_disabled_on_wayland_when_env_unset() {
assert!(!cef_prewarm_enabled(None, true));
}

#[test]
fn prewarm_explicit_disable_respected_on_non_wayland() {
assert!(!cef_prewarm_enabled(Some("0"), false));
assert!(!cef_prewarm_enabled(Some("false"), false));
assert!(!cef_prewarm_enabled(Some("no"), false));
assert!(!cef_prewarm_enabled(Some("off"), false));
}

#[test]
fn prewarm_explicit_disable_respected_on_wayland() {
assert!(!cef_prewarm_enabled(Some("0"), true));
assert!(!cef_prewarm_enabled(Some("false"), true));
}

#[test]
fn prewarm_explicit_enable_overrides_wayland_guard() {
// OPENHUMAN_CEF_PREWARM=1 (or any non-disable value) lets ops
// force prewarm even on Wayland sessions.
assert!(cef_prewarm_enabled(Some("1"), true));
assert!(cef_prewarm_enabled(Some("true"), true));
assert!(cef_prewarm_enabled(Some("yes"), true));
assert!(cef_prewarm_enabled(Some("on"), true));
}

#[test]
fn prewarm_disable_flags_are_case_insensitive() {
assert!(!cef_prewarm_enabled(Some("FALSE"), false));
assert!(!cef_prewarm_enabled(Some("OFF"), true));
assert!(!cef_prewarm_enabled(Some(" 0 "), false));
assert!(!cef_prewarm_enabled(Some(" No "), true));
}

#[test]
fn prewarm_unknown_env_value_treated_as_enable() {
// Any string that is not a recognised disable token → treat as enable.
assert!(cef_prewarm_enabled(Some("enabled"), false));
assert!(cef_prewarm_enabled(Some("yes"), false));
assert!(cef_prewarm_enabled(Some(""), false));
}

// -------------------------------------------------------------------------
// build_sentry_release_tag
// -------------------------------------------------------------------------
Expand Down
Loading