diff --git a/extension/background.js b/extension/background.js index a0f2196..d213403 100644 --- a/extension/background.js +++ b/extension/background.js @@ -75,6 +75,14 @@ async function connect() { if (!finalUrl.includes('://')) { finalUrl = 'ws://' + finalUrl; } + + // Strict WSS Enforcement + const urlObj = new URL(finalUrl); + const isLocal = urlObj.hostname === 'localhost' || urlObj.hostname === '127.0.0.1'; + if (!isLocal && urlObj.protocol === 'ws:') { + finalUrl = finalUrl.replace('ws://', 'wss://'); + addLog('Security: Upgraded to wss:// for remote host.', 'warn'); + } } addLog(`Connecting to ${isCustomServer ? finalUrl : 'Official Server'}...`, 'info'); diff --git a/extension/content.js b/extension/content.js index 8522d73..55e8bf0 100644 --- a/extension/content.js +++ b/extension/content.js @@ -144,6 +144,20 @@ } } + // SPA Navigation Handler (MutationObserver) + let mutationTimeout = null; + const observer = new MutationObserver((mutations) => { + if (mutationTimeout) clearTimeout(mutationTimeout); + mutationTimeout = setTimeout(() => { + const video = findVideo(); + if (video && !video.koalaSyncInitialized) { + console.log('KoalaSync: New video detected via navigation.'); + setupVideoListeners(video); + } + }, 1000); // 1s debounce + }); + observer.observe(document.body, { childList: true, subtree: true }); + // Heartbeat let heartbeatErrorCount = 0; const heartbeatInterval = setInterval(() => { @@ -162,6 +176,7 @@ console.warn('KoalaSync: Extension reloaded. Please refresh the page if sync stops working.'); } clearInterval(heartbeatInterval); + observer.disconnect(); } }); } diff --git a/extension/popup.js b/extension/popup.js index b802838..39ed01a 100644 --- a/extension/popup.js +++ b/extension/popup.js @@ -39,6 +39,7 @@ const elements = { }; let localPeerId = null; +let lastPeersJson = null; // --- Initialization --- async function init() { @@ -90,6 +91,12 @@ function updateUI(roomId, password) { function updatePeerList(peers) { if (!peers || !elements.peerList) return; + + // UI Throttle: Only re-render if the peer state actually changed + const currentPeersJson = JSON.stringify(peers); + if (currentPeersJson === lastPeersJson) return; + lastPeersJson = currentPeersJson; + elements.peerList.innerHTML = peers.map(p => { const id = escapeHtml(typeof p === 'object' ? p.peerId : p); const titleText = (typeof p === 'object' && p.tabTitle) ? escapeHtml(p.tabTitle) : '';