Bug 1313167 - Remove dom/downloads and related code. r?jst draft
authorMichelangelo De Simone <mdesimone@mozilla.com>
Thu, 17 Nov 2016 17:18:41 -0800
changeset 451692 3665c74b3be43f6df8678450c6542db7a40d4e44
parent 451656 7083c0d30e75fc102c715887af9faec933e936f8
child 540099 c737f744dd058d40d15841f93642a5db73b34fd3
push id39258
push userbmo:mdesimone@mozilla.com
push dateTue, 20 Dec 2016 21:31:53 +0000
reviewersjst
bugs1313167
milestone53.0a1
Bug 1313167 - Remove dom/downloads and related code. r?jst MozReview-Commit-ID: LBDkPKppTNO
b2g/app/b2g.js
dom/downloads/DownloadsAPI.js
dom/downloads/DownloadsAPI.jsm
dom/downloads/DownloadsAPI.manifest
dom/downloads/DownloadsIPC.jsm
dom/downloads/moz.build
dom/downloads/tests/clear_all_done_helper.js
dom/downloads/tests/mochitest.ini
dom/downloads/tests/serve_file.sjs
dom/downloads/tests/test_downloads_bad_file.html
dom/downloads/tests/test_downloads_basic.html
dom/downloads/tests/test_downloads_large.html
dom/downloads/tests/test_downloads_navigator_object.html
dom/downloads/tests/test_downloads_pause_remove.html
dom/downloads/tests/test_downloads_pause_resume.html
dom/moz.build
dom/webidl/DownloadEvent.webidl
dom/webidl/Downloads.webidl
dom/webidl/moz.build
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -838,20 +838,16 @@ pref("gfx.screen-mirroring.enabled", tru
 pref("b2g.neterror.url", "net_error.html");
 
 // Enable Web Speech synthesis API
 pref("media.webspeech.synth.enabled", true);
 
 // Enable Web Speech recognition API
 pref("media.webspeech.recognition.enable", true);
 
-// Downloads API
-pref("dom.mozDownloads.enabled", true);
-pref("dom.downloads.max_retention_days", 7);
-
 // External Helper Application Handling
 //
 // All external helper application handling can require the docshell to be
 // active before allowing the external helper app service to handle content.
 //
 // To prevent SD card DoS attacks via downloads we disable background handling.
 //
 pref("security.exthelperapp.disable_background_handling", true);
deleted file mode 100644
--- a/dom/downloads/DownloadsAPI.js
+++ /dev/null
@@ -1,508 +0,0 @@
-/* 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;
-const Cr = Components.results;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
-Cu.import("resource://gre/modules/DownloadsIPC.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-XPCOMUtils.defineLazyServiceGetter(this, "volumeService",
-                                   "@mozilla.org/telephony/volume-service;1",
-                                    "nsIVolumeService");
-
-/**
-  * The content process implementations of navigator.mozDownloadManager and its
-  * DOMDownload download objects.  Uses DownloadsIPC.jsm to communicate with
-  * DownloadsAPI.jsm in the parent process.
-  */
-
-function debug(aStr) {
-#ifdef MOZ_DEBUG
-  dump("-*- DownloadsAPI.js : " + aStr + "\n");
-#endif
-}
-
-function DOMDownloadManagerImpl() {
-  debug("DOMDownloadManagerImpl constructor");
-}
-
-DOMDownloadManagerImpl.prototype = {
-  __proto__: DOMRequestIpcHelper.prototype,
-
-  // nsIDOMGlobalPropertyInitializer implementation
-  init: function(aWindow) {
-    debug("DownloadsManager init");
-    this.initDOMRequestHelper(aWindow,
-                              ["Downloads:Added",
-                               "Downloads:Removed"]);
-  },
-
-  uninit: function() {
-    debug("uninit");
-    downloadsCache.evict(this._window);
-  },
-
-  set ondownloadstart(aHandler) {
-    this.__DOM_IMPL__.setEventHandler("ondownloadstart", aHandler);
-  },
-
-  get ondownloadstart() {
-    return this.__DOM_IMPL__.getEventHandler("ondownloadstart");
-  },
-
-  getDownloads: function() {
-    debug("getDownloads()");
-
-    return this.createPromise(function (aResolve, aReject) {
-      DownloadsIPC.getDownloads().then(
-        function(aDownloads) {
-          // Turn the list of download objects into DOM objects and
-          // send them.
-          let array = new this._window.Array();
-          for (let id in aDownloads) {
-            let dom = createDOMDownloadObject(this._window, aDownloads[id]);
-            array.push(this._prepareForContent(dom));
-          }
-          aResolve(array);
-        }.bind(this),
-        function() {
-          aReject("GetDownloadsError");
-        }
-      );
-    }.bind(this));
-  },
-
-  clearAllDone: function() {
-    debug("clearAllDone()");
-    // This is a void function; we just kick it off.  No promises, etc.
-    DownloadsIPC.clearAllDone();
-  },
-
-  remove: function(aDownload) {
-    debug("remove " + aDownload.url + " " + aDownload.id);
-    return this.createPromise(function (aResolve, aReject) {
-      if (!downloadsCache.has(this._window, aDownload.id)) {
-        debug("no download " + aDownload.id);
-        aReject("InvalidDownload");
-        return;
-      }
-
-      DownloadsIPC.remove(aDownload.id).then(
-        function(aResult) {
-          let dom = createDOMDownloadObject(this._window, aResult);
-          // Change the state right away to not race against the update message.
-          dom.wrappedJSObject.state = "finalized";
-          aResolve(this._prepareForContent(dom));
-        }.bind(this),
-        function() {
-          aReject("RemoveError");
-        }
-      );
-    }.bind(this));
-  },
-
-  adoptDownload: function(aAdoptDownloadDict) {
-    // Our AdoptDownloadDict only includes simple types, which WebIDL enforces.
-    // We have no object/any types so we do not need to worry about invoking
-    // JSON.stringify (and it inheriting our security privileges).
-    debug("adoptDownload");
-    return this.createPromise(function (aResolve, aReject) {
-      if (!aAdoptDownloadDict) {
-        debug("Download dictionary is required!");
-        aReject("InvalidDownload");
-        return;
-      }
-      if (!aAdoptDownloadDict.storageName || !aAdoptDownloadDict.storagePath ||
-          !aAdoptDownloadDict.contentType) {
-        debug("Missing one of: storageName, storagePath, contentType");
-        aReject("InvalidDownload");
-        return;
-      }
-
-      // Convert storageName/storagePath to a local filesystem path.
-      let volume;
-      // getVolumeByName throws if you give it something it doesn't like
-      // because XPConnect converts the NS_ERROR_NOT_AVAILABLE to an
-      // exception.  So catch it.
-      try {
-        volume = volumeService.getVolumeByName(aAdoptDownloadDict.storageName);
-      } catch (ex) {}
-      if (!volume) {
-        debug("Invalid storage name: " + aAdoptDownloadDict.storageName);
-        aReject("InvalidDownload");
-        return;
-      }
-      let computedPath = volume.mountPoint + '/' +
-                           aAdoptDownloadDict.storagePath;
-      // We validate that there is actually a file at the given path in the
-      // parent process in DownloadsAPI.js because that's where the file
-      // access would actually occur either way.
-
-      // Create a DownloadsAPI.jsm 'jsonDownload' style representation.
-      let jsonDownload = {
-        url: aAdoptDownloadDict.url,
-        path: computedPath,
-        contentType: aAdoptDownloadDict.contentType,
-        startTime: aAdoptDownloadDict.startTime.valueOf() || Date.now(),
-        sourceAppManifestURL: ""
-      };
-
-      DownloadsIPC.adoptDownload(jsonDownload).then(
-        function(aResult) {
-          let domDownload = createDOMDownloadObject(this._window, aResult);
-          aResolve(this._prepareForContent(domDownload));
-        }.bind(this),
-        function(aResult) {
-          // This will be one of: AdoptError (generic catch-all),
-          // AdoptNoSuchFile, AdoptFileIsDirectory
-          aReject(aResult.error);
-        }
-      );
-    }.bind(this));
-  },
-
-
-  /**
-    * Turns a chrome download object into a content accessible one.
-    * When we have __DOM_IMPL__ available we just use that, otherwise
-    * we run _create() with the wrapped js object.
-    */
-  _prepareForContent: function(aChromeObject) {
-    if (aChromeObject.__DOM_IMPL__) {
-      return aChromeObject.__DOM_IMPL__;
-    }
-    let res = this._window.DOMDownload._create(this._window,
-                                            aChromeObject.wrappedJSObject);
-    return res;
-  },
-
-  receiveMessage: function(aMessage) {
-    let data = aMessage.data;
-    switch(aMessage.name) {
-      case "Downloads:Added":
-        debug("Adding " + uneval(data));
-        let event = new this._window.DownloadEvent("downloadstart", {
-          download:
-            this._prepareForContent(createDOMDownloadObject(this._window, data))
-        });
-        this.__DOM_IMPL__.dispatchEvent(event);
-        break;
-    }
-  },
-
-  classID: Components.ID("{c6587afa-0696-469f-9eff-9dac0dd727fe}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsIObserver,
-                                         Ci.nsIDOMGlobalPropertyInitializer]),
-
-};
-
-/**
-  * Keep track of download objects per window.
-  */
-var downloadsCache = {
-  init: function() {
-    this.cache = new WeakMap();
-  },
-
-  has: function(aWindow, aId) {
-    let downloads = this.cache.get(aWindow);
-    return !!(downloads && downloads[aId]);
-  },
-
-  get: function(aWindow, aDownload) {
-    let downloads = this.cache.get(aWindow);
-    if (!(downloads && downloads[aDownload.id])) {
-      debug("Adding download " + aDownload.id + " to cache.");
-      if (!downloads) {
-        this.cache.set(aWindow, {});
-        downloads = this.cache.get(aWindow);
-      }
-      // Create the object and add it to the cache.
-      let impl = Cc["@mozilla.org/downloads/download;1"]
-                   .createInstance(Ci.nsISupports);
-      impl.wrappedJSObject._init(aWindow, aDownload);
-      downloads[aDownload.id] = impl;
-    }
-    return downloads[aDownload.id];
-  },
-
-  evict: function(aWindow) {
-    this.cache.delete(aWindow);
-  }
-};
-
-downloadsCache.init();
-
-/**
-  * The DOM facade of a download object.
-  */
-
-function createDOMDownloadObject(aWindow, aDownload) {
-  return downloadsCache.get(aWindow, aDownload);
-}
-
-function DOMDownloadImpl() {
-  debug("DOMDownloadImpl constructor ");
-
-  this.wrappedJSObject = this;
-  this.totalBytes = 0;
-  this.currentBytes = 0;
-  this.url = null;
-  this.path = null;
-  this.storageName = null;
-  this.storagePath = null;
-  this.contentType = null;
-
-  /* fields that require getters/setters */
-  this._error = null;
-  this._startTime = new Date();
-  this._state = "stopped";
-
-  /* private fields */
-  this.id = null;
-}
-
-DOMDownloadImpl.prototype = {
-
-  createPromise: function(aPromiseInit) {
-    return new this._window.Promise(aPromiseInit);
-  },
-
-  pause: function() {
-    debug("DOMDownloadImpl pause");
-    let id = this.id;
-    // We need to wrap the Promise.jsm promise in a "real" DOM promise...
-    return this.createPromise(function(aResolve, aReject) {
-      DownloadsIPC.pause(id).then(aResolve, aReject);
-    });
-  },
-
-  resume: function() {
-    debug("DOMDownloadImpl resume");
-    let id = this.id;
-    // We need to wrap the Promise.jsm promise in a "real" DOM promise...
-    return this.createPromise(function(aResolve, aReject) {
-      DownloadsIPC.resume(id).then(aResolve, aReject);
-    });
-  },
-
-  set onstatechange(aHandler) {
-    this.__DOM_IMPL__.setEventHandler("onstatechange", aHandler);
-  },
-
-  get onstatechange() {
-    return this.__DOM_IMPL__.getEventHandler("onstatechange");
-  },
-
-  get error() {
-    return this._error;
-  },
-
-  set error(aError) {
-    this._error = aError;
-  },
-
-  get startTime() {
-    return this._startTime;
-  },
-
-  set startTime(aStartTime) {
-    if (aStartTime instanceof Date) {
-      this._startTime = aStartTime;
-    }
-    else {
-      this._startTime = new Date(aStartTime);
-    }
-  },
-
-  get state() {
-    return this._state;
-  },
-
-  // We require a setter here to simplify the internals of the Download Manager
-  // since we actually pass dummy JSON objects to the child process and update
-  // them. This is the case for all other setters for read-only attributes
-  // implemented in this object.
-  set state(aState) {
-    // We need to ensure that XPCOM consumers of this API respect the enum
-    // values as well.
-    if (["downloading",
-         "stopped",
-         "succeeded",
-         "finalized"].indexOf(aState) != -1) {
-      this._state = aState;
-    }
-  },
-
-  /**
-    * Initialize a DOMDownload instance for the given window using the
-    * 'jsonDownload' serialized format of the download encoded by
-    * DownloadsAPI.jsm.
-    */
-  _init: function(aWindow, aDownload) {
-    this._window = aWindow;
-    this.id = aDownload.id;
-    this._update(aDownload);
-    Services.obs.addObserver(this, "downloads-state-change-" + this.id,
-                             /* ownsWeak */ true);
-    debug("observer set for " + this.id);
-  },
-
-  /**
-    * Updates the state of the object and fires the statechange event.
-    */
-  _update: function(aDownload) {
-    debug("update " + uneval(aDownload));
-    if (this.id != aDownload.id) {
-      return;
-    }
-
-    let props = ["totalBytes", "currentBytes", "url", "path", "storageName",
-                 "storagePath", "state", "contentType", "startTime",
-                 "sourceAppManifestURL"];
-    let changed = false;
-    let changedProps = {};
-
-    props.forEach((prop) => {
-      if (prop in aDownload && (aDownload[prop] != this[prop])) {
-        this[prop] = aDownload[prop];
-        changedProps[prop] = changed = true;
-      }
-    });
-
-    // When the path changes, we should update the storage name and
-    // storage path used for our downloaded file in case our download
-    // was re-targetted to a different storage and/or filename.
-    if (changedProps["path"]) {
-      let storages = this._window.navigator.getDeviceStorages("sdcard");
-      let preferredStorageName;
-      // Use the first one or the default storage. Just like jsdownloads picks
-      // the default / preferred download directory.
-      storages.forEach((aStorage) => {
-        if (aStorage.default || !preferredStorageName) {
-          preferredStorageName = aStorage.storageName;
-        }
-      });
-      // Now get the path for this storage area.
-      let volume;
-      if (preferredStorageName) {
-        let volume = volumeService.getVolumeByName(preferredStorageName);
-        if (volume) {
-          // Finally, create the relative path of the file that can be used
-          // later on to retrieve the file via DeviceStorage. Our path
-          // needs to omit the starting '/'.
-          this.storageName = preferredStorageName;
-          this.storagePath =
-            this.path.substring(this.path.indexOf(volume.mountPoint) +
-                                volume.mountPoint.length + 1);
-        }
-      }
-    }
-
-    if (aDownload.error) {
-      //
-      // When we get a generic error failure back from the js downloads api
-      // we will verify the status of device storage to see if we can't provide
-      // a better error result value.
-      //
-      // XXX If these checks expand further, consider moving them into their
-      // own function.
-      //
-      let result = aDownload.error.result;
-      let storage = this._window.navigator.getDeviceStorage("sdcard");
-
-      // If we don't have access to device storage we'll opt out of these
-      // extra checks as they are all dependent on the state of the storage.
-      if (result == Cr.NS_ERROR_FAILURE && storage) {
-        // We will delay sending the notification until we've inferred which
-        // error is really happening.
-        changed = false;
-        debug("Attempting to infer error via device storage sanity checks.");
-        // Get device storage and request availability status.
-        let available = storage.available();
-        available.onsuccess = (function() {
-          debug("Storage Status = '" + available.result + "'");
-          let inferredError = result;
-          switch (available.result) {
-            case "unavailable":
-              inferredError = Cr.NS_ERROR_FILE_NOT_FOUND;
-              break;
-            case "shared":
-              inferredError = Cr.NS_ERROR_FILE_ACCESS_DENIED;
-              break;
-          }
-          this._updateWithError(aDownload, inferredError);
-        }).bind(this);
-        available.onerror = (function() {
-          this._updateWithError(aDownload, result);
-        }).bind(this);
-      }
-
-      this.error =
-        new this._window.DOMError("DownloadError", result);
-    } else {
-      this.error = null;
-    }
-
-    // The visible state has not changed, so no need to fire an event.
-    if (!changed) {
-      return;
-    }
-
-    this._sendStateChange();
-  },
-
-  _updateWithError: function(aDownload, aError) {
-    this.error =
-      new this._window.DOMError("DownloadError", aError);
-    this._sendStateChange();
-  },
-
-  _sendStateChange: function() {
-    // __DOM_IMPL__ may not be available at first update.
-    if (this.__DOM_IMPL__) {
-      let event = new this._window.DownloadEvent("statechange", {
-        download: this.__DOM_IMPL__
-      });
-      debug("Dispatching statechange event. state=" + this.state);
-      this.__DOM_IMPL__.dispatchEvent(event);
-    }
-  },
-
-  observe: function(aSubject, aTopic, aData) {
-    debug("DOMDownloadImpl observe " + aTopic);
-    if (aTopic !== "downloads-state-change-" + this.id) {
-      return;
-    }
-
-    try {
-      let download = JSON.parse(aData);
-      // We get the start time as milliseconds, not as a Date object.
-      if (download.startTime) {
-        download.startTime = new Date(download.startTime);
-      }
-      this._update(download);
-    } catch(e) {}
-  },
-
-  classID: Components.ID("{96b81b99-aa96-439d-8c59-92eeed34705f}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference])
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMDownloadManagerImpl,
-                                                     DOMDownloadImpl]);
deleted file mode 100644
--- a/dom/downloads/DownloadsAPI.jsm
+++ /dev/null
@@ -1,360 +0,0 @@
-/* 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");
-Cu.import("resource://gre/modules/osfile.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
-                                   "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageBroadcaster");
-
-/**
-  * Parent process logic that services download API requests from the
-  * DownloadAPI.js instances in content processeses.  The actual work of managing
-  * downloads is done by Toolkit's Downloads.jsm.  This module is loaded by B2G's
-  * shell.js
-  */
-
-function debug(aStr) {
-#ifdef MOZ_DEBUG
-  dump("-*- DownloadsAPI.jsm : " + aStr + "\n");
-#endif
-}
-
-function sendPromiseMessage(aMm, aMessageName, aData, aError) {
-  debug("sendPromiseMessage " + aMessageName);
-  let msg = {
-    id: aData.id,
-    promiseId: aData.promiseId
-  };
-
-  if (aError) {
-    msg.error = aError;
-  }
-
-  aMm.sendAsyncMessage(aMessageName, msg);
-}
-
-var 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",
-     "Downloads:Adopt"].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(),
-      sourceAppManifestURL: aDownload._unknownProperties &&
-                              aDownload._unknownProperties.sourceAppManifestURL
-    };
-
-    if (aDownload.error) {
-      res.error = aDownload.error;
-    }
-
-    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) {
-    debug("message: " + aMessage.name);
-
-    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;
-    case "Downloads:Adopt":
-      this.adoptDownload(aMessage.data, aMessage.target);
-      break;
-    default:
-      debug("Invalid message: " + aMessage.name);
-    }
-  },
-
-  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!");
-    Task.spawn(function () {
-      let list = yield Downloads.getList(Downloads.ALL);
-      list.removeFinished();
-    }).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");
-      }
-    );
-  },
-
-  /**
-    * Receive a download to adopt in the same representation we produce from
-    * our "jsonDownload" normalizer and add it to the list of downloads.
-    */
-  adoptDownload: function(aData, aMm) {
-    let adoptJsonRep = aData.jsonDownload;
-    debug("adoptDownload " + uneval(adoptJsonRep));
-
-    Task.spawn(function* () {
-      // Verify that the file exists on disk.  This will result in a rejection
-      // if the file does not exist.  We will also use this information for the
-      // file size to avoid weird inconsistencies.  We ignore the filesystem
-      // timestamp in favor of whatever the caller is telling us.
-      let fileInfo = yield OS.File.stat(adoptJsonRep.path);
-
-      // We also require that the file is not a directory.
-      if (fileInfo.isDir) {
-        throw new Error("AdoptFileIsDirectory");
-      }
-
-      // We need to create a Download instance to add to the list.  Create a
-      // serialized representation and then from there the instance.
-      let serializedRep = {
-        // explicit initializations in toSerializable
-        source: {
-          url: adoptJsonRep.url
-          // This is where isPrivate would go if adoption supported private
-          // browsing.
-        },
-        target: {
-          path: adoptJsonRep.path,
-        },
-        startTime: adoptJsonRep.startTime,
-        // kPlainSerializableDownloadProperties propagations
-        succeeded: true, // (all adopted downloads are required to be completed)
-        totalBytes: fileInfo.size,
-        contentType: adoptJsonRep.contentType,
-        // unknown properties added/used by the DownloadsAPI
-        currentBytes: fileInfo.size,
-        sourceAppManifestURL: adoptJsonRep.sourceAppManifestURL
-      };
-
-      let download = yield Downloads.createDownload(serializedRep);
-
-      // The ALL list is a DownloadCombinedList instance that combines the
-      // PUBLIC (persisted to disk) and PRIVATE (ephemeral) download lists..
-      // When we call add on it, it dispatches to the appropriate list based on
-      // the 'isPrivate' field of the source.  (Which we don't initialize and
-      // defaults to false.)
-      let allDownloadList = yield Downloads.getList(Downloads.ALL);
-
-      // This add will automatically notify all views of the added download,
-      // including DownloadsAPI instances and the DownloadAutoSaveView that's
-      // subscribed to the PUBLIC list and will save the download.
-      yield allDownloadList.add(download);
-
-      debug("download adopted");
-      // The notification above occurred synchronously, and so we will have
-      // already dispatched an added notification for our download to the child
-      // process in question.  As such, we only need to relay the download id
-      // since the download will already have been cached.
-      return download;
-    }.bind(this)).then(
-      (download) => {
-        sendPromiseMessage(aMm, "Downloads:Adopt:Return",
-                           {
-                             id: this.downloadId(download),
-                             promiseId: aData.promiseId
-                           });
-      },
-      (ex) => {
-        let reportAs = "AdoptError";
-        // Provide better error codes for expected errors.
-        if (ex instanceof OS.File.Error && ex.becauseNoSuchFile) {
-          reportAs = "AdoptNoSuchFile";
-        } else if (ex.message === "AdoptFileIsDirectory") {
-          reportAs = ex.message;
-        } else {
-          // Anything else is unexpected and should be reported to help track
-          // down what's going wrong.
-          debug("unexpected download error: " + ex);
-          Cu.reportError(ex);
-        }
-        sendPromiseMessage(aMm, "Downloads:Adopt:Return",
-                           {
-                             promiseId: aData.promiseId
-                           },
-                           reportAs);
-    });
-  }
-};
-
-DownloadsAPI.init();
deleted file mode 100644
--- a/dom/downloads/DownloadsAPI.manifest
+++ /dev/null
@@ -1,6 +0,0 @@
-# DownloadsAPI.js
-component {c6587afa-0696-469f-9eff-9dac0dd727fe} DownloadsAPI.js
-contract @mozilla.org/downloads/manager;1 {c6587afa-0696-469f-9eff-9dac0dd727fe}
-
-component {96b81b99-aa96-439d-8c59-92eeed34705f} DownloadsAPI.js
-contract @mozilla.org/downloads/download;1 {96b81b99-aa96-439d-8c59-92eeed34705f}
deleted file mode 100644
--- a/dom/downloads/DownloadsIPC.jsm
+++ /dev/null
@@ -1,224 +0,0 @@
-/* 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 = ["DownloadsIPC"];
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-
-/**
-  * This module lives in the child process and receives the ipc messages
-  * from the parent. It saves the download's state and redispatch changes
-  * to DOM objects using an observer notification.
-  *
-  * This module needs to be loaded once and only once per process.
-  */
-
-function debug(aStr) {
-#ifdef MOZ_DEBUG
-  dump("-*- DownloadsIPC.jsm : " + aStr + "\n");
-#endif
-}
-
-const ipcMessages = ["Downloads:Added",
-                     "Downloads:Removed",
-                     "Downloads:Changed",
-                     "Downloads:GetList:Return",
-                     "Downloads:Remove:Return",
-                     "Downloads:Pause:Return",
-                     "Downloads:Resume:Return",
-                     "Downloads:Adopt:Return"];
-
-this.DownloadsIPC = {
-  downloads: {},
-
-  init: function() {
-    debug("init");
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
-    ipcMessages.forEach((aMessage) => {
-      cpmm.addMessageListener(aMessage, this);
-    });
-
-    // We need to get the list of current downloads.
-    this.ready = false;
-    this.getListPromises = [];
-    this.downloadPromises = {};
-    cpmm.sendAsyncMessage("Downloads:GetList", {});
-    this._promiseId = 0;
-  },
-
-  notifyChanges: function(aId) {
-    // TODO: use the subject instead of stringifying.
-    if (this.downloads[aId]) {
-      debug("notifyChanges notifying changes for " + aId);
-      Services.obs.notifyObservers(null, "downloads-state-change-" + aId,
-                                   JSON.stringify(this.downloads[aId]));
-    } else {
-      debug("notifyChanges failed for " + aId)
-    }
-  },
-
-  _updateDownloadsArray: function(aDownloads) {
-    this.downloads = [];
-    // We actually have an array of downloads.
-    aDownloads.forEach((aDownload) => {
-      this.downloads[aDownload.id] = aDownload;
-    });
-  },
-
-  receiveMessage: function(aMessage) {
-    let download = aMessage.data;
-    debug("message: " + aMessage.name);
-    switch(aMessage.name) {
-      case "Downloads:GetList:Return":
-        this._updateDownloadsArray(download);
-
-        if (!this.ready) {
-          this.getListPromises.forEach(aPromise =>
-                                       aPromise.resolve(this.downloads));
-          this.getListPromises.length = 0;
-        }
-        this.ready = true;
-        break;
-      case "Downloads:Added":
-        this.downloads[download.id] = download;
-        this.notifyChanges(download.id);
-        break;
-      case "Downloads:Removed":
-        if (this.downloads[download.id]) {
-          this.downloads[download.id] = download;
-          this.notifyChanges(download.id);
-          delete this.downloads[download.id];
-        }
-        break;
-      case "Downloads:Changed":
-        // Only update properties that actually changed.
-        let cached = this.downloads[download.id];
-        if (!cached) {
-          debug("No download found for " + download.id);
-          return;
-        }
-        let props = ["totalBytes", "currentBytes", "url", "path", "state",
-                     "contentType", "startTime"];
-        let changed = false;
-
-        props.forEach((aProp) => {
-          if (download[aProp] && (download[aProp] != cached[aProp])) {
-            cached[aProp] = download[aProp];
-            changed = true;
-          }
-        });
-
-        // Updating the error property. We always get a 'state' change as
-        // well.
-        cached.error = download.error;
-
-        if (changed) {
-          this.notifyChanges(download.id);
-        }
-        break;
-      case "Downloads:Remove:Return":
-      case "Downloads:Pause:Return":
-      case "Downloads:Resume:Return":
-      case "Downloads:Adopt:Return":
-        if (this.downloadPromises[download.promiseId]) {
-          if (!download.error) {
-            this.downloadPromises[download.promiseId].resolve(download);
-          } else {
-            this.downloadPromises[download.promiseId].reject(download);
-          }
-          delete this.downloadPromises[download.promiseId];
-        }
-        break;
-    }
-  },
-
-  /**
-    * Returns a promise that is resolved with the list of current downloads.
-    */
-  getDownloads: function() {
-    debug("getDownloads()");
-    let deferred = Promise.defer();
-    if (this.ready) {
-      debug("Returning existing list.");
-      deferred.resolve(this.downloads);
-    } else {
-      this.getListPromises.push(deferred);
-    }
-    return deferred.promise;
-  },
-
-  /**
-   * Void function to trigger removal of completed downloads.
-   */
-  clearAllDone: function() {
-    debug("clearAllDone");
-    cpmm.sendAsyncMessage("Downloads:ClearAllDone", {});
-  },
-
-  promiseId: function() {
-    return this._promiseId++;
-  },
-
-  remove: function(aId) {
-    debug("remove " + aId);
-    let deferred = Promise.defer();
-    let pId = this.promiseId();
-    this.downloadPromises[pId] = deferred;
-    cpmm.sendAsyncMessage("Downloads:Remove",
-                          { id: aId, promiseId: pId });
-    return deferred.promise;
-  },
-
-  pause: function(aId) {
-    debug("pause " + aId);
-    let deferred = Promise.defer();
-    let pId = this.promiseId();
-    this.downloadPromises[pId] = deferred;
-    cpmm.sendAsyncMessage("Downloads:Pause",
-                          { id: aId, promiseId: pId });
-    return deferred.promise;
-  },
-
-  resume: function(aId) {
-    debug("resume " + aId);
-    let deferred = Promise.defer();
-    let pId = this.promiseId();
-    this.downloadPromises[pId] = deferred;
-    cpmm.sendAsyncMessage("Downloads:Resume",
-                          { id: aId, promiseId: pId });
-    return deferred.promise;
-  },
-
-  adoptDownload: function(aJsonDownload) {
-    debug("adoptDownload");
-    let deferred = Promise.defer();
-    let pId = this.promiseId();
-    this.downloadPromises[pId] = deferred;
-    cpmm.sendAsyncMessage("Downloads:Adopt",
-                          { jsonDownload: aJsonDownload, promiseId: pId });
-    return deferred.promise;
-  },
-
-  observe: function(aSubject, aTopic, aData) {
-    if (aTopic == "xpcom-shutdown") {
-      ipcMessages.forEach((aMessage) => {
-        cpmm.removeMessageListener(aMessage, this);
-      });
-    }
-  }
-};
-
-DownloadsIPC.init();
deleted file mode 100644
--- a/dom/downloads/moz.build
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-if CONFIG["MOZ_B2G"]:
-    MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
-
-EXTRA_COMPONENTS += [
-    'DownloadsAPI.manifest',
-]
-
-EXTRA_PP_COMPONENTS += [
-    'DownloadsAPI.js',
-]
-
-EXTRA_PP_JS_MODULES += [
-    'DownloadsAPI.jsm',
-    'DownloadsIPC.jsm',
-]
deleted file mode 100644
--- a/dom/downloads/tests/clear_all_done_helper.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * A helper to clear out the existing downloads known to the mozDownloadManager
- * / downloads.js.
- *
- * It exists because previously mozDownloadManager.clearAllDone() thought that
- * when it returned that all the completed downloads would be cleared out.  It
- * was wrong and this led to various intermittent test failurse.  In discussion
- * on https://bugzil.la/979446#c13 and onwards, it was decided that
- * clearAllDone() was in the wrong and that the jsdownloads API it depends on
- * was not going to change to make it be in the right.
- *
- * The existing uses of clearAllDone() in tests seemed to be about:
- * - Exploding if there was somehow still a download in progress
- * - Clearing out the download list at the start of a test so that calls to
- *   getDownloads() wouldn't have to worry about existing downloads, etc.
- *
- * From discussion, the right way to handle clearing is to wait for the expected
- * removal events to occur for the existing downloads.  So that's what we do.
- * We still generate a test failure if there are any in-progress downloads.
- *
- * @param {Boolean} [getDownloads=false]
- *   If true, invoke getDownloads after clearing the download list and return
- *   its value.
- */
-function clearAllDoneHelper(getDownloads) {
-  var clearedPromise = new Promise(function(resolve, reject) {
-    function gotDownloads(downloads) {
-      // If there are no downloads, we're already done.
-      if (downloads.length === 0) {
-        resolve();
-        return;
-      }
-
-      // Track the set of expected downloads that will be finalized.
-      var expectedIds = new Set();
-      function changeHandler(evt) {
-        var download = evt.download;
-        if (download.state === "finalized") {
-          expectedIds.delete(download.id);
-          if (expectedIds.size === 0) {
-            resolve();
-          }
-        }
-      }
-      downloads.forEach(function(download) {
-        if (download.state === "downloading") {
-          ok(false, "A download is still active: " + download.path);
-          reject("Active download");
-        }
-        download.onstatechange = changeHandler;
-        expectedIds.add(download.id);
-      });
-      navigator.mozDownloadManager.clearAllDone();
-    }
-    function gotBadNews(err) {
-      ok(false, "Problem clearing all downloads: " + err);
-      reject(err);
-    }
-    navigator.mozDownloadManager.getDownloads().then(gotDownloads, gotBadNews);
- });
- if (!getDownloads) {
-   return clearedPromise;
- }
- return clearedPromise.then(function() {
-   return navigator.mozDownloadManager.getDownloads();
- });
-}
deleted file mode 100644
--- a/dom/downloads/tests/mochitest.ini
+++ /dev/null
@@ -1,15 +0,0 @@
-[DEFAULT]
-# The actual requirement for mozDownloadManager is MOZ_GONK because of
-# the nsIVolumeService dependency.  Until https://bugzil.la/1130264 is
-# addressed, there is no way for mulet to run these tests.
-run-if = toolkit == 'gonk'
-support-files =
-  serve_file.sjs
-  clear_all_done_helper.js
-
-[test_downloads_navigator_object.html]
-[test_downloads_basic.html]
-[test_downloads_large.html]
-[test_downloads_bad_file.html]
-[test_downloads_pause_remove.html]
-[test_downloads_pause_resume.html]
deleted file mode 100644
--- a/dom/downloads/tests/serve_file.sjs
+++ /dev/null
@@ -1,170 +0,0 @@
-// Serves a file with a given mime type and size at an optionally given rate.
-
-function getQuery(request) {
-  var query = {};
-  request.queryString.split('&').forEach(function (val) {
-    var [name, value] = val.split('=');
-    query[name] = unescape(value);
-  });
-  return query;
-}
-
-function handleResponse() {
-  // Is this a rate limited response?
-  if (this.state.rate > 0) {
-    // Calculate how many bytes we have left to send.
-    var bytesToWrite = this.state.totalBytes - this.state.sentBytes;
-
-    // Do we have any bytes left to send? If not we'll just fall thru and
-    // cancel our repeating timer and finalize the response.
-    if (bytesToWrite > 0) {
-      // Figure out how many bytes to send, based on the rate limit.
-      bytesToWrite =
-        (bytesToWrite > this.state.rate) ? this.state.rate : bytesToWrite;
-
-      for (let i = 0; i < bytesToWrite; i++) {
-        try {
-          this.response.bodyOutputStream.write("0", 1);
-        } catch (e) {
-          // Connection was closed by client.
-          if (e == Components.results.NS_ERROR_NOT_AVAILABLE) {
-            // There's no harm in calling this multiple times.
-            this.response.finish();
-
-            // It's possible that our timer wasn't cancelled in time
-            // and we'll be called again.
-            if (this.timer) {
-              this.timer.cancel();
-              this.timer = null;
-            }
-
-            return;
-          }
-        }
-      }
-
-      // Update the number of bytes we've sent to the client.
-      this.state.sentBytes += bytesToWrite;
-
-      // Wait until the next call to do anything else.
-      return;
-    }
-  }
-  else {
-    // Not rate limited, write it all out.
-    for (let i = 0; i < this.state.totalBytes; i++) {
-      this.response.write("0");
-    }
-  }
-
-  // Finalize the response.
-  this.response.finish();
-
-  // All done sending, go ahead and cancel our repeating timer.
-  this.timer.cancel();
-
-  // Clear the timer.
-  this.timer = null;
-}
-
-function handleRequest(request, response) {
-  var query = getQuery(request);
-
-  // sending at a specific rate requires our response to be asynchronous so
-  // we handle all requests asynchronously. See handleResponse().
-  response.processAsync();
-
-  // Default status when responding.
-  var version = "1.1";
-  var statusCode = 200;
-  var description = "OK";
-
-  // Default values for content type, size and rate.
-  var contentType = "text/plain";
-  var contentRange = null;
-  var size = 1024;
-  var rate = 0;
-
-  // optional content type to be used by our response.
-  if ("contentType" in query) {
-    contentType = query["contentType"];
-  }
-
-  // optional size (in bytes) for generated file.
-  if ("size" in query) {
-    size = parseInt(query["size"]);
-  }
-
-  // optional range request check.
-  if (request.hasHeader("range")) {
-    version = "1.1";
-    statusCode = 206;
-    description = "Partial Content";
-
-    // We'll only support simple range byte style requests.
-    var [offset, total] = request.getHeader("range").slice("bytes=".length).split("-");
-    // Enforce valid Number values.
-    offset = parseInt(offset);
-    offset = isNaN(offset) ? 0 : offset;
-    // Same.
-    total = parseInt(total);
-    total = isNaN(total) ? 0 : total;
-
-    // We'll need to original total size as part of the Content-Range header
-    // value in our response.
-    var originalSize = size;
-
-    // If we have a total size requested, we must make sure to send that number
-    // of bytes only (minus the start offset).
-    if (total && total < size) {
-      size = total - offset;
-    } else if (offset) {
-      // Looks like we just have a byte offset to deal with.
-      size = size - offset;
-    }
-
-    // We specifically need to add a Content-Range header to all responses for
-    // requests that include a range request header.
-    contentRange = "bytes " + offset + "-" + (size - 1) + "/" + originalSize;
-  }
-
-  // optional rate (in bytes/s) at which to send the file.
-  if ("rate" in query) {
-    rate = parseInt(query["rate"]);
-  }
-
-  // The context for the responseHandler.
-  var context = {
-    response: response,
-    state: {
-      contentType: contentType,
-      totalBytes: size,
-      sentBytes: 0,
-      rate: rate
-    },
-    timer: null
-  };
-
-  // The notify implementation for the timer.
-  context.notify = handleResponse.bind(context);
-
-  context.timer =
-    Components.classes["@mozilla.org/timer;1"]
-              .createInstance(Components.interfaces.nsITimer);
-
-  // generate the content.
-  response.setStatusLine(version, statusCode, description);
-  response.setHeader("Content-Type", contentType, false);
-  if (contentRange) {
-    response.setHeader("Content-Range", contentRange, false);
-  }
-  response.setHeader("Content-Length", size.toString(), false);
-
-  // initialize the timer and start writing out the response.
-  context.timer.initWithCallback(
-    context,
-    1000,
-    Components.interfaces.nsITimer.TYPE_REPEATING_SLACK
-  );
-
-}
deleted file mode 100644
--- a/dom/downloads/tests/test_downloads_bad_file.html
+++ /dev/null
@@ -1,93 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=960749
--->
-<head>
-  <title>Test for Bug 960749 Downloads API</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=960749">Mozilla Bug 960749</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<a href="serve_file.sjs?contentType=application/octet-stream&size=1024" download=".&lt;.EVIL.&gt;\ / : * ? &quot; |file.bin" id="download1">Download #1</a>
-<pre id="test">
-<script class="testbody" type="text/javascript;version=1.7">
-
-// Testing a simple download, waiting for it to be done.
-
-SimpleTest.waitForExplicitFinish();
-
-var index = -1;
-var expected = "_.EVIL.__ _ _ _ _ _ _file.bin";
-
-function next() {
-  index += 1;
-  if (index >= steps.length) {
-    ok(false, "Shouldn't get here!");
-    return;
-  }
-  try {
-    steps[index]();
-  } catch(ex) {
-    ok(false, "Caught exception", ex);
-  }
-}
-
-function checkTargetFilename(download) {
-  ok(download.path.endsWith(expected),
-     "Download path leaf name '" + download.path +
-     "' should match '" + expected + "' filename.");
-
-  SimpleTest.finish();
-}
-
-function downloadChange(evt) {
-  var download = evt.download;
-
-  if (download.state === "succeeded") {
-    checkTargetFilename(download);
-  }
-}
-
-function downloadStart(evt) {
-  var download = evt.download;
-  download.onstatechange = downloadChange;
-}
-
-var steps = [
-  // Start by setting the pref to true.
-  function() {
-    SpecialPowers.pushPrefEnv({
-      set: [["dom.mozDownloads.enabled", true]]
-    }, next);
-  },
-
-  // Setup the event listeners.
-  function() {
-    SpecialPowers.pushPermissions([
-      {type: "downloads", allow: true, context: document}
-    ], function() {
-      navigator.mozDownloadManager.ondownloadstart = downloadStart;
-      next();
-    });
-  },
-
-  // Click on the <a download> to start the download.
-  function() {
-    document.getElementById("download1").click();
-  }
-];
-
-next();
-
-</script>
-</pre>
-</body>
-</html>
-
deleted file mode 100644
--- a/dom/downloads/tests/test_downloads_basic.html
+++ /dev/null
@@ -1,128 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=938023
--->
-<head>
-  <title>Test for Bug 938023 Downloads API</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=938023">Mozilla Bug 938023</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<a href="serve_file.sjs?contentType=application/octet-stream&size=1024" download="test.bin" id="download1">Download #1</a>
-<pre id="test">
-<script class="testbody" type="text/javascript;version=1.7">
-
-// Testing a simple download, waiting for it to be done.
-
-SimpleTest.waitForExplicitFinish();
-
-var index = -1;
-var todayDate = new Date();
-var baseServeURL = "http://mochi.test:8888/tests/dom/downloads/tests/";
-var lastKnownCurrentBytes = 0;
-
-function next() {
-  index += 1;
-  if (index >= steps.length) {
-    ok(false, "Shouldn't get here!");
-    return;
-  }
-  try {
-    steps[index]();
-  } catch(ex) {
-    ok(false, "Caught exception", ex);
-  }
-}
-
-function checkConsistentDownloadAttributes(download) {
-  var href = document.getElementById("download1").getAttribute("href");
-  var expectedServeURL = baseServeURL + href;
-  var destinationRegEx = /test\(?[0-9]*\)?\.bin$/;
-
-  // bug 945323: Download path isn't honoring download attribute
-  ok(destinationRegEx.test(download.path),
-     "Download path '" + download.path +
-     "' should match '" + destinationRegEx + "' regexp.");
-
-  ok(download.startTime >= todayDate,
-     "Download start time should be greater than or equal to today");
-
-  is(download.error, null, "Download does not have an error");
-
-  is(download.url, expectedServeURL,
-     "Download URL = " + expectedServeURL);
-  ok(download.id !== null, "Download id is defined");
-  is(download.contentType, "application/octet-stream",
-     "Download content type is application/octet-stream");
-}
-
-function downloadChange(evt) {
-  var download = evt.download;
-  checkConsistentDownloadAttributes(download);
-  is(download.totalBytes, 1024, "Download total size is 1024 bytes");
-
-  if (download.state === "succeeded") {
-    is(download.currentBytes, 1024, "Download current size is 1024 bytes");
-    SimpleTest.finish();
-  } else if (download.state === "downloading") {
-    // Note that this case may or may not trigger, depending on whether the
-    // download is initially reported with 0 bytes (we should happen) or with
-    // 1024 bytes (we should not happen).  If we do happen, an additional 8
-    // TEST-PASS events should be logged.
-    ok(download.currentBytes > lastKnownCurrentBytes,
-       "Download current size is larger than last download change event");
-    lastKnownCurrentBytes = download.currentBytes;
-  } else {
-    ok(false, "Unexpected download state = " + download.state);
-  }
-}
-
-function downloadStart(evt) {
-  var download = evt.download;
-  checkConsistentDownloadAttributes(download);
-
-  // We used to check that the currentBytes was 0.  This was incorrect.  It
-  // is very common to first hear about the download already at 1024 bytes.
-  is(download.state, "downloading", "Download state is downloading");
-
-  download.onstatechange = downloadChange;
-}
-
-var steps = [
-  // Start by setting the pref to true.
-  function() {
-    SpecialPowers.pushPrefEnv({
-      set: [["dom.mozDownloads.enabled", true]]
-    }, next);
-  },
-
-  // Setup the event listeners.
-  function() {
-    SpecialPowers.pushPermissions([
-      {type: "downloads", allow: true, context: document}
-    ], function() {
-      navigator.mozDownloadManager.ondownloadstart = downloadStart;
-      next();
-    });
-  },
-
-  // Click on the <a download> to start the download.
-  function() {
-    document.getElementById("download1").click();
-  }
-];
-
-next();
-
-</script>
-</pre>
-</body>
-</html>
-
deleted file mode 100644
--- a/dom/downloads/tests/test_downloads_large.html
+++ /dev/null
@@ -1,110 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=938023
--->
-<head>
-  <title>Test for Bug 938023 Downloads API</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="clear_all_done_helper.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=938023">Mozilla Bug 938023</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<a href="serve_file.sjs?contentType=application/octet-stream&size=102400" download="test.bin" id="download1">Large Download</a>
-<pre id="test">
-<script class="testbody" type="text/javascript;version=1.7">
-
-// Testing downloading a file, then checking getDownloads() and clearAllDone().
-
-SimpleTest.waitForExplicitFinish();
-
-var index = -1;
-
-function next(args) {
-  index += 1;
-  if (index >= steps.length) {
-    ok(false, "Shouldn't get here!");
-    return;
-  }
-  try {
-    steps[index](args);
-  } catch(ex) {
-    ok(false, "Caught exception", ex);
-  }
-}
-
-// Catch all error function.
-function error() {
-  ok(false, "API failure");
-  SimpleTest.finish();
-}
-
-function getDownloads(downloads) {
-  ok(downloads.length == 1, "One downloads after getDownloads");
-  clearAllDoneHelper(true).then(clearAllDone, error);
-}
-
-function clearAllDone(downloads) {
-  ok(downloads.length == 0, "No downloads after clearAllDone");
-  SimpleTest.finish();
-}
-
-function downloadChange(evt) {
-  var download = evt.download;
-
-  if (download.state == "succeeded") {
-    ok(download.totalBytes == 102400, "Download size is 100k bytes.");
-    navigator.mozDownloadManager.getDownloads().then(getDownloads, error);
-  }
-}
-
-var steps = [
-  // Start by setting the pref to true.
-  function() {
-    SpecialPowers.pushPrefEnv({
-      set: [["dom.mozDownloads.enabled", true]]
-    }, next);
-  },
-
-  // Setup permission and clear current list.
-  function() {
-    SpecialPowers.pushPermissions([
-      {type: "downloads", allow: true, context: document}
-    ], function() {
-      clearAllDoneHelper(true).then(next, error);
-    });
-  },
-
-  function(downloads) {
-    ok(downloads.length == 0, "Start with an empty download list.");
-    next();
-  },
-
-  // Setup the event listeners.
-  function() {
-    navigator.mozDownloadManager.ondownloadstart =
-      function(evt) {
-        ok(true, "Download started");
-        evt.download.addEventListener("statechange", downloadChange);
-      }
-    next();
-  },
-
-  // Click on the <a download> to start the download.
-  function() {
-    document.getElementById("download1").click();
-  }
-];
-
-next();
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/downloads/tests/test_downloads_navigator_object.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=938023
--->
-<head>
-  <title>Test for Bug 938023 Downloads API</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=938023">Mozilla Bug 938023</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-<iframe></iframe>
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript;version=1.7">
-
-SimpleTest.waitForExplicitFinish();
-
-var index = -1;
-
-function next() {
-  index += 1;
-  if (index >= steps.length) {
-    ok(false, "Shouldn't get here!");
-    return;
-  }
-  try {
-    steps[index]();
-  } catch(ex) {
-    ok(false, "Caught exception", ex);
-  }
-}
-
-var steps = [
-  // Start by setting the pref to true.
-  function() {
-    SpecialPowers.pushPrefEnv({
-      set: [["dom.mozDownloads.enabled", true]]
-    }, next);
-  },
-
-  function() {
-    SpecialPowers.pushPermissions([
-      {type: "downloads", allow: 0, context: document}
-    ], function() {
-      is(frames[0].navigator.mozDownloadManager, null, "navigator.mozDownloadManager is null when the page doesn't have permissions");
-      next();
-    });
-  },
-
-  function() {
-    SpecialPowers.pushPrefEnv({
-      set: [["dom.mozDownloads.enabled", false]]
-    }, function() {
-      is(navigator.mozDownloadManager, undefined, "navigator.mozDownloadManager is undefined");
-      next();
-    });
-  },
-
-  function() {
-    SimpleTest.finish();
-  }
-];
-
-next();
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/downloads/tests/test_downloads_pause_remove.html
+++ /dev/null
@@ -1,117 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=938023
--->
-<head>
-  <title>Test for Bug 938023 Downloads API</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="clear_all_done_helper.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=938023">Mozilla Bug 938023</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<a href="serve_file.sjs?contentType=application/octet-stream&size=102400&rate=1024" download="test.bin" id="download1">Large Download</a>
-<pre id="test">
-<script class="testbody" type="text/javascript;version=1.7">
-
-// Testing pausing a download and then removing it.
-
-SimpleTest.waitForExplicitFinish();
-
-var index = -1;
-
-function next(args) {
-  index += 1;
-  if (index >= steps.length) {
-    ok(false, "Shouldn't get here!");
-    return;
-  }
-  try {
-    steps[index](args);
-  } catch(ex) {
-    ok(false, "Caught exception", ex);
-  }
-}
-
-var pausing = false;
-
-// Catch all error function.
-function error() {
-  ok(false, "API failure");
-  SimpleTest.finish();
-}
-
-function checkDownloadList(downloads) {
-  ok(downloads.length == 0, "No downloads left");
-  SimpleTest.finish();
-}
-
-function checkRemoved(download) {
-  ok(download.state == "finalized", "Download removed.");
-  navigator.mozDownloadManager.getDownloads()
-           .then(checkDownloadList, error);
-}
-
-function downloadChange(evt) {
-  var download = evt.download;
-
-  if (download.state == "downloading" && !pausing) {
-    pausing = true;
-    download.pause();
-  } else if (download.state == "stopped") {
-    ok(pausing, "Download stopped by pause()");
-    navigator.mozDownloadManager.remove(download)
-             .then(checkRemoved, error);
-  }
-}
-
-var steps = [
-  // Start by setting the pref to true.
-  function() {
-    SpecialPowers.pushPrefEnv({
-      set: [["dom.mozDownloads.enabled", true]]
-    }, next);
-  },
-
-  // Setup permission and clear current list.
-  function() {
-    SpecialPowers.pushPermissions([
-      {type: "downloads", allow: true, context: document}
-    ], function() {
-      clearAllDoneHelper(true).then(next, error);
-    });
-  },
-
-  function(downloads) {
-    ok(downloads.length == 0, "Start with an empty download list.");
-    next();
-  },
-
-  // Setup the event listeners.
-  function() {
-    navigator.mozDownloadManager.ondownloadstart =
-      function(evt) {
-        ok(true, "Download started");
-        evt.download.addEventListener("statechange", downloadChange);
-      }
-    next();
-  },
-
-  // Click on the <a download> to start the download.
-  function() {
-    document.getElementById("download1").click();
-  }
-];
-
-next();
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/downloads/tests/test_downloads_pause_resume.html
+++ /dev/null
@@ -1,121 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=938023
--->
-<head>
-  <title>Test for Bug 938023 Downloads API</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="clear_all_done_helper.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=938023">Mozilla Bug 938023</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<a href="serve_file.sjs?contentType=application/octet-stream&size=102400&rate=1024" download="test.bin" id="download1">Large Download</a>
-<pre id="test">
-<script class="testbody" type="text/javascript;version=1.7">
-
-// Testing pausing a download and then resuming it.
-
-SimpleTest.waitForExplicitFinish();
-
-var index = -1;
-
-function next(args) {
-  index += 1;
-  if (index >= steps.length) {
-    ok(false, "Shouldn't get here!");
-    return;
-  }
-  try {
-    steps[index](args);
-  } catch(ex) {
-    ok(false, "Caught exception", ex);
-  }
-}
-
-var pausing = false;
-var resuming = false;
-
-// Catch all error function.
-function error() {
-  ok(false, "API failure");
-  SimpleTest.finish();
-}
-
-function checkDownloadList(downloads) {
-  ok(downloads.length == 0, "No downloads left");
-  SimpleTest.finish();
-}
-
-function checkResumeSucceeded(download) {
-  ok(download.state == "succeeded", "Download resumed successfully.");
-  clearAllDoneHelper(true).then(checkDownloadList, error);
-}
-
-function downloadChange(evt) {
-  var download = evt.download;
-
-  info("got download event, state: " + download.state +
-       " current bytes: " + download.currentBytes +
-       " pausing?: " + pausing + " resuming?: " + resuming);
-  if (download.state == "downloading" && !pausing) {
-    pausing = true;
-    download.pause();
-  } else if (download.state == "stopped" && !resuming) {
-    resuming = true;
-    ok(pausing, "Download stopped by pause()");
-    download.resume()
-            .then(function() { checkResumeSucceeded(download); }, error);
-  }
-}
-
-var steps = [
-  // Start by setting the pref to true.
-  function() {
-    SpecialPowers.pushPrefEnv({
-      set: [["dom.mozDownloads.enabled", true]]
-    }, next);
-  },
-
-  // Setup permission and clear current list.
-  function() {
-    SpecialPowers.pushPermissions([
-      {type: "downloads", allow: true, context: document}
-    ], function() {
-      clearAllDoneHelper(true).then(next, error);
-    });
-  },
-
-  function(downloads) {
-    ok(downloads.length == 0, "Start with an empty download list.");
-    next();
-  },
-
-  // Setup the event listeners.
-  function() {
-    navigator.mozDownloadManager.ondownloadstart =
-      function(evt) {
-        ok(true, "Download started");
-        evt.download.addEventListener("statechange", downloadChange);
-      }
-    next();
-  },
-
-  // Click on the <a download> to start the download.
-  function() {
-    document.getElementById("download1").click();
-  }
-];
-
-next();
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -102,21 +102,16 @@ DIRS += [
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['plugins/ipc/hangui']
 
 if CONFIG['MOZ_SECUREELEMENT']:
     DIRS += ['secureelement']
 
-if CONFIG['MOZ_B2G']:
-    DIRS += [
-        'downloads',
-    ]
-
 DIRS += ['presentation']
 
 TEST_DIRS += [
     'tests',
     'imptests',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'cocoa', 'windows', 'android'):
deleted file mode 100644
--- a/dom/webidl/DownloadEvent.webidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/.
- */
-
-[Constructor(DOMString type, optional DownloadEventInit eventInitDict),
- Pref="dom.mozDownloads.enabled",
- ChromeOnly]
-interface DownloadEvent : Event
-{
-  readonly attribute DOMDownload? download;
-};
-
-dictionary DownloadEventInit : EventInit
-{
-  DOMDownload? download = null;
-};
deleted file mode 100644
--- a/dom/webidl/Downloads.webidl
+++ /dev/null
@@ -1,166 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/.
- */
-
-// Represents the state of a download.
-// "downloading": The resource is actively transfering.
-// "stopped"    : No network tranfer is happening.
-// "succeeded"  : The resource has been downloaded successfully.
-// "finalized"  : We won't try to download this resource, but the DOM
-//                object is still alive.
-enum DownloadState {
-  "downloading",
-  "stopped",
-  "succeeded",
-  "finalized"
-};
-
-[NoInterfaceObject,
- NavigatorProperty="mozDownloadManager",
- JSImplementation="@mozilla.org/downloads/manager;1",
- Pref="dom.mozDownloads.enabled",
- ChromeOnly]
-interface DOMDownloadManager : EventTarget {
-  // This promise returns an array of downloads with all the current
-  // download objects.
-  Promise<sequence<DOMDownload>> getDownloads();
-
-  // Removes one download from the downloads set. Returns a promise resolved
-  // with the finalized download.
-  [UnsafeInPrerendering]
-  Promise<DOMDownload> remove(DOMDownload download);
-
-  // Removes all completed downloads.  This kicks off an asynchronous process
-  // that will eventually complete, but will not have completed by the time this
-  // method returns.  If you care about the side-effects of this method, know
-  // that each existing download will have its onstatechange method invoked and
-  // will have a new state of "finalized".  (After the download is finalized, no
-  // further events will be generated on it.)
-  [UnsafeInPrerendering]
-  void clearAllDone();
-
-  // Add completed downloads from applications that must perform the download
-  // process themselves. For example, email.  The method is resolved with a
-  // fully populated DOMDownload instance on success, or rejected in the
-  // event all required options were not provided.
-  //
-  // The adopted download will also be reported via the ondownloadstart event
-  // handler.
-  //
-  // Applications must currently be certified to use this, but it could be
-  // widened at a later time.
-  //
-  // Note that "download" is not actually optional, but WebIDL requires that it
-  // be marked as such because it is not followed by a required argument.  The
-  // promise will be rejected if the dictionary is omitted or the specified
-  // file does not exist on disk.
-  Promise<DOMDownload> adoptDownload(optional AdoptDownloadDict download);
-
-  // Fires when a new download starts.
-  attribute EventHandler ondownloadstart;
-};
-
-[JSImplementation="@mozilla.org/downloads/download;1",
- Pref="dom.mozDownloads.enabled",
- ChromeOnly]
-interface DOMDownload : EventTarget {
-  // The full size of the resource.
-  readonly attribute long long totalBytes;
-
-  // The number of bytes that we have currently downloaded.
-  readonly attribute long long currentBytes;
-
-  // The url of the resource.
-  readonly attribute DOMString url;
-
-  // The full path in local storage where the file will end up once the download
-  // is complete. This is equivalent to the concatenation of the 'storagePath'
-  // to the 'mountPoint' of the nsIVolume associated with the 'storageName'
-  // (with delimiter).
-  readonly attribute DOMString path;
-
-  // The DeviceStorage volume name on which the file is being downloaded.
-  readonly attribute DOMString storageName;
-
-  // The DeviceStorage path on the volume with 'storageName' of the file being
-  // downloaded.
-  readonly attribute DOMString storagePath;
-
-  // The state of the download.  One of: downloading, stopped, succeeded, or
-  // finalized.  A finalized download is a download that has been removed /
-  // cleared and is no longer tracked by the download manager and will not
-  // receive any further onstatechange updates.
-  readonly attribute DownloadState state;
-
-  // The mime type for this resource.
-  readonly attribute DOMString contentType;
-
-  // The timestamp this download started.
-  readonly attribute Date startTime;
-
-  // An opaque identifier for this download. All instances of the same
-  // download (eg. in different windows) will have the same id.
-  readonly attribute DOMString id;
-
-  // The manifestURL of the application that added this download. Only used for
-  // downloads added via the adoptDownload API call.
-  readonly attribute DOMString? sourceAppManifestURL;
-
-  // A DOM error object, that will be not null when a download is stopped
-  // because something failed.
-  readonly attribute DOMError? error;
-
-  // Pauses the download.
-  [UnsafeInPrerendering]
-  Promise<DOMDownload> pause();
-
-  // Resumes the download. This resolves only once the download has
-  // succeeded.
-  [UnsafeInPrerendering]
-  Promise<DOMDownload> resume();
-
-  // This event is triggered anytime a property of the object changes:
-  // - when the transfer progresses, updating currentBytes.
-  // - when the state and/or error attributes change.
-  attribute EventHandler onstatechange;
-};
-
-// Used to initialize the DOMDownload object for adopted downloads.
-// fields directly maps to the DOMDownload fields.
-dictionary AdoptDownloadDict {
-  // The URL of this resource if there is one available. An empty string if
-  // the download is not accessible via URL. An empty string is chosen over
-  // null so that existinc code does not need to null-check but the value is
-  // still falsey.  (Note: If you do have a usable URL, you should probably not
-  // be using the adoptDownload API and instead be initiating downloads the
-  // normal way.)
-  DOMString url;
-
-  // The storageName of the DeviceStorage instance the file was saved to.
-  // Required but marked as optional so the bindings don't auto-coerce the value
-  // null to "null".
-  DOMString? storageName;
-  // The path of the file within the DeviceStorage instance named by
-  // 'storageName'.  This is used to automatically compute the 'path' of the
-  // download.  Note that when DeviceStorage gives you a path to a file, the
-  // first path segment is the name of the specific device storage and you do
-  // *not* want to include this.  For example, if DeviceStorage tells you the
-  // file has a path of '/sdcard1/actual/path/file.ext', then the storageName
-  // should be 'sdcard1' and the storagePath should be 'actual/path/file.ext'.
-  //
-  // The existence of the file will be validated will be validated with stat()
-  // and the size the file-system tells us will be what we use.
-  //
-  // Required but marked as optional so the bindings don't auto-coerce the value
-  // null to "null".
-  DOMString? storagePath;
-
-  // The mime type for this resource.  Required, but marked as optional because
-  // WebIDL otherwise auto-coerces the value null to "null".
-  DOMString? contentType;
-
-  // The time the download was started. If omitted, the current time is used.
-  Date? startTime;
-};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -123,17 +123,16 @@ WEBIDL_FILES = [
     'DOMPoint.webidl',
     'DOMQuad.webidl',
     'DOMRect.webidl',
     'DOMRectList.webidl',
     'DOMRequest.webidl',
     'DOMStringList.webidl',
     'DOMStringMap.webidl',
     'DOMTokenList.webidl',
-    'Downloads.webidl',
     'DragEvent.webidl',
     'DynamicsCompressorNode.webidl',
     'Element.webidl',
     'Event.webidl',
     'EventHandler.webidl',
     'EventListener.webidl',
     'EventSource.webidl',
     'EventTarget.webidl',
@@ -682,17 +681,16 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'BlobEvent.webidl',
     'CaretStateChangedEvent.webidl',
     'CloseEvent.webidl',
     'DeviceLightEvent.webidl',
     'DeviceOrientationEvent.webidl',
     'DeviceProximityEvent.webidl',
     'DeviceStorageAreaChangedEvent.webidl',
     'DeviceStorageChangeEvent.webidl',
-    'DownloadEvent.webidl',
     'ErrorEvent.webidl',
     'FontFaceSetLoadEvent.webidl',
     'GamepadAxisMoveEvent.webidl',
     'GamepadButtonEvent.webidl',
     'GamepadEvent.webidl',
     'GroupedHistoryEvent.webidl',
     'HashChangeEvent.webidl',
     'HiddenPluginEvent.webidl',