Spaces:
Sleeping
Sleeping
| // ββ AI Detector Β· background.js (service worker) βββββββββββββββββββββββββ | |
| // | |
| // Responsibilities: | |
| // 1. Keep a canonical scan-result cache (in-memory + storage) | |
| // 2. Handle OPEN_SIDE_PANEL messages from popup | |
| // 3. Relay GET_SCAN_RESULT / CLEAR_SCAN_RESULT messages to side panel | |
| // ββ Init side panel on action click βββββββββββββββββββββββββββββββββββββββ | |
| chrome.runtime.onInstalled.addListener(() => { | |
| chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch((error) => console.error(error)); | |
| }); | |
| // ββ In-memory result cache ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Survives for the lifetime of the service worker (typically a few minutes | |
| // of inactivity). chrome.storage.local is the durable fallback. | |
| let cachedResult = null; | |
| const activeScans = new Set(); | |
| chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { | |
| // Orchestrate scan logic globally to avoid duplication and sync state | |
| if (msg.type === "START_SCAN") { | |
| const tabId = msg.tabId; | |
| if (activeScans.has(tabId)) { | |
| sendResponse({ ok: false }); | |
| return false; | |
| } | |
| activeScans.add(tabId); | |
| console.log("SCAN_STARTED"); | |
| cachedResult = { status: "scanning", tabId, timestamp: Date.now() }; | |
| chrome.storage.local.set({ scanResult: cachedResult, lastScreenshot: msg.screenshot }).catch(()=>{}); | |
| chrome.scripting.executeScript({ | |
| target: { tabId }, | |
| files: ["content.js"] | |
| }).then(() => { | |
| const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Scan timed out after 90 seconds")), 90000)); | |
| const scanPromise = new Promise((resolve, reject) => { | |
| chrome.tabs.sendMessage(tabId, { type: "SCAN_IMAGES", screenshot: msg.screenshot }, (resp) => { | |
| if (chrome.runtime.lastError) reject(new Error(chrome.runtime.lastError.message)); | |
| else resolve(resp); | |
| }); | |
| }); | |
| return Promise.race([scanPromise, timeoutPromise]); | |
| }).then((response) => { | |
| if (!response) throw new Error("No response from content script"); | |
| let finalResult; | |
| if (response.stopped) { | |
| finalResult = { status: "stopped", risk: 0, count: 0, explanation: null, tabId, timestamp: Date.now() }; | |
| } else if (response.done) { | |
| const risk = typeof response.risk === "number" ? response.risk : 0; | |
| const count = typeof response.count === "number" ? response.count : 0; | |
| const explanation = response.explanation || null; | |
| const rawConfidence = typeof response.rawConfidence === "number" ? response.rawConfidence : risk / 100; | |
| finalResult = { status: "done", risk, count, explanation, rawConfidence, tabId, timestamp: Date.now() }; | |
| } else { | |
| throw new Error(response.error || "Unexpected response"); | |
| } | |
| console.log("SCAN_COMPLETED"); | |
| cachedResult = finalResult; | |
| chrome.storage.local.set({ scanResult: finalResult }).catch(()=>{}); | |
| }).catch(err => { | |
| console.warn("SCAN_ERROR:", err.message); | |
| const errResult = { status: "error", error: err.message, tabId, timestamp: Date.now() }; | |
| cachedResult = errResult; | |
| chrome.storage.local.set({ scanResult: errResult }).catch(()=>{}); | |
| }).finally(() => { | |
| activeScans.delete(tabId); | |
| }); | |
| sendResponse({ ok: true }); | |
| return false; | |
| } | |
| if (msg.type === "STOP_SCAN") { | |
| console.log("SCAN_STOPPED"); | |
| chrome.tabs.sendMessage(msg.tabId, { type: "STOP_SCAN" }, () => { | |
| void chrome.runtime.lastError; | |
| }); | |
| sendResponse({ ok: true }); | |
| return false; | |
| } | |
| // Side panel β background: retrieve latest scan result | |
| if (msg.type === "GET_SCAN_RESULT") { | |
| if (cachedResult) { | |
| sendResponse({ result: cachedResult }); | |
| return false; | |
| } | |
| // Fall back to storage (service worker may have been restarted) | |
| chrome.storage.local.get("scanResult", (data) => { | |
| if (chrome.runtime.lastError) { | |
| console.warn("[BG] storage read error:", chrome.runtime.lastError.message); | |
| sendResponse({ result: null }); | |
| return; | |
| } | |
| cachedResult = data.scanResult || null; | |
| sendResponse({ result: cachedResult }); | |
| }); | |
| return true; // async response | |
| } | |
| // Popup or side panel: clear stored result | |
| if (msg.type === "CLEAR_SCAN_RESULT") { | |
| cachedResult = null; | |
| chrome.storage.local.remove("scanResult"); | |
| sendResponse({ ok: true }); | |
| return false; | |
| } | |
| }); | |