2013-11-27 23:18:08 -08:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cu = Components.utils;
|
|
|
|
|
|
|
|
this.EXPORTED_SYMBOLS = [];
|
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
Cu.import("resource://gre/modules/Downloads.jsm");
|
|
|
|
Cu.import("resource://gre/modules/Task.jsm");
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
|
|
|
"@mozilla.org/parentprocessmessagemanager;1",
|
|
|
|
"nsIMessageBroadcaster");
|
|
|
|
|
|
|
|
function debug(aStr) {
|
2014-01-17 08:06:27 -08:00
|
|
|
#ifdef MOZ_DEBUG
|
2013-11-27 23:18:08 -08:00
|
|
|
dump("-*- DownloadsAPI.jsm : " + aStr + "\n");
|
2014-01-17 08:06:27 -08:00
|
|
|
#endif
|
2013-11-27 23:18:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
function sendPromiseMessage(aMm, aMessageName, aData, aError) {
|
2013-11-27 23:18:12 -08:00
|
|
|
debug("sendPromiseMessage " + aMessageName);
|
2013-11-27 23:18:08 -08:00
|
|
|
let msg = {
|
|
|
|
id: aData.id,
|
|
|
|
promiseId: aData.promiseId
|
|
|
|
};
|
|
|
|
|
|
|
|
if (aError) {
|
|
|
|
msg.error = aError;
|
|
|
|
}
|
|
|
|
|
|
|
|
aMm.sendAsyncMessage(aMessageName, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
let DownloadsAPI = {
|
|
|
|
init: function() {
|
|
|
|
debug("init");
|
|
|
|
|
|
|
|
this._ids = new WeakMap(); // Maps toolkit download objects to ids.
|
|
|
|
this._index = {}; // Maps ids to downloads.
|
|
|
|
|
|
|
|
["Downloads:GetList",
|
|
|
|
"Downloads:ClearAllDone",
|
|
|
|
"Downloads:Remove",
|
|
|
|
"Downloads:Pause",
|
|
|
|
"Downloads:Resume"].forEach((msgName) => {
|
|
|
|
ppmm.addMessageListener(msgName, this);
|
|
|
|
});
|
|
|
|
|
|
|
|
let self = this;
|
|
|
|
Task.spawn(function () {
|
|
|
|
let list = yield Downloads.getList(Downloads.ALL);
|
|
|
|
yield list.addView(self);
|
|
|
|
|
|
|
|
debug("view added to download list.");
|
|
|
|
}).then(null, Components.utils.reportError);
|
|
|
|
|
|
|
|
this._currentId = 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a unique id for each download, hashing the url and the path.
|
|
|
|
*/
|
|
|
|
downloadId: function(aDownload) {
|
|
|
|
let id = this._ids.get(aDownload, null);
|
|
|
|
if (!id) {
|
|
|
|
id = "download-" + this._currentId++;
|
|
|
|
this._ids.set(aDownload, id);
|
|
|
|
this._index[id] = aDownload;
|
|
|
|
}
|
|
|
|
return id;
|
|
|
|
},
|
|
|
|
|
|
|
|
getDownloadById: function(aId) {
|
|
|
|
return this._index[aId];
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts a download object into a plain json object that we'll
|
|
|
|
* send to the DOM side.
|
|
|
|
*/
|
|
|
|
jsonDownload: function(aDownload) {
|
|
|
|
let res = {
|
|
|
|
totalBytes: aDownload.totalBytes,
|
|
|
|
currentBytes: aDownload.currentBytes,
|
|
|
|
url: aDownload.source.url,
|
|
|
|
path: aDownload.target.path,
|
|
|
|
contentType: aDownload.contentType,
|
|
|
|
startTime: aDownload.startTime.getTime()
|
|
|
|
};
|
|
|
|
|
|
|
|
if (aDownload.error) {
|
2014-03-31 12:10:44 -07:00
|
|
|
res.error = aDownload.error;
|
2013-11-27 23:18:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
res.id = this.downloadId(aDownload);
|
|
|
|
|
|
|
|
// The state of the download. Can be any of "downloading", "stopped",
|
|
|
|
// "succeeded", finalized".
|
|
|
|
|
|
|
|
// Default to "stopped"
|
|
|
|
res.state = "stopped";
|
|
|
|
if (!aDownload.stopped &&
|
|
|
|
!aDownload.canceled &&
|
|
|
|
!aDownload.succeeded &&
|
|
|
|
!aDownload.DownloadError) {
|
|
|
|
res.state = "downloading";
|
|
|
|
} else if (aDownload.succeeded) {
|
|
|
|
res.state = "succeeded";
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* download view methods.
|
|
|
|
*/
|
|
|
|
onDownloadAdded: function(aDownload) {
|
|
|
|
let download = this.jsonDownload(aDownload);
|
|
|
|
debug("onDownloadAdded " + uneval(download));
|
|
|
|
ppmm.broadcastAsyncMessage("Downloads:Added", download);
|
|
|
|
},
|
|
|
|
|
|
|
|
onDownloadRemoved: function(aDownload) {
|
|
|
|
let download = this.jsonDownload(aDownload);
|
|
|
|
download.state = "finalized";
|
|
|
|
debug("onDownloadRemoved " + uneval(download));
|
|
|
|
ppmm.broadcastAsyncMessage("Downloads:Removed", download);
|
|
|
|
this._index[this._ids.get(aDownload)] = null;
|
|
|
|
this._ids.delete(aDownload);
|
|
|
|
},
|
|
|
|
|
|
|
|
onDownloadChanged: function(aDownload) {
|
|
|
|
let download = this.jsonDownload(aDownload);
|
|
|
|
debug("onDownloadChanged " + uneval(download));
|
|
|
|
ppmm.broadcastAsyncMessage("Downloads:Changed", download);
|
|
|
|
},
|
|
|
|
|
|
|
|
receiveMessage: function(aMessage) {
|
|
|
|
if (!aMessage.target.assertPermission("downloads")) {
|
|
|
|
debug("No 'downloads' permission!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug("message: " + aMessage.name);
|
2014-02-19 08:43:28 -08:00
|
|
|
|
|
|
|
switch (aMessage.name) {
|
|
|
|
case "Downloads:GetList":
|
|
|
|
this.getList(aMessage.data, aMessage.target);
|
|
|
|
break;
|
|
|
|
case "Downloads:ClearAllDone":
|
|
|
|
this.clearAllDone(aMessage.data, aMessage.target);
|
|
|
|
break;
|
|
|
|
case "Downloads:Remove":
|
|
|
|
this.remove(aMessage.data, aMessage.target);
|
|
|
|
break;
|
|
|
|
case "Downloads:Pause":
|
|
|
|
this.pause(aMessage.data, aMessage.target);
|
|
|
|
break;
|
|
|
|
case "Downloads:Resume":
|
|
|
|
this.resume(aMessage.data, aMessage.target);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
debug("Invalid message: " + aMessage.name);
|
2013-11-27 23:18:08 -08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
getList: function(aData, aMm) {
|
|
|
|
debug("getList called!");
|
|
|
|
let self = this;
|
|
|
|
Task.spawn(function () {
|
|
|
|
let list = yield Downloads.getList(Downloads.ALL);
|
|
|
|
let downloads = yield list.getAll();
|
|
|
|
let res = [];
|
|
|
|
downloads.forEach((aDownload) => {
|
|
|
|
res.push(self.jsonDownload(aDownload));
|
|
|
|
});
|
|
|
|
aMm.sendAsyncMessage("Downloads:GetList:Return", res);
|
|
|
|
}).then(null, Components.utils.reportError);
|
|
|
|
},
|
|
|
|
|
|
|
|
clearAllDone: function(aData, aMm) {
|
|
|
|
debug("clearAllDone called!");
|
|
|
|
let self = this;
|
|
|
|
Task.spawn(function () {
|
|
|
|
let list = yield Downloads.getList(Downloads.ALL);
|
|
|
|
yield list.removeFinished();
|
|
|
|
list = yield Downloads.getList(Downloads.ALL);
|
|
|
|
let downloads = yield list.getAll();
|
|
|
|
let res = [];
|
|
|
|
downloads.forEach((aDownload) => {
|
|
|
|
res.push(self.jsonDownload(aDownload));
|
|
|
|
});
|
|
|
|
aMm.sendAsyncMessage("Downloads:ClearAllDone:Return", res);
|
|
|
|
}).then(null, Components.utils.reportError);
|
|
|
|
},
|
|
|
|
|
|
|
|
remove: function(aData, aMm) {
|
|
|
|
debug("remove id " + aData.id);
|
|
|
|
let download = this.getDownloadById(aData.id);
|
|
|
|
if (!download) {
|
|
|
|
sendPromiseMessage(aMm, "Downloads:Remove:Return",
|
|
|
|
aData, "NoSuchDownload");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Task.spawn(function() {
|
|
|
|
yield download.finalize(true);
|
|
|
|
let list = yield Downloads.getList(Downloads.ALL);
|
|
|
|
yield list.remove(download);
|
|
|
|
}).then(
|
|
|
|
function() {
|
|
|
|
sendPromiseMessage(aMm, "Downloads:Remove:Return", aData);
|
|
|
|
},
|
|
|
|
function() {
|
|
|
|
sendPromiseMessage(aMm, "Downloads:Remove:Return",
|
|
|
|
aData, "RemoveError");
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
pause: function(aData, aMm) {
|
|
|
|
debug("pause id " + aData.id);
|
|
|
|
let download = this.getDownloadById(aData.id);
|
|
|
|
if (!download) {
|
|
|
|
sendPromiseMessage(aMm, "Downloads:Pause:Return",
|
|
|
|
aData, "NoSuchDownload");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
download.cancel().then(
|
|
|
|
function() {
|
|
|
|
sendPromiseMessage(aMm, "Downloads:Pause:Return", aData);
|
|
|
|
},
|
|
|
|
function() {
|
|
|
|
sendPromiseMessage(aMm, "Downloads:Pause:Return",
|
|
|
|
aData, "PauseError");
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
resume: function(aData, aMm) {
|
|
|
|
debug("resume id " + aData.id);
|
|
|
|
let download = this.getDownloadById(aData.id);
|
|
|
|
if (!download) {
|
|
|
|
sendPromiseMessage(aMm, "Downloads:Resume:Return",
|
|
|
|
aData, "NoSuchDownload");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
download.start().then(
|
|
|
|
function() {
|
|
|
|
sendPromiseMessage(aMm, "Downloads:Resume:Return", aData);
|
|
|
|
},
|
|
|
|
function() {
|
|
|
|
sendPromiseMessage(aMm, "Downloads:Resume:Return",
|
|
|
|
aData, "ResumeError");
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
DownloadsAPI.init();
|