Bug 888915 - Part 5. Remove remaining uses of nsIDownloadManager and fix preferences options. r=IanN
authorFrank-Rainer Grahl <frgrahl@gmx.net>
Mon, 09 Jul 2018 17:00:21 +0200
changeset 64792 6a6f9c616b1cf7686825d3bd61cc46619e185791
parent 64791 4d63eb6735dfa4402ed7fdbf78b90164119e156f
child 64793 7c799865bb7e5fc3c2517d1b0395a31b61494710
push id6502
push usermozilla@jorgk.com
push dateWed, 11 Jul 2018 13:18:44 +0000
treeherdertry-comm-central@623e5cfcc5de [default view] [failures only]
reviewersIanN
bugs888915
Bug 888915 - Part 5. Remove remaining uses of nsIDownloadManager and fix preferences options. r=IanN
suite/browser/pageinfo/pageInfo.js
suite/components/downloads/DownloadsCommon.jsm
suite/components/downloads/content/progressDialog.js
suite/components/nsSuiteGlue.js
suite/components/pref/content/pref-download.js
suite/components/pref/content/pref-download.xul
suite/modules/Sanitizer.jsm
--- a/suite/browser/pageinfo/pageInfo.js
+++ b/suite/browser/pageinfo/pageInfo.js
@@ -1,13 +1,20 @@
 /* -*- Mode: Javascript; 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/. */
 
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
+                                  "resource://gre/modules/Downloads.jsm");
+
 //******** define a js object to implement nsITreeView
 function pageInfoTreeView(copycol)
 {
   /* copycol is the index number for the column that we want to add to
    * the copy-n-paste buffer when the user hits accel-c.
    * Older pageInfo extensions might call pageInfoTreeView with copycol
    * as the second argument of two.
    */
@@ -854,41 +861,45 @@ function getSelectedRows(tree) {
 }
 
 function getSelectedRow(tree) {
   var rows = getSelectedRows(tree);
   return (rows.length == 1) ? rows[0] : -1;
 }
 
 function selectSaveFolder(aCallback) {
-  const nsIFile = Ci.nsIFile;
-  const nsIFilePicker = Ci.nsIFilePicker;
+  return selectSaveFolderTask(aCallback).catch(Cu.reportError);
+}
+
+async function selectSaveFolderTask(aCallback) {
   let titleText = gBundle.getString("mediaSelectFolder");
-  let fp = Cc["@mozilla.org/filepicker;1"]
-             .createInstance(nsIFilePicker);
-  let fpCallback = function fpCallback_done(aResult) {
-    if (aResult == nsIFilePicker.returnOK) {
-      aCallback(fp.file.QueryInterface(nsIFile));
-    } else {
-      aCallback(null);
+  let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+
+  fp.init(window, titleText, Ci.nsIFilePicker.modeGetFolder);
+  fp.appendFilters(Ci.nsIFilePicker.filterAll);
+  try {
+    let prefs = Cc[PREFERENCES_CONTRACTID].getService(Ci.nsIPrefBranch);
+    let initialDir = prefs.getComplexValue("browser.download.dir",
+                                           Ci.nsIFile);
+    if (!initialDir) {
+      let downloadsDir = await Downloads.getSystemDownloadsDirectory();
+      initialDir = new FileUtils.File(downloadsDir);
     }
-  };
 
-  fp.init(window, titleText, nsIFilePicker.modeGetFolder);
-  fp.appendFilters(nsIFilePicker.filterAll);
-  try {
-    let prefs = Cc[PREFERENCES_CONTRACTID]
-                  .getService(Ci.nsIPrefBranch);
-    let initialDir = prefs.getComplexValue("browser.download.dir", nsIFile);
-    if (initialDir) {
-      fp.displayDirectory = initialDir;
-    }
+    fp.displayDirectory = initialDir;
   } catch (ex) {
   }
-  fp.open(fpCallback);
+
+  let result = await new Promise(resolve => fp.open(resolve));
+
+  if (result == Ci.nsIFilePicker.returnOK) {
+    aCallback(fp.file.QueryInterface(Ci.nsIFile));
+  } else {
+    aCallback(null);
+  }
 }
 
 function saveMedia()
 {
   var tree = document.getElementById("imagetree");
   var rowArray = getSelectedRows(tree);
   if (rowArray.length == 1) {
     let row = rowArray[0];
--- a/suite/components/downloads/DownloadsCommon.jsm
+++ b/suite/components/downloads/DownloadsCommon.jsm
@@ -68,16 +68,17 @@ const kDownloadsStringsRequiringPluralFo
   otherDownloads3: true
 };
 
 const kPartialDownloadSuffix = ".part";
 
 const kPrefBranch = Services.prefs.getBranch("browser.download.");
 
 const PREF_DM_BEHAVIOR = "browser.download.manager.behavior";
+const PROGRESS_DIALOG_URL = "chrome://communicator/content/downloads/progressDialog.xul";
 
 var PrefObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
   getPref(name) {
     try {
       switch (typeof this.prefs[name]) {
         case "boolean":
@@ -547,16 +548,20 @@ XPCOMUtils.defineLazyGetter(this.Downloa
 
 DownloadsDataCtor.prototype = {
   /**
    * Starts receiving events for current downloads.
    */
   initializeDataLink() {},
 
   /**
+   * Used by sound logic when download ends.
+   */
+  _sound: null,
+  /**
    * Promise resolved with the underlying DownloadList object once we started
    * receiving events for current downloads.
    */
   _promiseList: null,
 
   _downloadsLoaded: null,
 
   /**
@@ -609,17 +614,16 @@ DownloadsDataCtor.prototype = {
     this.onDownloadChanged(download);
     if (!this._downloadsLoaded)
       return;
 
     var behavior = download.source.isPrivate ? 1 :
                      Services.prefs.getIntPref(PREF_DM_BEHAVIOR);
     switch (behavior) {
       case 0:
-        // TODO Better move this out of nsSuiteGlue.
         Cc["@mozilla.org/suite/suiteglue;1"]
           .getService(Ci.nsISuiteGlue)
           .showDownloadManager(download);
         break;
       case 1:
         Services.ww.openWindow(null, PROGRESS_DIALOG_URL, null,
                                "chrome,titlebar,centerscreen,minimizable=yes,dialog=no",
                                { wrappedJSObject: download });
@@ -636,52 +640,71 @@ DownloadsDataCtor.prototype = {
 
     if (oldState != newState &&
         (download.succeeded ||
          (download.canceled && !download.hasPartialData) ||
           download.error)) {
       // Store the end time that may be displayed by the views.
       download.endTime = Date.now();
 
-      DownloadsCommon.log("DownloadHistory.updateMetaData");
       // This state transition code should actually be located in a Downloads
       // API module (bug 941009).
       // This might end with an exception if it is an unsupported uri scheme.
       DownloadHistory.updateMetaData(download);
+
+      if (download.succeeded) {
+        this.playDownloadSound();
+      }
     }
   },
 
   onDownloadRemoved(download) {
     this.oldDownloadStates.delete(download);
   },
 
   // Download summary
-  onSummaryChanged:  function() {
+  onSummaryChanged: function() {
 
-  if (!gTaskbarProgress)
-    return;
+    if (!gTaskbarProgress)
+      return;
 
-  const nsITaskbarProgress = Ci.nsITaskbarProgress;
-  var currentBytes = gDownloadsSummary.progressCurrentBytes;
-  var totalBytes = gDownloadsSummary.progressTotalBytes;
-  var state = gDownloadsSummary.allHaveStopped ?
-                currentBytes ? nsITaskbarProgress.STATE_PAUSED :
-                               nsITaskbarProgress.STATE_NO_PROGRESS :
-                currentBytes < totalBytes ? nsITaskbarProgress.STATE_NORMAL :
-                             nsITaskbarProgress.STATE_INDETERMINATE;
-  switch (state) {
-    case nsITaskbarProgress.STATE_NO_PROGRESS:
-    case nsITaskbarProgress.STATE_INDETERMINATE:
-      gTaskbarProgress.setProgressState(state, 0, 0);
-      break;
-    default:
-      gTaskbarProgress.setProgressState(state, currentBytes, totalBytes);
-      break;
-  }
-},
+    const nsITaskbarProgress = Ci.nsITaskbarProgress;
+    var currentBytes = gDownloadsSummary.progressCurrentBytes;
+    var totalBytes = gDownloadsSummary.progressTotalBytes;
+    var state = gDownloadsSummary.allHaveStopped ?
+                  currentBytes ? nsITaskbarProgress.STATE_PAUSED :
+                                 nsITaskbarProgress.STATE_NO_PROGRESS :
+                  currentBytes < totalBytes ? nsITaskbarProgress.STATE_NORMAL :
+                               nsITaskbarProgress.STATE_INDETERMINATE;
+    switch (state) {
+      case nsITaskbarProgress.STATE_NO_PROGRESS:
+      case nsITaskbarProgress.STATE_INDETERMINATE:
+        gTaskbarProgress.setProgressState(state, 0, 0);
+        break;
+      default:
+        gTaskbarProgress.setProgressState(state, currentBytes, totalBytes);
+        break;
+    }
+  },
+
+  // Play a download sound.
+  playDownloadSound: function()
+  {
+    if (Services.prefs.getBoolPref("browser.download.finished_download_sound")) {
+      if (!this._sound)
+        this._sound = Cc["@mozilla.org/sound;1"].createInstance(Ci.nsISound);
+      try {
+        let url = Services.prefs.getComplexValue("browser.download.finished_sound_url",
+                                                 Ci.nsISupportsString);
+        this._sound.play(Services.io.newURI(url.data));
+      } catch (e) {
+        this._sound.beep();
+      }
+    }
+  },
 
   // Registration of views
 
   /**
    * Adds an object to be notified when the available download data changes.
    * The specified object is initialized with the currently available downloads.
    *
    * @param aView
--- a/suite/components/downloads/content/progressDialog.js
+++ b/suite/components/downloads/content/progressDialog.js
@@ -183,17 +183,17 @@ function updateButtons() {
   document.getElementById("cancelButton").hidden = !ProgressDlgController.isCommandEnabled("cmd_cancel");
 }
 
 /**
  * DlProgressListener "class" is used to help update download items shown
  * in the progress dialog such as displaying amount transferred, transfer
  * rate, and time left for the download.
  *
- * This class implements the nsIDownloadProgressListener interface.
+ * This class implements the downloadProgressListener interface.
  */
 function DlProgressListener() {}
 
 DlProgressListener.prototype = {
   onDownloadChanged: function(aDownload) {
     if (aDownload == gDownload) {
       if (gCloseWhenDone.checked && aDownload.succeeded) {
         window.close();
--- a/suite/components/nsSuiteGlue.js
+++ b/suite/components/nsSuiteGlue.js
@@ -91,17 +91,16 @@ const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 1
 const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
 
 // Devtools Preferences
 const DEBUGGER_REMOTE_ENABLED = "devtools.debugger.remote-enabled";
 const DEBUGGER_REMOTE_PORT = "devtools.debugger.remote-port";
 const DEBUGGER_FORCE_LOCAL = "devtools.debugger.force-local";
 const DEBUGGER_WIFI_VISIBLE = "devtools.remote.wifi.visible";
 const DOWNLOAD_MANAGER_URL = "chrome://communicator/content/downloads/downloadmanager.xul";
-const PROGRESS_DIALOG_URL = "chrome://communicator/content/downloads/progressDialog.xul";
 const PREF_FOCUS_WHEN_STARTING = "browser.download.manager.focusWhenStarting";
 const PREF_FLASH_COUNT = "browser.download.manager.flashCount";
 
 var gDownloadManager;
 
 // Constructor
 function SuiteGlue() {
   XPCOMUtils.defineLazyServiceGetter(this, "_idleService",
@@ -109,17 +108,16 @@ function SuiteGlue() {
                                      "nsIIdleService");
 
   this._init();
   extensionDefaults(); // extensionSupport.jsm
 }
 
 SuiteGlue.prototype = {
   _saveSession: false,
-  _sound: null,
   _isIdleObserver: false,
   _isPlacesDatabaseLocked: false,
   _migrationImportsDefaultBookmarks: false,
 
   _setPrefToSaveSession: function()
   {
     Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
   },
@@ -251,19 +249,16 @@ SuiteGlue.prototype = {
 //      case "weave:engine:clients:display-uri":
 //        this._onDisplaySyncURI(subject);
 //        break;
       case "session-save":
         this._setPrefToSaveSession();
         subject.QueryInterface(Ci.nsISupportsPRBool);
         subject.data = true;
         break;
-      case "dl-done":
-        this._playDownloadSound();
-        break;
       case "places-init-complete":
         if (!this._migrationImportsDefaultBookmarks)
           this._initPlaces(false);
 
         Services.obs.removeObserver(this, "places-init-complete");
         break;
       case "places-shutdown":
         Services.obs.removeObserver(this, "places-shutdown");
@@ -353,17 +348,16 @@ SuiteGlue.prototype = {
     Services.obs.addObserver(this, "quit-application-requested", true);
     Services.obs.addObserver(this, "quit-application-granted", true);
     Services.obs.addObserver(this, "browser-lastwindow-close-requested", true);
     Services.obs.addObserver(this, "browser-lastwindow-close-granted", true);
     Services.obs.addObserver(this, "console-api-log-event", true);
     Services.obs.addObserver(this, "weave:service:ready", true);
     Services.obs.addObserver(this, "weave:engine:clients:display-uri", true);
     Services.obs.addObserver(this, "session-save", true);
-    Services.obs.addObserver(this, "dl-done", true);
     Services.obs.addObserver(this, "places-init-complete", true);
     Services.obs.addObserver(this, "places-shutdown", true);
     Services.obs.addObserver(this, "browser-search-engine-modified", true);
     Services.obs.addObserver(this, "notifications-open-settings", true);
     Services.prefs.addObserver("devtools.debugger.", this, true);
     Services.obs.addObserver(this, "handle-xul-text-link", true);
     Cc['@mozilla.org/docloaderservice;1']
       .getService(Ci.nsIWebProgress)
@@ -790,32 +784,16 @@ SuiteGlue.prototype = {
             Services.prefs.setIntPref("browser.startup.page", 3);
           }
         }
         break;
       }
     }
   },
 
-  _playDownloadSound: function()
-  {
-    if (Services.prefs.getBoolPref("browser.download.finished_download_sound")) {
-      if (!this._sound)
-        this._sound = Cc["@mozilla.org/sound;1"]
-                        .createInstance(Ci.nsISound);
-      try {
-        var url = Services.prefs.getComplexValue("browser.download.finished_sound_url",
-                                                 Ci.nsISupportsString);
-        this._sound.play(Services.io.newURI(url.data));
-      } catch (e) {
-        this._sound.beep();
-      }
-    }
-  },
-
   _showPluginUpdatePage: function(aWindow) {
     Services.prefs.setBoolPref("plugins.update.notifyUser", false);
 
     var url = Services.urlFormatter.formatURLPref("plugins.update.url");
 
     aWindow.getBrowser().addTab(url, { focusNewTab: true });
   },
 
--- a/suite/components/pref/content/pref-download.js
+++ b/suite/components/pref/content/pref-download.js
@@ -1,159 +1,203 @@
 /* -*- Mode: Java; 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/. */
 
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
+                                  "resource://gre/modules/Downloads.jsm");
+
 const kDesktop = 0;
 const kDownloads = 1;
 const kUserDir = 2;
 var gFPHandler;
 var gSoundUrlPref;
 
 function Startup()
 {
   // Define globals
   gFPHandler = Services.io.getProtocolHandler("file")
                           .QueryInterface(Ci.nsIFileProtocolHandler);
   gSoundUrlPref = document.getElementById("browser.download.finished_sound_url");
-  SetSoundEnabled(document.getElementById("browser.download.finished_download_sound").value);
+  setSoundEnabled(document.getElementById("browser.download.finished_download_sound").value);
 
   // if we don't have the alert service, hide the pref UI for using alerts to
   // notify on download completion
   // see bug #158711
   var downloadDoneNotificationAlertUI = document.getElementById("finishedNotificationAlert");
   downloadDoneNotificationAlertUI.hidden = !("@mozilla.org/alerts-service;1" in Cc);
 }
 
 /**
-  * Enables/disables the folder field and Browse button based on whether a
-  * default download directory is being used.
-  */
-function ReadUseDownloadDir()
+ * Enables/disables the folder field and Browse button based on whether a
+ * default download directory is being used.
+ */
+function readUseDownloadDir()
 {
   var downloadFolder = document.getElementById("downloadFolder");
   var chooseFolder = document.getElementById("chooseFolder");
   var preference = document.getElementById("browser.download.useDownloadDir");
   downloadFolder.disabled = !preference.value;
   chooseFolder.disabled = !preference.value;
 }
 
 /**
-  * Displays a file picker in which the user can choose the location where
-  * downloads are automatically saved, updating preferences and UI in
-  * response to the choice, if one is made.
-  */
-function ChooseFolder()
+ * Displays a file picker in which the user can choose the location where
+ * downloads are automatically saved, updating preferences and UI in
+ * response to the choice, if one is made.
+ */
+function chooseFolder()
 {
-  const nsIFilePicker = Ci.nsIFilePicker;
-  let fp = Cc["@mozilla.org/filepicker;1"]
-             .createInstance(nsIFilePicker);
+  return chooseFolderTask().catch(Cu.reportError);
+}
+
+async function chooseFolderTask()
+{
   let title = document.getElementById("bundle_prefutilities")
                       .getString("downloadfolder");
-  fp.init(window, title, nsIFilePicker.modeGetFolder);
-  fp.appendFilters(nsIFilePicker.filterAll);
+  let folderListPref = document.getElementById("browser.download.folderList");
+  let currentDirPref = await _indexToFolder(folderListPref.value);
+  let defDownloads = await _indexToFolder(kDownloads);
+  let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
 
-  fp.displayDirectory = 
-    IndexToFolder(document.getElementById("browser.download.folderList")
-                          .value);
-  fp.open(rv => {
-    if (rv != nsIFilePicker.returnOK || !fp.file) {
-      return;
-    }
-    document.getElementById("browser.download.dir").value = fp.file;
-    document.getElementById("browser.download.folderList").value = 
-      FolderToIndex(fp.file);
-    // Note, the real prefs will not be updated yet, so dnld manager's
-    // userDownloadsDirectory may not return the right folder after
-    // this code executes. displayDownloadDirPref will be called on
-    // the assignment above to update the UI.
-  });
+  fp.init(window, title, Ci.nsIFilePicker.modeGetFolder);
+  fp.appendFilters(Ci.nsIFilePicker.filterAll);
+  // First try to open what's currently configured
+  if (currentDirPref && currentDirPref.exists()) {
+    fp.displayDirectory = currentDirPref;
+  } else if (defDownloads && defDownloads.exists()) {
+    // Try the system's download dir
+    fp.displayDirectory = defDownloads;
+  } else {
+    // Fall back to Desktop
+    fp.displayDirectory = await _indexToFolder(kDesktop);
+  }
+
+  let result = await new Promise(resolve => fp.open(resolve));
+  if (result != Ci.nsIFilePicker.returnOK) {
+    return;
+  }
+
+  document.getElementById("browser.download.dir").value = fp.file;
+  folderListPref.value = await _folderToIndex(fp.file);
+  // Note, the real prefs will not be updated yet, so dnld manager's
+  // userDownloadsDirectory may not return the right folder after
+  // this code executes. displayDownloadDirPref will be called on
+  // the assignment above to update the UI.
 }
 
 /**
-  * Initializes the download folder display settings based on the user's
-  * preferences.
-  */
-function DisplayDownloadDirPref()
+ * Initializes the download folder display settings based on the user's
+ * preferences.
+ */
+function displayDownloadDirPref()
+{
+  displayDownloadDirPrefTask().catch(Cu.reportError);
+}
+
+async function displayDownloadDirPrefTask()
 {
   var folderListPref = document.getElementById("browser.download.folderList");
-  var currentDirPref = IndexToFolder(folderListPref.value); // file
+  var currentDirPref = await _indexToFolder(folderListPref.value); // file
   var prefutilitiesBundle = document.getElementById("bundle_prefutilities");
   var iconUrlSpec = gFPHandler.getURLSpecFromFile(currentDirPref);
   var downloadFolder = document.getElementById("downloadFolder");
   downloadFolder.image = "moz-icon://" + iconUrlSpec + "?size=16";
 
   // Display a 'pretty' label or the path in the UI.
-  switch (FolderToIndex(currentDirPref)) {
+  switch (folderListPref.value) {
     case kDesktop:
       downloadFolder.label = prefutilitiesBundle.getString("desktopFolderName");
       break;
     case kDownloads:
       downloadFolder.label = prefutilitiesBundle.getString("downloadsFolderName");
       break;
     default:
       downloadFolder.label = currentDirPref ? currentDirPref.path : "";
       break;
   }
 }
 
 /**
-  * Returns the Downloads folder as determined by the XPCOM directory service
-  * via the download manager's attribute defaultDownloadsDirectory.
-  */
-function GetDownloadsFolder()
+ * Returns the Downloads folder.  If aFolder is "Desktop", then the Downloads
+ * folder returned is the desktop folder; otherwise, it is a folder whose name
+ * indicates that it is a download folder and whose path is as determined by
+ * the XPCOM directory service via the download manager's attribute
+ * defaultDownloadsDirectory.
+ *
+ * @throws if aFolder is not "Desktop" or "Downloads"
+ */
+async function _getDownloadsFolder(aFolder)
 {
-  return Cc["@mozilla.org/download-manager;1"]
-           .getService(Ci.nsIDownloadManager)
-           .defaultDownloadsDirectory;
+  switch (aFolder) {
+    case "Desktop":
+      return Services.dirsvc.get("Desk", Ci.nsIFile);
+    case "Downloads":
+      let downloadsDir = await Downloads.getSystemDownloadsDirectory();
+      return new FileUtils.File(downloadsDir);
+  }
+  throw "ASSERTION FAILED: folder type should be 'Desktop' or 'Downloads'";
 }
 
 /**
-  * Determines the type of the given folder.
-  *
-  * @param   aFolder
-  *          the folder whose type is to be determined
-  * @returns integer
-  *          kDesktop if aFolder is the Desktop or is unspecified,
-  *          kDownloads if aFolder is the Downloads folder,
-  *          kUserDir otherwise
-  */
-function FolderToIndex(aFolder)
+ * Determines the type of the given folder.
+ *
+ * @param   aFolder
+ *          the folder whose type is to be determined
+ * @returns integer
+ *          kDesktop if aFolder is the Desktop or is unspecified,
+ *          kDownloads if aFolder is the Downloads folder,
+ *          kUserDir otherwise
+ */
+async function _folderToIndex(aFolder)
 {
-  if (!aFolder || aFolder.equals(GetDesktopFolder()))
+  if (!aFolder || aFolder.equals(await _getDownloadsFolder("Desktop"))) {
     return kDesktop;
-  if (aFolder.equals(GetDownloadsFolder()))
+  }
+
+  if (aFolder.equals(await _getDownloadsFolder("Downloads"))) {
     return kDownloads;
+  }
+
   return kUserDir;
-}
+ }
 
 /**
-  * Converts an integer into the corresponding folder.
-  *
-  * @param   aIndex
-  *          an integer
-  * @returns the Desktop folder if aIndex == kDesktop,
-  *          the Downloads folder if aIndex == kDownloads,
-  *          the folder stored in browser.download.dir
-  */
-function IndexToFolder(aIndex)
+ * Converts an integer into the corresponding folder.
+ *
+ * @param   aIndex
+ *          an integer
+ * @returns the Desktop folder if aIndex == kDesktop,
+ *          the Downloads folder if aIndex == kDownloads,
+ *          the folder stored in browser.download.dir
+ */
+async function _indexToFolder(aIndex)
 {
   var folder;
   switch (aIndex) {
+    case kDownloads:
+      folder = await _getDownloadsFolder("Downloads");
+      break;
+    case kDesktop:
+      folder = await _getDownloadsFolder("Desktop");
+      break;
     default:
       folder = document.getElementById("browser.download.dir").value;
-      if (folder && folder.exists())
-        return folder;
-    case kDownloads:
-      folder = GetDownloadsFolder();
-      if (folder && folder.exists())
-        return folder;
-    case kDesktop:
-      return GetDesktopFolder();
+      break;
   }
+  if (!folder ||
+      !folder.exists()) {
+    return "";
+  }
+
+  return folder;
 }
 
-function SetSoundEnabled(aEnable)
+function setSoundEnabled(aEnable)
 {
   EnableElementById("downloadSndURL", aEnable, false);
   document.getElementById("downloadSndPlay").disabled = !aEnable;
 }
--- a/suite/components/pref/content/pref-download.xul
+++ b/suite/components/pref/content/pref-download.xul
@@ -26,24 +26,24 @@
                   name="browser.download.useDownloadDir"
                   type="bool"/>
       <preference id="browser.download.dir"
                   name="browser.download.dir"
                   type="file"/>
       <preference id="browser.download.folderList"
                   name="browser.download.folderList"
                   type="int"
-                  onchange="DisplayDownloadDirPref();"/>
+                  onchange="displayDownloadDirPref();"/>
       <preference id="browser.download.manager.retention"
                   name="browser.download.manager.retention"
                   type="int"/>
       <preference id="browser.download.finished_download_sound"
                   name="browser.download.finished_download_sound"
                   type="bool"
-                  onchange="SetSoundEnabled(this.value);"/>
+                  onchange="setSoundEnabled(this.value);"/>
       <preference id="browser.download.manager.showAlertOnComplete"
                   name="browser.download.manager.showAlertOnComplete"
                   type="bool"/>
       <preference id="browser.download.finished_sound_url"
                   name="browser.download.finished_sound_url"
                   type="string"/>
     </preferences>
 
@@ -67,28 +67,28 @@
                 label="&flashWhenOpen.label;"
                 accesskey="&flashWhenOpen.accesskey;"/>
     </groupbox>
 
     <groupbox>
       <caption label="&downloadLocation.label;"/>
       <radiogroup id="saveWhere"
                   preference="browser.download.useDownloadDir"
-                  onsyncfrompreference="return document.getElementById('download_pane').ReadUseDownloadDir();">
+                  onsyncfrompreference="return document.getElementById('download_pane').readUseDownloadDir();">
         <hbox id="saveToRow">
           <radio id="saveTo" value="true"
                  label="&saveTo.label;"
                  accesskey="&saveTo.accesskey;"
                  aria-labelledby="saveTo downloadFolder"/>
           <filefield id="downloadFolder" flex="1"
                      preference="browser.download.dir"
                      preference-editable="true"
                      aria-labelledby="saveTo"
-                     onsyncfrompreference="document.getElementById('download_pane').DisplayDownloadDirPref();"/>
-          <button id="chooseFolder" oncommand="ChooseFolder();"
+                     onsyncfrompreference="document.getElementById('download_pane').displayDownloadDirPref();"/>
+          <button id="chooseFolder" oncommand="chooseFolder();"
                   label="&chooseDownloadFolder.label;"
                   accesskey="&chooseDownloadFolder.accesskey;"/>
         </hbox>
         <radio id="alwaysAsk" value="false"
                label="&alwaysAsk.label;"
                accesskey="&alwaysAsk.accesskey;"/>
       </radiogroup>
     </groupbox>
--- a/suite/modules/Sanitizer.jsm
+++ b/suite/modules/Sanitizer.jsm
@@ -1,18 +1,25 @@
 /* -*- Mode: Java; 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/. */
 
 var EXPORTED_SYMBOLS = ["Sanitizer"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.defineModuleGetter(this, "FormHistory",
-                               "resource://gre/modules/FormHistory.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AppConstants: "resource://gre/modules/AppConstants.jsm",
+  Downloads: "resource://gre/modules/Downloads.jsm",
+  DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+  FormHistory: "resource://gre/modules/FormHistory.jsm",
+  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
+});
 
 var Sanitizer = {
   get _prefs() {
     delete this._prefs;
     return this._prefs = Services.prefs.getBranch("privacy.sanitize.");
   },
 
   /**
@@ -291,27 +298,26 @@ var Sanitizer = {
           }
         };
         FormHistory.count({}, countDone);
         return false;
       }
     },
 
     downloads: {
-      clear: function() {
-        var dlMgr = Cc["@mozilla.org/download-manager;1"]
-                      .getService(Ci.nsIDownloadManager);
-        dlMgr.cleanUp();
+      // Just say yes to avoid adding some async logic.
+      canClear: true,
+      async clear() {
+        try {
+          // Clear all completed/cancelled downloads.
+          let list = await Downloads.getList(Downloads.ALL);
+          list.removeFinished(null);
+        } finally {
+        }
       },
-
-      get canClear() {
-        var dlMgr = Cc["@mozilla.org/download-manager;1"]
-                      .getService(Ci.nsIDownloadManager);
-        return dlMgr.canCleanUp;
-      }
     },
 
     passwords: {
       clear: function() {
         var pwmgr = Cc["@mozilla.org/login-manager;1"]
                       .getService(Ci.nsILoginManager);
         pwmgr.removeAllLogins();
       },