It looks like the "proper" way to do this using WebExtensions is not (yet?) supported by Firefox. Google Chrome has the 'onDeterminingFilename' event which allows you to "suggest" a file name to the user. A "suggestion", which allows the user to override your extension, if desired in this instance, may be better.
The brute force method of doing this is to add a listener to downloads.onCreated
. That listener is passed a downloads.DownloadItem
which will give you both the url
and filename
. The listener can then downloads.cancel()
the download and initiate a new one, downloads.download()
, with the filename
you desire.
How this will work in reality depends on when, exactly, the downloads.onCreated
event actually fires with respect to the interaction with the user. Does it fire before, or after, the "Save As" dialog with the user? You will need to test this to see what works best for the feel you want the user to have. The limited set of values which the downloads.State
type can contain indicates that the event fires only after the "Save As" dialog has completed, but that may not be an accurate indication. If it does fire prior to the "Save As" dialog, we can have that dialog appear with the filename the add-on is suggesting by adding the saveAs: true
property to the object we pass to downloads.download()
Something like (untested):
var ignoreDownloadUrls=[];
var ignoreDownloadIds=[];
chrome.downloads.onCreated.addlistener(forceDownloadFilenameIfUrlMatch);
function forceDownloadFilenameIfUrlMatch(downloadItem){
//Check the URL for duplication (may not be needed, need to test).
let foundUrlIndex = ignoreDownloadUrls.findIndex(function(element,index){
if(element === downloadItem.url){
return true;
}//else
return false;
});
if(foundUrlIndex>-1) {
//This is the callback resulting from our creating the download
// with the new filename. We only want to ignore the URL once,
// so, remove the URL from our ignore list.
// This makes the assumption that the events we get are reasonably
// orderly (only one created event per URL which we want to
// change, etc.). This may need to be more complex in real use.
ignoreDownloadUrls.splice(foundUrlIndex,1);
//We do not want to change this download, as we created it.
return;
}
//Test the ID for one we created:
let foundIdIndex = ignoreDownloadIds.findIndex(function(element,index){
if(element === downloadItem.id){
return true;
}//else
return false;
});
if(foundIdIndex>-1) {
//This is an event resulting from our creating the download
// with the new filename.
//We do not want to change this download, as we created it.
return;
}
if(downloadItem.url.idexOf(myMatchString) >-1){
chrome.downloads.cancel(downloadItem.id);
//Remember the URL so we don't change it twice.
// Remembering the URL may not be the best way to do this. Need
// to test as to the order which events and the download callback
// occur.
ignoreDownloadUrls.push(download.url);
chrome.downloads.download({
url: downloadItem.url,
filename: myNewFilename
}, function(id){
//We could use the callback of the downloads.download() to remember
// the downloadId of the download we created and only ignore that
// one. That would rely on the callback being called prior to the
// new onCreated event. Might work that way in practice. It
// would certainly be better to remember the specifically created
// downloadId rather than just ignore (once) a specific URL.
// Testing should indicate if we need to use the download ID,
// or ignore the URL (once).
//Remember the downloadId so we don't change it twice.
ignoreDownloadIds.push(id);
});
}
}