mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2026-02-22 05:45:47 +01:00
Update downloads.jsm to use Downloads.jsm rather than defunct downloads service.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2011-2013 Kris Maglione <maglione.k@gmail.com>
|
||||
// Copyright (c) 2011-2014 Kris Maglione <maglione.k@gmail.com>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
@@ -10,8 +10,10 @@ defineModule("downloads", {
|
||||
});
|
||||
|
||||
lazyRequire("overlay", ["overlay"]);
|
||||
lazyRequire("promises", ["Task", "promises"]);
|
||||
|
||||
Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
|
||||
lazyRequire("resource://gre/modules/Downloads.jsm", ["Downloads"]);
|
||||
lazyRequire("resource://gre/modules/DownloadUtils.jsm", ["DownloadUtils"]);
|
||||
|
||||
var MAX_LOAD_TIME = 10 * 1000;
|
||||
|
||||
@@ -22,8 +24,8 @@ var states = iter([v, k.slice(prefix.length).toLowerCase()]
|
||||
.toObject();
|
||||
|
||||
var Download = Class("Download", {
|
||||
init: function init(id, list) {
|
||||
this.download = services.downloadManager.getDownload(id);
|
||||
init: function init(download, list) {
|
||||
this.download = download;
|
||||
this.list = list;
|
||||
|
||||
this.nodes = {
|
||||
@@ -39,11 +41,9 @@ var Download = Class("Download", {
|
||||
this.targetFile.path]]],
|
||||
["td", { highlight: "DownloadState", key: "state" }],
|
||||
["td", { highlight: "DownloadButtons Buttons" },
|
||||
["a", { highlight: "Button", href: "javascript:0", key: "pause" }, _("download.action.Pause")],
|
||||
["a", { highlight: "Button", href: "javascript:0", key: "stop" }, _("download.action.Stop")],
|
||||
["a", { highlight: "Button", href: "javascript:0", key: "remove" }, _("download.action.Remove")],
|
||||
["a", { highlight: "Button", href: "javascript:0", key: "resume" }, _("download.action.Resume")],
|
||||
["a", { highlight: "Button", href: "javascript:0", key: "retry" }, _("download.action.Retry")],
|
||||
["a", { highlight: "Button", href: "javascript:0", key: "cancel" }, _("download.action.Cancel")],
|
||||
["a", { highlight: "Button", href: "javascript:0", key: "delete" }, _("download.action.Delete")]],
|
||||
["td", { highlight: "DownloadProgress", key: "progress" },
|
||||
["span", { highlight: "DownloadProgressHave", key: "progressHave" }],
|
||||
@@ -53,8 +53,8 @@ var Download = Class("Download", {
|
||||
["td", { highlight: "DownloadSpeed", key: "speed" }],
|
||||
["td", { highlight: "DownloadTime", key: "time" }],
|
||||
["td", {},
|
||||
["a", { highlight: "DownloadSource", key: "source", href: this.source.spec },
|
||||
this.source.spec]]],
|
||||
["a", { highlight: "DownloadSource", key: "source", href: this.source.url },
|
||||
this.source.url]]],
|
||||
this.list.document, this.nodes);
|
||||
|
||||
this.nodes.launch.addEventListener("click", (event) => {
|
||||
@@ -68,21 +68,22 @@ var Download = Class("Download", {
|
||||
return this;
|
||||
},
|
||||
|
||||
get active() !this.stopped,
|
||||
|
||||
get targetFile() File(this.download.target.path),
|
||||
|
||||
get displayName() this.targetFile.leafName,
|
||||
|
||||
get status() states[this.state],
|
||||
|
||||
inState: function inState(states) states.indexOf(this.status) >= 0,
|
||||
|
||||
get alive() this.inState(["downloading", "notstarted", "paused", "queued", "scanning"]),
|
||||
|
||||
allowedCommands: Class.Memoize(function () let (self = this) ({
|
||||
get cancel() self.cancelable && self.inState(["downloading", "paused", "starting"]),
|
||||
get delete() !this.cancel && self.targetFile.exists(),
|
||||
get launch() self.targetFile.exists() && self.inState(["finished"]),
|
||||
get pause() self.inState(["downloading"]),
|
||||
get remove() self.inState(["blocked_parental", "blocked_policy",
|
||||
"canceled", "dirty", "failed", "finished"]),
|
||||
get resume() self.resumable && self.inState(["paused"]),
|
||||
get retry() self.inState(["canceled", "failed"])
|
||||
get delete() !self.active && (self.targetFile.exists() || self.hasPartialData),
|
||||
get launch() self.targetFile.exists() && self.succeeded,
|
||||
get stop() self.active,
|
||||
get remove() !self.active,
|
||||
get resume() self.canceled
|
||||
})),
|
||||
|
||||
command: function command(name) {
|
||||
@@ -91,15 +92,16 @@ var Download = Class("Download", {
|
||||
|
||||
if (Set.has(this.commands, name))
|
||||
this.commands[name].call(this);
|
||||
else
|
||||
services.downloadManager[name + "Download"](this.id);
|
||||
},
|
||||
|
||||
commands: {
|
||||
delete: function delete_() {
|
||||
this.targetFile.remove(false);
|
||||
delete: promises.task(function delete_() {
|
||||
if (this.hasPartialData)
|
||||
yield this.removePartialData();
|
||||
else if (self.targetFile.exists())
|
||||
this.targetFile.remove(false);
|
||||
this.updateStatus();
|
||||
},
|
||||
}),
|
||||
launch: function launch() {
|
||||
// Behavior mimics that of the builtin Download Manager.
|
||||
function action() {
|
||||
@@ -127,18 +129,28 @@ var Download = Class("Download", {
|
||||
});
|
||||
else
|
||||
action.call(this);
|
||||
}
|
||||
},
|
||||
resume: function resume() {
|
||||
this.download.start();
|
||||
},
|
||||
remove: promises.task(function remove() {
|
||||
yield this.list.list.remove(this.download);
|
||||
yield this.download.finalize(true);
|
||||
}),
|
||||
stop: function stop() {
|
||||
this.download.cancel();
|
||||
},
|
||||
},
|
||||
|
||||
_compare: {
|
||||
active: (a, b) => a.alive - b.alive,
|
||||
active: (a, b) => a.active - b.active,
|
||||
complete: (a, b) => a.percentComplete - b.percentComplete,
|
||||
date: (a, b) => a.startTime - b.startTime,
|
||||
date: (a, b) => a.startTime - b.startTime,
|
||||
filename: (a, b) => String.localeCompare(a.targetFile.leafName, b.targetFile.leafName),
|
||||
size: (a, b) => a.size - b.size,
|
||||
speed: (a, b) => a.speed - b.speed,
|
||||
time: (a, b) => a.timeRemaining - b.timeRemaining,
|
||||
url: (a, b) => String.localeCompare(a.source.spec, b.source.spec)
|
||||
size: (a, b) => a.totalBytes - b.totalBytes,
|
||||
speed: (a, b) => a.speed - b.speed,
|
||||
time: (a, b) => a.timeRemaining - b.timeRemaining,
|
||||
url: (a, b) => String.localeCompare(a.source.url, b.source.url)
|
||||
},
|
||||
|
||||
compare: function compare(other) values(this.list.sortOrder).map(function (order) {
|
||||
@@ -152,17 +164,17 @@ var Download = Class("Download", {
|
||||
updateProgress: function updateProgress() {
|
||||
let self = this.__proto__;
|
||||
|
||||
if (this.amountTransferred === this.size) {
|
||||
if (!this.active) {
|
||||
this.nodes.speed.textContent = "";
|
||||
this.nodes.time.textContent = "";
|
||||
}
|
||||
else {
|
||||
this.nodes.speed.textContent = util.formatBytes(this.speed, 1, true) + "/s";
|
||||
|
||||
if (this.speed == 0 || this.size == 0)
|
||||
if (this.speed == 0 || !this.hasProgress)
|
||||
this.nodes.time.textContent = _("download.unknown");
|
||||
else {
|
||||
let seconds = (this.size - this.amountTransferred) / this.speed;
|
||||
let seconds = (this.totalBytes - this.currentBytes) / this.speed;
|
||||
[, self.timeRemaining] = DownloadUtils.getTimeLeft(seconds, this.timeRemaining);
|
||||
if (this.timeRemaining)
|
||||
this.nodes.time.textContent = util.formatSeconds(this.timeRemaining);
|
||||
@@ -171,17 +183,20 @@ var Download = Class("Download", {
|
||||
}
|
||||
}
|
||||
|
||||
let total = this.nodes.progressTotal.textContent = this.size || !this.nActive ? util.formatBytes(this.size, 1, true)
|
||||
: _("download.unknown");
|
||||
let suffix = RegExp(/( [a-z]+)?$/i.exec(total)[0] + "$");
|
||||
this.nodes.progressHave.textContent = util.formatBytes(this.amountTransferred, 1, true).replace(suffix, "");
|
||||
let total = this.nodes.progressTotal.textContent =
|
||||
this.hasProgress && (this.totalBytes || !this.nActive)
|
||||
? util.formatBytes(this.totalBytes, 1, true)
|
||||
: _("download.unknown");
|
||||
|
||||
this.nodes.percent.textContent = this.size ? Math.round(this.amountTransferred * 100 / this.size) + "%" : "";
|
||||
let suffix = RegExp(/( [a-z]+)?$/i.exec(total)[0] + "$");
|
||||
this.nodes.progressHave.textContent = util.formatBytes(this.currentBytes, 1, true).replace(suffix, "");
|
||||
|
||||
this.nodes.percent.textContent = this.hasProgress ? this.progress + "%" : "";
|
||||
},
|
||||
|
||||
updateStatus: function updateStatus() {
|
||||
|
||||
this.nodes.row[this.alive ? "setAttribute" : "removeAttribute"]("active", "true");
|
||||
this.nodes.row[this.active ? "setAttribute" : "removeAttribute"]("active", "true");
|
||||
|
||||
this.nodes.row.setAttribute("status", this.status);
|
||||
this.nodes.state.textContent = util.capitalize(this.status);
|
||||
@@ -193,14 +208,6 @@ var Download = Class("Download", {
|
||||
this.updateProgress();
|
||||
}
|
||||
});
|
||||
Object.keys(XPCOMShim([Ci.nsIDownload])).forEach(function (key) {
|
||||
if (!(key in Download.prototype))
|
||||
Object.defineProperty(Download.prototype, key, {
|
||||
get: function get() this.download[key],
|
||||
set: function set(val) this.download[key] = val,
|
||||
configurable: true
|
||||
});
|
||||
});
|
||||
|
||||
var DownloadList = Class("DownloadList",
|
||||
XPCOM([Ci.nsIDownloadProgressListener,
|
||||
@@ -213,12 +220,13 @@ var DownloadList = Class("DownloadList",
|
||||
this.nodes = {
|
||||
commandTarget: this
|
||||
};
|
||||
this.downloads = {};
|
||||
this.downloads = Map();
|
||||
},
|
||||
|
||||
cleanup: function cleanup() {
|
||||
this.observe.unregister();
|
||||
services.downloadManager.removeListener(this);
|
||||
if (this.list)
|
||||
this.list.removeView(this);
|
||||
this.dead = true;
|
||||
},
|
||||
|
||||
message: Class.Memoize(function () {
|
||||
@@ -258,40 +266,44 @@ var DownloadList = Class("DownloadList",
|
||||
this.index = Array.indexOf(this.nodes.list.childNodes,
|
||||
this.nodes.head);
|
||||
|
||||
let start = Date.now();
|
||||
for (let row in iter(services.downloadManager.DBConnection
|
||||
.createStatement("SELECT id FROM moz_downloads"))) {
|
||||
if (Date.now() - start > MAX_LOAD_TIME) {
|
||||
util.dactyl.warn(_("download.givingUpAfter", (Date.now() - start) / 1000));
|
||||
break;
|
||||
}
|
||||
this.addDownload(row.id);
|
||||
}
|
||||
this.update();
|
||||
Task.spawn(function () {
|
||||
this.list = yield Downloads.getList(Downloads.ALL);
|
||||
|
||||
util.addObserver(this);
|
||||
services.downloadManager.addListener(this);
|
||||
let start = Date.now();
|
||||
for (let download of yield this.list.getAll()) {
|
||||
if (Date.now() - start > MAX_LOAD_TIME) {
|
||||
util.dactyl.warn(_("download.givingUpAfter", (Date.now() - start) / 1000));
|
||||
break;
|
||||
}
|
||||
this.addDownload(download);
|
||||
}
|
||||
this.update();
|
||||
|
||||
if (!this.dead)
|
||||
this.list.addView(this);
|
||||
}.bind(this));
|
||||
return this.nodes.list;
|
||||
}),
|
||||
|
||||
addDownload: function addDownload(id) {
|
||||
if (!(id in this.downloads)) {
|
||||
let download = Download(id, this);
|
||||
addDownload: function addDownload(download) {
|
||||
if (!this.downloads.has(download)) {
|
||||
download = Download(download, this);
|
||||
if (this.filter && download.displayName.indexOf(this.filter) === -1)
|
||||
return;
|
||||
|
||||
this.downloads[id] = download;
|
||||
let index = values(this.downloads).sort((a, b) => a.compare(b))
|
||||
.indexOf(download);
|
||||
this.downloads.set(download.download, download);
|
||||
let index = values(this.downloads).toArray()
|
||||
.sort((a, b) => a.compare(b))
|
||||
.indexOf(download);
|
||||
|
||||
this.nodes.list.insertBefore(download.nodes.row,
|
||||
this.nodes.list.childNodes[index + this.index + 1]);
|
||||
}
|
||||
},
|
||||
removeDownload: function removeDownload(id) {
|
||||
if (id in this.downloads) {
|
||||
this.nodes.list.removeChild(this.downloads[id].nodes.row);
|
||||
delete this.downloads[id];
|
||||
removeDownload: function removeDownload(download) {
|
||||
if (this.downloads.has(download)) {
|
||||
this.nodes.list.removeChild(this.downloads.get(download).nodes.row);
|
||||
delete this.downloads.delete(download);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -301,17 +313,17 @@ var DownloadList = Class("DownloadList",
|
||||
},
|
||||
|
||||
allowedCommands: Class.Memoize(function () let (self = this) ({
|
||||
get clear() values(self.downloads).some(dl => dl.allowedCommands.remove)
|
||||
get clear() iter(self.downloads.values()).some(dl => dl.allowedCommands.remove)
|
||||
})),
|
||||
|
||||
commands: {
|
||||
clear: function () {
|
||||
services.downloadManager.cleanUp();
|
||||
this.list.removeFinished();
|
||||
}
|
||||
},
|
||||
|
||||
sort: function sort() {
|
||||
let list = values(this.downloads).sort((a, b) => a.compare(b));
|
||||
let list = iter(this.downloads.values()).sort((a, b) => a.compare(b));
|
||||
|
||||
for (let [i, download] in iter(list))
|
||||
if (this.nodes.list.childNodes[i + 1] != download.nodes.row)
|
||||
@@ -335,16 +347,19 @@ var DownloadList = Class("DownloadList",
|
||||
timeRemaining: Infinity,
|
||||
|
||||
updateProgress: function updateProgress() {
|
||||
let downloads = values(this.downloads).toArray();
|
||||
let active = downloads.filter(d => d.alive);
|
||||
let downloads = iter(this.downloads.values()).toArray();
|
||||
let active = downloads.filter(d => d.active);
|
||||
|
||||
let self = Object.create(this);
|
||||
for (let prop in values(["amountTransferred", "size", "speed", "timeRemaining"]))
|
||||
for (let prop in values(["currentBytes", "totalBytes", "speed", "timeRemaining"]))
|
||||
this[prop] = active.reduce((acc, dl) => dl[prop] + acc, 0);
|
||||
|
||||
this.hasProgress = active.every(d => d.hasProgress);
|
||||
this.progress = Math.round((this.currentBytes / this.totalBytes) * 100);
|
||||
this.nActive = active.length;
|
||||
|
||||
Download.prototype.updateProgress.call(self);
|
||||
|
||||
this.nActive = active.length;
|
||||
if (active.length)
|
||||
this.nodes.total.textContent = _("download.nActive", active.length);
|
||||
else for (let key in values(["total", "percent", "speed", "time"]))
|
||||
@@ -354,68 +369,87 @@ var DownloadList = Class("DownloadList",
|
||||
this.sort();
|
||||
},
|
||||
|
||||
observers: {
|
||||
"download-manager-remove-download": function (id) {
|
||||
if (id == null)
|
||||
id = [k for ([k, dl] in iter(this.downloads)) if (dl.allowedCommands.remove)];
|
||||
else
|
||||
id = [id.QueryInterface(Ci.nsISupportsPRUint32).data];
|
||||
onDownloadAdded: function onDownloadAdded(download) {
|
||||
this.addDownload(download);
|
||||
|
||||
Array.concat(id).map(this.closure.removeDownload);
|
||||
this.update();
|
||||
}
|
||||
this.modules.mow.resize(false);
|
||||
this.nodes.list.scrollIntoView(false);
|
||||
},
|
||||
|
||||
onDownloadStateChange: function (state, download) {
|
||||
try {
|
||||
if (download.id in this.downloads)
|
||||
this.downloads[download.id].updateStatus();
|
||||
else {
|
||||
this.addDownload(download.id);
|
||||
onDownloadRemoved: function onDownloadRemoved(download) {
|
||||
this.removeDownload(download);
|
||||
},
|
||||
|
||||
onDownloadChanged: function onDownloadChanged(download) {
|
||||
if (this.downloads.has(download)) {
|
||||
download = this.downloads.get(download)
|
||||
|
||||
download.updateStatus();
|
||||
download.updateProgress();
|
||||
|
||||
this.modules.mow.resize(false);
|
||||
this.nodes.list.scrollIntoView(false);
|
||||
}
|
||||
this.update();
|
||||
|
||||
if (this.shouldSort("active"))
|
||||
this.sort();
|
||||
}
|
||||
catch (e) {
|
||||
util.reportError(e);
|
||||
}
|
||||
},
|
||||
|
||||
onProgressChange: function (webProgress, request,
|
||||
curProgress, maxProgress,
|
||||
curTotalProgress, maxTotalProgress,
|
||||
download) {
|
||||
try {
|
||||
if (download.id in this.downloads)
|
||||
this.downloads[download.id].updateProgress();
|
||||
this.updateProgress();
|
||||
}
|
||||
catch (e) {
|
||||
util.reportError(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
["canceled",
|
||||
"contentType",
|
||||
"currentBytes",
|
||||
"error",
|
||||
"hasPartialData",
|
||||
"hasProgress",
|
||||
"launchWhenSucceeded",
|
||||
"launcherPath",
|
||||
"progress",
|
||||
"saver",
|
||||
"source",
|
||||
"speed",
|
||||
"startTime",
|
||||
"stopped",
|
||||
"succeeded",
|
||||
"target",
|
||||
"totalBytes",
|
||||
"tryToKeepPartialData"].forEach(key => {
|
||||
if (!(key in Download.prototype))
|
||||
Object.defineProperty(Download.prototype, key, {
|
||||
get: function get() this.download[key],
|
||||
set: function set(val) this.download[key] = val,
|
||||
configurable: true
|
||||
});
|
||||
});
|
||||
|
||||
var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
|
||||
|
||||
var Downloads_ = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
|
||||
init: function () {
|
||||
services.downloadManager.addListener(this);
|
||||
Downloads.getList(Downloads.ALL).then(list => {
|
||||
this.list = list;
|
||||
if (!this.dead)
|
||||
this.list.addView(this);
|
||||
});
|
||||
},
|
||||
|
||||
cleanup: function destroy() {
|
||||
services.downloadManager.removeListener(this);
|
||||
if (this.list)
|
||||
this.list.removeView(this);
|
||||
this.dead = true;
|
||||
},
|
||||
|
||||
onDownloadStateChange: function (state, download) {
|
||||
if (download.state == services.downloadManager.DOWNLOAD_FINISHED) {
|
||||
let url = download.source.spec;
|
||||
let title = download.displayName;
|
||||
let file = download.targetFile.path;
|
||||
let size = download.size;
|
||||
onDownloadAdded: function onDownloadAdded(download) {
|
||||
},
|
||||
|
||||
onDownloadRemoved: function onDownloadRemoved(download) {
|
||||
},
|
||||
|
||||
onDownloadChanged: function onDownloadChanged(download) {
|
||||
if (download.succeeded) {
|
||||
let target = File(download.target.path);
|
||||
|
||||
let url = download.source.url;
|
||||
let title = target.leafName;
|
||||
let file = target.path;
|
||||
let size = download.totalBytes;
|
||||
|
||||
overlay.modules.forEach(function (modules) {
|
||||
modules.dactyl.echomsg({ domains: [util.getHost(url)], message: _("io.downloadFinished", title, file) },
|
||||
@@ -451,7 +485,7 @@ var Downloads = Module("downloads", XPCOM(Ci.nsIDownloadProgressListener), {
|
||||
|
||||
commands.add(["dlc[lear]"],
|
||||
"Clear completed downloads",
|
||||
function (args) { services.downloadManager.cleanUp(); });
|
||||
function (args) { downloads.list.removeFinished(); });
|
||||
},
|
||||
options: function initOptions(dactyl, modules, window) {
|
||||
const { options } = modules;
|
||||
|
||||
Reference in New Issue
Block a user