diff --git a/app/ui.js b/app/ui.js index bf72c9a4..bbdea60d 100644 --- a/app/ui.js +++ b/app/ui.js @@ -1155,6 +1155,8 @@ const UI = { UI.showStatus(msg); UI.updateVisualState('connected'); + UI.updateBeforeUnload(); + // Do this last because it can only be used on rendered elements UI.rfb.focus(); }, @@ -1191,6 +1193,8 @@ const UI = { UI.showStatus(_("Disconnected"), 'normal'); } + UI.updateBeforeUnload(); + document.title = PAGE_TITLE; UI.openControlbar(); @@ -1211,6 +1215,24 @@ const UI = { UI.showStatus(msg, 'error'); }, + handleBeforeUnload(e) { + // Trigger a "Leave site?" warning prompt before closing the + // page. Modern browsers (Oct 2025) accept either (or both) + // preventDefault() or a nonempty returnValue, though the latter is + // considered legacy. The custom string is ignored by modern browsers, + // which display a native message, but older browsers will show it. + e.preventDefault(); + e.returnValue = _("Are you sure you want to disconnect the session?"); + }, + + updateBeforeUnload() { + // Remove first to avoid adding duplicates + window.removeEventListener("beforeunload", UI.handleBeforeUnload); + if (!UI.rfb?.viewOnly && UI.connected) { + window.addEventListener("beforeunload", UI.handleBeforeUnload); + } + }, + /* ------^------- * /CONNECTION * ============== @@ -1737,6 +1759,8 @@ const UI = { if (!UI.rfb) return; UI.rfb.viewOnly = UI.getSetting('view_only'); + UI.updateBeforeUnload(); + // Hide input related buttons in view only mode if (UI.rfb.viewOnly) { document.getElementById('noVNC_keyboard_button')