From b2da17ab6207211fdaf0353e493e8c055d69596c Mon Sep 17 00:00:00 2001 From: KoalaDev <6156589+Shik3i@users.noreply.github.com> Date: Tue, 16 Jun 2026 05:39:17 +0200 Subject: [PATCH] fix(lazy-connect): harden connection lifecycle against race conditions and edge cases - Prevent concurrent connect() by removing isConnecting reset from forceDisconnect(); the guard in connect() now reliably blocks re-entry since no external caller can defeat it. - Reset isConnecting at end of connect() so subsequent calls can proceed without waiting for the async '40' handler. - Clear connectIntent and cancel reconnect timer on server ERROR when no currentRoom exists, preventing infinite reconnect loops after failed join attempts (e.g. wrong password via invite link). - Move connectIntent assignment after !roomId guard in WEB_JOIN_REQUEST to avoid clobbering existing intent on invalid input. - Broadcast JOIN_STATUS failure to popup and bridge tabs when WEB_JOIN_REQUEST receives an invalid room ID, so the website join page receives feedback instead of hanging forever. - Clear joinBtnTimeout on CONNECTION_STATUS connected/disconnected to avoid stale timeout error messages in the popup. --- extension/background.js | 23 ++++++++++++++++++++--- extension/popup.js | 3 +++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/extension/background.js b/extension/background.js index 89a3ec9..9eb8320 100644 --- a/extension/background.js +++ b/extension/background.js @@ -343,7 +343,6 @@ function forceDisconnect() { socket = null; } currentServerUrl = null; - isConnecting = false; isNamespaceJoined = false; isForceSyncInitiator = false; expectedAcksCount = 0; @@ -606,6 +605,8 @@ async function connect() { addLog('WebSocket Error: Connection failed', logType); }; + isConnecting = false; + } catch (e) { isConnecting = false; const logType = reconnectAttempts > 1 ? 'error' : 'warn'; @@ -856,6 +857,14 @@ function handleServerEvent(event, data) { break; case EVENTS.ERROR: isConnecting = false; + // If we get a server error before successfully joining a room, + // clear connectIntent to prevent an infinite reconnect loop. + if (!currentRoom) { + connectIntent = false; + if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } + reconnectAttempts = 0; + reconnectFailed = false; + } broadcastConnectionStatus('disconnected'); addLog(`Server Error: ${data.message}`, 'error'); chrome.storage.local.get(['browserNotifications', 'locale'], async (settings) => { @@ -1554,8 +1563,16 @@ async function handleAsyncMessage(message, sender, sendResponse) { } else if (message.type === 'WEB_JOIN_REQUEST') { const { roomId: rawRoomId, password, useCustomServer, serverUrl } = message; const roomId = typeof rawRoomId === 'string' ? rawRoomId.replace(/[^a-zA-Z0-9\-]/g, '') : ''; - connectIntent = !!roomId; - if (!roomId) { sendResponse({ error: 'invalid_room_id' }); return; } + if (!roomId) { + const errMsg = { type: 'JOIN_STATUS', success: false, message: 'Invalid room ID' }; + chrome.runtime.sendMessage(errMsg).catch(() => {}); + chrome.tabs.query({}, (tabs) => { + tabs.forEach(tab => chrome.tabs.sendMessage(tab.id, errMsg).catch(() => {})); + }); + sendResponse({ status: 'invalid_room_id' }); + return; + } + connectIntent = true; chrome.storage.local.set({ roomId, password, diff --git a/extension/popup.js b/extension/popup.js index d1b9ebf..4f6a142 100644 --- a/extension/popup.js +++ b/extension/popup.js @@ -1623,6 +1623,9 @@ chrome.runtime.onMessage.addListener((msg) => { updatePeerList(msg.peers); if (msg.peers) detectPeerChanges(msg.peers); } else if (msg.type === 'CONNECTION_STATUS') { + if (msg.status === 'connected' || msg.status === 'disconnected') { + if (joinBtnTimeout) { clearTimeout(joinBtnTimeout); joinBtnTimeout = null; } + } if (msg.status === 'connected') { clearConnectionErrorTimer(); }