I have a browserAction which, in effect, downloads multiple files in quick succession. Although the call to downloads.download
comes from a message event handler, the behavior is the same as if the downloads are simply issued sequentially in one function.
Consider this code
var src_url = "http://some_url/hi_my_name_is_bob.jpg";
var filename = "something.jpg";
browser.downloads.download({url: src_url, filename: filename, conflictAction: "uniquify"});
browser.downloads.download({url: src_url, filename: filename, conflictAction: "uniquify"});
// ^ 2nd src url may be different or same - doesn't matter
The second downloads.download
call (and any more calls if one were to add them) fails on Firefox 61.0.1. This is the entire error message from the console:
Handler function threw an exception: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsITraceableChannel.setNewListener]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: resource://devtools/shared/base-loader.js -> resource://devtools/shared/webconsole/network-monitor.js :: _setupResponseListener :: line 1310" data: no]
Stack: _setupResponseListener@resource://devtools/shared/base-loader.js -> resource://devtools/shared/webconsole/network-monitor.js:1310:28
_createNetworkEvent@resource://devtools/shared/base-loader.js -> resource://devtools/shared/webconsole/network-monitor.js:1186:5
_onRequestHeader@resource://devtools/shared/base-loader.js -> resource://devtools/shared/webconsole/network-monitor.js:1211:5
NetworkMonitor.prototype.observeActivity<@resource://devtools/shared/base-loader.js -> resource://devtools/shared/webconsole/network-monitor.js:1063:7
exports.makeInfallible/<@resource://devtools/shared/base-loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:109:14
Line: 1310, column: 0
Additionally, the download manager shows a download which never progresses and must eventually be canceled. The error only happens if the filenames are identical.
My guts tell me that there's a race condition when allocating the files on the disk. However that theory isn't entirely conclusive, as I get these files on the disk:
something.jpg (which is the first file, fully downloaded)
something(1).jpg (0 bytes, file handle is NOT opened by firefox)
something(1).jpg.part (0 bytes, file handle is opened by firefox - must cancel the broken download to delete this file)
It looks like Firefox does resolve the conflict correctly, but that information gets lost somewhere along the way.
Be that as it may, I tried implementing a download queue using the promise returned by downloads.download
, similar to this:
browser.downloads.download({url: src_url, filename: filename, conflictAction: "uniquify"})
.then(function()
{
browser.downloads.download({url: src_url, filename: filename, conflictAction: "uniquify"});
});
Hoping that the callback would only be called after the file had already been safely allocated on the disk, preventing any threading issues between checking if the file exists, resolving conflicts (if applicable) and allocating it. That didn't work at all though - as far as I can tell, the callback is called synchronously from within downloads.download
. I couldn't tell any difference.
I got it to work properly using a delay of 1000 ms:
browser.downloads.download({url: src_url, filename: filename, conflictAction: "uniquify"});
setTimeout(function()
{
browser.downloads.download({url: src_url, filename: filename, conflictAction: "uniquify"});
}, 1000);
By "work properly" I mean that the filename conflict is recognized by the browser, the conflictAction
does its thing and all files are downloaded properly. With only 500 ms it worked only sometimes on my system.
Obviously the arbitrary timeout isn't a good measure. How to implement downloading multiple files (which may have the same filename) in quick succession reliably without causing this problem?
User contributions licensed under CC BY-SA 3.0