From 4dc5945019bd30e0bbb2ee28f12a206b3f78ff4f Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:21:52 +0300 Subject: [PATCH] fix(web): run navigator.clipboard.write only when window has focus (#858) When we receive clipboard update from the server and the browser window is not in focus (for example, when the user copies some text directly on the machine, not via the browser's VNC viewer), we got an error that `navigator.clipboard.write` is not allowed when window is not in focus. This PR adds a window check that the window has focus, and now `clipboard.write` runs only when the window is in focus. --- .../src/iron-remote-desktop.svelte | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/web-client/iron-remote-desktop/src/iron-remote-desktop.svelte b/web-client/iron-remote-desktop/src/iron-remote-desktop.svelte index b21b46f3..53083d68 100644 --- a/web-client/iron-remote-desktop/src/iron-remote-desktop.svelte +++ b/web-client/iron-remote-desktop/src/iron-remote-desktop.svelte @@ -72,6 +72,7 @@ let lastClipboardMonitorLoopError: Error | null = null; let componentDestroyed = false; + let runWhenFocusedQueue: (() => void)[] = []; /* Firefox-specific BEGIN */ @@ -173,13 +174,23 @@ } } + function runWhenWindowFocused(fn: () => void) { + if (document.hasFocus()) { + fn(); + } else { + runWhenFocusedQueue.push(fn); + } + } + // This callback is required to update client clipboard state when remote side has changed. function onRemoteClipboardChanged(data: ClipboardData) { try { const mime_formats = clipboardDataToRecord(data); const clipboard_item = new ClipboardItem(mime_formats); - lastReceivedClipboardData = clipboardDataToClipboardItemsRecord(data); - navigator.clipboard.write([clipboard_item]); + runWhenWindowFocused(() => { + lastReceivedClipboardData = clipboardDataToClipboardItemsRecord(data); + navigator.clipboard.write([clipboard_item]); + }); } catch (err) { console.error('Failed to set client clipboard: ' + err); } @@ -445,6 +456,8 @@ window.addEventListener('keydown', captureKeys, false); window.addEventListener('keyup', captureKeys, false); + + window.addEventListener('focus', focusEventHandler); } function resetHostStyle() { @@ -717,6 +730,13 @@ inner.dispatchEvent(new CustomEvent('ready', { detail: result, bubbles: true, composed: true })); } + function focusEventHandler() { + while (runWhenFocusedQueue.length > 0) { + const fn = runWhenFocusedQueue.shift(); + fn?.(); + } + } + onMount(async () => { loggingService.verbose = verbose === 'true'; loggingService.info('Dom ready'); @@ -726,6 +746,7 @@ onDestroy(() => { window.removeEventListener('resize', resizeHandler); + window.removeEventListener('focus', focusEventHandler); componentDestroyed = true; });