11// Import IndexedDB storage module
22importScripts ( 'src/indexeddb.js' ) ;
33
4- let attachedTabs = new Map ( ) ;
5- let collectedData = { jsFiles : { } , apiCalls : [ ] , webSockets : [ ] } ;
6- let windowModeActive = false ; // Track if window mode is active
7- let deletedWebSocketIds = new Set ( ) ; // Track deleted WebSocket IDs to prevent re-adding
4+ const attachedTabs = new Map ( ) ;
5+ // REMOVED: large in-memory collectedData object. We rely on IndexedDB + small cache for active WebSocket control.
6+ let collectedData = { webSockets : [ ] } ; // Only keep minimum needed for WS control
7+ let windowModeActive = false ;
8+ let deletedWebSocketIds = new Set ( ) ;
9+ // Optimized Tracking BlockList (Regex is faster than array.some + includes)
10+ const TRACKING_REGEX = new RegExp (
11+ [
12+ 'google-analytics' , 'googletagmanager' , 'g\\.doubleclick' , 'googleads' , 'doubleclick' , 'facebook\\.com/tr' ,
13+ 'analytics' , 'telemetry' , 'pixel' , 'tracker' , 'tracking' ,
14+ 'firebase' , 'fcm\\.googleapis' , 'onesignal' , 'braze' ,
15+ 'segment' , 'mixpanel' , 'amplitude' , 'adjust' , 'appsflyer' , 'heapanalytics' ,
16+ 'hotjar' , 'clarity' , 'sentry' , 'newrelic' , 'datadog' ,
17+ 'collect' , 'measure' , 'beacon' ,
18+ 'notifyvisitors' , 'clevertap' , 'heatmaps' , 'event-api' , 'google\\.com/xjs' , 'gstatic\\.com/_/mss' , 'connect\\.facebook\\.net/signals'
19+ ] . join ( '|' ) , 'i'
20+ ) ;
21+
22+ // --- STATE RESTORATION FOR EXTENSION RESTARTS (Manfiest V3) ---
23+ async function restoreState ( ) {
24+ try {
25+ const saved = await StorageDB . getSetting ( 'activeSessions' ) ;
26+ if ( saved && Array . isArray ( saved ) ) {
27+ console . log ( '[Background] Restoring active sessions:' , saved . length ) ;
28+ for ( const session of saved ) {
29+ if ( ! attachedTabs . has ( session . tabId ) ) {
30+ // Re-hydrate session object
31+ attachedTabs . set ( session . tabId , {
32+ domain : session . domain ,
33+ captureAllRequests : session . captureAllRequests ,
34+ requests : new Map ( ) , // Start fresh for in-memory requests map (ok since we store to DB)
35+ websockets : new Map ( )
36+ } ) ;
37+
38+ // Re-arm debugger if needed?
39+ // Usually debugger stays attached, we just lost our local map.
40+ // We can Verify attachment:
41+ chrome . debugger . getTargets ( ( targets ) => {
42+ const isAttached = targets . some ( t => t . tabId === session . tabId && t . attached ) ;
43+ if ( ! isAttached ) {
44+ console . warn ( `[Background] Tab ${ session . tabId } was marked active but is not attached. Cleaning up.` ) ;
45+ attachedTabs . delete ( session . tabId ) ;
46+ saveSessionState ( ) ;
47+ }
48+ } ) ;
49+ }
50+ }
51+ }
52+
53+ // Restore window mode state
54+ const winState = await StorageDB . getSetting ( 'windowModeActive' ) ;
55+ windowModeActive = ! ! winState ;
56+
57+ } catch ( e ) {
58+ console . error ( 'Failed to restore state:' , e ) ;
59+ }
60+ }
61+
62+ // Save current attachedTabs state to DB
63+ async function saveSessionState ( ) {
64+ const sessions = [ ] ;
65+ for ( const [ tabId , data ] of attachedTabs ) {
66+ sessions . push ( {
67+ tabId,
68+ domain : data . domain ,
69+ captureAllRequests : data . captureAllRequests
70+ } ) ;
71+ }
72+ await StorageDB . setSetting ( 'activeSessions' , sessions ) ;
73+ await StorageDB . setSetting ( 'windowModeActive' , windowModeActive ) ;
74+ }
75+
76+ // Init
77+ chrome . runtime . onStartup . addListener ( restoreState ) ;
78+ // Also run immediately in case of update/reload
79+ restoreState ( ) ;
80+
881
982function isJavaScriptFile ( type , url ) {
1083 if ( ! url ) return false ;
11- const urlLower = url . toLowerCase ( ) ;
84+ // Fast path
1285 if ( type === 'Script' ) return true ;
13- if ( urlLower . endsWith ( '.js' ) || urlLower . endsWith ( '.mjs' ) || urlLower . endsWith ( '.jsx' ) || urlLower . includes ( '.js?' ) || urlLower . includes ( '.mjs?' ) || urlLower . includes ( '.jsx?' ) ) return true ;
14- if ( urlLower . includes ( 'javascript ') || urlLower . includes ( '/script ') || urlLower . includes ( 'type=script' ) ) return true ;
15- return false ;
86+ const u = url . split ( '?' ) [ 0 ] . toLowerCase ( ) ; // ignore params for extension check
87+ return u . endsWith ( '.js ') || u . endsWith ( '.mjs ') || u . endsWith ( '.jsx' ) ||
88+ url . includes ( 'javascript:' ) || url . includes ( '/script' ) ;
1689}
1790
1891function matchesDomainOrSubdomain ( hostname , targetDomain ) {
1992 if ( ! hostname || ! targetDomain ) return false ;
20- const hostnameLower = hostname . toLowerCase ( ) ;
21- const targetLower = targetDomain . toLowerCase ( ) ;
22- if ( hostnameLower === targetLower ) return true ;
23- if ( hostnameLower . endsWith ( '.' + targetLower ) ) {
24- const depth = hostnameLower . split ( '.' ) . length - targetLower . split ( '.' ) . length ;
25- if ( depth > 0 && depth <= 10 ) return true ;
26- }
27- if ( targetLower . endsWith ( '.' + hostnameLower ) ) {
28- const depth = targetLower . split ( '.' ) . length - hostnameLower . split ( '.' ) . length ;
29- if ( depth > 0 && depth <= 10 ) return true ;
30- }
31- return false ;
93+ if ( hostname === targetDomain ) return true ;
94+ return hostname . endsWith ( '.' + targetDomain ) ; // Optimized simple suffix check
3295}
3396
3497chrome . runtime . onInstalled . addListener ( async ( ) => {
35- // Initialize IndexedDB settings
3698 await StorageDB . setSettings ( {
3799 isRecording : false ,
38100 lastError : null
39101 } ) ;
40- // Data is now stored in IndexedDB object stores, not as a single 'collectedData' key
41102 console . log ( '[Background] Extension installed/updated, IndexedDB initialized' ) ;
42103} ) ;
43104
44105chrome . runtime . onMessage . addListener ( ( request , sender , sendResponse ) => {
45- console . log ( '[Background] Message received:' , JSON . stringify ( request ) ) ;
46-
47- const action = request . action ? request . action . trim ( ) : '' ;
106+ const action = request . action || '' ;
48107
49108 if ( action === 'startRecording' ) {
50109 doStartRecording ( request . windowId , request . tabId , request . domain , request . captureAllRequests )
51110 . then ( ( ) => sendResponse ( { success : true , status : 'started' } ) )
52- . catch ( ( error ) => sendResponse ( { success : false , error : error . message } ) ) ;
111+ . catch ( ( error ) => sendResponse ( { success : false , error : ( error . message || error ) } ) ) ;
53112 return true ;
54113 }
55114 if ( action === 'stopRecording' ) {
56115 doStopRecording ( request . tabId )
57116 . then ( ( ) => sendResponse ( { success : true } ) )
58- . catch ( ( error ) => sendResponse ( { success : false , error : error . message } ) ) ;
117+ . catch ( ( error ) => sendResponse ( { success : false , error : ( error . message || error ) } ) ) ;
59118 return true ;
60119 }
61120 if ( action === 'clearData' ) {
@@ -71,36 +130,12 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
71130 return true ;
72131 }
73132
74- // Catch-all for unknown actions to aid debugging
75- console . warn ( '[Background] Unknown action received:' , request . action ) ;
76- // Log char codes to detect hidden characters
77- if ( request . action ) {
78- console . warn ( 'Action char codes:' , request . action . split ( '' ) . map ( c => c . charCodeAt ( 0 ) ) ) ;
79- }
80- sendResponse ( { success : false , error : 'Unknown action: ' + request . action } ) ;
81- return false ;
133+ return false ; // let other listeners handle if any
82134} ) ;
83135
84-
85-
86136async function updateStorage ( entry ) {
87- // Use IndexedDB with automatic batching for optimal performance
88- // The batching mechanism will automatically flush after 100ms or 10 items
137+ // Direct to IndexedDB, bypass memory cache
89138 await StorageDB . addApiCall ( entry ) ;
90-
91- // Update in-memory cache for quick access
92- if ( ! collectedData . apiCalls ) collectedData . apiCalls = [ ] ;
93- const index = collectedData . apiCalls . findIndex ( req => req . id === entry . id ) ;
94- if ( index !== - 1 ) {
95- collectedData . apiCalls [ index ] = entry ;
96- } else {
97- collectedData . apiCalls . push ( entry ) ;
98- }
99-
100- // Optimize memory by keeping only last 2000 requests in memory
101- if ( collectedData . apiCalls . length > 2000 ) {
102- collectedData . apiCalls . shift ( ) ;
103- }
104139}
105140
106141// Helper to get hostname safely
@@ -222,6 +257,7 @@ async function attachToTab(tabId, domain, captureAllRequests) {
222257 websockets : new Map ( )
223258 } ;
224259 attachedTabs . set ( tabId , session ) ;
260+ await saveSessionState ( ) ; // Persist state
225261}
226262
227263// Listen for new tabs in window mode
@@ -283,6 +319,7 @@ async function doStopRecording(tabId) {
283319 attachedTabs . delete ( tid ) ;
284320 }
285321
322+ await saveSessionState ( ) ; // Update state
286323 await StorageDB . setSetting ( 'isRecording' , false ) ;
287324}
288325
@@ -336,6 +373,8 @@ chrome.debugger.onDetach.addListener((source, reason) => {
336373 }
337374
338375 attachedTabs . delete ( source . tabId ) ;
376+ saveSessionState ( ) ; // Update state
377+
339378 // Only stop recording if no tabs are attached and not in window mode
340379 if ( attachedTabs . size === 0 && ! windowModeActive ) {
341380 StorageDB . setSetting ( 'isRecording' , false ) ;
@@ -376,21 +415,9 @@ async function handleRequest(tabId, params, session) {
376415 // Filters control display only, not capture
377416
378417 // Layer 4: Block Analytics, Tracking, and Push Notifications
379- // Blocks common services: Google Analytics, GTM, Firebase, OneSignal, Segment, Mixpanel, etc.
380- // Blocks keywords: analytics, tracking, telemetry, pixel, metric, etc.
381- const trackingPatterns = [
382- 'google-analytics' , 'googletagmanager' , 'g.doubleclick' , 'googleads' , 'doubleclick' , 'facebook.com/tr' ,
383- 'analytics' , 'telemetry' , 'pixel' , 'tracker' , 'tracking' ,
384- 'firebase' , 'fcm.googleapis' , 'onesignal' , 'braze' , 'push' , 'notification' ,
385- 'segment' , 'mixpanel' , 'amplitude' , 'adjust' , 'appsflyer' , 'heapanalytics' ,
386- 'hotjar' , 'clarity' , 'sentry' , 'newrelic' , 'datadog' ,
387- 'collect' , 'measure' , 'beacon' ,
388- // Added based on user feedback:
389- 'notifyvisitors' , 'clevertap' , 'heatmaps' , 'event-api' , 'google.com/xjs' , 'gstatic.com/_/mss' , 'connect.facebook.net/signals'
390- ] ;
391-
392- if ( trackingPatterns . some ( p => urlLower . includes ( p ) ) ) {
393- console . log ( `[API Inspector] ❌ Blocked tracking/analytics: ${ url } ` ) ;
418+ // Optimized: Use pre-compiled Regex
419+ if ( TRACKING_REGEX . test ( urlLower ) ) {
420+ // console.log(`[API Inspector] ❌ Blocked tracking/analytics: ${url}`);
394421 return ;
395422 }
396423
0 commit comments