dom/downloads/src/DownloadsAPI.jsm

changeset 0
6474c204b198
     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();

mercurial