mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-20 17:20:54 +00:00
Bug 1751093 - Clean up Cloud Storage API and its usage in about:preferences r=preferences-reviewers,Gijs
Differential Revision: https://phabricator.services.mozilla.com/D137248
This commit is contained in:
parent
9bd876e7c3
commit
2b39c9ff18
@ -425,10 +425,6 @@
|
||||
class="accessory-button"
|
||||
data-l10n-id="download-choose-folder"/>
|
||||
</hbox>
|
||||
<!-- Additional radio button added to support CloudStorage - Bug 1357171 -->
|
||||
<radio id="saveToCloud"
|
||||
value="true"
|
||||
hidden="true"/>
|
||||
<radio id="alwaysAsk"
|
||||
value="false"
|
||||
data-l10n-id="download-always-ask-where"/>
|
||||
|
@ -724,7 +724,6 @@ var gMainPane = {
|
||||
setEventListener("typeColumn", "click", gMainPane.sort);
|
||||
setEventListener("actionColumn", "click", gMainPane.sort);
|
||||
setEventListener("chooseFolder", "command", gMainPane.chooseFolder);
|
||||
setEventListener("saveWhere", "command", gMainPane.handleSaveToCommand);
|
||||
Preferences.get("browser.download.folderList").on(
|
||||
"change",
|
||||
gMainPane.displayDownloadDirPref.bind(gMainPane)
|
||||
@ -2920,8 +2919,6 @@ var gMainPane = {
|
||||
* 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
|
||||
@ -2945,102 +2942,10 @@ var gMainPane = {
|
||||
chooseFolder.disabled =
|
||||
!useDownloadDirPreference.value || dirPreference.locked;
|
||||
|
||||
this.readCloudStorage().catch(Cu.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");
|
||||
document.l10n.setAttributes(
|
||||
saveToCloudRadio,
|
||||
"save-files-to-cloud-storage",
|
||||
{
|
||||
"service-name": providerDisplayName,
|
||||
}
|
||||
);
|
||||
saveToCloudRadio.hidden = false;
|
||||
|
||||
let useDownloadDirPref = Preferences.get(
|
||||
"browser.download.useDownloadDir"
|
||||
);
|
||||
let folderListPref = Preferences.get("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(Cu.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 = Preferences.get(
|
||||
"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. If currentDirPref is unspecified folderList should
|
||||
// default to 1
|
||||
let folderListPref = Preferences.get("browser.download.folderList");
|
||||
let saveTo = document.getElementById("saveTo");
|
||||
if (saveWhere.selectedItem == saveToCloudRadio) {
|
||||
folderListPref.value = 3;
|
||||
} else if (saveWhere.selectedItem == saveTo) {
|
||||
let currentDirPref = Preferences.get("browser.download.dir");
|
||||
folderListPref.value = currentDirPref.value
|
||||
? await this._folderToIndex(currentDirPref.value)
|
||||
: 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays a file picker in which the user can choose the location where
|
||||
* downloads are automatically saved, updating preferences and UI in
|
||||
@ -3120,11 +3025,9 @@ var gMainPane = {
|
||||
var iconUrlSpec;
|
||||
|
||||
let folderIndex = folderListPref.value;
|
||||
// For legacy users using cloudstorage pref with folderIndex as 3 (See bug 1751093),
|
||||
// compute folderIndex using value in currentDirPref
|
||||
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
|
||||
// Set folderIndex to 1 if currentDirPref is unspecified
|
||||
folderIndex = currentDirPref.value
|
||||
? await this._folderToIndex(currentDirPref.value)
|
||||
: 1;
|
||||
|
@ -62,7 +62,6 @@ XPCOMUtils.defineLazyServiceGetters(this, {
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AMTelemetry: "resource://gre/modules/AddonManager.jsm",
|
||||
BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
|
||||
CloudStorage: "resource://gre/modules/CloudStorage.jsm",
|
||||
ContextualIdentityService:
|
||||
"resource://gre/modules/ContextualIdentityService.jsm",
|
||||
DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
|
||||
|
@ -58,7 +58,6 @@ support-files =
|
||||
[browser_change_app_handler.js]
|
||||
skip-if = os != "win" # Windows-specific handler application selection dialog
|
||||
[browser_checkspelling.js]
|
||||
[browser_cloud_storage.js]
|
||||
[browser_connection.js]
|
||||
[browser_connection_bug388287.js]
|
||||
[browser_connection_bug1445991.js]
|
||||
|
@ -1,159 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"CloudStorage",
|
||||
"resource://gre/modules/CloudStorage.jsm"
|
||||
);
|
||||
|
||||
const DROPBOX_DOWNLOAD_FOLDER = "Dropbox";
|
||||
const CLOUD_SERVICES_PREF = "cloud.services.";
|
||||
|
||||
function create_subdir(dir, subdirname) {
|
||||
let subdir = dir.clone();
|
||||
subdir.append(subdirname);
|
||||
if (subdir.exists()) {
|
||||
subdir.remove(true);
|
||||
}
|
||||
subdir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
|
||||
return subdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a directory service entry with a given nsIFile.
|
||||
*/
|
||||
function registerFakePath(key, folderName) {
|
||||
let dirsvc = Services.dirsvc.QueryInterface(Ci.nsIProperties);
|
||||
|
||||
// Create a directory inside the profile and register it as key
|
||||
let profD = dirsvc.get("ProfD", Ci.nsIFile);
|
||||
// create a subdir just to keep our files out of the way
|
||||
let file = create_subdir(profD, folderName);
|
||||
|
||||
let originalFile;
|
||||
try {
|
||||
// If a file is already provided save it and undefine, otherwise set will
|
||||
// throw for persistent entries (ones that are cached).
|
||||
originalFile = dirsvc.get(key, Ci.nsIFile);
|
||||
dirsvc.undefine(key);
|
||||
} catch (e) {
|
||||
// dirsvc.get will throw if nothing provides for the key and dirsvc.undefine
|
||||
// will throw if it's not a persistent entry, in either case we don't want
|
||||
// to set the original file in cleanup.
|
||||
originalFile = undefined;
|
||||
}
|
||||
|
||||
dirsvc.set(key, file);
|
||||
registerCleanupFunction(() => {
|
||||
dirsvc.undefine(key);
|
||||
if (originalFile) {
|
||||
dirsvc.set(key, originalFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function mock_dropbox() {
|
||||
// Mock Dropbox Download folder in Home directory
|
||||
let downloadFolder = PathUtils.join(
|
||||
Services.dirsvc.get("Home", Ci.nsIFile).path,
|
||||
DROPBOX_DOWNLOAD_FOLDER,
|
||||
"Downloads"
|
||||
);
|
||||
|
||||
await IOUtils.makeDirectory(downloadFolder);
|
||||
console.log(downloadFolder);
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
await IOUtils.remove(downloadFolder, {
|
||||
recursive: true,
|
||||
ignoreAbsent: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add_setup(async function() {
|
||||
// Create mock Dropbox download folder for cloudstorage API
|
||||
// Set prefs required to display second radio option
|
||||
// 'Save to Dropbox' under Downloads
|
||||
let folderName = "CloudStorage";
|
||||
registerFakePath("Home", folderName);
|
||||
await mock_dropbox();
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[CLOUD_SERVICES_PREF + "api.enabled", true],
|
||||
[CLOUD_SERVICES_PREF + "storage.key", "Dropbox"],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_initProvider() {
|
||||
// Get preferred provider key
|
||||
let preferredProvider = await CloudStorage.getPreferredProvider();
|
||||
is(preferredProvider, "Dropbox", "Cloud Storage preferred provider key");
|
||||
|
||||
let isInitialized = await CloudStorage.init();
|
||||
is(isInitialized, true, "Providers Metadata successfully initialized");
|
||||
|
||||
// Get preferred provider in use display name
|
||||
let providerDisplayName = await CloudStorage.getProviderIfInUse();
|
||||
is(
|
||||
providerDisplayName,
|
||||
"Dropbox",
|
||||
"Cloud Storage preferred provider display name"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function() {
|
||||
await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true });
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let saveWhereOptions = doc.getElementById("saveWhere");
|
||||
let saveToCloud = doc.getElementById("saveToCloud");
|
||||
is(saveWhereOptions.itemCount, 3, "Radio options count");
|
||||
is_element_visible(saveToCloud, "Save to Dropbox option is visible");
|
||||
|
||||
let saveTo = doc.getElementById("saveTo");
|
||||
ok(saveTo.selected, "Ensure first option is selected by default");
|
||||
is(
|
||||
Services.prefs.getIntPref("browser.download.folderList"),
|
||||
1,
|
||||
"Set to system downloadsfolder as the default download location"
|
||||
);
|
||||
|
||||
let downloadFolder = doc.getElementById("downloadFolder");
|
||||
let chooseFolder = doc.getElementById("chooseFolder");
|
||||
is(downloadFolder.disabled, false, "downloadFolder filefield is enabled");
|
||||
is(chooseFolder.disabled, false, "chooseFolder button is enabled");
|
||||
|
||||
// Test click of second radio option sets browser.download.folderList as 3
|
||||
// which means the default download location is elsewhere as specified by
|
||||
// cloud storage API getDownloadFolder and pref cloud.services.storage.key
|
||||
saveToCloud.click();
|
||||
is(
|
||||
Services.prefs.getIntPref("browser.download.folderList"),
|
||||
3,
|
||||
"Default download location is elsewhere as specified by cloud storage API"
|
||||
);
|
||||
is(downloadFolder.disabled, true, "downloadFolder filefield is disabled");
|
||||
is(chooseFolder.disabled, true, "chooseFolder button is disabled");
|
||||
|
||||
// Test selecting first radio option enables downloadFolder filefield and chooseFolder button
|
||||
saveTo.click();
|
||||
is(downloadFolder.disabled, false, "downloadFolder filefield is enabled");
|
||||
is(chooseFolder.disabled, false, "chooseFolder button is enabled");
|
||||
|
||||
// Test selecting third radio option keeps downloadFolder and chooseFolder elements disabled
|
||||
let alwaysAsk = doc.getElementById("alwaysAsk");
|
||||
saveToCloud.click();
|
||||
alwaysAsk.click();
|
||||
is(downloadFolder.disabled, true, "downloadFolder filefield is disabled");
|
||||
is(chooseFolder.disabled, true, "chooseFolder button is disabled");
|
||||
saveTo.click();
|
||||
ok(saveTo.selected, "Reset back first option as selected by default");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
@ -1404,8 +1404,3 @@ httpsonly-radio-disabled =
|
||||
desktop-folder-name = Desktop
|
||||
downloads-folder-name = Downloads
|
||||
choose-download-folder-title = Choose Download Folder:
|
||||
|
||||
# Variables:
|
||||
# $service-name (String) - Name of a cloud storage provider like Dropbox, Google Drive, etc...
|
||||
save-files-to-cloud-storage =
|
||||
.label = Save files to { $service-name }
|
||||
|
@ -2111,7 +2111,6 @@
|
||||
"toolkit/components/captivedetect/test/unit/xpcshell.ini": 14.38,
|
||||
"toolkit/components/cascade_bloom_filter/test/xpcshell/xpcshell.ini": 1.22,
|
||||
"toolkit/components/cleardata/tests/unit/xpcshell.ini": 17.69,
|
||||
"toolkit/components/cloudstorage/tests/unit/xpcshell.ini": 2.12,
|
||||
"toolkit/components/commandlines/test/unit/xpcshell.ini": 2.35,
|
||||
"toolkit/components/commandlines/test/unit_unix/xpcshell.ini": 1.0,
|
||||
"toolkit/components/contentprefs/tests/unit_cps2/xpcshell.ini": 38.97,
|
||||
|
@ -2106,7 +2106,6 @@
|
||||
"toolkit/components/captivedetect/test/unit/xpcshell.ini": 6.31,
|
||||
"toolkit/components/cascade_bloom_filter/test/xpcshell/xpcshell.ini": 0.44,
|
||||
"toolkit/components/cleardata/tests/unit/xpcshell.ini": 7.83,
|
||||
"toolkit/components/cloudstorage/tests/unit/xpcshell.ini": 11.11,
|
||||
"toolkit/components/commandlines/test/unit/xpcshell.ini": 1.35,
|
||||
"toolkit/components/commandlines/test/unit_win/xpcshell.ini": 0.49,
|
||||
"toolkit/components/contentprefs/tests/unit_cps2/xpcshell.ini": 19.36,
|
||||
|
@ -1,769 +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/. */
|
||||
|
||||
/**
|
||||
* Java Script module that helps consumers store data directly
|
||||
* to cloud storage provider download folders.
|
||||
*
|
||||
* Takes cloud storage providers metadata as JSON input on Mac, Linux and Windows.
|
||||
*
|
||||
* Handles scan, prompt response save and exposes preferred storage provider.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["CloudStorage"];
|
||||
const { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"Downloads",
|
||||
"resource://gre/modules/Downloads.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
const CLOUD_SERVICES_PREF = "cloud.services.";
|
||||
const CLOUD_PROVIDERS_URI = "resource://cloudstorage/providers.json";
|
||||
|
||||
/**
|
||||
* Provider metadata JSON is loaded from resource://cloudstorage/providers.json
|
||||
* Sample providers.json format
|
||||
*
|
||||
* {
|
||||
* "Dropbox": {
|
||||
* "displayName": "Dropbox",
|
||||
* "relativeDownloadPath": ["homeDir", "Dropbox"],
|
||||
* "relativeDiscoveryPath": {
|
||||
* "linux": ["homeDir", ".dropbox", "info.json"],
|
||||
* "macosx": ["homeDir", ".dropbox", "info.json"],
|
||||
* "win": ["LocalAppData", "Dropbox", "info.json"]
|
||||
* },
|
||||
* "typeSpecificData": {
|
||||
* "default": "Downloads",
|
||||
* "screenshot": "Screenshots"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Providers JSON is flat list of providers metdata with property as key in format @Provider
|
||||
*
|
||||
* @Provider - Unique cloud provider key, possible values: "Dropbox", "GDrive"
|
||||
*
|
||||
* @displayName - cloud storage name displayed in the prompt.
|
||||
*
|
||||
* @relativeDownloadPath - download path on user desktop for a cloud storage provider.
|
||||
* By default downloadPath is a concatenation of home dir and name of dropbox folder.
|
||||
* Example value: ["homeDir", "Dropbox"]
|
||||
*
|
||||
* @relativeDiscoveryPath - Lists discoveryPath by platform. Provider is not supported on a platform
|
||||
* if its value doesn't exist in relativeDiscoveryPath. relativeDiscoveryPath by platform is stored
|
||||
* as an array ofsubdirectories, which when concatenated, forms discovery path.
|
||||
* During scan discoveryPath is checked for the existence of cloud storage provider on user desktop.
|
||||
*
|
||||
* @typeSpecificData - provides folder name for a cloud storage depending
|
||||
* 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
|
||||
*
|
||||
* browser.download.folderList - set to int and indicates the location users wish to save downloaded files to.
|
||||
* 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.dir - local file handle
|
||||
* A local folder user may have selected for downloaded files to be
|
||||
* saved. This folder is enabled when folderList equals 2.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The external API exported by this module.
|
||||
*/
|
||||
|
||||
var CloudStorage = {
|
||||
/**
|
||||
* Init method to initialize providers metadata
|
||||
*/
|
||||
async init() {
|
||||
let isInitialized = null;
|
||||
try {
|
||||
// Invoke internal method asynchronously to read and
|
||||
// parse providers metadata from JSON
|
||||
isInitialized = await CloudStorageInternal.initProviders();
|
||||
} catch (err) {
|
||||
Cu.reportError(err);
|
||||
}
|
||||
return isInitialized;
|
||||
},
|
||||
|
||||
/**
|
||||
* A promise to wait for init to complete, used for tests.
|
||||
*/
|
||||
get promiseInit() {
|
||||
return CloudStorageInternal.promiseInit;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns information to allow the consumer to decide whether showing
|
||||
* a doorhanger prompt is appropriate. If a preferred provider is set
|
||||
* on desktop, user is not prompted again and method returns null.
|
||||
*
|
||||
* @return {Promise} which resolves to an object with property name
|
||||
* as 'key' and 'value'.
|
||||
* 'key' property is provider key such as 'Dropbox', 'GDrive'.
|
||||
* 'value' property contains metadata for respective provider.
|
||||
* Resolves null if it's not appropriate to prompt.
|
||||
*/
|
||||
promisePromptInfo() {
|
||||
return CloudStorageInternal.promisePromptInfo();
|
||||
},
|
||||
|
||||
/**
|
||||
* Save user response from doorhanger prompt.
|
||||
* If user confirms and checks 'always remember', update prefs
|
||||
* cloud.services.storage.key and browser.download.folderList to pick
|
||||
* download location from cloud storage API
|
||||
* If user denies, save provider as rejected in cloud.services.rejected.key
|
||||
*
|
||||
* @param key
|
||||
* cloud storage provider key from provider metadata
|
||||
* @param remember
|
||||
* bool value indicating whether user has asked to always remember
|
||||
* the settings
|
||||
* @param selected
|
||||
* bool value by default set to false indicating if user has selected
|
||||
* to save downloaded file with cloud provider
|
||||
*/
|
||||
savePromptResponse(key, remember, selected = false) {
|
||||
Services.prefs.setIntPref(
|
||||
CLOUD_SERVICES_PREF + "lastprompt",
|
||||
Math.floor(Date.now() / 1000)
|
||||
);
|
||||
if (remember) {
|
||||
if (selected) {
|
||||
CloudStorageInternal.setCloudStoragePref(key);
|
||||
} else {
|
||||
// Store provider as rejected by setting cloud.services.rejected.key
|
||||
// and not use in re-prompt
|
||||
CloudStorageInternal.handleRejected(key);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve download folder of an opted-in storage provider
|
||||
* by type specific data
|
||||
* @param typeSpecificData
|
||||
* 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 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() {
|
||||
return CloudStorageInternal.getStorageProviders();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* The internal API for the CloudStorage module.
|
||||
*/
|
||||
|
||||
var CloudStorageInternal = {
|
||||
/**
|
||||
* promiseInit saves returned init method promise and is
|
||||
* used to wait for initialization to complete.
|
||||
*/
|
||||
promiseInit: null,
|
||||
|
||||
/**
|
||||
* Internal property having storage providers data
|
||||
*/
|
||||
providersMetaData: null,
|
||||
|
||||
async _downloadJSON(uri) {
|
||||
let json = null;
|
||||
try {
|
||||
let response = await fetch(uri);
|
||||
if (response.ok) {
|
||||
json = await response.json();
|
||||
}
|
||||
} 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 = null;
|
||||
try {
|
||||
let file = Services.prefs.getComplexValue(
|
||||
"browser.download.dir",
|
||||
Ci.nsIFile
|
||||
);
|
||||
downloadDirPath = file.path;
|
||||
} catch (e) {}
|
||||
|
||||
if (
|
||||
!downloadDirPath ||
|
||||
downloadDirPath === (await Downloads.getSystemDownloadsDirectory())
|
||||
) {
|
||||
// if downloadDirPath is the Downloads folder path or unspecified
|
||||
folderListValue = 1;
|
||||
} else if (
|
||||
downloadDirPath === Services.dirsvc.get("Desk", Ci.nsIFile).path
|
||||
) {
|
||||
// if downloadDirPath is the Desktop path
|
||||
folderListValue = 0;
|
||||
} 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) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load parsed metadata inside providers object
|
||||
*/
|
||||
_parseProvidersJSON(providers) {
|
||||
if (!providers) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Use relativeDiscoveryPath to filter providers object by platform.
|
||||
// DownloadPath and discoveryPath are stored as
|
||||
// array of subdirectories inside providers.json
|
||||
// Update providers object discoveryPath and downloadPath
|
||||
// property values by concatenating subdirectories and forming platform
|
||||
// specific directory path
|
||||
|
||||
Object.getOwnPropertyNames(providers).forEach(key => {
|
||||
if (
|
||||
providers[key].relativeDiscoveryPath.hasOwnProperty(
|
||||
AppConstants.platform
|
||||
)
|
||||
) {
|
||||
providers[key].discoveryPath = this._concatPath(
|
||||
providers[key].relativeDiscoveryPath[AppConstants.platform]
|
||||
);
|
||||
providers[key].downloadPath = this._concatPath(
|
||||
providers[key].relativeDownloadPath
|
||||
);
|
||||
} else {
|
||||
// delete key not supported on AppConstants.platform
|
||||
delete providers[key];
|
||||
}
|
||||
});
|
||||
return providers;
|
||||
},
|
||||
|
||||
/**
|
||||
* Concatenate subdir value inside array to form
|
||||
* platform specific directory path
|
||||
*
|
||||
* @param arrDirs
|
||||
* String Array containing sub directories name
|
||||
* @returns Path of type String
|
||||
*/
|
||||
_concatPath(arrDirs) {
|
||||
let dirPath = "";
|
||||
for (let subDir of arrDirs) {
|
||||
switch (subDir) {
|
||||
case "homeDir":
|
||||
subDir = OS.Constants.Path.homeDir ? OS.Constants.Path.homeDir : "";
|
||||
break;
|
||||
case "LocalAppData":
|
||||
if (OS.Constants.Win) {
|
||||
let nsIFileLocal = Services.dirsvc.get("LocalAppData", Ci.nsIFile);
|
||||
subDir = nsIFileLocal && nsIFileLocal.path ? nsIFileLocal.path : "";
|
||||
} else {
|
||||
subDir = "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
dirPath = OS.Path.join(dirPath, subDir);
|
||||
}
|
||||
return dirPath;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check for custom download paths and override providers metadata
|
||||
* downloadPath property
|
||||
*
|
||||
* For dropbox open config file ~/.dropbox/info.json
|
||||
* and override downloadPath with path found
|
||||
* See https://www.dropbox.com/en/help/desktop-web/find-folder-paths
|
||||
*
|
||||
* For all other providers we are using downloadpath from providers.json
|
||||
*
|
||||
* @returns {Promise} with array boolean values for respective provider. Value is true if a
|
||||
* provider exist on user desktop and its downloadPath is updated. Promise returns with
|
||||
* resolved array value when all providers in metadata are handled.
|
||||
*/
|
||||
initDownloadPathIfProvidersExist() {
|
||||
let providerKeys = Object.keys(this.providersMetaData);
|
||||
let promises = providerKeys.map(key => {
|
||||
return key === "Dropbox"
|
||||
? this._initDropbox(key)
|
||||
: Promise.resolve(false);
|
||||
});
|
||||
return Promise.all(promises);
|
||||
},
|
||||
|
||||
/**
|
||||
* Read Dropbox info.json and override providers metadata
|
||||
* downloadPath property
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves
|
||||
* false if dropbox provider is not found. Returns true if dropbox service exist
|
||||
* on user desktop and downloadPath in providermetadata is updated with
|
||||
* value read from config file info.json
|
||||
*/
|
||||
async _initDropbox(key) {
|
||||
// Check if Dropbox provider exist on desktop before continuing
|
||||
if (
|
||||
!(await this._checkIfAssetExists(
|
||||
this.providersMetaData[key].discoveryPath
|
||||
))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check in cloud.services.rejected.key if Dropbox is previously rejected before continuing
|
||||
let rejectedKeys = this.cloudStorageRejectedKeys.split(",");
|
||||
if (rejectedKeys.includes(key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let file = null;
|
||||
try {
|
||||
file = new FileUtils.File(this.providersMetaData[key].discoveryPath);
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let data = await this._downloadJSON(Services.io.newFileURI(file).spec);
|
||||
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let path = data && data.personal && data.personal.path;
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
let isUsable = await this._isUsableDirectory(path);
|
||||
if (isUsable) {
|
||||
this.providersMetaData.Dropbox.downloadPath = path;
|
||||
}
|
||||
return isUsable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if a given directory is valid and can be used to download files
|
||||
*
|
||||
* @param full absolute path to the directory
|
||||
*
|
||||
* @return {Promise} which resolves true if we can use the directory, false otherwise.
|
||||
*/
|
||||
async _isUsableDirectory(path) {
|
||||
let isUsable = false;
|
||||
try {
|
||||
let info = await OS.File.stat(path);
|
||||
isUsable = info.isDir;
|
||||
} catch (e) {
|
||||
// Directory doesn't exist, so isUsable will still be false
|
||||
}
|
||||
return isUsable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve download folder of preferred provider by type specific data
|
||||
*
|
||||
* @param dataType
|
||||
* type of data downloaded, options are 'default', 'screenshot'
|
||||
* default value is 'default'
|
||||
* @return {Promise} which resolves to full path to download folder
|
||||
* Resolves null if a valid download folder is not found.
|
||||
*/
|
||||
async getDownloadFolder(dataType = "default") {
|
||||
// Wait for cloudstorage to initialize if providers metadata is not available
|
||||
if (!this.providersMetaData) {
|
||||
let isInitialized = await this.promiseInit;
|
||||
if (!isInitialized && !this.providersMetaData) {
|
||||
Cu.reportError(
|
||||
"CloudStorage: Failed to initialize and retrieve download folder "
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
let key = this.preferredProviderKey;
|
||||
if (!key || !this.providersMetaData.hasOwnProperty(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let provider = this.providersMetaData[key];
|
||||
if (!provider.typeSpecificData[dataType]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let downloadDirPath = OS.Path.join(
|
||||
provider.downloadPath,
|
||||
provider.typeSpecificData[dataType]
|
||||
);
|
||||
if (!(await this._isUsableDirectory(downloadDirPath))) {
|
||||
return null;
|
||||
}
|
||||
return downloadDirPath;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return scanned provider info used by consumer inside doorhanger prompt.
|
||||
* @return {Promise}
|
||||
* which resolves to an object with property 'key' as found provider and
|
||||
* property 'value' as provider metadata.
|
||||
* Resolves null if no provider info is returned.
|
||||
*/
|
||||
async promisePromptInfo() {
|
||||
// Check if user has not previously opted-in for preferred provider download folder
|
||||
// and if time elapsed since last prompt shown has exceeded maximum allowed interval
|
||||
// in pref cloud.services.interval.prompt before continuing to scan for providers
|
||||
if (!this.preferredProviderKey && this.shouldPrompt()) {
|
||||
return this.scan();
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if its time to prompt by reading lastprompt service pref.
|
||||
* Return true if pref doesn't exist or last prompt time is
|
||||
* more than prompt interval
|
||||
*/
|
||||
shouldPrompt() {
|
||||
let lastPrompt = this.lastPromptTime;
|
||||
let now = Math.floor(Date.now() / 1000);
|
||||
let interval = now - lastPrompt;
|
||||
|
||||
// Convert prompt interval to seconds
|
||||
let maxAllow = this.promptInterval * 24 * 60 * 60;
|
||||
return interval >= maxAllow;
|
||||
},
|
||||
|
||||
/**
|
||||
* Scans for local storage providers available on user desktop
|
||||
*
|
||||
* providers list is read in order as specified in providers.json.
|
||||
* If a user has multiple cloud storage providers on desktop, return the first
|
||||
* provider after filtering the rejected keys
|
||||
*
|
||||
* @return {Promise}
|
||||
* which resolves to an object providerInfo with found provider key and value
|
||||
* as provider metadata. Resolves null if no valid provider found
|
||||
*/
|
||||
async scan() {
|
||||
let providers = await this.getStorageProviders();
|
||||
if (!providers.size) {
|
||||
// No storage services installed on user desktop
|
||||
return null;
|
||||
}
|
||||
|
||||
// Filter the rejected providers in cloud.services.rejected.key
|
||||
// from the providers map object
|
||||
let rejectedKeys = this.cloudStorageRejectedKeys.split(",");
|
||||
for (let rejectedKey of rejectedKeys) {
|
||||
providers.delete(rejectedKey);
|
||||
}
|
||||
|
||||
// Pick first storage provider from providers
|
||||
let provider = providers.entries().next().value;
|
||||
if (provider) {
|
||||
return { key: provider[0], value: provider[1] };
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the asset with input path exist on
|
||||
* file system
|
||||
* @return {Promise}
|
||||
* @resolves
|
||||
* boolean value of file existence check
|
||||
*/
|
||||
_checkIfAssetExists(path) {
|
||||
return OS.File.exists(path).catch(err => {
|
||||
Cu.reportError(`Couldn't check existance of ${path}`, err);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* get access to all local storage providers available on user desktop
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves
|
||||
* Map object with entries key set to storage provider key and values set to
|
||||
* storage provider metadata
|
||||
*/
|
||||
async getStorageProviders() {
|
||||
let providers = Object.entries(this.providersMetaData || {});
|
||||
|
||||
// Array of promises with boolean value exist for respective storage.
|
||||
let promises = providers.map(([, provider]) =>
|
||||
this._checkIfAssetExists(provider.discoveryPath)
|
||||
);
|
||||
let results = await Promise.all(promises);
|
||||
|
||||
// Filter providers array to remove provider with discoveryPath asset exist resolved value false
|
||||
providers = providers.filter((_, idx) => results[idx]);
|
||||
return new Map(providers);
|
||||
},
|
||||
|
||||
/**
|
||||
* Save the rejected provider in cloud.services.rejected.key. Pref
|
||||
* stores rejected keys value as comma separated string.
|
||||
*
|
||||
* @param key
|
||||
* Provider key to be saved in cloud.services.rejected.key pref
|
||||
*/
|
||||
handleRejected(key) {
|
||||
let rejected = this.cloudStorageRejectedKeys;
|
||||
|
||||
if (!rejected) {
|
||||
Services.prefs.setCharPref(CLOUD_SERVICES_PREF + "rejected.key", key);
|
||||
} else {
|
||||
// Pref exists with previous rejected keys, append
|
||||
// key at the end and update pref
|
||||
let keys = rejected.split(",");
|
||||
if (key) {
|
||||
keys.push(key);
|
||||
}
|
||||
Services.prefs.setCharPref(
|
||||
CLOUD_SERVICES_PREF + "rejected.key",
|
||||
keys.join(",")
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets pref cloud.services.storage.key. It updates download browser.download.folderList
|
||||
* value to 3 indicating download location is stored elsewhere, as specified by
|
||||
* cloud storage API getDownloadFolder
|
||||
*
|
||||
* @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",
|
||||
""
|
||||
);
|
||||
|
||||
/**
|
||||
* Provider keys rejected by user for default download
|
||||
*/
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
CloudStorageInternal,
|
||||
"cloudStorageRejectedKeys",
|
||||
CLOUD_SERVICES_PREF + "rejected.key",
|
||||
""
|
||||
);
|
||||
|
||||
/**
|
||||
* Lastprompt time in seconds, by default set to 0
|
||||
*/
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
CloudStorageInternal,
|
||||
"lastPromptTime",
|
||||
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();
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"Dropbox": {
|
||||
"displayName": "Dropbox",
|
||||
"relativeDownloadPath": ["homeDir", "Dropbox"],
|
||||
"relativeDiscoveryPath": {
|
||||
"linux": ["homeDir", ".dropbox", "info.json"],
|
||||
"macosx": ["homeDir", ".dropbox", "info.json"],
|
||||
"win": ["LocalAppData", "Dropbox", "info.json"]
|
||||
},
|
||||
"typeSpecificData": {
|
||||
"default": "Downloads",
|
||||
"screenshot": "Screenshots"
|
||||
}
|
||||
},
|
||||
|
||||
"GDrive": {
|
||||
"displayName": "Google Drive",
|
||||
"relativeDownloadPath": ["homeDir", "Google Drive"],
|
||||
"relativeDiscoveryPath": {
|
||||
"macosx": ["homeDir", "Library", "Application Support", "Google", "Drive"],
|
||||
"win": ["LocalAppData", "Google", "Drive"]
|
||||
},
|
||||
"typeSpecificData": {
|
||||
"default": "Downloads"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +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/.
|
||||
|
||||
toolkit.jar:
|
||||
% resource cloudstorage %content/
|
||||
content/ (content/*)
|
@ -1,18 +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/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
"tests/unit/xpcshell.ini",
|
||||
]
|
||||
|
||||
JAR_MANIFESTS += ["jar.mn"]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
"CloudStorage.jsm",
|
||||
]
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Toolkit", "General")
|
@ -1 +0,0 @@
|
||||
{"personal": {"path": "Test"}}
|
@ -1,321 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
// Globals
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"CloudStorage",
|
||||
"resource://gre/modules/CloudStorage.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm"
|
||||
);
|
||||
|
||||
const CLOUD_SERVICES_PREF = "cloud.services.";
|
||||
const DROPBOX_DOWNLOAD_FOLDER = "Dropbox";
|
||||
const GOOGLE_DRIVE_DOWNLOAD_FOLDER = "Google Drive";
|
||||
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/"));
|
||||
registerCleanupFunction(() => {
|
||||
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
|
||||
// throw for persistent entries (ones that are cached).
|
||||
originalFile = dirsvc.get(key, Ci.nsIFile);
|
||||
dirsvc.undefine(key);
|
||||
} catch (e) {
|
||||
// dirsvc.get will throw if nothing provides for the key and dirsvc.undefine
|
||||
// will throw if it's not a persistent entry, in either case we don't want
|
||||
// to set the original file in cleanup.
|
||||
originalFile = undefined;
|
||||
}
|
||||
|
||||
dirsvc.set(key, file);
|
||||
registerCleanupFunction(() => {
|
||||
dirsvc.undefine(key);
|
||||
if (originalFile) {
|
||||
dirsvc.set(key, originalFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function mock_dropbox() {
|
||||
let discoveryFolder = null;
|
||||
if (AppConstants.platform === "win") {
|
||||
discoveryFolder = FileUtils.getFile("LocalAppData", [
|
||||
DROPBOX_CONFIG_FOLDER,
|
||||
]);
|
||||
} else {
|
||||
discoveryFolder = FileUtils.getFile("Home", [DROPBOX_CONFIG_FOLDER]);
|
||||
}
|
||||
discoveryFolder.append("info.json");
|
||||
let fileDir = discoveryFolder.parent;
|
||||
if (!fileDir.exists()) {
|
||||
fileDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
}
|
||||
do_get_file("cloud/info.json").copyTo(fileDir, "info.json");
|
||||
let exist = fileDir.exists();
|
||||
Assert.ok(exist, "file exists on desktop");
|
||||
|
||||
// Mock Dropbox Download folder in Home directory
|
||||
let downloadFolder = FileUtils.getFile("Home", [
|
||||
DROPBOX_DOWNLOAD_FOLDER,
|
||||
"Downloads",
|
||||
]);
|
||||
if (!downloadFolder.exists()) {
|
||||
downloadFolder.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
}
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
if (discoveryFolder.exists()) {
|
||||
discoveryFolder.remove(false);
|
||||
}
|
||||
if (downloadFolder.exists()) {
|
||||
downloadFolder.remove(false);
|
||||
}
|
||||
});
|
||||
|
||||
return discoveryFolder;
|
||||
}
|
||||
|
||||
function mock_gdrive() {
|
||||
let discoveryFolder = null;
|
||||
if (AppConstants.platform === "win") {
|
||||
discoveryFolder = FileUtils.getFile("LocalAppData", ["Google", "Drive"]);
|
||||
} else {
|
||||
discoveryFolder = FileUtils.getFile("Home", [
|
||||
"Library",
|
||||
"Application Support",
|
||||
"Google",
|
||||
"Drive",
|
||||
]);
|
||||
}
|
||||
if (!discoveryFolder.exists()) {
|
||||
discoveryFolder.createUnique(
|
||||
Ci.nsIFile.DIRECTORY_TYPE,
|
||||
FileUtils.PERMS_DIRECTORY
|
||||
);
|
||||
}
|
||||
let exist = discoveryFolder.exists();
|
||||
Assert.ok(exist, "file exists on desktop");
|
||||
|
||||
// Mock Google Drive Download folder in Home directory
|
||||
let downloadFolder = FileUtils.getFile("Home", [
|
||||
GOOGLE_DRIVE_DOWNLOAD_FOLDER,
|
||||
"Downloads",
|
||||
]);
|
||||
if (!downloadFolder.exists()) {
|
||||
downloadFolder.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
}
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
if (discoveryFolder.exists()) {
|
||||
discoveryFolder.remove(false);
|
||||
}
|
||||
if (downloadFolder.exists()) {
|
||||
downloadFolder.remove(false);
|
||||
}
|
||||
});
|
||||
|
||||
return discoveryFolder;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
function promisePromptInfo() {
|
||||
return CloudStorage.promisePromptInfo();
|
||||
}
|
||||
|
||||
async function checkScan(expectedKey) {
|
||||
let metadata = await promiseGetStorageProviders();
|
||||
let scanProvider = await promisePromptInfo();
|
||||
|
||||
if (!expectedKey) {
|
||||
Assert.equal(metadata.size, 0, "Number of storage providers");
|
||||
Assert.ok(!scanProvider, "No provider in scan results");
|
||||
} else {
|
||||
Assert.ok(metadata.size, "Number of storage providers");
|
||||
Assert.equal(
|
||||
scanProvider.key,
|
||||
expectedKey,
|
||||
"Scanned provider key returned"
|
||||
);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
async function checkSavedPromptResponse(
|
||||
aKey,
|
||||
metadata,
|
||||
remember,
|
||||
selected = false
|
||||
) {
|
||||
CloudStorage.savePromptResponse(aKey, remember, selected);
|
||||
|
||||
if (remember && selected) {
|
||||
// Save prompt response with option to always remember the setting
|
||||
// and provider with aKey selected as cloud storage provider
|
||||
// Sets user download settings to always save to cloud
|
||||
|
||||
// Check preferred provider key, should be set to dropbox
|
||||
let prefProvider = CloudStorage.getPreferredProvider();
|
||||
Assert.equal(
|
||||
prefProvider,
|
||||
aKey,
|
||||
"Saved Response preferred provider key returned"
|
||||
);
|
||||
// Check browser.download.folderlist pref should be set to 3
|
||||
Assert.equal(
|
||||
Services.prefs.getIntPref("browser.download.folderList"),
|
||||
3,
|
||||
"Default download location set to 3"
|
||||
);
|
||||
|
||||
// Preferred download folder should be set to provider downloadPath from metadata
|
||||
let path = await CloudStorage.getDownloadFolder();
|
||||
let nsIDownloadFolder = new FileUtils.File(path);
|
||||
Assert.ok(nsIDownloadFolder, "Download folder retrieved");
|
||||
Assert.equal(
|
||||
nsIDownloadFolder.parent.path,
|
||||
metadata.get(aKey).downloadPath,
|
||||
"Default download Folder Path"
|
||||
);
|
||||
} else if (remember && !selected) {
|
||||
// Save prompt response with option to always remember the setting
|
||||
// and provider with aKey rejected as cloud storage provider
|
||||
// Sets cloud.services.rejected.key pref with provider key.
|
||||
// Provider is ignored in next scan and never re-prompted again
|
||||
|
||||
let scanResult = await promisePromptInfo();
|
||||
if (scanResult) {
|
||||
Assert.notEqual(
|
||||
scanResult.key,
|
||||
DROPBOX_KEY,
|
||||
"Scanned provider key returned is not Dropbox"
|
||||
);
|
||||
} else {
|
||||
Assert.ok(!scanResult, "No provider in scan results");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function test_checkInit() {
|
||||
let isInitialized = await CloudStorage.promiseInit;
|
||||
Assert.ok(isInitialized, "Providers Metadata successfully initialized");
|
||||
});
|
||||
|
||||
add_task(async function test_noStorageProvider() {
|
||||
await checkScan();
|
||||
cleanupPrefs();
|
||||
});
|
||||
|
||||
/**
|
||||
* Check scan and save prompt response flow if only dropbox exists on desktop.
|
||||
*/
|
||||
add_task(async function test_dropboxStorageProvider() {
|
||||
nsIDropboxFile = mock_dropbox();
|
||||
let result = await checkScan(DROPBOX_KEY);
|
||||
|
||||
// Always save to cloud
|
||||
await checkSavedPromptResponse(DROPBOX_KEY, result, true, true);
|
||||
cleanupPrefs();
|
||||
|
||||
// Reject dropbox as cloud storage provider and never re-prompt again
|
||||
await checkSavedPromptResponse(DROPBOX_KEY, result, true);
|
||||
|
||||
// Uninstall dropbox by removing discovery folder
|
||||
nsIDropboxFile.remove(false);
|
||||
cleanupPrefs();
|
||||
});
|
||||
|
||||
/**
|
||||
* Check scan and save prompt response flow if only gdrive exists on desktop.
|
||||
*/
|
||||
add_task(async function test_gDriveStorageProvider() {
|
||||
nsIGDriveFile = mock_gdrive();
|
||||
let result;
|
||||
if (AppConstants.platform === "linux") {
|
||||
result = await checkScan();
|
||||
} else {
|
||||
result = await checkScan(GDRIVE_KEY);
|
||||
}
|
||||
|
||||
if (result.size || AppConstants.platform !== "linux") {
|
||||
// Always save to cloud
|
||||
await checkSavedPromptResponse(GDRIVE_KEY, result, true, true);
|
||||
cleanupPrefs();
|
||||
|
||||
// Reject Google Drive as cloud storage provider and never re-prompt again
|
||||
await checkSavedPromptResponse(GDRIVE_KEY, result, true);
|
||||
}
|
||||
// Uninstall gDrive by removing discovery folder /Home/Library/Application Support/Google/Drive
|
||||
nsIGDriveFile.remove(false);
|
||||
cleanupPrefs();
|
||||
});
|
||||
|
||||
/**
|
||||
* Check scan and save prompt response flow if multiple provider exists on desktop.
|
||||
*/
|
||||
add_task(async function test_multipleStorageProvider() {
|
||||
nsIDropboxFile = mock_dropbox();
|
||||
nsIGDriveFile = mock_gdrive();
|
||||
|
||||
// Dropbox picked by scan if multiple providers found
|
||||
let result = await checkScan(DROPBOX_KEY);
|
||||
|
||||
// Always save to cloud
|
||||
await checkSavedPromptResponse(DROPBOX_KEY, result, true, true);
|
||||
cleanupPrefs();
|
||||
|
||||
// Reject dropbox as cloud storage provider and never re-prompt again
|
||||
await checkSavedPromptResponse(DROPBOX_KEY, result, true);
|
||||
|
||||
// Uninstall dropbox and gdrive by removing discovery folder
|
||||
nsIDropboxFile.remove(false);
|
||||
nsIGDriveFile.remove(false);
|
||||
cleanupPrefs();
|
||||
});
|
@ -1,8 +0,0 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android'
|
||||
support-files =
|
||||
cloud/**
|
||||
|
||||
[test_cloudstorage.js]
|
@ -68,11 +68,6 @@ ChromeUtils.defineModuleGetter(
|
||||
"NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"CloudStorage",
|
||||
"resource://gre/modules/CloudStorage.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
@ -381,14 +376,6 @@ var DownloadIntegration = {
|
||||
directoryPath = await this.getSystemDownloadsDirectory();
|
||||
}
|
||||
break;
|
||||
case 3: // Cloud Storage
|
||||
try {
|
||||
directoryPath = await CloudStorage.getDownloadFolder();
|
||||
} catch (ex) {}
|
||||
if (!directoryPath) {
|
||||
directoryPath = await this.getSystemDownloadsDirectory();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
directoryPath = await this.getSystemDownloadsDirectory();
|
||||
}
|
||||
|
@ -135,13 +135,22 @@ add_task(async function test_getPreferredDownloadsDirectory() {
|
||||
}
|
||||
registerCleanupFunction(cleanupPrefs);
|
||||
|
||||
// Should return the system downloads directory.
|
||||
Services.prefs.setIntPref(folderListPrefName, 1);
|
||||
// For legacy cloudstorage users with folderListPrefName as 3,
|
||||
// Should return the system downloads directory because the dir preference
|
||||
// is not set.
|
||||
Services.prefs.setIntPref(folderListPrefName, 3);
|
||||
let systemDir = await DownloadIntegration.getSystemDownloadsDirectory();
|
||||
let downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
Assert.notEqual(downloadDir, "");
|
||||
Assert.equal(downloadDir, systemDir);
|
||||
|
||||
// Should return the system downloads directory.
|
||||
Services.prefs.setIntPref(folderListPrefName, 1);
|
||||
systemDir = await DownloadIntegration.getSystemDownloadsDirectory();
|
||||
downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
Assert.notEqual(downloadDir, "");
|
||||
Assert.equal(downloadDir, systemDir);
|
||||
|
||||
// Should return the desktop directory.
|
||||
Services.prefs.setIntPref(folderListPrefName, 0);
|
||||
downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
|
@ -27,7 +27,6 @@ DIRS += [
|
||||
"certviewer",
|
||||
"cleardata",
|
||||
"clearsitedata",
|
||||
"cloudstorage",
|
||||
"commandlines",
|
||||
"contentprefs",
|
||||
"contextualidentity",
|
||||
|
@ -306,13 +306,6 @@ nsUnknownContentTypeDialog.prototype = {
|
||||
|
||||
// Check to make sure we have a valid directory, otherwise, prompt
|
||||
if (result) {
|
||||
// Notifications for CloudStorage API consumers to show offer
|
||||
// prompts while downloading. See Bug 1365129
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
"cloudstorage-prompt-notification",
|
||||
result.path
|
||||
);
|
||||
// This path is taken when we have a writable default download directory.
|
||||
aLauncher.saveDestinationAvailable(result);
|
||||
return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user