@@ -169,9 +169,20 @@ chrome.runtime.onStartup &&
169169 } , 3000 )
170170 await refreshSettings ( )
171171 await refreshIsRunning ( )
172-
172+
173173 // Initialize context menus
174174 initContextMenus ( )
175+
176+ // Cleanup old entries from downloadEventSkipMap every 30 seconds
177+ // to prevent memory leaks in case onCreated never fires
178+ setInterval ( ( ) => {
179+ // Note: Since we're using URL as key and deleting immediately after use,
180+ // this is just a safety net. In practice, entries should be deleted quickly.
181+ if ( downloadEventSkipMap . size > 100 ) {
182+ // If map grows too large, clear it entirely as a safety measure
183+ downloadEventSkipMap . clear ( )
184+ }
185+ } , 30000 )
175186} ) ( )
176187
177188interface DownloadInfo {
@@ -181,6 +192,7 @@ interface DownloadInfo {
181192 ua ?: string
182193 referrer ?: string
183194 cookieStoreId ?: string
195+ tabId ?: number
184196}
185197
186198function downloadFilter ( info : DownloadInfo , settings : Settings ) : boolean {
@@ -247,7 +259,8 @@ const downloadEvent =
247259 chrome . downloads . onDeterminingFilename || chrome . downloads . onCreated
248260// In Firefox, the download interception logic will be triggered twice, the order is onHeadersReceived -> onCreated, so a variable is needed to skip the onCreated event to avoid duplicate processing of download tasks.
249261// PS: Why not use the onCreated event uniformly? Because the onCreated event cannot get the size of the downloaded file in Firefox.
250- let downloadEventSkip = false
262+ // Use a Map to track skip status per-download to avoid race conditions with multiple tabs
263+ const downloadEventSkipMap = new Map < string , boolean > ( )
251264
252265downloadEvent . addListener ( async function ( item ) {
253266 const info : DownloadInfo = {
@@ -258,8 +271,9 @@ downloadEvent.addListener(async function (item) {
258271 referrer : item . referrer ,
259272 cookieStoreId : ( item as any ) . cookieStoreId
260273 }
261- if ( isFirefox && downloadEventSkip ) {
262- downloadEventSkip = false
274+ const downloadUrl = item . finalUrl || item . url
275+ if ( isFirefox && downloadEventSkipMap . get ( downloadUrl ) ) {
276+ downloadEventSkipMap . delete ( downloadUrl )
263277 return
264278 }
265279
@@ -309,7 +323,7 @@ if (isFirefox) {
309323 }
310324
311325 // Skip the onCreated event to avoid duplicate processing of download tasks.
312- downloadEventSkip = true
326+ downloadEventSkipMap . set ( res . url , true )
313327
314328 let filename = ""
315329 // Parse filename from content-disposition
@@ -334,7 +348,8 @@ if (isFirefox) {
334348 filesize,
335349 ua : navigator . userAgent ,
336350 referrer : ( res as any ) . originUrl ,
337- cookieStoreId : ( res as any ) . cookieStoreId
351+ cookieStoreId : ( res as any ) . cookieStoreId ,
352+ tabId : res . tabId
338353 }
339354 if ( ! downloadFilter ( info , settingsCache ) ) {
340355 return
@@ -375,23 +390,26 @@ function handleRemoteDownload(
375390 // Multiple servers available and manual selection is enabled - show server selector
376391 return async ( ) => {
377392 try {
378- // Show server selector overlay
379- const tabs = await chrome . tabs . query ( {
380- active : true ,
381- currentWindow : true
382- } )
383- if ( tabs . length === 0 ) {
384- // Fallback to default server if no active tab
385- const defaultServer = settings . remote . servers . find (
386- ( server ) => getFullUrl ( server ) === settings . remote . selectedServer
387- )
388- if ( defaultServer ) {
389- await createDownloadTask ( info , defaultServer , settings ) ( )
393+ // Use the tabId from the download info (for Firefox webRequest) or query for active tab
394+ let tabId = info . tabId
395+ if ( tabId === undefined ) {
396+ const tabs = await chrome . tabs . query ( {
397+ active : true ,
398+ currentWindow : true
399+ } )
400+ if ( tabs . length === 0 ) {
401+ // Fallback to default server if no active tab
402+ const defaultServer = settings . remote . servers . find (
403+ ( server ) => getFullUrl ( server ) === settings . remote . selectedServer
404+ )
405+ if ( defaultServer ) {
406+ await createDownloadTask ( info , defaultServer , settings ) ( )
407+ }
408+ return
390409 }
391- return
410+ tabId = tabs [ 0 ] . id !
392411 }
393412
394- const tabId = tabs [ 0 ] . id !
395413 const requestId = tabId . toString ( )
396414
397415 // Send message to content script to show server selector
0 commit comments