1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/downloads/src/DownloadsAPI.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,268 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +const Cc = Components.classes; 1.11 +const Ci = Components.interfaces; 1.12 +const Cu = Components.utils; 1.13 + 1.14 +this.EXPORTED_SYMBOLS = []; 1.15 + 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 +Cu.import("resource://gre/modules/Downloads.jsm"); 1.18 +Cu.import("resource://gre/modules/Task.jsm"); 1.19 + 1.20 +XPCOMUtils.defineLazyServiceGetter(this, "ppmm", 1.21 + "@mozilla.org/parentprocessmessagemanager;1", 1.22 + "nsIMessageBroadcaster"); 1.23 + 1.24 +function debug(aStr) { 1.25 +#ifdef MOZ_DEBUG 1.26 + dump("-*- DownloadsAPI.jsm : " + aStr + "\n"); 1.27 +#endif 1.28 +} 1.29 + 1.30 +function sendPromiseMessage(aMm, aMessageName, aData, aError) { 1.31 + debug("sendPromiseMessage " + aMessageName); 1.32 + let msg = { 1.33 + id: aData.id, 1.34 + promiseId: aData.promiseId 1.35 + }; 1.36 + 1.37 + if (aError) { 1.38 + msg.error = aError; 1.39 + } 1.40 + 1.41 + aMm.sendAsyncMessage(aMessageName, msg); 1.42 +} 1.43 + 1.44 +let DownloadsAPI = { 1.45 + init: function() { 1.46 + debug("init"); 1.47 + 1.48 + this._ids = new WeakMap(); // Maps toolkit download objects to ids. 1.49 + this._index = {}; // Maps ids to downloads. 1.50 + 1.51 + ["Downloads:GetList", 1.52 + "Downloads:ClearAllDone", 1.53 + "Downloads:Remove", 1.54 + "Downloads:Pause", 1.55 + "Downloads:Resume"].forEach((msgName) => { 1.56 + ppmm.addMessageListener(msgName, this); 1.57 + }); 1.58 + 1.59 + let self = this; 1.60 + Task.spawn(function () { 1.61 + let list = yield Downloads.getList(Downloads.ALL); 1.62 + yield list.addView(self); 1.63 + 1.64 + debug("view added to download list."); 1.65 + }).then(null, Components.utils.reportError); 1.66 + 1.67 + this._currentId = 0; 1.68 + }, 1.69 + 1.70 + /** 1.71 + * Returns a unique id for each download, hashing the url and the path. 1.72 + */ 1.73 + downloadId: function(aDownload) { 1.74 + let id = this._ids.get(aDownload, null); 1.75 + if (!id) { 1.76 + id = "download-" + this._currentId++; 1.77 + this._ids.set(aDownload, id); 1.78 + this._index[id] = aDownload; 1.79 + } 1.80 + return id; 1.81 + }, 1.82 + 1.83 + getDownloadById: function(aId) { 1.84 + return this._index[aId]; 1.85 + }, 1.86 + 1.87 + /** 1.88 + * Converts a download object into a plain json object that we'll 1.89 + * send to the DOM side. 1.90 + */ 1.91 + jsonDownload: function(aDownload) { 1.92 + let res = { 1.93 + totalBytes: aDownload.totalBytes, 1.94 + currentBytes: aDownload.currentBytes, 1.95 + url: aDownload.source.url, 1.96 + path: aDownload.target.path, 1.97 + contentType: aDownload.contentType, 1.98 + startTime: aDownload.startTime.getTime() 1.99 + }; 1.100 + 1.101 + if (aDownload.error) { 1.102 + res.error = aDownload.error; 1.103 + } 1.104 + 1.105 + res.id = this.downloadId(aDownload); 1.106 + 1.107 + // The state of the download. Can be any of "downloading", "stopped", 1.108 + // "succeeded", finalized". 1.109 + 1.110 + // Default to "stopped" 1.111 + res.state = "stopped"; 1.112 + if (!aDownload.stopped && 1.113 + !aDownload.canceled && 1.114 + !aDownload.succeeded && 1.115 + !aDownload.DownloadError) { 1.116 + res.state = "downloading"; 1.117 + } else if (aDownload.succeeded) { 1.118 + res.state = "succeeded"; 1.119 + } 1.120 + return res; 1.121 + }, 1.122 + 1.123 + /** 1.124 + * download view methods. 1.125 + */ 1.126 + onDownloadAdded: function(aDownload) { 1.127 + let download = this.jsonDownload(aDownload); 1.128 + debug("onDownloadAdded " + uneval(download)); 1.129 + ppmm.broadcastAsyncMessage("Downloads:Added", download); 1.130 + }, 1.131 + 1.132 + onDownloadRemoved: function(aDownload) { 1.133 + let download = this.jsonDownload(aDownload); 1.134 + download.state = "finalized"; 1.135 + debug("onDownloadRemoved " + uneval(download)); 1.136 + ppmm.broadcastAsyncMessage("Downloads:Removed", download); 1.137 + this._index[this._ids.get(aDownload)] = null; 1.138 + this._ids.delete(aDownload); 1.139 + }, 1.140 + 1.141 + onDownloadChanged: function(aDownload) { 1.142 + let download = this.jsonDownload(aDownload); 1.143 + debug("onDownloadChanged " + uneval(download)); 1.144 + ppmm.broadcastAsyncMessage("Downloads:Changed", download); 1.145 + }, 1.146 + 1.147 + receiveMessage: function(aMessage) { 1.148 + if (!aMessage.target.assertPermission("downloads")) { 1.149 + debug("No 'downloads' permission!"); 1.150 + return; 1.151 + } 1.152 + 1.153 + debug("message: " + aMessage.name); 1.154 + 1.155 + switch (aMessage.name) { 1.156 + case "Downloads:GetList": 1.157 + this.getList(aMessage.data, aMessage.target); 1.158 + break; 1.159 + case "Downloads:ClearAllDone": 1.160 + this.clearAllDone(aMessage.data, aMessage.target); 1.161 + break; 1.162 + case "Downloads:Remove": 1.163 + this.remove(aMessage.data, aMessage.target); 1.164 + break; 1.165 + case "Downloads:Pause": 1.166 + this.pause(aMessage.data, aMessage.target); 1.167 + break; 1.168 + case "Downloads:Resume": 1.169 + this.resume(aMessage.data, aMessage.target); 1.170 + break; 1.171 + default: 1.172 + debug("Invalid message: " + aMessage.name); 1.173 + } 1.174 + }, 1.175 + 1.176 + getList: function(aData, aMm) { 1.177 + debug("getList called!"); 1.178 + let self = this; 1.179 + Task.spawn(function () { 1.180 + let list = yield Downloads.getList(Downloads.ALL); 1.181 + let downloads = yield list.getAll(); 1.182 + let res = []; 1.183 + downloads.forEach((aDownload) => { 1.184 + res.push(self.jsonDownload(aDownload)); 1.185 + }); 1.186 + aMm.sendAsyncMessage("Downloads:GetList:Return", res); 1.187 + }).then(null, Components.utils.reportError); 1.188 + }, 1.189 + 1.190 + clearAllDone: function(aData, aMm) { 1.191 + debug("clearAllDone called!"); 1.192 + let self = this; 1.193 + Task.spawn(function () { 1.194 + let list = yield Downloads.getList(Downloads.ALL); 1.195 + yield list.removeFinished(); 1.196 + list = yield Downloads.getList(Downloads.ALL); 1.197 + let downloads = yield list.getAll(); 1.198 + let res = []; 1.199 + downloads.forEach((aDownload) => { 1.200 + res.push(self.jsonDownload(aDownload)); 1.201 + }); 1.202 + aMm.sendAsyncMessage("Downloads:ClearAllDone:Return", res); 1.203 + }).then(null, Components.utils.reportError); 1.204 + }, 1.205 + 1.206 + remove: function(aData, aMm) { 1.207 + debug("remove id " + aData.id); 1.208 + let download = this.getDownloadById(aData.id); 1.209 + if (!download) { 1.210 + sendPromiseMessage(aMm, "Downloads:Remove:Return", 1.211 + aData, "NoSuchDownload"); 1.212 + return; 1.213 + } 1.214 + 1.215 + Task.spawn(function() { 1.216 + yield download.finalize(true); 1.217 + let list = yield Downloads.getList(Downloads.ALL); 1.218 + yield list.remove(download); 1.219 + }).then( 1.220 + function() { 1.221 + sendPromiseMessage(aMm, "Downloads:Remove:Return", aData); 1.222 + }, 1.223 + function() { 1.224 + sendPromiseMessage(aMm, "Downloads:Remove:Return", 1.225 + aData, "RemoveError"); 1.226 + } 1.227 + ); 1.228 + }, 1.229 + 1.230 + pause: function(aData, aMm) { 1.231 + debug("pause id " + aData.id); 1.232 + let download = this.getDownloadById(aData.id); 1.233 + if (!download) { 1.234 + sendPromiseMessage(aMm, "Downloads:Pause:Return", 1.235 + aData, "NoSuchDownload"); 1.236 + return; 1.237 + } 1.238 + 1.239 + download.cancel().then( 1.240 + function() { 1.241 + sendPromiseMessage(aMm, "Downloads:Pause:Return", aData); 1.242 + }, 1.243 + function() { 1.244 + sendPromiseMessage(aMm, "Downloads:Pause:Return", 1.245 + aData, "PauseError"); 1.246 + } 1.247 + ); 1.248 + }, 1.249 + 1.250 + resume: function(aData, aMm) { 1.251 + debug("resume id " + aData.id); 1.252 + let download = this.getDownloadById(aData.id); 1.253 + if (!download) { 1.254 + sendPromiseMessage(aMm, "Downloads:Resume:Return", 1.255 + aData, "NoSuchDownload"); 1.256 + return; 1.257 + } 1.258 + 1.259 + download.start().then( 1.260 + function() { 1.261 + sendPromiseMessage(aMm, "Downloads:Resume:Return", aData); 1.262 + }, 1.263 + function() { 1.264 + sendPromiseMessage(aMm, "Downloads:Resume:Return", 1.265 + aData, "ResumeError"); 1.266 + } 1.267 + ); 1.268 + } 1.269 +}; 1.270 + 1.271 +DownloadsAPI.init();