// CrowdProof Observe - Background Script // Handles sidebar toggle and API communication // Toggle sidebar when browser action button is clicked browser.browserAction.onClicked.addListener(() => { browser.sidebarAction.toggle(); }); // Create context menu for links browser.contextMenus.create({ id: "send-url-to-crowdproof", title: "Send URL to CrowdProof", contexts: ["link"] }); // Create context menu for images browser.contextMenus.create({ id: "send-image-to-crowdproof", title: "Send Image to CrowdProof", contexts: ["image"] }); // Handle context menu click browser.contextMenus.onClicked.addListener((info, tab) => { if (info.menuItemId === "send-url-to-crowdproof") { // Open sidebar immediately (must be synchronous for user action context) browser.sidebarAction.open(); // Then store the URL browser.storage.local.set({ pendingUrl: info.linkUrl }); } else if (info.menuItemId === "send-image-to-crowdproof") { // Open sidebar immediately browser.sidebarAction.open(); // Then capture and store the image const targetElementId = info.targetElementId; browser.tabs.executeScript(tab.id, { code: ` (function() { try { const img = browser.menus.getTargetElement(${targetElementId}); if (!img || img.tagName !== 'IMG') return null; const canvas = document.createElement('canvas'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); return canvas.toDataURL('image/png'); } catch (e) { return null; } })(); ` }).then((results) => { const dataUrl = results && results[0]; browser.storage.local.set({ pendingImage: { dataUrl: dataUrl, sourceUrl: info.srcUrl } }); }); } }); const API = { async getServerUrl() { const result = await browser.storage.local.get('serverUrl'); return result.serverUrl || null; }, async request(endpoint, options = {}) { const serverUrl = await this.getServerUrl(); if (!serverUrl) { throw new Error('Server URL not configured. Please set it in extension options.'); } const url = `${serverUrl}${endpoint}`; const fetchOptions = { credentials: 'include', ...options }; const response = await fetch(url, fetchOptions); return response; }, async login(username, password) { const formData = new FormData(); formData.append('username', username); formData.append('password', password); const response = await this.request('/login', { method: 'POST', body: formData }); // Login redirects on success, returns to login page on failure // We check if we got redirected to a non-login page return { success: response.ok && !response.url.includes('/login'), response }; }, async logout() { const response = await this.request('/logout', { method: 'GET' }); return response.ok; }, async checkAuth() { // Try to access a protected page to check if we're logged in const response = await this.request('/profile', { method: 'GET' }); // If we get redirected to login, we're not authenticated return !response.url.includes('/login'); }, async createObservation(type, formData) { const response = await this.request(`/create_observation/${type}`, { method: 'POST', body: formData }); return { success: response.ok, response }; } }; // Handle messages from popup browser.runtime.onMessage.addListener(async (message, sender) => { try { switch (message.action) { case 'getServerUrl': return { serverUrl: await API.getServerUrl() }; case 'checkAuth': const isAuthenticated = await API.checkAuth(); return { authenticated: isAuthenticated }; case 'login': const loginResult = await API.login(message.username, message.password); return { success: loginResult.success }; case 'logout': const logoutSuccess = await API.logout(); return { success: logoutSuccess }; case 'createObservation': const obsResult = await API.createObservation(message.type, message.formData); return { success: obsResult.success }; default: return { error: 'Unknown action' }; } } catch (error) { return { error: error.message }; } });