// background.js // Listen for messages from content scripts or offscreen document let isSniffing = false; let lastClipboardContent = ''; let creatingOffscreenDocument; // Hot-reconnect: Inject content script into existing tabs upon installation/update/restart const injectContentScriptIfNeeded = async () => { const tabs = await chrome.tabs.query({ url: ['http://localhost/*', 'http://localhost:*/*', 'https://tools.7u.pl/*'] }); for (const tab of tabs) { try { // Try to ping the tab first try { await chrome.tabs.sendMessage(tab.id, { action: 'ping' }); // console.log('Content script already active in tab:', tab.id); } catch (e) { // If ping fails (no listener), inject script await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['content.js'] }); // console.log('Injected content script into existing tab:', tab.id); } } catch (err) { // console.error('Failed to handle tab:', tab.id, err); } } }; chrome.runtime.onInstalled.addListener(injectContentScriptIfNeeded); // Also run on startup (when extension is enabled/reloaded) injectContentScriptIfNeeded(); // Listen for alarms try { if (chrome.alarms) { chrome.alarms.onAlarm.addListener((alarm) => { if (alarm.name === 'keepAlive') { refreshOffscreenDocument(); } }); } else { // console.warn('chrome.alarms API is not available.'); } } catch (e) { // console.error('Error initializing alarms:', e); } // Setup offscreen document async function setupOffscreenDocument(path) { // Check if an offscreen document already exists const existingContexts = await chrome.runtime.getContexts({ contextTypes: ['OFFSCREEN_DOCUMENT'], }); if (existingContexts.length > 0) { return; } // Create an offscreen document if (creatingOffscreenDocument) { await creatingOffscreenDocument; } else { creatingOffscreenDocument = chrome.offscreen.createDocument({ url: path, reasons: ['CLIPBOARD', 'AUDIO_PLAYBACK'], justification: 'To read clipboard content in the background and play notification sounds', }); await creatingOffscreenDocument; creatingOffscreenDocument = null; } } // Lifecycle management: Refresh offscreen document every 25s to avoid 30s timeout async function refreshOffscreenDocument() { if (isSniffing) { await chrome.offscreen.closeDocument(); await setupOffscreenDocument('offscreen.html'); } } // Start sniffing when requested chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => { if (request.action === 'startSniffing') { if (isSniffing) { sendResponse({ status: 'already_started' }); return true; } isSniffing = true; // console.log('Starting sniffing process...'); await setupOffscreenDocument('offscreen.html'); // Setup interval to keep offscreen alive - more aggressive chrome.alarms.create('keepAlive', { periodInMinutes: 0.1 }); // every 6 seconds sendResponse({ status: 'started' }); return true; } if (request.action === 'stopSniffing') { if (!isSniffing) { sendResponse({ status: 'not_running' }); return true; } isSniffing = false; // console.log('Stopping sniffing process...'); // Stop alarm chrome.alarms.clear('keepAlive'); // Close offscreen document if (creatingOffscreenDocument) { await creatingOffscreenDocument; } await chrome.offscreen.closeDocument().catch(() => {}); creatingOffscreenDocument = null; sendResponse({ status: 'stopped' }); return true; } if (request.type === 'clipboard-data' && request.target === 'background') { // Received data from offscreen document if (isSniffing && request.data && request.data !== lastClipboardContent) { lastClipboardContent = request.data; // console.log('Clipboard changed:', request.data.substring(0, 20) + '...'); // Check if sound should be played chrome.storage.local.get(['playSound'], (result) => { if (result.playSound !== false) { // Send message to offscreen document to play sound chrome.runtime.sendMessage({ target: 'offscreen', type: 'play-sound' }); } }); // Broadcast to all active tabs (content scripts) // We could filter by sender.tab.id if we knew which tab started sniffing, // but broadcasting is simpler for now and covers multiple open tabs of the app. const tabs = await chrome.tabs.query({ url: ['http://localhost/*', 'http://localhost:*/*', 'https://tools.7u.pl/*'] }); for (const tab of tabs) { chrome.tabs.sendMessage(tab.id, { action: 'clipboardUpdate', content: request.data }).catch(() => { // Tab might be closed or content script not injected yet }); } } } });