120 lines
4.2 KiB
JavaScript
120 lines
4.2 KiB
JavaScript
"use strict";
|
|
|
|
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
|
"resource://gre/modules/Downloads.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
|
|
"resource://gre/modules/DownloadPaths.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
|
"resource://gre/modules/osfile.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
|
"resource://gre/modules/FileUtils.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
|
"resource://gre/modules/NetUtil.jsm");
|
|
|
|
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
|
const {
|
|
ignoreEvent,
|
|
} = ExtensionUtils;
|
|
|
|
let currentId = 0;
|
|
|
|
extensions.registerSchemaAPI("downloads", "downloads", (extension, context) => {
|
|
return {
|
|
downloads: {
|
|
download(options) {
|
|
if (options.filename != null) {
|
|
if (options.filename.length == 0) {
|
|
return Promise.reject({message: "filename must not be empty"});
|
|
}
|
|
|
|
let path = OS.Path.split(options.filename);
|
|
if (path.absolute) {
|
|
return Promise.reject({message: "filename must not be an absolute path"});
|
|
}
|
|
|
|
if (path.components.some(component => component == "..")) {
|
|
return Promise.reject({message: "filename must not contain back-references (..)"});
|
|
}
|
|
}
|
|
|
|
if (options.conflictAction == "prompt") {
|
|
// TODO
|
|
return Promise.reject({message: "conflictAction prompt not yet implemented"});
|
|
}
|
|
|
|
function createTarget(downloadsDir) {
|
|
// TODO
|
|
// if (options.saveAs) { }
|
|
|
|
let target;
|
|
if (options.filename) {
|
|
target = OS.Path.join(downloadsDir, options.filename);
|
|
} else {
|
|
let uri = NetUtil.newURI(options.url).QueryInterface(Ci.nsIURL);
|
|
target = OS.Path.join(downloadsDir, uri.fileName);
|
|
}
|
|
|
|
// This has a race, something else could come along and create
|
|
// the file between this test and them time the download code
|
|
// creates the target file. But we can't easily fix it without
|
|
// modifying DownloadCore so we live with it for now.
|
|
return OS.File.exists(target).then(exists => {
|
|
if (exists) {
|
|
switch (options.conflictAction) {
|
|
case "uniquify":
|
|
default:
|
|
target = DownloadPaths.createNiceUniqueFile(new FileUtils.File(target)).path;
|
|
break;
|
|
|
|
case "overwrite":
|
|
break;
|
|
}
|
|
}
|
|
return target;
|
|
});
|
|
}
|
|
|
|
let download;
|
|
return Downloads.getPreferredDownloadsDirectory()
|
|
.then(downloadsDir => createTarget(downloadsDir))
|
|
.then(target => Downloads.createDownload({
|
|
source: options.url,
|
|
target: target,
|
|
})).then(dl => {
|
|
download = dl;
|
|
return Downloads.getList(Downloads.ALL);
|
|
}).then(list => {
|
|
list.add(download);
|
|
|
|
// This is necessary to make pause/resume work.
|
|
download.tryToKeepPartialData = true;
|
|
download.start();
|
|
|
|
// Without other chrome.downloads methods, we can't actually
|
|
// do anything with the id so just return a dummy value for now.
|
|
return currentId++;
|
|
});
|
|
},
|
|
|
|
// When we do open(), check for additional downloads.open permission.
|
|
// i.e.:
|
|
// open(downloadId) {
|
|
// if (!extension.hasPermission("downloads.open")) {
|
|
// throw new context.cloneScope.Error("Permission denied because 'downloads.open' permission is missing.");
|
|
// }
|
|
// ...
|
|
// }
|
|
// likewise for setShelfEnabled() and the "download.shelf" permission
|
|
|
|
onCreated: ignoreEvent(context, "downloads.onCreated"),
|
|
onErased: ignoreEvent(context, "downloads.onErased"),
|
|
onChanged: ignoreEvent(context, "downloads.onChanged"),
|
|
onDeterminingFilename: ignoreEvent(context, "downloads.onDeterminingFilename"),
|
|
},
|
|
};
|
|
});
|