diff --git a/src/background/background.js b/src/background/background.js index d73ae08..b113a1f 100644 --- a/src/background/background.js +++ b/src/background/background.js @@ -3,11 +3,14 @@ class SafeWebBackground { constructor() { + console.log("Safe-Web: Background script constructor called"); this.initializeExtension(); this.setupEventListeners(); + console.log("Safe-Web: Background script initialized successfully"); } async initializeExtension() { + console.log("Safe-Web: Initializing extension..."); // Initialize default settings const defaultSettings = { maskingEnabled: false, @@ -34,6 +37,11 @@ class SafeWebBackground { if (!existingSettings.safeWebSettings) { await chrome.storage.sync.set({ safeWebSettings: defaultSettings }); console.log("Safe-Web: Default settings initialized"); + } else { + console.log( + "Safe-Web: Existing settings found:", + existingSettings.safeWebSettings + ); } } catch (error) { console.error("Safe-Web: Failed to initialize settings:", error); @@ -41,6 +49,7 @@ class SafeWebBackground { } setupEventListeners() { + console.log("Safe-Web: Setting up event listeners..."); // Extension installation/update handling chrome.runtime.onInstalled.addListener(this.handleInstallation.bind(this)); @@ -55,6 +64,8 @@ class SafeWebBackground { // Action (browser button) click handling chrome.action.onClicked.addListener(this.handleActionClick.bind(this)); + + console.log("Safe-Web: Event listeners set up successfully"); } async handleInstallation(details) { @@ -69,50 +80,76 @@ class SafeWebBackground { } } - async handleMessage(message, sender, sendResponse) { + handleMessage(message, sender, sendResponse) { + console.log("Safe-Web: Received message:", message.type, "from:", sender); const { type, data } = message; - try { - switch (type) { - case "GET_SETTINGS": - const settings = await this.getSettings(); - sendResponse({ success: true, data: settings }); - break; - - case "UPDATE_SETTINGS": - await this.updateSettings(data); - sendResponse({ success: true }); - break; - - case "TOGGLE_MASKING": - await this.toggleMasking(sender.tab.id); - sendResponse({ success: true }); - break; - - case "GET_TAB_STATE": - const tabState = await this.getTabState(sender.tab.id); - sendResponse({ success: true, data: tabState }); - break; - - case "UPDATE_TAB_STATE": - await this.updateTabState(sender.tab.id, data); - sendResponse({ success: true }); - break; - - case "ANALYZE_CONTENT": - const analysis = await this.analyzeContent(data); - sendResponse({ success: true, data: analysis }); - break; - - default: - console.warn("Safe-Web: Unknown message type:", type); - sendResponse({ success: false, error: "Unknown message type" }); - } - } catch (error) { - console.error("Safe-Web: Message handling error:", error); - sendResponse({ success: false, error: error.message }); + // Handle PING synchronously + if (type === "PING") { + console.log("Safe-Web: Ping received from:", sender); + const response = { success: true, data: "pong" }; + console.log("Safe-Web: Sending response:", response); + sendResponse(response); + return false; // Don't keep channel open } + // Handle async operations + const handleAsyncOperation = async () => { + try { + let response; + + switch (type) { + case "GET_SETTINGS": + console.log("Safe-Web: Getting settings..."); + const settings = await this.getSettings(); + console.log("Safe-Web: Settings retrieved:", settings); + response = { success: true, data: settings }; + break; + + case "UPDATE_SETTINGS": + console.log("Safe-Web: Updating settings:", data); + await this.updateSettings(data); + console.log("Safe-Web: Settings updated successfully"); + response = { success: true }; + break; + + case "TOGGLE_MASKING": + console.log("Safe-Web: Toggling masking for tab:", sender.tab?.id); + await this.toggleMasking(sender.tab.id); + response = { success: true }; + break; + + case "GET_TAB_STATE": + const tabState = await this.getTabState(sender.tab.id); + response = { success: true, data: tabState }; + break; + + case "UPDATE_TAB_STATE": + await this.updateTabState(sender.tab.id, data); + response = { success: true }; + break; + + case "ANALYZE_CONTENT": + const analysis = await this.analyzeContent(data); + response = { success: true, data: analysis }; + break; + + default: + console.warn("Safe-Web: Unknown message type:", type); + response = { success: false, error: "Unknown message type" }; + } + + console.log("Safe-Web: Sending response:", response); + sendResponse(response); + } catch (error) { + console.error("Safe-Web: Message handling error:", error); + sendResponse({ success: false, error: error.message }); + } + }; + + // Start async operation + handleAsyncOperation(); + // Keep message channel open for async response return true; } @@ -153,12 +190,12 @@ class SafeWebBackground { async getSettings() { try { const result = await chrome.storage.sync.get("safeWebSettings"); - + // Return default settings if none exist if (!result.safeWebSettings) { const defaultSettings = { maskingEnabled: false, - maskingStyle: 'blur', + maskingStyle: "blur", maskingIntensity: 5, sensitivePatterns: { email: true, @@ -167,20 +204,20 @@ class SafeWebBackground { creditCard: true, names: false, addresses: false, - customPatterns: [] + customPatterns: [], }, shortcuts: { - toggleMasking: 'Ctrl+Shift+M' + toggleMasking: "Ctrl+Shift+M", }, - theme: 'dark', - animations: true + theme: "dark", + animations: true, }; - + // Initialize with default settings await chrome.storage.sync.set({ safeWebSettings: defaultSettings }); return defaultSettings; } - + return result.safeWebSettings; } catch (error) { console.error("Safe-Web: Failed to get settings:", error); @@ -288,9 +325,35 @@ class SafeWebBackground { async handleUpdate(previousVersion) { console.log(`Safe-Web updated from ${previousVersion}`); - // Handle any migration logic here } } -// Initialize the background service -new SafeWebBackground(); +// Initialize the background service worker +console.log("Safe-Web: Starting background script initialization..."); +const safeWebBackground = new SafeWebBackground(); +console.log( + "Safe-Web: Background script instance created:", + !!safeWebBackground +); + +// Ensure the service worker stays alive +self.addEventListener("install", (event) => { + console.log("Safe-Web service worker installing..."); + self.skipWaiting(); +}); + +self.addEventListener("activate", (event) => { + console.log("Safe-Web service worker activated"); + event.waitUntil(self.clients.claim()); +}); + +// Keep service worker alive +self.addEventListener("message", (event) => { + console.log("Safe-Web: Service worker received message:", event.data); + if (event.data && event.data.type === "KEEP_ALIVE") { + // Respond to keep-alive ping + event.ports[0].postMessage({ type: "KEEP_ALIVE_RESPONSE" }); + } +}); + +console.log("Safe-Web: Background script setup completed"); diff --git a/src/content/content.js b/src/content/content.js index 2664799..74f6bcd 100644 --- a/src/content/content.js +++ b/src/content/content.js @@ -3,6 +3,7 @@ class SafeWebContentScript { constructor() { + console.log("Safe-Web Content Script: Constructor called"); this.settings = null; this.maskingActive = false; this.maskedElements = new Map(); @@ -15,30 +16,45 @@ class SafeWebContentScript { async init() { if (this.initialized) return; + console.log("Safe-Web Content Script: Initializing..."); try { await this.loadSettings(); + console.log("Safe-Web Content Script: Settings loaded:", this.settings); this.setupMessageListener(); this.setupMutationObserver(); this.setupKeyboardShortcuts(); // Initial scan if masking is enabled if (this.settings?.maskingEnabled) { + console.log( + "Safe-Web Content Script: Masking is enabled, starting masking..." + ); await this.startMasking(); + } else { + console.log("Safe-Web Content Script: Masking is disabled"); } this.initialized = true; - console.log("Safe-Web content script initialized"); + console.log("Safe-Web content script initialized successfully"); } catch (error) { console.error("Safe-Web: Content script initialization failed:", error); } } async loadSettings() { + console.log("Safe-Web Content Script: Loading settings..."); return new Promise((resolve) => { chrome.runtime.sendMessage({ type: "GET_SETTINGS" }, (response) => { + console.log("Safe-Web Content Script: Settings response:", response); if (response?.success) { this.settings = response.data; this.maskingActive = this.settings.maskingEnabled; + console.log( + "Safe-Web Content Script: Settings applied, maskingActive:", + this.maskingActive + ); + } else { + console.error("Safe-Web Content Script: Failed to load settings"); } resolve(); }); @@ -163,14 +179,31 @@ class SafeWebContentScript { } async scanPage() { + console.log( + "Safe-Web Content Script: Scanning page for sensitive information..." + ); if (!this.settings) await this.loadSettings(); const patterns = this.getSensitivePatterns(); + console.log("Safe-Web Content Script: Using patterns:", patterns); const textNodes = this.getTextNodes(document.body); + console.log( + "Safe-Web Content Script: Found", + textNodes.length, + "text nodes to scan" + ); + let maskedCount = 0; textNodes.forEach((node) => { - this.scanTextNode(node, patterns); + const result = this.scanTextNode(node, patterns); + if (result) maskedCount++; }); + + console.log( + "Safe-Web Content Script: Masked", + maskedCount, + "text nodes with sensitive information" + ); } async scanAndMaskNewContent() { @@ -255,20 +288,34 @@ class SafeWebContentScript { scanTextNode(textNode, patterns) { const text = textNode.textContent; let hasMatches = false; + let matchDetails = []; for (const [type, pattern] of Object.entries(patterns)) { - if (pattern.regex.test(text)) { + const matches = text.match(pattern.regex); + if (matches) { hasMatches = true; - break; + matchDetails.push({ type, matches: matches.length }); } } if (hasMatches) { + console.log( + "Safe-Web Content Script: Found matches in text node:", + matchDetails, + "Text:", + text.substring(0, 100) + ); this.maskTextNode(textNode, patterns); + return true; } + return false; } maskTextNode(textNode, patterns) { + console.log( + "Safe-Web Content Script: Masking text node:", + textNode.textContent.substring(0, 100) + ); const parent = textNode.parentElement; if (!parent) return; @@ -278,12 +325,14 @@ class SafeWebContentScript { // Apply masking for each pattern type for (const [type, pattern] of Object.entries(patterns)) { maskedHTML = maskedHTML.replace(pattern.regex, (match) => { + console.log("Safe-Web Content Script: Masking", type, ":", match); return this.createMaskedElement(match, pattern.className, type); }); } // Only replace if we actually found matches if (maskedHTML !== originalText) { + console.log("Safe-Web Content Script: Applying masking to element"); const tempDiv = document.createElement("div"); tempDiv.innerHTML = maskedHTML; @@ -301,6 +350,8 @@ class SafeWebContentScript { textNode, type: "text", }); + } else { + console.log("Safe-Web Content Script: No changes made to text node"); } } diff --git a/src/hooks/useSettings.js b/src/hooks/useSettings.js index 0b8d92f..0bceda6 100644 --- a/src/hooks/useSettings.js +++ b/src/hooks/useSettings.js @@ -37,6 +37,10 @@ export function useSettings() { setLoading(true); setError(null); + console.log("Testing background script connection..."); + const isConnected = await ChromeAPI.testConnection(); + console.log("Background script connection status:", isConnected); + const storedSettings = await ChromeAPI.getSettings(); if (storedSettings) { @@ -48,8 +52,21 @@ export function useSettings() { } } catch (err) { console.error("Failed to load settings:", err); - setError(err.message); - setSettings(defaultSettings); // Fallback to defaults + + let errorMessage = err.message; + if (err.message.includes("Background script not available")) { + errorMessage = + "Extension needs to be reloaded. Please refresh the page and try again."; + } else if (err.message.includes("message port closed")) { + errorMessage = + "Connection lost. Please close and reopen the extension."; + } else if (err.message.includes("Chrome runtime not available")) { + errorMessage = + "Extension not properly loaded. Please reload the extension."; + } + + setError(errorMessage); + setSettings(defaultSettings); } finally { setLoading(false); } diff --git a/src/utils/chrome.js b/src/utils/chrome.js index 60a02c1..3d1384a 100644 --- a/src/utils/chrome.js +++ b/src/utils/chrome.js @@ -14,12 +14,33 @@ export class ChromeAPI { return; } + const timeout = setTimeout(() => { + reject( + new Error("Message timeout - background script may not be responding") + ); + }, 10000); + try { chrome.runtime.sendMessage(message, (response) => { + clearTimeout(timeout); + // Check for runtime errors first if (chrome.runtime.lastError) { - console.error("Chrome runtime error:", chrome.runtime.lastError); - reject(new Error(chrome.runtime.lastError.message)); + const errorMsg = chrome.runtime.lastError.message; + console.error("Chrome runtime error:", errorMsg); + + if ( + errorMsg.includes("message port closed") || + errorMsg.includes("receiving end does not exist") + ) { + reject( + new Error( + "Background script not available - extension may need to be reloaded" + ) + ); + } else { + reject(new Error(errorMsg)); + } return; } @@ -41,28 +62,58 @@ export class ChromeAPI { } }); } catch (error) { + clearTimeout(timeout); console.error("Error sending message:", error); reject(error); } }); } + /** + * Test connection to background script + * @returns {Promise} Connection status + */ + static async testConnection() { + try { + const response = await this.sendMessage({ type: "PING" }); + console.log("Background script connection test successful"); + return true; + } catch (error) { + console.error("Background script connection test failed:", error); + return false; + } + } + /** * Get settings from storage with fallback * @returns {Promise} Settings object */ static async getSettings() { try { - return await this.sendMessage({ type: "GET_SETTINGS" }); + console.log( + "ChromeAPI: Attempting to get settings via background script..." + ); + const result = await this.sendMessage({ type: "GET_SETTINGS" }); + console.log( + "ChromeAPI: Settings received from background script:", + result + ); + return result; } catch (error) { console.error("Failed to get settings:", error); - + // Fallback: try to get settings directly from storage try { + console.log("ChromeAPI: Attempting fallback direct storage access..."); const result = await this.getStorageDirectly(); + console.log( + "ChromeAPI: Settings retrieved from direct storage:", + result + ); return result; } catch (fallbackError) { console.error("Fallback storage access failed:", fallbackError); + console.log("ChromeAPI: Using default settings as final fallback"); return this.getDefaultSettings(); } } @@ -75,7 +126,7 @@ export class ChromeAPI { static getDefaultSettings() { return { maskingEnabled: false, - maskingStyle: 'blur', + maskingStyle: "blur", maskingIntensity: 5, sensitivePatterns: { email: true, @@ -84,13 +135,13 @@ export class ChromeAPI { creditCard: true, names: false, addresses: false, - customPatterns: [] + customPatterns: [], }, shortcuts: { - toggleMasking: 'Ctrl+Shift+M' + toggleMasking: "Ctrl+Shift+M", }, - theme: 'dark', - animations: true + theme: "dark", + animations: true, }; } @@ -105,12 +156,12 @@ export class ChromeAPI { return; } - chrome.storage.sync.get('safeWebSettings', (result) => { + chrome.storage.sync.get("safeWebSettings", (result) => { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } - + const settings = result.safeWebSettings || this.getDefaultSettings(); resolve(settings); }); @@ -128,7 +179,7 @@ export class ChromeAPI { return true; } catch (error) { console.error("Failed to update settings via background:", error); - + // Fallback: update storage directly try { await this.updateStorageDirectly(updates); @@ -175,11 +226,13 @@ export class ChromeAPI { return true; } catch (error) { console.error("Failed to toggle masking:", error); - + // Fallback: update settings directly try { const currentSettings = await this.getSettings(); - await this.updateSettings({ maskingEnabled: !currentSettings.maskingEnabled }); + await this.updateSettings({ + maskingEnabled: !currentSettings.maskingEnabled, + }); return true; } catch (fallbackError) { console.error("Fallback toggle failed:", fallbackError); @@ -232,17 +285,20 @@ export class ChromeAPI { return; } - chrome.scripting.executeScript({ - target: { tabId: tabs[0].id }, - func: new Function(code) - }, (results) => { - if (chrome.runtime.lastError) { - reject(new Error(chrome.runtime.lastError.message)); - return; + chrome.scripting.executeScript( + { + target: { tabId: tabs[0].id }, + func: new Function(code), + }, + (results) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + return; + } + + resolve(results); } - - resolve(results); - }); + ); }); }); } @@ -254,9 +310,12 @@ export class ChromeAPI { static async hasTabPermission() { try { const tab = await this.getCurrentTab(); - return tab.url && (tab.url.startsWith('http://') || tab.url.startsWith('https://')); + return ( + tab.url && + (tab.url.startsWith("http://") || tab.url.startsWith("https://")) + ); } catch (error) { - console.error('Failed to check tab permission:', error); + console.error("Failed to check tab permission:", error); return false; } } @@ -269,7 +328,7 @@ export class ChromeAPI { chrome.runtime.openOptionsPage(); } else { // Fallback for older Chrome versions - chrome.tabs.create({ url: chrome.runtime.getURL('options.html') }); + chrome.tabs.create({ url: chrome.runtime.getURL("options.html") }); } } @@ -278,7 +337,7 @@ export class ChromeAPI { * @returns {string} Extension version */ static getVersion() { - return chrome?.runtime?.getManifest?.()?.version || '1.0.0'; + return chrome?.runtime?.getManifest?.()?.version || "1.0.0"; } /** @@ -314,7 +373,7 @@ export class StorageAPI { static getSync(keys) { return new Promise((resolve, reject) => { if (!chrome?.storage?.sync) { - reject(new Error('Chrome storage API not available')); + reject(new Error("Chrome storage API not available")); return; } @@ -336,7 +395,7 @@ export class StorageAPI { static setSync(data) { return new Promise((resolve, reject) => { if (!chrome?.storage?.sync) { - reject(new Error('Chrome storage API not available')); + reject(new Error("Chrome storage API not available")); return; } @@ -358,7 +417,7 @@ export class StorageAPI { static getLocal(keys) { return new Promise((resolve, reject) => { if (!chrome?.storage?.local) { - reject(new Error('Chrome storage API not available')); + reject(new Error("Chrome storage API not available")); return; } @@ -380,7 +439,7 @@ export class StorageAPI { static setLocal(data) { return new Promise((resolve, reject) => { if (!chrome?.storage?.local) { - reject(new Error('Chrome storage API not available')); + reject(new Error("Chrome storage API not available")); return; } @@ -429,7 +488,7 @@ export const utils = { if (!inThrottle) { func.apply(this, args); inThrottle = true; - setTimeout(() => inThrottle = false, limit); + setTimeout(() => (inThrottle = false), limit); } }; }, @@ -440,10 +499,10 @@ export const utils = { * @returns {any} Cloned object */ deepClone(obj) { - if (obj === null || typeof obj !== 'object') return obj; + if (obj === null || typeof obj !== "object") return obj; if (obj instanceof Date) return new Date(obj.getTime()); - if (obj instanceof Array) return obj.map(item => this.deepClone(item)); - if (typeof obj === 'object') { + if (obj instanceof Array) return obj.map((item) => this.deepClone(item)); + if (typeof obj === "object") { const clonedObj = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { @@ -460,11 +519,11 @@ export const utils = { * @returns {string} Formatted size string */ formatFileSize(bytes) { - if (bytes === 0) return '0 Bytes'; + if (bytes === 0) return "0 Bytes"; const k = 1024; - const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const sizes = ["Bytes", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }, /** @@ -481,7 +540,7 @@ export const utils = { * @returns {boolean} Is valid web page */ isValidWebPage(url) { - return url && (url.startsWith('http://') || url.startsWith('https://')); + return url && (url.startsWith("http://") || url.startsWith("https://")); }, /** @@ -490,8 +549,8 @@ export const utils = { * @returns {string} Sanitized string */ sanitizeHTML(str) { - const temp = document.createElement('div'); + const temp = document.createElement("div"); temp.textContent = str; return temp.innerHTML; - } + }, };