@@ -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