Bug 1387153 - Preferences UI to support cloud storage download. r=Gijs, a=lizzard
authorPunam <pdahiya@mozilla.com>
Tue, 15 Aug 2017 16:57:23 -0700
changeset 423669 95e016111eb8d1cf9a4353d4bfc073c13443313f
parent 423668 5c4087baab4401370982b7ba72eba6426128afa1
child 423670 c4306d552f7ae8fb567dc085cf58fb65bc421939
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs, lizzard
bugs1387153
milestone56.0
Bug 1387153 - Preferences UI to support cloud storage download. r=Gijs, a=lizzard * Add third radio button to support cloud storage download preference * Handle download preferences and UI states, while switching between three options * Add method to check if provider is in use in Cloud Storage API * Preferences cloud storage strings saved as resource url * Updated old in-content with cloud storage pref UI changes * Turn cloud storage API on/off using generic pref MozReview-Commit-ID: AE3MlhgKp2C
browser/components/preferences/in-content-new/main.js
browser/components/preferences/in-content-new/main.xul
browser/components/preferences/in-content/main.js
browser/components/preferences/in-content/main.xul
toolkit/components/cloudstorage/CloudStorage.jsm
toolkit/components/cloudstorage/content/preferences.properties
toolkit/components/cloudstorage/tests/unit/test_cloudstorage.js
--- a/browser/components/preferences/in-content-new/main.js
+++ b/browser/components/preferences/in-content-new/main.js
@@ -10,16 +10,18 @@ Components.utils.import("resource://gre/
 Components.utils.import("resource://gre/modules/Downloads.jsm");
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource:///modules/ShellService.jsm");
 Components.utils.import("resource:///modules/TransientPrefs.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/AppConstants.jsm");
 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
 Components.utils.import("resource://gre/modules/LoadContextInfo.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CloudStorage",
+                                  "resource://gre/modules/CloudStorage.jsm");
 
 // Constants & Enumeration Values
 const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
 const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
 const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
 const TYPE_PDF = "application/pdf";
 
 const PREF_PDFJS_DISABLED = "pdfjs.disabled";
@@ -404,16 +406,17 @@ var gMainPane = {
     setEventListener("focusSearch2", "command", gMainPane.focusFilterBox);
     setEventListener("filter", "command", gMainPane.filter);
     setEventListener("handlersView", "select",
       gMainPane.onSelectionChanged);
     setEventListener("typeColumn", "click", gMainPane.sort);
     setEventListener("actionColumn", "click", gMainPane.sort);
     setEventListener("chooseFolder", "command", gMainPane.chooseFolder);
     setEventListener("browser.download.dir", "change", gMainPane.displayDownloadDirPref);
+    setEventListener("saveWhere", "command", gMainPane.handleSaveToCommand);
 
     // Listen for window unload so we can remove our preference observers.
     window.addEventListener("unload", this);
 
     // Figure out how we should be sorting the list.  We persist sort settings
     // across sessions, so we can't assume the default sort column/direction.
     // XXX should we be using the XUL sort service instead?
     if (document.getElementById("actionColumn").hasAttribute("sortDirection")) {
@@ -2244,16 +2247,18 @@ var gMainPane = {
    *   Indicates the location users wish to save downloaded files too.
    *   It is also used to display special file labels when the default
    *   download location is either the Desktop or the Downloads folder.
    *   Values:
    *     0 - The desktop is the default download location.
    *     1 - The system's downloads folder is the default download location.
    *     2 - The default download location is elsewhere as specified in
    *         browser.download.dir.
+   *     3 - The default download location is elsewhere as specified by
+   *         cloud storage API getDownloadFolder
    * browser.download.downloadDir
    *   deprecated.
    * browser.download.defaultFolder
    *   deprecated.
    */
 
   /**
    * Enables/disables the folder field and Browse button based on whether a
@@ -2261,21 +2266,100 @@ var gMainPane = {
    */
   readUseDownloadDir() {
     var downloadFolder = document.getElementById("downloadFolder");
     var chooseFolder = document.getElementById("chooseFolder");
     var preference = document.getElementById("browser.download.useDownloadDir");
     downloadFolder.disabled = !preference.value || preference.locked;
     chooseFolder.disabled = !preference.value || preference.locked;
 
+    this.readCloudStorage().catch(Components.utils.reportError);
     // don't override the preference's value in UI
     return undefined;
   },
 
   /**
+   * Show/Hide the cloud storage radio button with provider name as label if
+   * cloud storage provider is in use.
+   * Select cloud storage radio button if browser.download.useDownloadDir is true
+   * and browser.download.folderList has value 3. Enables/disables the folder field
+   * and Browse button if cloud storage radio button is selected.
+   *
+   */
+  async readCloudStorage() {
+    // Get preferred provider in use display name
+    let providerDisplayName = await CloudStorage.getProviderIfInUse();
+    if (providerDisplayName) {
+      // Show cloud storage radio button with provider name in label
+      let saveToCloudRadio = document.getElementById("saveToCloud");
+      let cloudStrings = Services.strings.createBundle("resource://cloudstorage/preferences.properties");
+      saveToCloudRadio.label = cloudStrings.formatStringFromName("saveFilesToCloudStorage",
+                                                                 [providerDisplayName], 1);
+      saveToCloudRadio.hidden = false;
+
+      let useDownloadDirPref = document.getElementById("browser.download.useDownloadDir");
+      let folderListPref = document.getElementById("browser.download.folderList");
+
+      // Check if useDownloadDir is true and folderListPref is set to Cloud Storage value 3
+      // before selecting cloudStorageradio button. Disable folder field and Browse button if
+      // 'Save to Cloud Storage Provider' radio option is selected
+      if (useDownloadDirPref.value && folderListPref.value === 3) {
+        document.getElementById("saveWhere").selectedItem = saveToCloudRadio;
+        document.getElementById("downloadFolder").disabled = true;
+        document.getElementById("chooseFolder").disabled = true;
+      }
+    }
+  },
+
+  /**
+   * Handle clicks to 'Save To <custom path> or <system default downloads>' and
+   * 'Save to <cloud storage provider>' if cloud storage radio button is displayed in UI.
+   * Sets browser.download.folderList value and Enables/disables the folder field and Browse
+   * button based on option selected.
+   */
+  handleSaveToCommand(event) {
+    return this.handleSaveToCommandTask(event).catch(Components.utils.reportError);
+  },
+  async handleSaveToCommandTask(event) {
+    if (event.target.id !== "saveToCloud" && event.target.id !== "saveTo") {
+      return;
+    }
+    // Check if Save To Cloud Storage Provider radio option is displayed in UI
+    // before continuing.
+    let saveToCloudRadio = document.getElementById("saveToCloud");
+    if (!saveToCloudRadio.hidden) {
+      // When switching between SaveTo and SaveToCloud radio button
+      // with useDownloadDirPref value true, if selectedIndex is other than
+      // SaveTo radio button disable downloadFolder filefield and chooseFolder button
+      let saveWhere = document.getElementById("saveWhere");
+      let useDownloadDirPref = document.getElementById("browser.download.useDownloadDir");
+      if (useDownloadDirPref.value) {
+        let downloadFolder = document.getElementById("downloadFolder");
+        let chooseFolder = document.getElementById("chooseFolder");
+        downloadFolder.disabled = saveWhere.selectedIndex || useDownloadDirPref.locked;
+        chooseFolder.disabled = saveWhere.selectedIndex || useDownloadDirPref.locked;
+      }
+
+      // Set folderListPref value depending on radio option
+      // selected. folderListPref should be set to 3 if Save To Cloud Storage Provider
+      // option is selected. If user switch back to 'Save To' custom path or system
+      // default Downloads, check pref 'browser.download.dir' before setting respective
+      // folderListPref value
+      let folderListPref = document.getElementById("browser.download.folderList");
+      let saveTo = document.getElementById("saveTo");
+      if (saveWhere.selectedItem == saveToCloudRadio) {
+        folderListPref.value = 3;
+      } else if (saveWhere.selectedItem == saveTo) {
+        let currentDirPref = document.getElementById("browser.download.dir");
+        folderListPref.value = await this._folderToIndex(currentDirPref.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.
    */
   chooseFolder() {
     return this.chooseFolderTask().catch(Components.utils.reportError);
   },
   async chooseFolderTask() {
@@ -2333,22 +2417,30 @@ var gMainPane = {
 
     // Used in defining the correct path to the folder icon.
     var ios = Components.classes["@mozilla.org/network/io-service;1"]
                         .getService(Components.interfaces.nsIIOService);
     var fph = ios.getProtocolHandler("file")
                  .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
     var iconUrlSpec;
 
+    let folderIndex = folderListPref.value;
+    if (folderIndex == 3) {
+      // When user has selected cloud storage, use value in currentDirPref to
+      // compute index to display download folder label and icon to avoid
+      // displaying blank downloadFolder label and icon on load of preferences UI
+      folderIndex = await this._folderToIndex(currentDirPref.value);
+    }
+
     // Display a 'pretty' label or the path in the UI.
-    if (folderListPref.value == 2) {
+    if (folderIndex == 2) {
       // Custom path selected and is configured
       downloadFolder.label = this._getDisplayNameOfFile(currentDirPref.value);
       iconUrlSpec = fph.getURLSpecFromFile(currentDirPref.value);
-    } else if (folderListPref.value == 1) {
+    } else if (folderIndex == 1) {
       // 'Downloads'
       downloadFolder.label = bundlePreferences.getString("downloadsFolderName");
       iconUrlSpec = fph.getURLSpecFromFile(await this._indexToFolder(1));
     } else {
       // 'Desktop'
       downloadFolder.label = bundlePreferences.getString("desktopFolderName");
       iconUrlSpec = fph.getURLSpecFromFile(await this._getDownloadsFolder("Desktop"));
     }
--- a/browser/components/preferences/in-content-new/main.xul
+++ b/browser/components/preferences/in-content-new/main.xul
@@ -679,16 +679,20 @@
               accesskey="&chooseFolderMac.accesskey;"
               label="&chooseFolderMac.label;"
 #else
               accesskey="&chooseFolderWin.accesskey;"
               label="&chooseFolderWin.label;"
 #endif
       />
     </hbox>
+    <!-- Additional radio button added to support CloudStorage - Bug 1357171 -->
+    <radio id="saveToCloud"
+          value="true"
+          hidden="true"/>
     <radio id="alwaysAsk"
           value="false"
           label="&alwaysAskWhere.label;"
           accesskey="&alwaysAskWhere.accesskey;"/>
   </radiogroup>
 </groupbox>
 
 <groupbox id="applicationsGroup" data-category="paneGeneral" hidden="true">
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -6,16 +6,18 @@
 
 Components.utils.import("resource://gre/modules/Downloads.jsm");
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource:///modules/ShellService.jsm");
 Components.utils.import("resource:///modules/TransientPrefs.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CloudStorage",
+                                  "resource://gre/modules/CloudStorage.jsm");
 
 if (AppConstants.E10S_TESTING_ONLY) {
   XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                     "resource://gre/modules/UpdateUtils.jsm");
 }
 
 if (AppConstants.MOZ_DEV_EDITION) {
   XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
@@ -83,16 +85,18 @@ var gMainPane = {
       document.getElementById("warnCloseMultiple").hidden = true;
     if (!TransientPrefs.prefShouldBeVisible("browser.tabs.warnOnOpen"))
       document.getElementById("warnOpenMany").hidden = true;
 
     setEventListener("browser.privatebrowsing.autostart", "change",
                      gMainPane.updateBrowserStartupLastSession);
     setEventListener("browser.download.dir", "change",
                      gMainPane.displayDownloadDirPref);
+    setEventListener("saveWhere", "command",
+                     gMainPane.handleSaveToCommand);
     if (AppConstants.HAVE_SHELL_SERVICE) {
       setEventListener("setDefaultButton", "command",
                        gMainPane.setDefaultBrowser);
     }
     setEventListener("useCurrent", "command",
                      gMainPane.setHomePageToCurrent);
     setEventListener("useBookmark", "command",
                      gMainPane.setHomePageToBookmark);
@@ -498,16 +502,18 @@ var gMainPane = {
    *   Indicates the location users wish to save downloaded files too.
    *   It is also used to display special file labels when the default
    *   download location is either the Desktop or the Downloads folder.
    *   Values:
    *     0 - The desktop is the default download location.
    *     1 - The system's downloads folder is the default download location.
    *     2 - The default download location is elsewhere as specified in
    *         browser.download.dir.
+   *     3 - The default download location is elsewhere as specified by
+   *         cloud storage API getDownloadFolder
    * browser.download.downloadDir
    *   deprecated.
    * browser.download.defaultFolder
    *   deprecated.
    */
 
   /**
    * Enables/disables the folder field and Browse button based on whether a
@@ -515,21 +521,100 @@ var gMainPane = {
    */
   readUseDownloadDir() {
     var downloadFolder = document.getElementById("downloadFolder");
     var chooseFolder = document.getElementById("chooseFolder");
     var preference = document.getElementById("browser.download.useDownloadDir");
     downloadFolder.disabled = !preference.value || preference.locked;
     chooseFolder.disabled = !preference.value || preference.locked;
 
+    this.readCloudStorage().catch(Components.utils.reportError);
     // don't override the preference's value in UI
     return undefined;
   },
 
   /**
+   * Show/Hide the cloud storage radio button with provider name as label if
+   * cloud storage provider is in use.
+   * Select cloud storage radio button if browser.download.useDownloadDir is true
+   * and browser.download.folderList has value 3. Enables/disables the folder field
+   * and Browse button if cloud storage radio button is selected.
+   *
+   */
+  async readCloudStorage() {
+    // Get preferred provider in use display name
+    let providerDisplayName = await CloudStorage.getProviderIfInUse();
+    if (providerDisplayName) {
+      // Show cloud storage radio button with provider name in label
+      let saveToCloudRadio = document.getElementById("saveToCloud");
+      let cloudStrings = Services.strings.createBundle("resource://cloudstorage/preferences.properties");
+      saveToCloudRadio.label = cloudStrings.formatStringFromName("saveFilesToCloudStorage",
+                                                                 [providerDisplayName], 1);
+      saveToCloudRadio.hidden = false;
+
+      let useDownloadDirPref = document.getElementById("browser.download.useDownloadDir");
+      let folderListPref = document.getElementById("browser.download.folderList");
+
+      // Check if useDownloadDir is true and folderListPref is set to Cloud Storage value 3
+      // before selecting cloudStorageradio button. Disable folder field and Browse button if
+      // 'Save to Cloud Storage Provider' radio option is selected
+      if (useDownloadDirPref.value && folderListPref.value === 3) {
+        document.getElementById("saveWhere").selectedItem = saveToCloudRadio;
+        document.getElementById("downloadFolder").disabled = true;
+        document.getElementById("chooseFolder").disabled = true;
+      }
+    }
+  },
+
+  /**
+   * Handle clicks to 'Save To <custom path> or <system default downloads>' and
+   * 'Save to <cloud storage provider>' if cloud storage radio button is displayed in UI.
+   * Sets browser.download.folderList value and Enables/disables the folder field and Browse
+   * button based on option selected.
+   */
+  handleSaveToCommand(event) {
+    return this.handleSaveToCommandTask(event).catch(Components.utils.reportError);
+  },
+  async handleSaveToCommandTask(event) {
+    if (event.target.id !== "saveToCloud" && event.target.id !== "saveTo") {
+      return;
+    }
+    // Check if Save To Cloud Storage Provider radio option is displayed in UI
+    // before continuing.
+    let saveToCloudRadio = document.getElementById("saveToCloud");
+    if (!saveToCloudRadio.hidden) {
+      // When switching between SaveTo and SaveToCloud radio button
+      // with useDownloadDirPref value true, if selectedIndex is other than
+      // SaveTo radio button disable downloadFolder filefield and chooseFolder button
+      let saveWhere = document.getElementById("saveWhere");
+      let useDownloadDirPref = document.getElementById("browser.download.useDownloadDir");
+      if (useDownloadDirPref.value) {
+        let downloadFolder = document.getElementById("downloadFolder");
+        let chooseFolder = document.getElementById("chooseFolder");
+        downloadFolder.disabled = saveWhere.selectedIndex || useDownloadDirPref.locked;
+        chooseFolder.disabled = saveWhere.selectedIndex || useDownloadDirPref.locked;
+      }
+
+      // Set folderListPref value depending on radio option
+      // selected. folderListPref should be set to 3 if Save To Cloud Storage Provider
+      // option is selected. If user switch back to 'Save To' custom path or system
+      // default Downloads, check pref 'browser.download.dir' before setting respective
+      // folderListPref value
+      let folderListPref = document.getElementById("browser.download.folderList");
+      let saveTo = document.getElementById("saveTo");
+      if (saveWhere.selectedItem == saveToCloudRadio) {
+        folderListPref.value = 3;
+      } else if (saveWhere.selectedItem == saveTo) {
+        let currentDirPref = document.getElementById("browser.download.dir");
+        folderListPref.value = await this._folderToIndex(currentDirPref.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.
    */
   chooseFolder() {
     return this.chooseFolderTask().catch(Components.utils.reportError);
   },
   async chooseFolderTask() {
@@ -587,22 +672,30 @@ var gMainPane = {
 
     // Used in defining the correct path to the folder icon.
     var ios = Components.classes["@mozilla.org/network/io-service;1"]
                         .getService(Components.interfaces.nsIIOService);
     var fph = ios.getProtocolHandler("file")
                  .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
     var iconUrlSpec;
 
+    let folderIndex = folderListPref.value;
+    if (folderIndex == 3) {
+      // When user has selected cloud storage, use value in currentDirPref to
+      // compute index to display download folder label and icon to avoid
+      // displaying blank downloadFolder label and icon on load of preferences UI
+      folderIndex = await this._folderToIndex(currentDirPref.value);
+    }
+
     // Display a 'pretty' label or the path in the UI.
-    if (folderListPref.value == 2) {
+    if (folderIndex == 2) {
       // Custom path selected and is configured
       downloadFolder.label = this._getDisplayNameOfFile(currentDirPref.value);
       iconUrlSpec = fph.getURLSpecFromFile(currentDirPref.value);
-    } else if (folderListPref.value == 1) {
+    } else if (folderIndex == 1) {
       // 'Downloads'
       downloadFolder.label = bundlePreferences.getString("downloadsFolderName");
       iconUrlSpec = fph.getURLSpecFromFile(await this._indexToFolder(1));
     } else {
       // 'Desktop'
       downloadFolder.label = bundlePreferences.getString("desktopFolderName");
       iconUrlSpec = fph.getURLSpecFromFile(await this._getDownloadsFolder("Desktop"));
     }
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -275,20 +275,26 @@
               label="&chooseFolderMac.label;"
 #else
               accesskey="&chooseFolderWin.accesskey;"
               label="&chooseFolderWin.label;"
 #endif
       />
     </hbox>
     <hbox>
+      <!-- Additional radio button added to support CloudStorage - Bug 1357171 -->
+      <radio id="saveToCloud"
+            value="true"
+            hidden="true"/>
+    </hbox>
+    <hbox>
       <radio id="alwaysAsk"
-             value="false"
-             label="&alwaysAskWhere.label;"
-             accesskey="&alwaysAskWhere.accesskey;"/>
+            value="false"
+            label="&alwaysAskWhere.label;"
+            accesskey="&alwaysAskWhere.accesskey;"/>
     </hbox>
   </radiogroup>
 </groupbox>
 
 <!-- Tab preferences -->
 <groupbox data-category="paneGeneral"
           hidden="true" align="start">
     <caption><label>&tabsGroup.label;</label></caption>
--- a/toolkit/components/cloudstorage/CloudStorage.jsm
+++ b/toolkit/components/cloudstorage/CloudStorage.jsm
@@ -17,16 +17,18 @@ this.EXPORTED_SYMBOLS = ["CloudStorage"]
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.importGlobalProperties(["fetch"]);
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
+                                  "resource://gre/modules/Downloads.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 
 const CLOUD_SERVICES_PREF = "cloud.services.";
 const CLOUD_PROVIDERS_URI = "resource://cloudstorage/providers.json";
 
@@ -68,16 +70,18 @@ const CLOUD_PROVIDERS_URI = "resource://
  * on type of data downloaded. Default folder is 'Downloads'. Other options are
  * 'screenshot' depending on provider support.
  */
 
 /**
  *
  * Internal cloud services prefs
  *
+ * cloud.services.api.enabled - set to true to initialize and use Cloud Storage module
+ *
  * cloud.services.storage.key - set to string with preferred provider key
  *
  * cloud.services.lastPrompt - set to time when last prompt was shown
  *
  * cloud.services.interval.prompt - set to time interval in days after which prompt should be shown
  *
  * cloud.services.rejected.key - set to string with comma separated provider keys rejected
  * by user when prompted to opt-in
@@ -167,27 +171,51 @@ this.CloudStorage = {
    *        type of data downloaded, options are 'default', 'screenshot'
    * @return {Promise} which resolves to full path to provider download folder
    */
   getDownloadFolder(typeSpecificData) {
     return CloudStorageInternal.getDownloadFolder(typeSpecificData);
   },
 
   /**
-   * Get provider opted-in by user to store downloaded files
+   * Get key of provider opted-in by user to store downloaded files
    *
    * @return {String}
    * Storage provider key from provider metadata. Return empty string
    * if user has not selected a preferred provider.
    */
   getPreferredProvider() {
     return CloudStorageInternal.preferredProviderKey;
   },
 
   /**
+   * Get metadata of provider opted-in by user to store downloaded files.
+   * Return preferred provider metadata without scanning by doing simple lookup
+   * inside storage providers metadata using preferred provider key
+   *
+   * @return {Object}
+   * Object with preferred provider metadata. Return null
+   * if user has not selected a preferred provider.
+   */
+  getPreferredProviderMetaData() {
+    return CloudStorageInternal.getPreferredProviderMetaData();
+  },
+
+  /**
+   * Get display name of a provider actively in use to store downloaded files
+   *
+   * @return {String}
+   * String with provider display name. Returns null if a provider
+   * is not in use.
+   */
+  getProviderIfInUse() {
+    return CloudStorageInternal.getProviderIfInUse();
+  },
+
+  /**
    * Get providers found on user desktop. Used for unit tests
    *
    * @return {Promise}
    * @resolves
    * Map object with entries key set to storage provider key and values set to
    * storage provider metadata
    */
   getStorageProviders() {
@@ -220,22 +248,59 @@ var CloudStorageInternal = {
       }
     } catch (e) {
       Cu.reportError("Fetching " + uri + " results in error: " + e);
     }
     return json;
   },
 
   /**
+   * Reset 'browser.download.folderList' cloud storage value '3' back
+   * to '2' or '1' depending on custom path or system default Downloads path
+   * in pref 'browser.download.dir'.
+   */
+  async resetFolderListPref() {
+    let folderListValue = Services.prefs.getIntPref("browser.download.folderList", 0);
+    if (folderListValue !== 3) {
+      return;
+    }
+
+    let downloadDirPath = Services.prefs.getComplexValue("browser.download.dir",
+                                                         Ci.nsIFile).path;
+    if (!downloadDirPath ||
+        (downloadDirPath === Services.dirsvc.get("Desk", Ci.nsIFile).path)) {
+      // if downloadDirPath is the Desktop path or is unspecified
+      folderListValue = 0;
+    } else if (downloadDirPath === await Downloads.getSystemDownloadsDirectory()) {
+      // if downloadDirPath is the Downloads folder path
+      folderListValue = 1;
+    } else {
+      // otherwise
+      folderListValue = 2;
+    }
+    Services.prefs.setIntPref("browser.download.folderList", folderListValue);
+  },
+
+  /**
    * Loads storage providers metadata asynchronously from providers.json.
    *
    * @returns {Promise} with resolved boolean value true if providers
    * metadata is successfully initialized
    */
   async initProviders() {
+    // Cloud Storage API should continue initialization and load providers metadata
+    // only if a consumer add-on using API sets pref 'cloud.services.api.enabled' to true
+    // If API is not enabled, check and reset cloud storage value in folderList pref.
+    if (!this.isAPIEnabled) {
+      this.resetFolderListPref().catch((err) => {
+        Cu.reportError("CloudStorage: Failed to reset folderList pref " + err);
+      });
+      return false;
+    }
+
     let response = await this._downloadJSON(CLOUD_PROVIDERS_URI);
     this.providersMetaData = await this._parseProvidersJSON(response);
 
     let providersCount = Object.keys(this.providersMetaData).length;
     if (providersCount > 0) {
       // Array of boolean results for each provider handled for custom downloadpath
       let handledProviders = await this.initDownloadPathIfProvidersExist();
       if (handledProviders.length === providersCount) {
@@ -558,16 +623,46 @@ var CloudStorageInternal = {
    *
    * @param key
    *        cloud storage provider key from provider metadata
    */
   setCloudStoragePref(key) {
     Services.prefs.setCharPref(CLOUD_SERVICES_PREF + "storage.key", key);
     Services.prefs.setIntPref("browser.download.folderList", 3);
   },
+
+  /**
+   * get access to preferred provider metadata by using preferred provider key
+   *
+   * @return {Object}
+   * Object with preferred provider metadata. Returns null if preferred provider is not set
+   */
+  getPreferredProviderMetaData() {
+    // Use preferred provider key to retrieve metadata from ProvidersMetaData
+    return this.providersMetaData.hasOwnProperty(this.preferredProviderKey) ?
+      this.providersMetaData[this.preferredProviderKey] : null;
+  },
+
+  /**
+   * Get provider display name if cloud storage API is used by an add-on
+   * and user has set preferred provider and a valid download directory
+   * path exists on user desktop.
+   *
+   * @return {String}
+   * String with preferred provider display name. Returns null if provider is not in use.
+   */
+  async getProviderIfInUse() {
+    // Check if consumer add-on is present and user has set preferred provider key
+    // and a valid download path exist on user desktop
+    if (this.isAPIEnabled && this.preferredProviderKey && await this.getDownloadFolder()) {
+      let provider = this.getPreferredProviderMetaData();
+      return provider.displayName || null;
+    }
+    return null;
+  },
 };
 
 /**
  * Provider key retrieved from service pref cloud.services.storage.key
  */
 XPCOMUtils.defineLazyPreferenceGetter(CloudStorageInternal, "preferredProviderKey",
   CLOUD_SERVICES_PREF + "storage.key", "");
 
@@ -584,9 +679,16 @@ XPCOMUtils.defineLazyPreferenceGetter(Cl
   CLOUD_SERVICES_PREF + "lastprompt", 0 /* 0 second */);
 
 /**
  * show prompt interval in days, by default set to 0
  */
 XPCOMUtils.defineLazyPreferenceGetter(CloudStorageInternal, "promptInterval",
   CLOUD_SERVICES_PREF + "interval.prompt", 0 /* 0 days */);
 
+/**
+ * generic pref that shows if cloud storage API is in use, by default set to false.
+ * Re-run CloudStorage init evertytime pref is set.
+ */
+XPCOMUtils.defineLazyPreferenceGetter(CloudStorageInternal, "isAPIEnabled",
+  CLOUD_SERVICES_PREF + "api.enabled", false, () => CloudStorage.init());
+
 CloudStorageInternal.promiseInit = CloudStorage.init();
new file mode 100644
--- /dev/null
+++ b/toolkit/components/cloudstorage/content/preferences.properties
@@ -0,0 +1,9 @@
+# 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/.
+
+#### Cloud Storage Downloads
+
+# NOTE (saveFilesToCloudStorage): %S is replaced by a service name (Dropbox, Google Drive)
+# String below is hardcoded for en-US and not localized
+saveFilesToCloudStorage=Save files to %S
--- a/toolkit/components/cloudstorage/tests/unit/test_cloudstorage.js
+++ b/toolkit/components/cloudstorage/tests/unit/test_cloudstorage.js
@@ -21,24 +21,29 @@ const GOOGLE_DRIVE_DOWNLOAD_FOLDER = "Go
 const DROPBOX_CONFIG_FOLDER = (AppConstants.platform === "win") ? "Dropbox" :
                                                                   ".dropbox";
 const DROPBOX_KEY = "Dropbox";
 const GDRIVE_KEY = "GDrive";
 
 var nsIDropboxFile, nsIGDriveFile;
 
 function run_test() {
+  initPrefs();
   registerFakePath("Home", do_get_file("cloud/"));
   registerFakePath("LocalAppData", do_get_file("cloud/"));
   do_register_cleanup(() => {
     cleanupPrefs();
   });
   run_next_test();
 }
 
+function initPrefs() {
+  Services.prefs.setBoolPref(CLOUD_SERVICES_PREF + "api.enabled", true);
+}
+
 /**
  * Replaces a directory service entry with a given nsIFile.
  */
 function registerFakePath(key, file) {
   let dirsvc = Services.dirsvc.QueryInterface(Ci.nsIProperties);
   let originalFile;
   try {
     // If a file is already provided save it and undefine, otherwise set will
@@ -127,16 +132,17 @@ function mock_gdrive() {
 }
 
 function cleanupPrefs() {
   try {
     Services.prefs.clearUserPref(CLOUD_SERVICES_PREF + "lastprompt");
     Services.prefs.clearUserPref(CLOUD_SERVICES_PREF + "storage.key");
     Services.prefs.clearUserPref(CLOUD_SERVICES_PREF + "rejected.key");
     Services.prefs.clearUserPref(CLOUD_SERVICES_PREF + "interval.prompt");
+    Services.prefs.clearUserPref(CLOUD_SERVICES_PREF + "api.enabled");
     Services.prefs.setIntPref("browser.download.folderList", 2);
   } catch (e) {
     do_throw("Failed to cleanup prefs: " + e);
   }
 }
 
 function promiseGetStorageProviders() {
   return CloudStorage.getStorageProviders();