Skip to content

Commit 71cd274

Browse files
SieteSieteSieteMarcker
andauthored
fix: Firefox multi-tab download capture (#101)
Co-authored-by: Marcker <yalamoozla@protonmail.com>
1 parent 519b4a0 commit 71cd274

File tree

1 file changed

+38
-20
lines changed

1 file changed

+38
-20
lines changed

background/index.ts

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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

177188
interface DownloadInfo {
@@ -181,6 +192,7 @@ interface DownloadInfo {
181192
ua?: string
182193
referrer?: string
183194
cookieStoreId?: string
195+
tabId?: number
184196
}
185197

186198
function 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

252265
downloadEvent.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

Comments
 (0)