@@ -53,44 +53,60 @@ initWASM().catch(err => {
5353
5454/**
5555 * Message handler for all extension communication
56+ * Includes global error handling to prevent extension crashes
5657 */
5758chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
58- // Handle screenshot requests (existing functionality)
59- if (request.action === 'captureScreenshot') {
60- handleScreenshotCapture(sender.tab.id, request.options)
61- .then(screenshot => {
62- sendResponse({ success: true, screenshot });
63- })
64- .catch(error => {
65- console.error('[Sentience Background] Screenshot capture failed:', error);
66- sendResponse({
67- success: false,
68- error: error.message || 'Screenshot capture failed'
59+ // Global error handler to prevent extension crashes
60+ try {
61+ // Handle screenshot requests (existing functionality)
62+ if (request.action === 'captureScreenshot') {
63+ handleScreenshotCapture(sender.tab.id, request.options)
64+ .then(screenshot => {
65+ sendResponse({ success: true, screenshot });
66+ })
67+ .catch(error => {
68+ console.error('[Sentience Background] Screenshot capture failed:', error);
69+ sendResponse({
70+ success: false,
71+ error: error.message || 'Screenshot capture failed'
72+ });
6973 });
70- });
71- return true; // Async response
72- }
74+ return true; // Async response
75+ }
7376
74- // Handle WASM processing requests (NEW!)
75- if (request.action === 'processSnapshot') {
76- handleSnapshotProcessing(request.rawData, request.options)
77- .then(result => {
78- sendResponse({ success: true, result });
79- })
80- .catch(error => {
81- console.error('[Sentience Background] Snapshot processing failed:', error);
82- sendResponse({
83- success: false,
84- error: error.message || 'Snapshot processing failed'
77+ // Handle WASM processing requests (NEW!)
78+ if (request.action === 'processSnapshot') {
79+ handleSnapshotProcessing(request.rawData, request.options)
80+ .then(result => {
81+ sendResponse({ success: true, result });
82+ })
83+ .catch(error => {
84+ console.error('[Sentience Background] Snapshot processing failed:', error);
85+ sendResponse({
86+ success: false,
87+ error: error.message || 'Snapshot processing failed'
88+ });
8589 });
90+ return true; // Async response
91+ }
92+
93+ // Unknown action
94+ console.warn('[Sentience Background] Unknown action:', request.action);
95+ sendResponse({ success: false, error: 'Unknown action' });
96+ return false;
97+ } catch (error) {
98+ // Catch any synchronous errors that might crash the extension
99+ console.error('[Sentience Background] Fatal error in message handler:', error);
100+ try {
101+ sendResponse({
102+ success: false,
103+ error: `Fatal error: ${error.message || 'Unknown error'}`
86104 });
87- return true; // Async response
105+ } catch (e) {
106+ // If sendResponse already called, ignore
107+ }
108+ return false;
88109 }
89-
90- // Unknown action
91- console.warn('[Sentience Background] Unknown action:', request.action);
92- sendResponse({ success: false, error: 'Unknown action' });
93- return false;
94110});
95111
96112/**
@@ -119,13 +135,27 @@ async function handleScreenshotCapture(_tabId, options = {}) {
119135/**
120136 * Handle snapshot processing with WASM (NEW!)
121137 * This is where the magic happens - completely CSP-immune!
138+ * Includes safeguards to prevent crashes and hangs.
122139 *
123140 * @param {Array} rawData - Raw element data from injected_api.js
124141 * @param {Object} options - Snapshot options (limit, filter, etc.)
125142 * @returns {Promise<Object>} Processed snapshot result
126143 */
127144async function handleSnapshotProcessing(rawData, options = {}) {
145+ const MAX_ELEMENTS = 10000; // Safety limit to prevent hangs
146+ const startTime = performance.now();
147+
128148 try {
149+ // Safety check: limit element count to prevent hangs
150+ if (!Array.isArray(rawData)) {
151+ throw new Error('rawData must be an array');
152+ }
153+
154+ if (rawData.length > MAX_ELEMENTS) {
155+ console.warn(`[Sentience Background] ⚠️ Large dataset: ${rawData.length} elements. Limiting to ${MAX_ELEMENTS} to prevent hangs.`);
156+ rawData = rawData.slice(0, MAX_ELEMENTS);
157+ }
158+
129159 // Ensure WASM is initialized
130160 await initWASM();
131161 if (!wasmReady) {
@@ -135,15 +165,35 @@ async function handleSnapshotProcessing(rawData, options = {}) {
135165 console.log(`[Sentience Background] Processing ${rawData.length} elements with options:`, options);
136166
137167 // Run WASM processing using the imported functions directly
168+ // Wrap in try-catch with timeout protection
138169 let analyzedElements;
139170 try {
140- if (options.limit || options.filter) {
141- analyzedElements = analyze_page_with_options(rawData, options);
142- } else {
143- analyzedElements = analyze_page(rawData);
144- }
171+ // Use a timeout wrapper to prevent infinite hangs
172+ const wasmPromise = new Promise((resolve, reject) => {
173+ try {
174+ let result;
175+ if (options.limit || options.filter) {
176+ result = analyze_page_with_options(rawData, options);
177+ } else {
178+ result = analyze_page(rawData);
179+ }
180+ resolve(result);
181+ } catch (e) {
182+ reject(e);
183+ }
184+ });
185+
186+ // Add timeout protection (18 seconds - less than content.js timeout)
187+ analyzedElements = await Promise.race([
188+ wasmPromise,
189+ new Promise((_, reject) =>
190+ setTimeout(() => reject(new Error('WASM processing timeout (>18s)')), 18000)
191+ )
192+ ]);
145193 } catch (e) {
146- throw new Error(`WASM analyze_page failed: ${e.message}`);
194+ const errorMsg = e.message || 'Unknown WASM error';
195+ console.error(`[Sentience Background] WASM analyze_page failed: ${errorMsg}`, e);
196+ throw new Error(`WASM analyze_page failed: ${errorMsg}`);
147197 }
148198
149199 // Prune elements for API (prevents 413 errors on large sites)
@@ -155,16 +205,29 @@ async function handleSnapshotProcessing(rawData, options = {}) {
155205 prunedRawData = rawData;
156206 }
157207
158- console.log(`[Sentience Background] ✓ Processed: ${analyzedElements.length} analyzed, ${prunedRawData.length} pruned`);
208+ const duration = performance.now() - startTime;
209+ console.log(`[Sentience Background] ✓ Processed: ${analyzedElements.length} analyzed, ${prunedRawData.length} pruned (${duration.toFixed(1)}ms)`);
159210
160211 return {
161212 elements: analyzedElements,
162213 raw_elements: prunedRawData
163214 };
164215 } catch (error) {
165- console.error('[Sentience Background] Processing error:', error);
216+ const duration = performance.now() - startTime;
217+ console.error(`[Sentience Background] Processing error after ${duration.toFixed(1)}ms:`, error);
166218 throw error;
167219 }
168220}
169221
170222console.log('[Sentience Background] Service worker ready');
223+
224+ // Global error handlers to prevent extension crashes
225+ self.addEventListener('error', (event) => {
226+ console.error('[Sentience Background] Global error caught:', event.error);
227+ event.preventDefault(); // Prevent extension crash
228+ });
229+
230+ self.addEventListener('unhandledrejection', (event) => {
231+ console.error('[Sentience Background] Unhandled promise rejection:', event.reason);
232+ event.preventDefault(); // Prevent extension crash
233+ });
0 commit comments