Bug 936342 - Make downloads end up in the default storage area on b2g r=dhylands,paolo

This commit is contained in:
Fabrice Desré 2013-11-12 13:17:24 -08:00
Родитель 74ff1235b7
Коммит 05ec709d36
6 изменённых файлов: 215 добавлений и 4 удалений

Просмотреть файл

@ -71,3 +71,7 @@ contract @mozilla.org/filepicker;1 {436ff8f9-0acc-4b11-8ec7-e293efba3141}
component {637b0f77-2429-49a0-915f-abf5d0db8b9a} WebappsUpdateTimer.js
contract @mozilla.org/b2g/webapps-update-timer;1 {637b0f77-2429-49a0-915f-abf5d0db8b9a}
category update-timer WebappsUpdateTimer @mozilla.org/b2g/webapps-update-timer;1,getService,background-update-timer,webapps.update.interval,86400
# HelperAppDialog.js
component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js
contract @mozilla.org/helperapplauncherdialog;1 {710322af-e6ae-4b0c-b2c9-1474a87b077e}

Просмотреть файл

@ -0,0 +1,123 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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/. */
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
// -----------------------------------------------------------------------
// HelperApp Launcher Dialog
//
// For now on b2g we never prompt and just download to the default
// location.
//
// -----------------------------------------------------------------------
function HelperAppLauncherDialog() { }
HelperAppLauncherDialog.prototype = {
classID: Components.ID("{710322af-e6ae-4b0c-b2c9-1474a87b077e}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]),
show: function(aLauncher, aContext, aReason) {
aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToDisk;
aLauncher.saveToDisk(null, false);
},
promptForSaveToFile: function(aLauncher,
aContext,
aDefaultFile,
aSuggestedFileExt,
aForcePrompt) {
throw Cr.NS_ERROR_NOT_AVAILABLE;
},
promptForSaveToFileAsync: function(aLauncher,
aContext,
aDefaultFile,
aSuggestedFileExt,
aForcePrompt) {
// Retrieve the user's default download directory.
Task.spawn(function() {
let file = null;
try {
let defaultFolder = yield Downloads.getPreferredDownloadsDirectory();
let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
dir.initWithPath(defaultFolder);
file = this.validateLeafName(dir, aDefaultFile, aSuggestedFileExt);
} catch(e) { }
aLauncher.saveDestinationAvailable(file);
}.bind(this)).then(null, Cu.reportError);
},
validateLeafName: function(aLocalFile, aLeafName, aFileExt) {
if (!(aLocalFile && this.isUsableDirectory(aLocalFile)))
return null;
// Remove any leading periods, since we don't want to save hidden files
// automatically.
aLeafName = aLeafName.replace(/^\.+/, "");
if (aLeafName == "")
aLeafName = "unnamed" + (aFileExt ? "." + aFileExt : "");
aLocalFile.append(aLeafName);
this.makeFileUnique(aLocalFile);
return aLocalFile;
},
makeFileUnique: function(aLocalFile) {
try {
// Note - this code is identical to that in
// toolkit/content/contentAreaUtils.js.
// If you are updating this code, update that code too! We can't share code
// here since this is called in a js component.
let collisionCount = 0;
while (aLocalFile.exists()) {
collisionCount++;
if (collisionCount == 1) {
// Append "(2)" before the last dot in (or at the end of) the filename
// special case .ext.gz etc files so we don't wind up with .tar(2).gz
if (aLocalFile.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i))
aLocalFile.leafName = aLocalFile.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
else
aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
}
else {
// replace the last (n) in the filename with (n+1)
aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
}
}
aLocalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
}
catch (e) {
dump("*** exception in makeFileUnique: " + e + "\n");
if (e.result == Cr.NS_ERROR_FILE_ACCESS_DENIED)
throw e;
if (aLocalFile.leafName == "" || aLocalFile.isDirectory()) {
aLocalFile.append("unnamed");
if (aLocalFile.exists())
aLocalFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
}
}
},
isUsableDirectory: function(aDirectory) {
return aDirectory.exists() &&
aDirectory.isDirectory() &&
aDirectory.isWritable();
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HelperAppLauncherDialog]);

Просмотреть файл

@ -15,6 +15,7 @@ EXTRA_COMPONENTS += [
'ContentHandler.js',
'ContentPermissionPrompt.js',
'FilePicker.js',
'HelperAppDialog.js',
'MailtoProtocolHandler.js',
'PaymentGlue.js',
'ProcessGlobal.js',

Просмотреть файл

@ -395,8 +395,6 @@
@BINPATH@/components/crypto-SDR.js
@BINPATH@/components/jsconsole-clhandler.manifest
@BINPATH@/components/jsconsole-clhandler.js
@BINPATH@/components/nsHelperAppDlg.manifest
@BINPATH@/components/nsHelperAppDlg.js
@BINPATH@/components/nsDownloadManagerUI.manifest
@BINPATH@/components/nsDownloadManagerUI.js
@BINPATH@/components/nsSidebar.manifest
@ -774,6 +772,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/TelProtocolHandler.js
@BINPATH@/components/B2GAboutRedirector.js
@BINPATH@/components/FilePicker.js
@BINPATH@/components/HelperAppDialog.js
@BINPATH@/components/DataStore.manifest
@BINPATH@/components/DataStoreService.js

Просмотреть файл

@ -83,6 +83,10 @@ XPCOMUtils.defineLazyGetter(this, "gInternetZoneIdentifier", function() {
return new TextEncoder().encode("[ZoneTransfer]\r\nZoneId=3\r\n");
});
XPCOMUtils.defineLazyServiceGetter(this, "volumeService",
"@mozilla.org/telephony/volume-service;1",
"nsIVolumeService");
const Timer = Components.Constructor("@mozilla.org/timer;1", "nsITimer",
"initWithCallback");
@ -231,6 +235,48 @@ this.DownloadIntegration = {
}.bind(this));
},
#ifdef MOZ_WIDGET_GONK
/**
* Finds the default download directory which can be either in the
* internal storage or on the sdcard.
*
* @return {Promise}
* @resolves The downloads directory string path.
*/
_getDefaultDownloadDirectory: function() {
return Task.spawn(function() {
let directoryPath;
let win = Services.wm.getMostRecentWindow("navigator:browser");
let storages = win.navigator.getDeviceStorages("sdcard");
let preferredStorageName;
// Use the first one or the default storage.
storages.forEach((aStorage) => {
if (aStorage.default || !preferredStorageName) {
preferredStorageName = aStorage.storageName;
}
});
// Now get the path for this storage area.
if (preferredStorageName) {
let volume = volumeService.getVolumeByName(preferredStorageName);
if (volume &&
volume.isMediaPresent &&
!volume.isMountLocked &&
!volume.isSharing) {
directoryPath = OS.Path.join(volume.mountPoint, "downloads");
yield OS.File.makeDir(directoryPath, { ignoreExisting: true });
}
}
if (directoryPath) {
throw new Task.Result(directoryPath);
} else {
throw new Components.Exception("No suitable storage for downloads.",
Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH);
}
});
},
#endif
/**
* Determines if a Download object from the list of persistent downloads
* should be saved into a file, so that it can be restored across sessions.
@ -286,7 +332,7 @@ this.DownloadIntegration = {
directoryPath = this._getDirectory("DfltDwnld");
}
#elifdef XP_UNIX
#ifdef ANDROID
#ifdef MOZ_WIDGET_ANDROID
// Android doesn't have a $HOME directory, and by default we only have
// write access to /data/data/org.mozilla.{$APP} and /sdcard
directoryPath = gEnvironment.get("DOWNLOADS_DIRECTORY");
@ -294,6 +340,8 @@ this.DownloadIntegration = {
throw new Components.Exception("DOWNLOADS_DIRECTORY is not set.",
Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH);
}
#elifdef MOZ_WIDGET_GONK
directoryPath = this._getDefaultDownloadDirectory();
#else
// For Linux, use XDG download dir, with a fallback to Home/Downloads
// if the XDG user dirs are disabled.
@ -321,6 +369,9 @@ this.DownloadIntegration = {
getPreferredDownloadsDirectory: function DI_getPreferredDownloadsDirectory() {
return Task.spawn(function() {
let directoryPath = null;
#ifdef MOZ_WIDGET_GONK
directoryPath = this._getDefaultDownloadDirectory();
#else
let prefValue = 1;
try {
@ -348,6 +399,7 @@ this.DownloadIntegration = {
default:
directoryPath = yield this.getSystemDownloadsDirectory();
}
#endif
throw new Task.Result(directoryPath);
}.bind(this));
},
@ -363,7 +415,9 @@ this.DownloadIntegration = {
let directoryPath = null;
#ifdef XP_MACOSX
directoryPath = yield this.getPreferredDownloadsDirectory();
#elifdef ANDROID
#elifdef MOZ_WIDGET_ANDROID
directoryPath = yield this.getSystemDownloadsDirectory();
#elifdef MOZ_WIDGET_GONK
directoryPath = yield this.getSystemDownloadsDirectory();
#else
// For Metro mode on Windows 8, we want searchability for documents

Просмотреть файл

@ -116,6 +116,10 @@
#include "mozilla/Preferences.h"
#include "mozilla/ipc/URIUtils.h"
#ifdef MOZ_WIDGET_GONK
#include "nsDeviceStorage.h"
#endif
using namespace mozilla;
using namespace mozilla::ipc;
@ -328,6 +332,32 @@ static nsresult GetDownloadDirectory(nsIFile **_directory)
getter_AddRefs(dir));
NS_ENSURE_SUCCESS(rv, rv);
}
#elif defined(MOZ_WIDGET_GONK)
// On Gonk, store the files on the sdcard in the downloads directory.
// We need to check with the volume manager which storage point is
// available.
// Pick the default storage in case multiple (internal and external) ones
// are available.
nsString storageName;
nsDOMDeviceStorage::GetDefaultStorageName(NS_LITERAL_STRING("sdcard"),
storageName);
NS_ENSURE_TRUE(!storageName.IsEmpty(), NS_ERROR_FAILURE);
DeviceStorageFile dsf(NS_LITERAL_STRING("sdcard"),
storageName,
NS_LITERAL_STRING("downloads"));
NS_ENSURE_TRUE(dsf.mFile, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(dsf.IsAvailable(), NS_ERROR_FAILURE);
bool alreadyThere;
nsresult rv = dsf.mFile->Exists(&alreadyThere);
NS_ENSURE_SUCCESS(rv, rv);
if (!alreadyThere) {
rv = dsf.mFile->Create(nsIFile::DIRECTORY_TYPE, 0770);
NS_ENSURE_SUCCESS(rv, rv);
}
dir = dsf.mFile;
#elif defined(ANDROID)
// On mobile devices, we are avoiding exposing users to the file
// system, and don't save downloads to temp directories