From 8f8841a1c45ed44f03c8a6061b759f703fb99def Mon Sep 17 00:00:00 2001 From: Dave Hylands Date: Sat, 11 May 2013 02:10:18 -0700 Subject: [PATCH] Bug 858416 - Create a composite device storage interface. r=dougt * * * Bug 858416 - Fix try test failures --- b2g/chrome/content/settings.js | 5 + dom/base/Navigator.cpp | 35 +- dom/bluetooth/BluetoothOppManager.cpp | 66 +- dom/bluetooth/BluetoothOppManager.h | 1 - dom/camera/GonkCameraControl.cpp | 19 +- dom/devicestorage/DeviceStorage.h | 113 +- .../DeviceStorageRequestChild.cpp | 7 +- .../DeviceStorageRequestParent.cpp | 106 +- dom/devicestorage/PDeviceStorageRequest.ipdl | 3 +- dom/devicestorage/nsDeviceStorage.cpp | 1095 ++++++++++++----- dom/devicestorage/nsDeviceStorage.h | 65 +- dom/devicestorage/test/test_823965.html | 14 +- dom/devicestorage/test/test_enumerate.html | 14 +- .../test/test_enumerateNoParam.html | 18 +- .../test/test_lastModificationFilter.html | 12 + dom/devicestorage/test/test_watch.html | 12 +- dom/devicestorage/test/test_watchOther.html | 12 +- .../devicestorage/nsIDOMDeviceStorage.idl | 6 +- dom/ipc/ContentChild.cpp | 13 +- dom/ipc/ContentChild.h | 7 +- dom/ipc/ContentParent.cpp | 10 +- dom/ipc/ContentParent.h | 1 + dom/ipc/PContent.ipdl | 16 +- dom/system/gonk/nsVolumeService.cpp | 18 +- 24 files changed, 1161 insertions(+), 507 deletions(-) diff --git a/b2g/chrome/content/settings.js b/b2g/chrome/content/settings.js index 6923fc47ac4..6c9e5c81918 100644 --- a/b2g/chrome/content/settings.js +++ b/b2g/chrome/content/settings.js @@ -261,6 +261,11 @@ SettingsListener.observe('debug.log-animations.enabled', false, function(value) Services.prefs.setBoolPref('layers.offmainthreadcomposition.log-animations', value); }); +// =================== Device Storage ==================== +SettingsListener.observe('device.storage.writable.name', false, function(value) { + Services.prefs.setBoolPref('device.storage.writable.name', value); +}); + // =================== Privacy ==================== SettingsListener.observe('privacy.donottrackheader.enabled', false, function(value) { Services.prefs.setBoolPref('privacy.donottrackheader.enabled', value); diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 2ee0b5aa9ee..af55bb988fa 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -953,38 +953,21 @@ NS_IMETHODIMP Navigator::GetDeviceStorage(const nsAString &aType, nsIDOMDeviceSt return NS_OK; } - // We're going to obsolete getDeviceStorage, but want to leave it in for - // compatability right now. So we do essentially the same thing as GetDeviceStorages - // but only take the first element of the array. + nsCOMPtr win(do_QueryReferent(mWindow)); - NS_WARNING("navigator.getDeviceStorage is deprecated. Returning navigator.getDeviceStorages[0]"); + if (!win || !win->GetOuterWindow() || !win->GetDocShell()) { + return NS_ERROR_FAILURE; + } - nsCOMPtr variantArray; + nsRefPtr storage; + nsDOMDeviceStorage::CreateDeviceStorageFor(win, aType, getter_AddRefs(storage)); - nsresult rv = GetDeviceStorages(aType, getter_AddRefs(variantArray)); - NS_ENSURE_SUCCESS(rv, rv); - - uint16_t dataType; - variantArray->GetDataType(&dataType); - - if (dataType != nsIDataType::VTYPE_ARRAY) { - NS_ASSERTION(dataType == nsIDataType::VTYPE_EMPTY_ARRAY, - "Expecting an empty array"); + if (!storage) { return NS_OK; } - uint16_t valueType; - nsIID iid; - uint32_t valueCount; - void* rawArray; - variantArray->GetAsArray(&valueType, &iid, &valueCount, &rawArray); - NS_ASSERTION(valueCount > 0, "Expecting non-zero array size"); - nsIDOMDeviceStorage** values = static_cast(rawArray); - *_retval = values[0]; - for (uint32_t i = 1; i < valueCount; i++) { - values[i]->Release(); - } - nsMemory::Free(rawArray); + NS_ADDREF(*_retval = storage.get()); + mDeviceStorageStores.AppendElement(storage); return NS_OK; } diff --git a/dom/bluetooth/BluetoothOppManager.cpp b/dom/bluetooth/BluetoothOppManager.cpp index 3bfad4533ac..c04a626f352 100644 --- a/dom/bluetooth/BluetoothOppManager.cpp +++ b/dom/bluetooth/BluetoothOppManager.cpp @@ -29,9 +29,7 @@ #include "nsIOutputStream.h" #include "nsNetUtil.h" -#define TARGET_ROOT "/sdcard/" #define TARGET_SUBDIR "downloads/bluetooth/" -#define TARGET_FOLDER TARGET_ROOT TARGET_SUBDIR USING_BLUETOOTH_NAMESPACE using namespace mozilla; @@ -485,53 +483,13 @@ BluetoothOppManager::AfterOppDisconnected() void BluetoothOppManager::DeleteReceivedFile() { - nsString path; - path.AssignLiteral(TARGET_FOLDER); - - nsCOMPtr f; - nsresult rv = NS_NewLocalFile(path + sFileName, false, getter_AddRefs(f)); - if (NS_FAILED(rv)) { - NS_WARNING("Couldn't find received file, nothing to delete."); - return; - } - if (mOutputStream) { mOutputStream->Close(); mOutputStream = nullptr; } - f->Remove(false); -} - -DeviceStorageFile* -BluetoothOppManager::CreateDeviceStorageFile(nsIFile* aFile) -{ - nsString fullFilePath; - aFile->GetPath(fullFilePath); - - MOZ_ASSERT(StringBeginsWith(fullFilePath, NS_LITERAL_STRING(TARGET_ROOT))); - - nsDependentSubstring storagePath = - Substring(fullFilePath, strlen(TARGET_ROOT)); - - nsCOMPtr mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID); - NS_ENSURE_TRUE(mimeSvc, nullptr); - - nsCString mimeType; - nsresult rv = mimeSvc->GetTypeFromFile(aFile, mimeType); - if (NS_FAILED(rv)) { - return nullptr; - } - - if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("image/"))) { - return new DeviceStorageFile(NS_LITERAL_STRING("pictures"), storagePath); - } else if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("video/"))) { - return new DeviceStorageFile(NS_LITERAL_STRING("videos"), storagePath); - } else if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("audio/"))) { - return new DeviceStorageFile(NS_LITERAL_STRING("music"), storagePath); - } else { - NS_WARNING("Couldn't recognize the mimetype of received file."); - return nullptr; + if (mDsFile && mDsFile->mFile) { + mDsFile->mFile->Remove(false); } } @@ -541,22 +499,18 @@ BluetoothOppManager::CreateFile() MOZ_ASSERT(mPacketLeftLength == 0); nsString path; - path.AssignLiteral(TARGET_FOLDER); + path.AssignLiteral(TARGET_SUBDIR); + path.Append(sFileName); - nsCOMPtr f; - nsresult rv; - rv = NS_NewLocalFile(path + sFileName, false, getter_AddRefs(f)); - if (NS_FAILED(rv)) { - NS_WARNING("Couldn't new a local file"); - return false; - } - - rv = f->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00644); - if (NS_FAILED(rv)) { + mDsFile = DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644); + if (!mDsFile) { NS_WARNING("Couldn't create the file"); return false; } + nsCOMPtr f; + mDsFile->mFile->Clone(getter_AddRefs(f)); + /* * The function CreateUnique() may create a file with a different file * name from the original sFileName. Therefore we have to retrieve @@ -564,8 +518,6 @@ BluetoothOppManager::CreateFile() */ f->GetLeafName(sFileName); - mDsFile = CreateDeviceStorageFile(f); - NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f); NS_ENSURE_TRUE(mOutputStream, false); diff --git a/dom/bluetooth/BluetoothOppManager.h b/dom/bluetooth/BluetoothOppManager.h index 52220231b4e..36bd7ca0359 100644 --- a/dom/bluetooth/BluetoothOppManager.h +++ b/dom/bluetooth/BluetoothOppManager.h @@ -109,7 +109,6 @@ private: bool IsReservedChar(PRUnichar c); void ClearQueue(); void RetrieveSentFileName(); - DeviceStorageFile* CreateDeviceStorageFile(nsIFile* aFile); void NotifyAboutFileChange(); /** diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp index 85907d7ca0f..946a4716a09 100644 --- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -34,6 +34,8 @@ #include #include "nsPrintfCString.h" #include "nsIObserverService.h" +#include "nsIVolume.h" +#include "nsIVolumeService.h" #include "DOMCameraManager.h" #include "GonkCameraHwMgr.h" #include "DOMCameraCapabilities.h" @@ -905,7 +907,22 @@ nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) */ nsCOMPtr filename = aStartRecording->mFolder; filename->AppendRelativePath(aStartRecording->mFilename); + + nsString fullpath; + filename->GetPath(fullpath); + + nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); + NS_ENSURE_TRUE(vs, NS_ERROR_FAILURE); + + nsCOMPtr vol; + nsresult rv = vs->GetVolumeByPath(fullpath, getter_AddRefs(vol)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG); + + nsString volName; + vol->GetName(volName); + mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"), + volName, aStartRecording->mFilename); nsAutoCString nativeFilename; @@ -923,7 +940,7 @@ nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) return NS_ERROR_FAILURE; } - nsresult rv = SetupRecording(fd, aStartRecording->mOptions.rotation, aStartRecording->mOptions.maxFileSizeBytes, aStartRecording->mOptions.maxVideoLengthMs); + rv = SetupRecording(fd, aStartRecording->mOptions.rotation, aStartRecording->mOptions.maxFileSizeBytes, aStartRecording->mOptions.maxVideoLengthMs); NS_ENSURE_SUCCESS(rv, rv); if (mRecorder->start() != OK) { diff --git a/dom/devicestorage/DeviceStorage.h b/dom/devicestorage/DeviceStorage.h index c50b1f2e08f..dc3cd43e982 100644 --- a/dom/devicestorage/DeviceStorage.h +++ b/dom/devicestorage/DeviceStorage.h @@ -12,7 +12,9 @@ #include "nsIPrincipal.h" #include "nsIObserver.h" #include "nsDOMEventTargetHelper.h" +#include "mozilla/RefPtr.h" #include "mozilla/StaticPtr.h" +#include "DOMRequest.h" #define DEVICESTORAGE_PICTURES "pictures" #define DEVICESTORAGE_VIDEOS "videos" @@ -24,49 +26,73 @@ class DeviceStorageFile MOZ_FINAL : public nsISupports { public: nsCOMPtr mFile; - nsString mPath; nsString mStorageType; + nsString mStorageName; nsString mRootDir; + nsString mPath; bool mEditable; // Used when the path will be set later via SetPath. - DeviceStorageFile(const nsAString& aStorageType); + DeviceStorageFile(const nsAString& aStorageType, + const nsAString& aStorageName); // Used for non-enumeration purposes. - DeviceStorageFile(const nsAString& aStorageType, const nsAString& aPath); + DeviceStorageFile(const nsAString& aStorageType, + const nsAString& aStorageName, + const nsAString& aPath); // Used for enumerations. When you call Enumerate, you can pass in a directory to enumerate // and the results that are returned are relative to that directory, files related to an // enumeration need to know the "root of the enumeration" directory. - DeviceStorageFile(const nsAString& aStorageType, const nsAString& aRootDir, const nsAString& aPath); + DeviceStorageFile(const nsAString& aStorageType, + const nsAString& aStorageName, + const nsAString& aRootDir, + const nsAString& aPath); void SetPath(const nsAString& aPath); void SetEditable(bool aEditable); + static already_AddRefed CreateUnique(nsAString& aFileName, + uint32_t aFileType, + uint32_t aFileAttributes); + NS_DECL_ISUPPORTS + bool IsAvailable(); + bool IsComposite(); + void GetCompositePath(nsAString& aCompositePath); + // we want to make sure that the names of file can't reach // outside of the type of storage the user asked for. bool IsSafePath(); bool IsSafePath(const nsAString& aPath); + void Dump(const char* label); + nsresult Remove(); nsresult Write(nsIInputStream* aInputStream); nsresult Write(InfallibleTArray& bits); - void CollectFiles(nsTArray > &aFiles, PRTime aSince = 0); - void collectFilesInternal(nsTArray > &aFiles, PRTime aSince, nsAString& aRootPath); + void CollectFiles(nsTArray >& aFiles, + PRTime aSince = 0); + void collectFilesInternal(nsTArray >& aFiles, + PRTime aSince, nsAString& aRootPath); - static void DirectoryDiskUsage(nsIFile* aFile, - uint64_t* aPicturesSoFar, - uint64_t* aVideosSoFar, - uint64_t* aMusicSoFar, - uint64_t* aTotalSoFar); + void AccumDiskUsage(uint64_t* aPicturesSoFar, uint64_t* aVideosSoFar, + uint64_t* aMusicSoFar, uint64_t* aTotalSoFar); - static void GetRootDirectoryForType(const nsAString& aType, - const nsAString& aVolName, + void GetDiskFreeSpace(int64_t* aSoFar); + void GetStatus(nsAString& aStatus); + static void GetRootDirectoryForType(const nsAString& aStorageType, + const nsAString& aStorageName, nsIFile** aFile); private: - void Init(const nsAString& aStorageType); + void Init(); void NormalizeFilePath(); void AppendRelativePath(const nsAString& aPath); + void GetStatusInternal(nsAString& aStorageName, nsAString& aStatus); + void AccumDirectoryUsage(nsIFile* aFile, + uint64_t* aPicturesSoFar, + uint64_t* aVideosSoFar, + uint64_t* aMusicSoFar, + uint64_t* aTotalSoFar); }; /* @@ -100,6 +126,8 @@ class nsDOMDeviceStorage MOZ_FINAL , public nsIObserver { public: + typedef nsTArray VolumeNameArray; + NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMDEVICESTORAGE @@ -117,18 +145,32 @@ public: nsDOMDeviceStorage(); - nsresult Init(nsPIDOMWindow* aWindow, const nsAString &aType, const nsAString &aVolName); + nsresult Init(nsPIDOMWindow* aWindow, const nsAString& aType, + nsTArray >& aStores); + nsresult Init(nsPIDOMWindow* aWindow, const nsAString& aType, + const nsAString& aVolName); - void SetRootDirectoryForType(const nsAString& aType, const nsAString &aVolName); + bool IsAvailable(); - static void GetOrderedVolumeNames(const nsAString &aType, - nsTArray &aVolumeNames); + void SetRootDirectoryForType(const nsAString& aType, const nsAString& aVolName); + + static void CreateDeviceStorageFor(nsPIDOMWindow* aWin, + const nsAString& aType, + nsDOMDeviceStorage** aStore); static void CreateDeviceStoragesFor(nsPIDOMWindow* aWin, - const nsAString &aType, - nsTArray > &aStores); + const nsAString& aType, + nsTArray >& aStores); void Shutdown(); + static void GetOrderedVolumeNames(nsTArray& aVolumeNames); + + static void GetWritableStorageName(const nsAString& aStorageType, + nsAString &aStorageName); + + static bool ParseCompositePath(const nsAString& aCompositePath, + nsAString& aOutStorageName, + nsAString& aOutStoragePath); private: ~nsDOMDeviceStorage(); @@ -137,8 +179,17 @@ private: nsIDOMDOMRequest** aRetval, bool aEditable); - nsresult EnumerateInternal(const JS::Value & aName, - const JS::Value & aOptions, + nsresult GetInternal(nsPIDOMWindow* aWin, + const nsAString& aPath, + mozilla::dom::DOMRequest* aRequest, + bool aEditable); + + nsresult DeleteInternal(nsPIDOMWindow* aWin, + const nsAString& aPath, + mozilla::dom::DOMRequest* aRequest); + + nsresult EnumerateInternal(const JS::Value& aName, + const JS::Value& aOptions, JSContext* aCx, uint8_t aArgc, bool aEditable, @@ -146,7 +197,13 @@ private: nsString mStorageType; nsCOMPtr mRootDirectory; - nsString mVolumeName; + nsString mStorageName; + + bool IsComposite() { return mStores.Length() > 0; } + nsTArray > mStores; + already_AddRefed GetStorage(const nsAString& aCompositePath, + nsAString& aOutStoragePath); + already_AddRefed GetStorageByName(const nsAString &aStorageName); nsCOMPtr mPrincipal; @@ -158,8 +215,16 @@ private: friend class WatchFileEvent; friend class DeviceStorageRequest; + class VolumeNameCache : public mozilla::RefCounted + { + public: + nsTArray mVolumeNames; + }; + static mozilla::StaticRefPtr sVolumeNameCache; + #ifdef MOZ_WIDGET_GONK - void DispatchMountChangeEvent(nsAString& aType); + void DispatchMountChangeEvent(nsAString& aVolumeName, + nsAString& aVolumeStatus); #endif // nsIDOMDeviceStorage.type diff --git a/dom/devicestorage/DeviceStorageRequestChild.cpp b/dom/devicestorage/DeviceStorageRequestChild.cpp index 9c90591c3b4..41e03457a15 100644 --- a/dom/devicestorage/DeviceStorageRequestChild.cpp +++ b/dom/devicestorage/DeviceStorageRequestChild.cpp @@ -51,7 +51,9 @@ DeviceStorageRequestChild::Recv__delete__(const DeviceStorageResponseValue& aVal case DeviceStorageResponseValue::TSuccessResponse: { - JS::Value result = StringToJsval(mRequest->GetOwner(), mFile->mPath); + nsString compositePath; + mFile->GetCompositePath(compositePath); + JS::Value result = StringToJsval(mRequest->GetOwner(), compositePath); mRequest->FireSuccess(result); break; } @@ -101,7 +103,8 @@ DeviceStorageRequestChild::Recv__delete__(const DeviceStorageResponseValue& aVal uint32_t count = r.paths().Length(); for (uint32_t i = 0; i < count; i++) { nsRefPtr dsf = new DeviceStorageFile(r.type(), - r.relpath(), + r.paths()[i].storageName(), + r.rootdir(), r.paths()[i].name()); cursor->mFiles.AppendElement(dsf); } diff --git a/dom/devicestorage/DeviceStorageRequestParent.cpp b/dom/devicestorage/DeviceStorageRequestParent.cpp index dbbe6606935..f624784d3c9 100644 --- a/dom/devicestorage/DeviceStorageRequestParent.cpp +++ b/dom/devicestorage/DeviceStorageRequestParent.cpp @@ -37,7 +37,8 @@ DeviceStorageRequestParent::Dispatch() { DeviceStorageAddParams p = mParams; - nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); + nsRefPtr dsf = + new DeviceStorageFile(p.type(), p.storageName(), p.relpath()); BlobParent* bp = static_cast(p.blobParent()); nsCOMPtr blob = bp->GetBlob(); @@ -56,7 +57,8 @@ DeviceStorageRequestParent::Dispatch() case DeviceStorageParams::TDeviceStorageGetParams: { DeviceStorageGetParams p = mParams; - nsRefPtr dsf = new DeviceStorageFile(p.type(), p.rootDir(), p.relpath()); + nsRefPtr dsf = + new DeviceStorageFile(p.type(), p.storageName(), p.rootDir(), p.relpath()); nsRefPtr r = new ReadFileEvent(this, dsf); nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); @@ -69,7 +71,8 @@ DeviceStorageRequestParent::Dispatch() { DeviceStorageDeleteParams p = mParams; - nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); + nsRefPtr dsf = + new DeviceStorageFile(p.type(), p.storageName(), p.relpath()); nsRefPtr r = new DeleteFileEvent(this, dsf); nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); @@ -82,7 +85,8 @@ DeviceStorageRequestParent::Dispatch() { DeviceStorageFreeSpaceParams p = mParams; - nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); + nsRefPtr dsf = + new DeviceStorageFile(p.type(), p.storageName()); nsRefPtr r = new FreeSpaceFileEvent(this, dsf); nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); @@ -98,7 +102,8 @@ DeviceStorageRequestParent::Dispatch() DeviceStorageUsedSpaceParams p = mParams; - nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); + nsRefPtr dsf = + new DeviceStorageFile(p.type(), p.storageName()); nsRefPtr r = new UsedSpaceFileEvent(this, dsf); usedSpaceCache->Dispatch(r); @@ -109,7 +114,8 @@ DeviceStorageRequestParent::Dispatch() { DeviceStorageAvailableParams p = mParams; - nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); + nsRefPtr dsf = + new DeviceStorageFile(p.type(), p.storageName()); nsRefPtr r = new PostAvailableResultEvent(this, dsf); NS_DispatchToMainThread(r); break; @@ -118,7 +124,8 @@ DeviceStorageRequestParent::Dispatch() case DeviceStorageParams::TDeviceStorageEnumerationParams: { DeviceStorageEnumerationParams p = mParams; - nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath(), NS_LITERAL_STRING("")); + nsRefPtr dsf + = new DeviceStorageFile(p.type(), p.storageName(), p.rootdir(), NS_LITERAL_STRING("")); nsRefPtr r = new EnumerateFileEvent(this, dsf, p.since()); nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); @@ -351,7 +358,9 @@ DeviceStorageRequestParent::PostBlobSuccessEvent::CancelableRun() { nsString mime; CopyASCIItoUTF16(mMimeType, mime); - nsCOMPtr blob = new nsDOMFileFile(mFile->mPath, mime, mLength, mFile->mFile, mLastModificationDate); + nsString compositePath; + mFile->GetCompositePath(compositePath); + nsCOMPtr blob = new nsDOMFileFile(compositePath, mime, mLength, mFile->mFile, mLastModificationDate); ContentParent* cp = static_cast(mParent->Manager()); BlobParent* actor = cp->GetOrCreateActorForBlob(blob); @@ -487,13 +496,12 @@ DeviceStorageRequestParent::FreeSpaceFileEvent::CancelableRun() { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - nsCOMPtr r; int64_t freeSpace = 0; - nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace); - if (NS_FAILED(rv)) { - freeSpace = 0; + if (mFile) { + mFile->GetDiskFreeSpace(&freeSpace); } + nsCOMPtr r; r = new PostFreeSpaceResultEvent(mParent, static_cast(freeSpace)); NS_DispatchToMainThread(r); return NS_OK; @@ -515,40 +523,20 @@ DeviceStorageRequestParent::UsedSpaceFileEvent::CancelableRun() { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - DeviceStorageUsedSpaceCache* usedSpaceCache = DeviceStorageUsedSpaceCache::CreateOrGet(); - NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null"); - - nsCOMPtr r; - - uint64_t usedSize; - nsresult rv = usedSpaceCache->GetUsedSizeForType(mFile->mStorageType, &usedSize); - if (NS_SUCCEEDED(rv)) { - r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, usedSize); - NS_DispatchToMainThread(r); - return NS_OK; - } - uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0; - DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &picturesUsage, - &videosUsage, &musicUsage, - &totalUsage); - if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) { - // Don't cache this since it's for a different volume from the media. - r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, totalUsage); + mFile->AccumDiskUsage(&picturesUsage, &videosUsage, + &musicUsage, &totalUsage); + nsCOMPtr r; + if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { + r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, picturesUsage); + } + else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { + r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, videosUsage); + } + else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { + r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, musicUsage); } else { - usedSpaceCache->SetUsedSizes(picturesUsage, videosUsage, musicUsage, totalUsage); - - if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { - r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, picturesUsage); - } - else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { - r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, videosUsage); - } - else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { - r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, musicUsage); - } else { - r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, totalUsage); - } + r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, totalUsage); } NS_DispatchToMainThread(r); return NS_OK; @@ -627,12 +615,14 @@ DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun() NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); nsCOMPtr r; - bool check = false; - mFile->mFile->Exists(&check); - if (!check) { - r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST); - NS_DispatchToMainThread(r); - return NS_OK; + if (mFile->mFile) { + bool check = false; + mFile->mFile->Exists(&check); + if (!check) { + r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST); + NS_DispatchToMainThread(r); + return NS_OK; + } } nsTArray > files; @@ -642,7 +632,7 @@ DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun() uint32_t count = files.Length(); for (uint32_t i = 0; i < count; i++) { - DeviceStorageFileValue dsvf(files[i]->mPath); + DeviceStorageFileValue dsvf(files[i]->mStorageName, files[i]->mPath); values.AppendElement(dsvf); } @@ -689,18 +679,10 @@ DeviceStorageRequestParent::PostAvailableResultEvent::CancelableRun() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - nsString state; - state.Assign(NS_LITERAL_STRING("available")); -#ifdef MOZ_WIDGET_GONK - nsString path; - nsresult rv = mFile->mFile->GetPath(path); - if (NS_SUCCEEDED(rv)) { - rv = GetSDCardStatus(path, state); + nsString state = NS_LITERAL_STRING("unavailable"); + if (mFile) { + mFile->GetStatus(state); } - if (NS_FAILED(rv)) { - state.Assign(NS_LITERAL_STRING("unavailable")); - } -#endif AvailableStorageResponse response(state); unused << mParent->Send__delete__(mParent, response); diff --git a/dom/devicestorage/PDeviceStorageRequest.ipdl b/dom/devicestorage/PDeviceStorageRequest.ipdl index a6252713f5c..6e2e7e6a506 100644 --- a/dom/devicestorage/PDeviceStorageRequest.ipdl +++ b/dom/devicestorage/PDeviceStorageRequest.ipdl @@ -27,13 +27,14 @@ struct BlobResponse struct DeviceStorageFileValue { + nsString storageName; nsString name; }; struct EnumerationResponse { nsString type; - nsString relpath; + nsString rootdir; DeviceStorageFileValue[] paths; }; diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index ad73116e13c..01871f906b1 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -67,7 +67,6 @@ using namespace mozilla::dom::devicestorage; StaticAutoPtr DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache; DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache() - : mDirty(true) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -94,74 +93,100 @@ DeviceStorageUsedSpaceCache::CreateOrGet() return sDeviceStorageUsedSpaceCache; } -nsresult -DeviceStorageUsedSpaceCache::GetPicturesUsedSize(uint64_t* usedSize) { - if (mDirty == true) { - return NS_ERROR_NOT_AVAILABLE; +TemporaryRef +DeviceStorageUsedSpaceCache::GetCacheEntry(const nsAString& aStorageName) +{ + nsTArray >::size_type numEntries = mCacheEntries.Length(); + nsTArray >::index_type i; + for (i = 0; i < numEntries; i++) { + RefPtr cacheEntry = mCacheEntries[i]; + if (cacheEntry->mStorageName.Equals(aStorageName)) { + return cacheEntry; + } } - *usedSize = mPicturesUsedSize; - return NS_OK; + return nullptr; } nsresult -DeviceStorageUsedSpaceCache::GetMusicUsedSize(uint64_t* usedSize) { - if (mDirty == true) { +DeviceStorageUsedSpaceCache::GetUsedSizeForType(const nsAString& aStorageType, + const nsAString& aStorageName, + uint64_t* usedSize) +{ + RefPtr cacheEntry = GetCacheEntry(aStorageName); + if (!cacheEntry || cacheEntry->mDirty) { return NS_ERROR_NOT_AVAILABLE; } - *usedSize = mMusicUsedSize; - return NS_OK; -} -nsresult -DeviceStorageUsedSpaceCache::GetVideosUsedSize(uint64_t* usedSize) { - if (mDirty == true) { - return NS_ERROR_NOT_AVAILABLE; - } - *usedSize = mVideosUsedSize; - return NS_OK; -} + printf_stderr("Getting used size for %s from cache\n", + NS_LossyConvertUTF16toASCII(aStorageName).get()); -nsresult -DeviceStorageUsedSpaceCache::GetTotalUsedSize(uint64_t* usedSize) { - if (mDirty == true) { - return NS_ERROR_NOT_AVAILABLE; - } - *usedSize = mTotalUsedSize; - return NS_OK; -} - -nsresult -DeviceStorageUsedSpaceCache::GetUsedSizeForType(const nsAString& aType, - uint64_t* usedSize) { - if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { - return GetPicturesUsedSize(usedSize); + if (aStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { + *usedSize = cacheEntry->mPicturesUsedSize; + return NS_OK; } - if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { - return GetVideosUsedSize(usedSize); + if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { + *usedSize = cacheEntry->mVideosUsedSize; + return NS_OK; } - if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { - return GetMusicUsedSize(usedSize); + if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { + *usedSize = cacheEntry->mMusicUsedSize; + return NS_OK; } - if (aType.EqualsLiteral(DEVICESTORAGE_SDCARD)) { - return GetTotalUsedSize(usedSize); + if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) { + *usedSize = cacheEntry->mTotalUsedSize; + return NS_OK; } return NS_ERROR_FAILURE; } +nsresult DeviceStorageUsedSpaceCache::AccumUsedSizes(const nsAString& aStorageName, + uint64_t* aPicturesSoFar, + uint64_t* aVideosSoFar, + uint64_t* aMusicSoFar, + uint64_t* aTotalSoFar) +{ + RefPtr cacheEntry = GetCacheEntry(aStorageName); + if (!cacheEntry || cacheEntry->mDirty) { + return NS_ERROR_NOT_AVAILABLE; + } + + printf_stderr("Getting used size for %s from cache\n", + NS_LossyConvertUTF16toASCII(aStorageName).get()); + + *aPicturesSoFar += cacheEntry->mPicturesUsedSize; + *aVideosSoFar += cacheEntry->mVideosUsedSize; + *aMusicSoFar += cacheEntry->mMusicUsedSize; + *aTotalSoFar += cacheEntry->mTotalUsedSize; + + return NS_OK; +} + void -DeviceStorageUsedSpaceCache::SetUsedSizes(uint64_t aPictureSize, +DeviceStorageUsedSpaceCache::SetUsedSizes(const nsAString& aStorageName, + uint64_t aPictureSize, uint64_t aVideosSize, uint64_t aMusicSize, - uint64_t aTotalUsedSize) { - mPicturesUsedSize = aPictureSize; - mVideosUsedSize = aVideosSize; - mMusicUsedSize = aMusicSize; - mTotalUsedSize = aTotalUsedSize; - mDirty = false; + uint64_t aTotalUsedSize) +{ + RefPtr cacheEntry = GetCacheEntry(aStorageName); + if (!cacheEntry) { + cacheEntry = new CacheEntry; + cacheEntry->mStorageName = aStorageName; + mCacheEntries.AppendElement(cacheEntry); + } + + printf_stderr("Setting cache of used sizes for %s\n", + NS_LossyConvertUTF16toASCII(aStorageName).get()); + + cacheEntry->mPicturesUsedSize = aPictureSize; + cacheEntry->mVideosUsedSize = aVideosSize; + cacheEntry->mMusicUsedSize = aMusicSize; + cacheEntry->mTotalUsedSize = aTotalUsedSize; + cacheEntry->mDirty = false; } class GlobalDirs : public RefCounted @@ -300,19 +325,26 @@ DeviceStorageTypeChecker::GetTypeFromFile(nsIFile* aFile, nsAString& aType) { NS_ASSERTION(aFile, "Calling Check without a file"); - aType.AssignLiteral("Unknown"); - nsString path; aFile->GetPath(path); - int32_t dotIdx = path.RFindChar(PRUnichar('.')); + GetTypeFromFileName(path, aType); +} + +void +DeviceStorageTypeChecker::GetTypeFromFileName(const nsAString& aFileName, nsAString& aType) +{ + aType.AssignLiteral("Unknown"); + + nsString fileName(aFileName); + int32_t dotIdx = fileName.RFindChar(PRUnichar('.')); if (dotIdx == kNotFound) { return; } nsAutoString extensionMatch; extensionMatch.AssignLiteral("*"); - extensionMatch.Append(Substring(path, dotIdx)); + extensionMatch.Append(Substring(aFileName, dotIdx)); extensionMatch.AppendLiteral(";"); if (CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions)) { @@ -367,14 +399,19 @@ DeviceStorageTypeChecker::GetAccessForRequest(const DeviceStorageRequestType aRe return NS_OK; } +//static bool DeviceStorageTypeChecker::IsVolumeBased(const nsAString& aType) { +#ifdef MOZ_WIDGET_GONK // The apps aren't stored in the same place as the media, so // we only ever return a single apps object, and not an array // with one per volume (as is the case for the remaining // storage types). return !aType.EqualsLiteral(DEVICESTORAGE_APPS); +#else + return false; +#endif } NS_IMPL_ISUPPORTS1(FileUpdateDispatcher, nsIObserver) @@ -409,6 +446,7 @@ FileUpdateDispatcher::Observe(nsISupports *aSubject, return NS_OK; } ContentChild::GetSingleton()->SendFilePathUpdateNotify(file->mStorageType, + file->mStorageName, file->mPath, NS_ConvertUTF16toUTF8(aData)); } else { @@ -440,7 +478,7 @@ public: DeviceStorageUsedSpaceCache* usedSpaceCache = DeviceStorageUsedSpaceCache::CreateOrGet(); NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null"); - usedSpaceCache->Invalidate(); + usedSpaceCache->Invalidate(mFile->mStorageName); return NS_OK; } @@ -450,14 +488,16 @@ private: }; DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, + const nsAString& aStorageName, const nsAString& aRootDir, const nsAString& aPath) - : mPath(aPath) - , mStorageType(aStorageType) + : mStorageType(aStorageType) + , mStorageName(aStorageName) , mRootDir(aRootDir) + , mPath(aPath) , mEditable(false) { - Init(aStorageType); + Init(); AppendRelativePath(mRootDir); if (!mPath.EqualsLiteral("")) { AppendRelativePath(mPath); @@ -466,29 +506,57 @@ DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, } DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, + const nsAString& aStorageName, const nsAString& aPath) - : mPath(aPath) - , mStorageType(aStorageType) + : mStorageType(aStorageType) + , mStorageName(aStorageName) + , mPath(aPath) , mEditable(false) { - Init(aStorageType); + Init(); AppendRelativePath(aPath); NormalizeFilePath(); } -DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType) +DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, + const nsAString& aStorageName) : mStorageType(aStorageType) + , mStorageName(aStorageName) , mEditable(false) { - Init(aStorageType); + Init(); } void -DeviceStorageFile::Init(const nsAString& aStorageType) +DeviceStorageFile::Dump(const char* label) { - // The hard-coded sdcard below will change as part of bug 858416 - DeviceStorageFile::GetRootDirectoryForType(aStorageType, - NS_LITERAL_STRING("sdcard"), + nsString path; + if (mFile) { + mFile->GetPath(path); + } else { + path = NS_LITERAL_STRING("(null)"); + } + const char* ptStr; + if (XRE_GetProcessType() == GeckoProcessType_Default) { + ptStr = "parent"; + } else { + ptStr = "child"; + } + + printf_stderr("DSF (%s) %s: mStorageType '%s' mStorageName '%s' mRootDir '%s' mPath '%s' mFile->GetPath '%s'\n", + ptStr, label, + NS_LossyConvertUTF16toASCII(mStorageType).get(), + NS_LossyConvertUTF16toASCII(mStorageName).get(), + NS_LossyConvertUTF16toASCII(mRootDir).get(), + NS_LossyConvertUTF16toASCII(mPath).get(), + NS_LossyConvertUTF16toASCII(path).get()); +} + +void +DeviceStorageFile::Init() +{ + DeviceStorageFile::GetRootDirectoryForType(mStorageType, + mStorageName, getter_AddRefs(mFile)); DebugOnly typeChecker = DeviceStorageTypeChecker::CreateOrGet(); @@ -547,32 +615,61 @@ InitDirs() } } +bool DeviceStorageFile::IsComposite() +{ + return DeviceStorageTypeChecker::IsVolumeBased(mStorageType) && + mStorageName.EqualsLiteral(""); +} + void -DeviceStorageFile::GetRootDirectoryForType(const nsAString &aType, const - nsAString &aVolName, +DeviceStorageFile::GetCompositePath(nsAString &aCompositePath) +{ + aCompositePath.AssignLiteral(""); + if (!mStorageName.EqualsLiteral("")) { + aCompositePath.AppendLiteral("/"); + aCompositePath.Append(mStorageName); + aCompositePath.AppendLiteral("/"); + } + if (!mRootDir.EqualsLiteral("")) { + aCompositePath.Append(mRootDir); + aCompositePath.AppendLiteral("/"); + } + aCompositePath.Append(mPath); +} + + +void +DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType, + const nsAString& aStorageName, nsIFile** aFile) { nsCOMPtr f; + *aFile = nullptr; InitDirs(); #ifdef MOZ_WIDGET_GONK - nsString volMountPoint(NS_LITERAL_STRING("/sdcard")); - if (!aVolName.EqualsLiteral("")) { - nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); - if (vs) { - nsresult rv; - nsCOMPtr vol; - rv = vs->GetVolumeByName(aVolName, getter_AddRefs(vol)); - if (NS_SUCCEEDED(rv)) { - vol->GetMountPoint(volMountPoint); - } + nsString volMountPoint; + if (DeviceStorageTypeChecker::IsVolumeBased(aStorageType)) { + if (aStorageName.EqualsLiteral("")) { + // This DeviceStorageFile is for a composite device. Since the composite + // device doesn't have a root, we just allow mFile to be null. These + // should get resolved to real device objects later on. + return; } + + nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); + NS_ENSURE_TRUE_VOID(vs); + nsresult rv; + nsCOMPtr vol; + rv = vs->GetVolumeByName(aStorageName, getter_AddRefs(vol)); + NS_ENSURE_SUCCESS_VOID(rv); + vol->GetMountPoint(volMountPoint); } #endif // Picture directory - if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { + if (aStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { #ifdef MOZ_WIDGET_GONK NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); #else @@ -581,7 +678,7 @@ DeviceStorageFile::GetRootDirectoryForType(const nsAString &aType, const } // Video directory - else if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { + else if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { #ifdef MOZ_WIDGET_GONK NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); #else @@ -590,7 +687,7 @@ DeviceStorageFile::GetRootDirectoryForType(const nsAString &aType, const } // Music directory - else if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { + else if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { #ifdef MOZ_WIDGET_GONK NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); #else @@ -599,7 +696,7 @@ DeviceStorageFile::GetRootDirectoryForType(const nsAString &aType, const } // Apps directory - else if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) { + else if (aStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) { #ifdef MOZ_WIDGET_GONK NS_NewLocalFile(NS_LITERAL_STRING("/data"), false, getter_AddRefs(f)); #else @@ -608,7 +705,7 @@ DeviceStorageFile::GetRootDirectoryForType(const nsAString &aType, const } // default SDCard - else if (aType.EqualsLiteral(DEVICESTORAGE_SDCARD)) { + else if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) { #ifdef MOZ_WIDGET_GONK NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); #else @@ -623,11 +720,50 @@ DeviceStorageFile::GetRootDirectoryForType(const nsAString &aType, const if (f) { f->Clone(aFile); - } else { - *aFile = nullptr; } } +//static +already_AddRefed +DeviceStorageFile::CreateUnique(nsAString& aFileName, + uint32_t aFileType, + uint32_t aFileAttributes) +{ + DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet(); + NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null"); + + nsString storageType; + typeChecker->GetTypeFromFileName(aFileName, storageType); + + nsString storageName; + nsString storagePath; + if (!nsDOMDeviceStorage::ParseCompositePath(aFileName, storageName, storagePath)) { + return nullptr; + } + nsRefPtr dsf = + new DeviceStorageFile(storageType, storageName, storagePath); + if (!dsf->mFile) { + return nullptr; + } + + nsresult rv = dsf->mFile->CreateUnique(aFileType, aFileAttributes); + NS_ENSURE_SUCCESS(rv, nullptr); + + // CreateUnique may cause the filename to change. So we need to update mPath to reflect that. + + int32_t lastSlashIndex = dsf->mPath.RFindChar('/'); + if (lastSlashIndex == kNotFound) { + dsf->mPath.AssignLiteral(""); + } else { + dsf->mPath = Substring(dsf->mPath, 0, lastSlashIndex); + } + nsString leafName; + dsf->mFile->GetLeafName(leafName); + dsf->AppendRelativePath(leafName); + + return dsf.forget(); +} + void DeviceStorageFile::SetPath(const nsAString& aPath) { mPath.Assign(aPath); @@ -691,6 +827,9 @@ DeviceStorageFile::NormalizeFilePath() { void DeviceStorageFile::AppendRelativePath(const nsAString& aPath) { + if (!mFile) { + return; + } #if defined(XP_WIN) // replace forward slashes with backslashes, // since nsLocalFileWin chokes on them @@ -713,7 +852,7 @@ DeviceStorageFile::AppendRelativePath(const nsAString& aPath) { nsresult DeviceStorageFile::Write(nsIInputStream* aInputStream) { - if (!aInputStream) { + if (!aInputStream || !mFile) { return NS_ERROR_FAILURE; } @@ -764,7 +903,11 @@ DeviceStorageFile::Write(nsIInputStream* aInputStream) } nsresult -DeviceStorageFile::Write(InfallibleTArray& aBits) { +DeviceStorageFile::Write(InfallibleTArray& aBits) +{ + if (!mFile) { + return NS_ERROR_FAILURE; + } nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600); if (NS_FAILED(rv)) { @@ -799,6 +942,10 @@ DeviceStorageFile::Remove() { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + if (!mFile) { + return NS_ERROR_FAILURE; + } + bool check; nsresult rv = mFile->Exists(&check); if (NS_FAILED(rv)) { @@ -823,12 +970,22 @@ void DeviceStorageFile::CollectFiles(nsTArray > &aFiles, PRTime aSince) { - nsString rootPath; - nsresult rv = mFile->GetPath(rootPath); - if (NS_FAILED(rv)) { + nsString fullRootPath; + + if (IsComposite()) { + nsDOMDeviceStorage::VolumeNameArray volNames; + nsDOMDeviceStorage::GetOrderedVolumeNames(volNames); + nsDOMDeviceStorage::VolumeNameArray::size_type numVolumes = volNames.Length(); + nsDOMDeviceStorage::VolumeNameArray::index_type i; + for (i = 0; i < numVolumes; i++) { + DeviceStorageFile dsf(mStorageType, volNames[i], mRootDir, NS_LITERAL_STRING("")); + dsf.mFile->GetPath(fullRootPath); + dsf.collectFilesInternal(aFiles, aSince, fullRootPath); + } return; } - return collectFilesInternal(aFiles, aSince, rootPath); + mFile->GetPath(fullRootPath); + collectFilesInternal(aFiles, aSince, fullRootPath); } void @@ -836,6 +993,10 @@ DeviceStorageFile::collectFilesInternal(nsTArray > & PRTime aSince, nsAString& aRootPath) { + if (!mFile || !IsAvailable()) { + return; + } + nsCOMPtr e; mFile->GetDirectoryEntries(getter_AddRefs(e)); @@ -876,22 +1037,83 @@ DeviceStorageFile::collectFilesInternal(nsTArray > & nsDependentSubstring newPath = Substring(fullpath, len); if (isDir) { - DeviceStorageFile dsf(mStorageType, mRootDir, newPath); + DeviceStorageFile dsf(mStorageType, mStorageName, mRootDir, newPath); dsf.collectFilesInternal(aFiles, aSince, aRootPath); } else if (isFile) { - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDir, newPath); + nsRefPtr dsf = + new DeviceStorageFile(mStorageType, mStorageName, mRootDir, newPath); aFiles.AppendElement(dsf); } } } void -DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, - uint64_t* aPicturesSoFar, - uint64_t* aVideosSoFar, - uint64_t* aMusicSoFar, - uint64_t* aTotalSoFar) { +DeviceStorageFile::AccumDiskUsage(uint64_t* aPicturesSoFar, + uint64_t* aVideosSoFar, + uint64_t* aMusicSoFar, + uint64_t* aTotalSoFar) +{ + if (IsComposite()) { + nsDOMDeviceStorage::VolumeNameArray volNames; + nsDOMDeviceStorage::GetOrderedVolumeNames(volNames); + nsDOMDeviceStorage::VolumeNameArray::size_type numVolumes = volNames.Length(); + nsDOMDeviceStorage::VolumeNameArray::index_type i; + for (i = 0; i < numVolumes; i++) { + DeviceStorageFile dsf(mStorageType, volNames[i]); + dsf.AccumDiskUsage(aPicturesSoFar, aVideosSoFar, + aMusicSoFar, aTotalSoFar); + } + return; + } +printf_stderr("AccumDiskUsage '%s'\n", + NS_LossyConvertUTF16toASCII(mStorageName).get()); + + if (!IsAvailable()) { +printf_stderr("AccumDiskUsage Not Available '%s'\n", + NS_LossyConvertUTF16toASCII(mStorageName).get()); + return; + } + + uint64_t pictureUsage = 0, videoUsage = 0, musicUsage = 0, totalUsage = 0; + + if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) { + DeviceStorageUsedSpaceCache* usedSpaceCache = + DeviceStorageUsedSpaceCache::CreateOrGet(); + NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null"); + nsresult rv = usedSpaceCache->AccumUsedSizes(mStorageName, + aPicturesSoFar, aVideosSoFar, + aMusicSoFar, aTotalSoFar); + if (NS_SUCCEEDED(rv)) { +printf_stderr("Accumulated from cache\n"); + return; + } +printf_stderr("Not cached 1 - calling AccumDirectoryUsage\n"); + AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage, + &musicUsage, &totalUsage); + usedSpaceCache->SetUsedSizes(mStorageName, pictureUsage, videoUsage, + musicUsage, totalUsage); + } else { +printf_stderr("Not cached 2 - calling AccumDirectoryUsage\n"); + AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage, + &musicUsage, &totalUsage); + } +printf_stderr("p=%llu v=%llu m=%llu t=%llu\n", pictureUsage, videoUsage, musicUsage, totalUsage); + + *aPicturesSoFar += pictureUsage; + *aVideosSoFar += videoUsage; + *aMusicSoFar += musicUsage; + *aTotalSoFar += totalUsage; +} + +void +DeviceStorageFile::AccumDirectoryUsage(nsIFile* aFile, + uint64_t* aPicturesSoFar, + uint64_t* aVideosSoFar, + uint64_t* aMusicSoFar, + uint64_t* aTotalSoFar) +{ if (!aFile) { +printf_stderr("AccumDirectoryUsage aFile == null\n"); return; } @@ -900,17 +1122,13 @@ DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, rv = aFile->GetDirectoryEntries(getter_AddRefs(e)); if (NS_FAILED(rv) || !e) { +printf_stderr("AccumDirectoryUsage failed to get directory entries\n"); return; } nsCOMPtr files = do_QueryInterface(e); NS_ASSERTION(files, "GetDirectoryEntries must return a nsIDirectoryEnumerator"); - DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet(); - if (!typeChecker) { - return; - } - nsCOMPtr f; while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) { bool isDir; @@ -935,7 +1153,7 @@ DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, // for now, lets just totally ignore symlinks. NS_WARNING("DirectoryDiskUsage ignores symlinks"); } else if (isDir) { - DirectoryDiskUsage(f, aPicturesSoFar, aVideosSoFar, aMusicSoFar, aTotalSoFar); + AccumDirectoryUsage(f, aPicturesSoFar, aVideosSoFar, aMusicSoFar, aTotalSoFar); } else if (isFile) { int64_t size; @@ -962,37 +1180,111 @@ DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, } } +void +DeviceStorageFile::GetDiskFreeSpace(int64_t* aSoFar) +{ + DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet(); + if (!typeChecker) { + return; + } + if (IsComposite()) { + nsDOMDeviceStorage::VolumeNameArray volNames; + nsDOMDeviceStorage::GetOrderedVolumeNames(volNames); + nsDOMDeviceStorage::VolumeNameArray::size_type numVolumes = volNames.Length(); + nsDOMDeviceStorage::VolumeNameArray::index_type i; + for (i = 0; i < numVolumes; i++) { + DeviceStorageFile dsf(mStorageType, volNames[i]); + dsf.GetDiskFreeSpace(aSoFar); + } + return; + } + + if (!mFile || !IsAvailable()) { + return; + } + + int64_t storageAvail = 0; + nsresult rv = mFile->GetDiskSpaceAvailable(&storageAvail); + if (NS_SUCCEEDED(rv)) { + *aSoFar += storageAvail; + } +} + +bool +DeviceStorageFile::IsAvailable() +{ + nsString status; + GetStatus(status); + return status.EqualsLiteral("available"); +} + +void +DeviceStorageFile::GetStatus(nsAString& aStatus) +{ + DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet(); + if (!typeChecker) { + return; + } + if (!typeChecker->IsVolumeBased(mStorageType)) { + aStatus.AssignLiteral("available"); + return; + } + + if (!mStorageName.EqualsLiteral("")) { + GetStatusInternal(mStorageName, aStatus); + return; + } + + // We want a composite status. + + aStatus.AssignLiteral("unavailable"); + + nsDOMDeviceStorage::VolumeNameArray volNames; + nsDOMDeviceStorage::GetOrderedVolumeNames(volNames); + nsDOMDeviceStorage::VolumeNameArray::size_type numVolumes = volNames.Length(); + nsDOMDeviceStorage::VolumeNameArray::index_type i; + for (i = 0; i < numVolumes; i++) { + nsString volStatus; + GetStatusInternal(volNames[i], volStatus); + if (volStatus.EqualsLiteral("available")) { + // We found an available volume. We can quit now, since the composite + // status is available if any are available + aStatus = volStatus; + return; + } + if (volStatus.EqualsLiteral("shared")) { + aStatus = volStatus; + // need to keep looking since we might find an available volume later. + } + } +} + +void +DeviceStorageFile::GetStatusInternal(nsAString& aStorageName, nsAString& aStatus) +{ + aStatus.AssignLiteral("unavailable"); + +#ifdef MOZ_WIDGET_GONK + nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); + NS_ENSURE_TRUE_VOID(vs); + + nsCOMPtr vol; + nsresult rv = vs->GetVolumeByName(aStorageName, getter_AddRefs(vol)); + NS_ENSURE_SUCCESS_VOID(rv); + int32_t volState; + rv = vol->GetState(&volState); + NS_ENSURE_SUCCESS_VOID(rv); + if (volState == nsIVolume::STATE_MOUNTED) { + aStatus.AssignLiteral("available"); + } else if (volState == nsIVolume::STATE_SHARED || volState == nsIVolume::STATE_SHAREDMNT) { + aStatus.AssignLiteral("shared"); + } +#endif +} + NS_IMPL_THREADSAFE_ISUPPORTS0(DeviceStorageFile) #ifdef MOZ_WIDGET_GONK -nsresult -GetSDCardStatus(nsAString& aPath, nsAString& aState) { - nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); - if (!vs) { - return NS_ERROR_FAILURE; - } - nsCOMPtr vol; - vs->GetVolumeByPath(aPath, getter_AddRefs(vol)); - if (!vol) { - return NS_ERROR_FAILURE; - } - - int32_t state; - nsresult rv = vol->GetState(&state); - if (NS_FAILED(rv)) { - return NS_ERROR_FAILURE; - } - - if (state == nsIVolume::STATE_MOUNTED) { - aState.AssignLiteral("available"); - } else if (state == nsIVolume::STATE_SHARED || state == nsIVolume::STATE_SHAREDMNT) { - aState.AssignLiteral("shared"); - } else { - aState.AssignLiteral("unavailable"); - } - return NS_OK; -} - static void RegisterForSDCardChanges(nsIObserver* aObserver) { @@ -1009,30 +1301,15 @@ UnregisterForSDCardChanges(nsIObserver* aObserver) #endif void -nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType, - const nsAString& aVolName) +nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aStorageType, + const nsAString& aStorageName) { nsCOMPtr f; - DeviceStorageFile::GetRootDirectoryForType(aType, - aVolName, + DeviceStorageFile::GetRootDirectoryForType(aStorageType, + aStorageName, getter_AddRefs(f)); - mVolumeName = NS_LITERAL_STRING(""); #ifdef MOZ_WIDGET_GONK - nsString volMountPoint(NS_LITERAL_STRING("/sdcard")); - if (!aVolName.EqualsLiteral("")) { - nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); - if (vs) { - nsresult rv; - nsCOMPtr vol; - rv = vs->GetVolumeByName(aVolName, getter_AddRefs(vol)); - if (NS_SUCCEEDED(rv)) { - vol->GetMountPoint(volMountPoint); - mVolumeName = aVolName; - } - } - } - RegisterForSDCardChanges(this); #endif @@ -1040,7 +1317,8 @@ nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType, obs->AddObserver(this, "file-watcher-update", false); obs->AddObserver(this, "disk-space-watcher", false); mRootDirectory = f; - mStorageType = aType; + mStorageType = aStorageType; + mStorageName = aStorageName; } JS::Value @@ -1090,7 +1368,9 @@ nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile) return JSVAL_NULL; } - nsCOMPtr blob = new nsDOMFileFile(aFile->mFile, aFile->mPath, + nsString compositePath; + aFile->GetCompositePath(compositePath); + nsCOMPtr blob = new nsDOMFileFile(aFile->mFile, compositePath, EmptyString()); return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob)); } @@ -1262,7 +1542,10 @@ ContinueCursorEvent::Continue() DeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, file); child->SetCallback(cursor); - DeviceStorageGetParams params(cursorStorageType, file->mRootDir, file->mPath); + DeviceStorageGetParams params(cursorStorageType, + file->mStorageName, + file->mRootDir, + file->mPath); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); mRequest = nullptr; } @@ -1404,7 +1687,10 @@ nsDOMDeviceStorageCursor::Allow() if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(this, mFile); - DeviceStorageEnumerationParams params(mFile->mStorageType, mFile->mRootDir, mSince); + DeviceStorageEnumerationParams params(mFile->mStorageType, + mFile->mStorageName, + mFile->mRootDir, + mSince); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -1480,18 +1766,10 @@ public: { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - nsString state; - state.Assign(NS_LITERAL_STRING("available")); -#ifdef MOZ_WIDGET_GONK - nsString path; - nsresult rv = mFile->mFile->GetPath(path); - if (NS_SUCCEEDED(rv)) { - rv = GetSDCardStatus(path, state); + nsString state = NS_LITERAL_STRING("unavailable"); + if (mFile) { + mFile->GetStatus(state); } - if (NS_FAILED(rv)) { - state.Assign(NS_LITERAL_STRING("unavailable")); - } -#endif JS::Value result = StringToJsval(mRequest->GetOwner(), state); mRequest->FireSuccess(result); @@ -1692,40 +1970,20 @@ public: { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - DeviceStorageUsedSpaceCache* usedSpaceCache = DeviceStorageUsedSpaceCache::CreateOrGet(); - NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null"); - - uint64_t usedSize; - nsresult rv = usedSpaceCache->GetUsedSizeForType(mFile->mStorageType, &usedSize); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr r = new PostResultEvent(mRequest, usedSize); - NS_DispatchToMainThread(r); - return NS_OK; - } - uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0; - DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &picturesUsage, - &videosUsage, &musicUsage, - &totalUsage); + mFile->AccumDiskUsage(&picturesUsage, &videosUsage, + &musicUsage, &totalUsage); nsCOMPtr r; - - if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) { - // Don't cache this since it's for a different volume from the media. - r = new PostResultEvent(mRequest, totalUsage); + if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { + r = new PostResultEvent(mRequest, picturesUsage); + } + else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { + r = new PostResultEvent(mRequest, videosUsage); + } + else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { + r = new PostResultEvent(mRequest, musicUsage); } else { - usedSpaceCache->SetUsedSizes(picturesUsage, videosUsage, musicUsage, totalUsage); - - if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { - r = new PostResultEvent(mRequest, picturesUsage); - } - else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { - r = new PostResultEvent(mRequest, videosUsage); - } - else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { - r = new PostResultEvent(mRequest, musicUsage); - } else { - r = new PostResultEvent(mRequest, totalUsage); - } + r = new PostResultEvent(mRequest, totalUsage); } NS_DispatchToMainThread(r); @@ -1751,13 +2009,13 @@ public: NS_IMETHOD Run() { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - nsCOMPtr r; + int64_t freeSpace = 0; - nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace); - if (NS_FAILED(rv)) { - freeSpace = 0; + if (mFile) { + mFile->GetDiskFreeSpace(&freeSpace); } + nsCOMPtr r; r = new PostResultEvent(mRequest, static_cast(freeSpace)); NS_DispatchToMainThread(r); return NS_OK; @@ -1903,7 +2161,7 @@ public: switch(mRequestType) { case DEVICE_STORAGE_REQUEST_CREATE: { - if (!mBlob) { + if (!mBlob || !mFile->mFile) { return NS_ERROR_FAILURE; } @@ -1929,6 +2187,7 @@ public: DeviceStorageAddParams params; params.blobChild() = actor; params.type() = mFile->mStorageType; + params.storageName() = mFile->mStorageName; params.relpath() = mFile->mPath; PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); @@ -1942,6 +2201,10 @@ public: case DEVICE_STORAGE_REQUEST_READ: case DEVICE_STORAGE_REQUEST_WRITE: { + if (!mFile->mFile) { + return NS_ERROR_FAILURE; + } + DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet(); if (!typeChecker) { return NS_OK; @@ -1955,7 +2218,10 @@ public: if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageGetParams params(mFile->mStorageType, mFile->mRootDir, mFile->mPath); + DeviceStorageGetParams params(mFile->mStorageType, + mFile->mStorageName, + mFile->mRootDir, + mFile->mPath); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -1966,6 +2232,10 @@ public: case DEVICE_STORAGE_REQUEST_DELETE: { + if (!mFile->mFile) { + return NS_ERROR_FAILURE; + } + DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet(); if (!typeChecker) { return NS_OK; @@ -1979,7 +2249,9 @@ public: if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageDeleteParams params(mFile->mStorageType, mFile->mPath); + DeviceStorageDeleteParams params(mFile->mStorageType, + mFile->mStorageName, + mFile->mPath); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -1991,7 +2263,8 @@ public: { if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageFreeSpaceParams params(mFile->mStorageType, mFile->mPath); + DeviceStorageFreeSpaceParams params(mFile->mStorageType, + mFile->mStorageName); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -2003,7 +2276,8 @@ public: { if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageUsedSpaceParams params(mFile->mStorageType, mFile->mPath); + DeviceStorageUsedSpaceParams params(mFile->mStorageType, + mFile->mStorageName); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -2020,7 +2294,8 @@ public: { if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageAvailableParams params(mFile->mStorageType, mFile->mPath); + DeviceStorageAvailableParams params(mFile->mStorageType, + mFile->mStorageName); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -2104,6 +2379,17 @@ nsDOMDeviceStorage::nsDOMDeviceStorage() , mAllowedToWatchFile(false) { } +nsresult +nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType, + nsTArray > &aStores) +{ + mStores.AppendElements(aStores); + nsresult rv = Init(aWindow, aType, NS_LITERAL_STRING("")); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + nsresult nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType, const nsAString &aVolName) { @@ -2112,9 +2398,13 @@ nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType, const n NS_ASSERTION(aWindow, "Must have a content dom"); - SetRootDirectoryForType(aType, aVolName); - if (!mRootDirectory) { - return NS_ERROR_NOT_AVAILABLE; + if (IsComposite()) { + mStorageType = aType; + } else { + SetRootDirectoryForType(aType, aVolName); + if (!mRootDirectory) { + return NS_ERROR_NOT_AVAILABLE; + } } BindToOwner(aWindow); @@ -2165,33 +2455,71 @@ nsDOMDeviceStorage::Shutdown() obs->RemoveObserver(this, "disk-space-watcher"); } +StaticRefPtr nsDOMDeviceStorage::sVolumeNameCache; + +// static void -nsDOMDeviceStorage::GetOrderedVolumeNames(const nsAString &aType, - nsTArray &aVolumeNames) +nsDOMDeviceStorage::GetOrderedVolumeNames(nsDOMDeviceStorage::VolumeNameArray &aVolumeNames) { + if (sVolumeNameCache && sVolumeNameCache->mVolumeNames.Length() > 0) { + aVolumeNames.AppendElements(sVolumeNameCache->mVolumeNames); + return; + } #ifdef MOZ_WIDGET_GONK - if (DeviceStorageTypeChecker::IsVolumeBased(aType)) { - nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); - if (vs) { - vs->GetVolumeNames(aVolumeNames); + nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); + if (vs) { + vs->GetVolumeNames(aVolumeNames); - // If the volume sdcard exists, then we want it to be first. + // If the volume sdcard exists, then we want it to be first. - nsTArray::index_type sdcardIndex; - sdcardIndex = aVolumeNames.IndexOf(NS_LITERAL_STRING("sdcard")); - if ((sdcardIndex != nsTArray::NoIndex) - && (sdcardIndex > 0)) { - aVolumeNames.RemoveElementAt(sdcardIndex); - aVolumeNames.InsertElementAt(0, NS_LITERAL_STRING("sdcard")); - } + VolumeNameArray::index_type sdcardIndex; + sdcardIndex = aVolumeNames.IndexOf(NS_LITERAL_STRING("sdcard")); + if ((sdcardIndex != VolumeNameArray::NoIndex) + && (sdcardIndex > 0)) { + aVolumeNames.RemoveElementAt(sdcardIndex); + aVolumeNames.InsertElementAt(0, NS_LITERAL_STRING("sdcard")); } } #endif if (aVolumeNames.Length() == 0) { aVolumeNames.AppendElement(NS_LITERAL_STRING("")); } + sVolumeNameCache = new VolumeNameCache; + sVolumeNameCache->mVolumeNames.AppendElements(aVolumeNames); } +// static +void +nsDOMDeviceStorage::CreateDeviceStorageFor(nsPIDOMWindow* aWin, + const nsAString &aType, + nsDOMDeviceStorage** aStore) +{ + // Create the underlying non-composite device storage objects + nsTArray > stores; + CreateDeviceStoragesFor(aWin, aType, stores); + if (stores.Length() == 0) { + *aStore = nullptr; + return; + } + + if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) { + // Since the storage type isn't volume based, don't bother creating + // a composite object. Just use the one we got. + NS_ASSERTION(stores.Length() == 1, "Only expecting a single storage object"); + NS_ADDREF(*aStore = stores[0].get()); + return; + } + + // Create the composite device storage object + nsRefPtr composite = new nsDOMDeviceStorage(); + if (NS_FAILED(composite->Init(aWin, aType, stores))) { + *aStore = nullptr; + return; + } + NS_ADDREF(*aStore = composite.get()); +} + +// static void nsDOMDeviceStorage::CreateDeviceStoragesFor(nsPIDOMWindow* aWin, const nsAString &aType, @@ -2204,15 +2532,14 @@ nsDOMDeviceStorage::CreateDeviceStoragesFor(nsPIDOMWindow* aWin, rv = storage->Init(aWin, aType, NS_LITERAL_STRING("")); if (NS_SUCCEEDED(rv)) { aStores.AppendElement(storage); - } else { } return; } - nsTArray volNames; - GetOrderedVolumeNames(aType, volNames); + VolumeNameArray volNames; + GetOrderedVolumeNames(volNames); - nsTArray::size_type numVolumeNames = volNames.Length(); - for (nsTArray::index_type i = 0; i < numVolumeNames; i++) { + VolumeNameArray::size_type numVolumeNames = volNames.Length(); + for (VolumeNameArray::index_type i = 0; i < numVolumeNames; i++) { nsRefPtr storage = new nsDOMDeviceStorage(); rv = storage->Init(aWin, aType, volNames[i]); if (NS_FAILED(rv)) { @@ -2222,6 +2549,119 @@ nsDOMDeviceStorage::CreateDeviceStoragesFor(nsPIDOMWindow* aWin, } } +// static +bool +nsDOMDeviceStorage::ParseCompositePath(const nsAString& aCompositePath, + nsAString& aOutStorageName, + nsAString& aOutStoragePath) +{ + aOutStorageName.AssignLiteral(""); + aOutStoragePath.AssignLiteral(""); + + NS_NAMED_LITERAL_STRING(slash, "/"); + + nsDependentSubstring storageName; + + if (StringBeginsWith(aCompositePath, slash)) { + int32_t slashIndex = aCompositePath.FindChar('/', 1); + if (slashIndex == kNotFound) { + // names of the form /filename are illegal + return false; + } + storageName.Rebind(aCompositePath, 1, slashIndex - 1); + aOutStoragePath = Substring(aCompositePath, slashIndex + 1); + } else { + aOutStoragePath = aCompositePath; + } + + if (!storageName.EqualsLiteral("")) { + aOutStorageName = storageName; + return true; + } + + DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet(); + NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null"); + nsString storageType; + typeChecker->GetTypeFromFileName(aOutStoragePath, storageType); + + nsString defStorageName; + GetWritableStorageName(storageType, defStorageName); + if (defStorageName.EqualsLiteral("")) { + return false; + } + aOutStorageName = defStorageName; + return true; +} + +already_AddRefed +nsDOMDeviceStorage::GetStorage(const nsAString& aCompositePath, nsAString& aOutStoragePath) +{ + MOZ_ASSERT(IsComposite()); + + nsString storageName; + if (!ParseCompositePath(aCompositePath, storageName, aOutStoragePath)) { + return nullptr; + } + + nsRefPtr ds; + ds = GetStorageByName(storageName); + return ds.forget(); +} + +already_AddRefed +nsDOMDeviceStorage::GetStorageByName(const nsAString& aStorageName) +{ + nsTArray >::size_type n = mStores.Length(); + nsTArray >::index_type i; + for (i = 0; i < n; i++) { + nsRefPtr ds = mStores[i]; + if (ds->mStorageName == aStorageName) { + return ds.forget(); + } + } + return nullptr; +} + +// static +void +nsDOMDeviceStorage::GetWritableStorageName(const nsAString& aStorageType, + nsAString& aStorageName) +{ + // See if the preferred volume is available. + nsRefPtr ds; + nsAdoptingString prefStorageName = + mozilla::Preferences::GetString("device.storage.writable.name"); + if (prefStorageName) { + DeviceStorageFile dsf(aStorageType, prefStorageName); + if (dsf.IsAvailable()) { + aStorageName = prefStorageName; + return; + } + } + + // No preferred storage, or the preferred storage is unavailable. Find + // the first available storage area. + + VolumeNameArray volNames; + GetOrderedVolumeNames(volNames); + VolumeNameArray::size_type numVolumes = volNames.Length(); + VolumeNameArray::index_type i; + for (i = 0; i < numVolumes; i++) { + DeviceStorageFile dsf(aStorageType, volNames[i]); + if (dsf.IsAvailable()) { + aStorageName = volNames[i]; + return; + } + } +} + +bool +nsDOMDeviceStorage::IsAvailable() +{ + DeviceStorageFile dsf(mStorageType, mStorageName); + return dsf.IsAvailable(); +} + NS_IMETHODIMP nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval) { @@ -2247,8 +2687,8 @@ nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval) // will post an onerror to the requestee. // possible race here w/ unique filename - char buffer[128]; - NS_MakeRandomString(buffer, ArrayLength(buffer)); + char buffer[32]; + NS_MakeRandomString(buffer, ArrayLength(buffer) - 1); nsAutoCString path; path.Assign(nsDependentCString(buffer)); @@ -2277,12 +2717,26 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob, return NS_ERROR_FAILURE; } + nsCOMPtr r; + + if (IsComposite()) { + nsString storagePath; + nsRefPtr ds = GetStorage(aPath, storagePath); + if (!ds) { + nsRefPtr request = new DOMRequest(win); + NS_ADDREF(*_retval = request); + r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN); + NS_DispatchToMainThread(r); + } + return ds->AddNamed(aBlob, storagePath, _retval); + } + nsRefPtr request = new DOMRequest(win); NS_ADDREF(*_retval = request); - nsCOMPtr r; - - nsRefPtr dsf = new DeviceStorageFile(mStorageType, aPath); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, + mStorageName, + aPath); if (!dsf->IsSafePath()) { r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED); } else if (!typeChecker->Check(mStorageType, dsf->mFile) || @@ -2338,13 +2792,35 @@ nsDOMDeviceStorage::GetInternal(const JS::Value & aPath, return NS_OK; } - nsRefPtr dsf = new DeviceStorageFile(mStorageType, path); + if (IsComposite()) { + nsString storagePath; + nsRefPtr ds = GetStorage(path, storagePath); + if (!ds) { + r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN); + NS_DispatchToMainThread(r); + return NS_OK; + } + return ds->GetInternal(win, storagePath, request.get(), aEditable); + } + return GetInternal(win, path, request.get(), aEditable); +} + +nsresult +nsDOMDeviceStorage::GetInternal(nsPIDOMWindow *aWin, + const nsAString& aPath, + DOMRequest* aRequest, + bool aEditable) +{ + nsCOMPtr r; + nsRefPtr dsf = new DeviceStorageFile(mStorageType, + mStorageName, + aPath); dsf->SetEditable(aEditable); if (!dsf->IsSafePath()) { - r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED); + r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED); } else { r = new DeviceStorageRequest(aEditable ? DEVICE_STORAGE_REQUEST_WRITE : DEVICE_STORAGE_REQUEST_READ, - win, mPrincipal, dsf, request); + aWin, mPrincipal, dsf, aRequest); } NS_DispatchToMainThread(r); return NS_OK; @@ -2371,14 +2847,34 @@ nsDOMDeviceStorage::Delete(const JS::Value & aPath, JSContext* aCx, nsIDOMDOMReq return NS_OK; } - nsRefPtr dsf = new DeviceStorageFile(mStorageType, path); + if (IsComposite()) { + nsString storagePath; + nsRefPtr ds = GetStorage(path, storagePath); + if (!ds) { + r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN); + NS_DispatchToMainThread(r); + return NS_OK; + } + return ds->DeleteInternal(win, storagePath, request.get()); + } + return DeleteInternal(win, path, request.get()); +} +nsresult +nsDOMDeviceStorage::DeleteInternal(nsPIDOMWindow *aWin, + const nsAString& aPath, + mozilla::dom::DOMRequest* aRequest) +{ + nsCOMPtr r; + nsRefPtr dsf = new DeviceStorageFile(mStorageType, + mStorageName, + aPath); if (!dsf->IsSafePath()) { - r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED); + r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED); } else { r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE, - win, mPrincipal, dsf, request); + aWin, mPrincipal, dsf, aRequest); } NS_DispatchToMainThread(r); return NS_OK; @@ -2395,7 +2891,8 @@ nsDOMDeviceStorage::FreeSpace(nsIDOMDOMRequest** aRetval) nsRefPtr request = new DOMRequest(win); NS_ADDREF(*aRetval = request); - nsRefPtr dsf = new DeviceStorageFile(mStorageType); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, + mStorageName); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE, win, mPrincipal, @@ -2419,7 +2916,8 @@ nsDOMDeviceStorage::UsedSpace(nsIDOMDOMRequest** aRetval) nsRefPtr request = new DOMRequest(win); NS_ADDREF(*aRetval = request); - nsRefPtr dsf = new DeviceStorageFile(mStorageType); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, + mStorageName); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE, win, mPrincipal, @@ -2440,7 +2938,8 @@ nsDOMDeviceStorage::Available(nsIDOMDOMRequest** aRetval) nsRefPtr request = new DOMRequest(win); NS_ADDREF(*aRetval = request); - nsRefPtr dsf = new DeviceStorageFile(mStorageType); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, + mStorageName); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE, win, mPrincipal, @@ -2460,9 +2959,9 @@ nsDOMDeviceStorage::GetRootDirectory(nsIFile** aRootDirectory) } NS_IMETHODIMP -nsDOMDeviceStorage::GetVolumeName(nsAString & aVolumeName) +nsDOMDeviceStorage::GetStorageName(nsAString& aStorageName) { - aVolumeName = mVolumeName; + aStorageName = mStorageName; return NS_OK; } @@ -2540,7 +3039,10 @@ nsDOMDeviceStorage::EnumerateInternal(const JS::Value & aName, since = ExtractDateFromOptions(aCx, aOptions); } - nsRefPtr dsf = new DeviceStorageFile(mStorageType, path, NS_LITERAL_STRING("")); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, + mStorageName, + path, + NS_LITERAL_STRING("")); dsf->SetEditable(aEditable); nsRefPtr cursor = new nsDOMDeviceStorageCursor(win, mPrincipal, @@ -2587,7 +3089,8 @@ nsDOMDeviceStorage::EnumerateInternal(const JS::Value & aName, #ifdef MOZ_WIDGET_GONK void -nsDOMDeviceStorage::DispatchMountChangeEvent(nsAString& aType) +nsDOMDeviceStorage::DispatchMountChangeEvent(nsAString& aVolumeName, + nsAString& aVolumeStatus) { nsCOMPtr event; NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this, nullptr, nullptr); @@ -2595,8 +3098,8 @@ nsDOMDeviceStorage::DispatchMountChangeEvent(nsAString& aType) nsCOMPtr ce = do_QueryInterface(event); nsresult rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"), true, false, - NS_LITERAL_STRING(""), - aType); + aVolumeName, + aVolumeStatus); if (NS_FAILED(rv)) { return; } @@ -2616,12 +3119,14 @@ nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRU DeviceStorageFile* file = static_cast(aSubject); Notify(NS_ConvertUTF16toUTF8(aData).get(), file); return NS_OK; - } else if (!strcmp(aTopic, "disk-space-watcher")) { + } + if (!strcmp(aTopic, "disk-space-watcher")) { // 'disk-space-watcher' notifications are sent when there is a modification // of a file in a specific location while a low device storage situation // exists or after recovery of a low storage situation. For Firefox OS, // these notifications are specific for apps storage. - nsRefPtr file = new DeviceStorageFile(mStorageType); + nsRefPtr file = + new DeviceStorageFile(mStorageType, mStorageName); if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "full")) { Notify("low-disk-space", file); } else if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "free")) { @@ -2636,12 +3141,6 @@ nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRU if (!vol) { return NS_OK; } - nsString volName; - vol->GetName(volName); - if (!volName.EqualsLiteral("sdcard")) { - return NS_OK; - } - int32_t state; nsresult rv = vol->GetState(&state); if (NS_FAILED(rv)) { @@ -2659,12 +3158,14 @@ nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRU // ignore anything else. return NS_OK; } + nsString volName; + vol->GetName(volName); DeviceStorageUsedSpaceCache* usedSpaceCache = DeviceStorageUsedSpaceCache::CreateOrGet(); NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null"); - usedSpaceCache->Invalidate(); + usedSpaceCache->Invalidate(volName); - DispatchMountChangeEvent(type); + DispatchMountChangeEvent(volName, type); return NS_OK; } #endif @@ -2678,32 +3179,12 @@ nsDOMDeviceStorage::Notify(const char* aReason, DeviceStorageFile* aFile) return NS_OK; } - if (!mStorageType.Equals(aFile->mStorageType)) { + if (!mStorageType.Equals(aFile->mStorageType) || + !mStorageName.Equals(aFile->mStorageName)) { // Ignore this return NS_OK; } - if (!mRootDirectory) { - return NS_ERROR_FAILURE; - } - - nsString rootpath; - nsresult rv = mRootDirectory->GetPath(rootpath); - if (NS_FAILED(rv)) { - return NS_OK; - } - - nsString fullpath; - rv = aFile->mFile->GetPath(fullpath); - if (NS_FAILED(rv)) { - return NS_OK; - } - - NS_ASSERTION(fullpath.Length() >= rootpath.Length(), "Root path longer than full path!"); - - nsAString::size_type len = rootpath.Length() + 1; // +1 for the trailing / - nsDependentSubstring newPath (fullpath, len, fullpath.Length() - len); - nsCOMPtr event; NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this, nullptr, nullptr); @@ -2712,7 +3193,12 @@ nsDOMDeviceStorage::Notify(const char* aReason, DeviceStorageFile* aFile) nsString reason; reason.AssignWithConversion(aReason); - rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"), true, false, newPath, reason); + + nsString compositePath; + aFile->GetCompositePath(compositePath); + nsresult rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"), + true, false, compositePath, + reason); NS_ENSURE_SUCCESS(rv, rv); bool ignore; @@ -2731,9 +3217,19 @@ nsDOMDeviceStorage::AddEventListener(const nsAString & aType, if (!win) { return NS_ERROR_UNEXPECTED; } + if (IsComposite()) { + nsTArray >::size_type n = mStores.Length(); + nsTArray >::index_type i; + for (i = 0; i < n; i++) { + nsresult rv = mStores[i]->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted, aArgc); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; + } nsRefPtr request = new DOMRequest(win); - nsRefPtr dsf = new DeviceStorageFile(mStorageType); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, + mStorageName); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH, win, mPrincipal, dsf, request, this); NS_DispatchToMainThread(r); @@ -2752,9 +3248,18 @@ nsDOMDeviceStorage::AddEventListener(const nsAString & aType, aRv.Throw(NS_ERROR_UNEXPECTED); return; } + if (IsComposite()) { + nsTArray >::size_type n = mStores.Length(); + nsTArray >::index_type i; + for (i = 0; i < n; i++) { + mStores[i]->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted, aRv); + } + return; + } nsRefPtr request = new DOMRequest(win); - nsRefPtr dsf = new DeviceStorageFile(mStorageType); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, + mStorageName); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH, win, mPrincipal, dsf, request, this); NS_DispatchToMainThread(r); @@ -2768,6 +3273,15 @@ nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType, bool aWantsUntrusted, uint8_t aArgc) { + if (IsComposite()) { + nsTArray >::size_type n = mStores.Length(); + nsTArray >::index_type i; + for (i = 0; i < n; i++) { + nsresult rv = mStores[i]->AddSystemEventListener(aType, aListener, aUseCapture, aWantsUntrusted, aArgc); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; + } if (!mIsWatchingFile) { nsCOMPtr obs = mozilla::services::GetObserverService(); obs->AddObserver(this, "file-watcher-update", false); @@ -2782,6 +3296,15 @@ nsDOMDeviceStorage::RemoveEventListener(const nsAString & aType, nsIDOMEventListener *aListener, bool aUseCapture) { + if (IsComposite()) { + nsTArray >::size_type n = mStores.Length(); + nsTArray >::index_type i; + for (i = 0; i < n; i++) { + nsresult rv = mStores[i]->RemoveEventListener(aType, aListener, aUseCapture); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; + } nsDOMEventTargetHelper::RemoveEventListener(aType, aListener, false); if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) { @@ -2798,6 +3321,14 @@ nsDOMDeviceStorage::RemoveEventListener(const nsAString& aType, bool aCapture, ErrorResult& aRv) { + if (IsComposite()) { + nsTArray >::size_type n = mStores.Length(); + nsTArray >::index_type i; + for (i = 0; i < n; i++) { + mStores[i]->RemoveEventListener(aType, aListener, aCapture, aRv); + } + return; + } nsDOMEventTargetHelper::RemoveEventListener(aType, aListener, aCapture, aRv); if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) { diff --git a/dom/devicestorage/nsDeviceStorage.h b/dom/devicestorage/nsDeviceStorage.h index 3339422b551..03b54008e0d 100644 --- a/dom/devicestorage/nsDeviceStorage.h +++ b/dom/devicestorage/nsDeviceStorage.h @@ -28,6 +28,7 @@ class nsPIDOMWindow; #include "prtime.h" #include "DeviceStorage.h" #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h" +#include "mozilla/RefPtr.h" #include "mozilla/StaticPtr.h" namespace mozilla { @@ -64,52 +65,71 @@ public: class InvalidateRunnable MOZ_FINAL : public nsRunnable { public: - InvalidateRunnable(DeviceStorageUsedSpaceCache* aCache) { - mOwner = aCache; - } + InvalidateRunnable(DeviceStorageUsedSpaceCache* aCache, + const nsAString& aStorageName) + : mCache(aCache) + , mStorageName(aStorageName) {} ~InvalidateRunnable() {} - NS_IMETHOD Run() { - mOwner->mDirty = true; + NS_IMETHOD Run() + { + mozilla::RefPtr cacheEntry; + cacheEntry = mCache->GetCacheEntry(mStorageName); + if (cacheEntry) { + cacheEntry->mDirty = true; + } return NS_OK; } private: - DeviceStorageUsedSpaceCache* mOwner; + DeviceStorageUsedSpaceCache* mCache; + nsString mStorageName; }; - void Invalidate() { + void Invalidate(const nsAString& aStorageName) + { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(mIOThread, "Null mIOThread!"); - nsRefPtr r = new InvalidateRunnable(this); + nsRefPtr r = new InvalidateRunnable(this, aStorageName); mIOThread->Dispatch(r, NS_DISPATCH_NORMAL); } - void Dispatch(nsIRunnable* aRunnable) { + void Dispatch(nsIRunnable* aRunnable) + { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(mIOThread, "Null mIOThread!"); mIOThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL); } - nsresult GetUsedSizeForType(const nsAString& aStorageType, uint64_t* usedSize); - void SetUsedSizes(uint64_t aPictureSize, uint64_t aVideosSize, + nsresult GetUsedSizeForType(const nsAString& aStorageType, + const nsAString& aStorageName, + uint64_t* usedSize); + nsresult AccumUsedSizes(const nsAString& aStorageName, + uint64_t* aPictureSize, uint64_t* aVideosSize, + uint64_t* aMusicSize, uint64_t* aTotalSize); + + void SetUsedSizes(const nsAString& aStorageName, + uint64_t aPictureSize, uint64_t aVideosSize, uint64_t aMusicSize, uint64_t aTotalSize); private: friend class InvalidateRunnable; - nsresult GetPicturesUsedSize(uint64_t* usedSize); - nsresult GetMusicUsedSize(uint64_t* usedSize); - nsresult GetVideosUsedSize(uint64_t* usedSize); - nsresult GetTotalUsedSize(uint64_t* usedSize); + class CacheEntry : public mozilla::RefCounted + { + public: + bool mDirty; + nsString mStorageName; + uint64_t mPicturesUsedSize; + uint64_t mVideosUsedSize; + uint64_t mMusicUsedSize; + uint64_t mTotalUsedSize; + }; + mozilla::TemporaryRef GetCacheEntry(const nsAString& aStorageName); - bool mDirty; - uint64_t mPicturesUsedSize; - uint64_t mVideosUsedSize; - uint64_t mMusicUsedSize; - uint64_t mTotalUsedSize; + nsTArray > mCacheEntries; nsCOMPtr mIOThread; @@ -129,6 +149,7 @@ public: bool Check(const nsAString& aType, nsIDOMBlob* aBlob); bool Check(const nsAString& aType, nsIFile* aFile); void GetTypeFromFile(nsIFile* aFile, nsAString& aType); + void GetTypeFromFileName(const nsAString& aFileName, nsAString& aType); static nsresult GetPermissionForType(const nsAString& aType, nsACString& aPermissionResult); static nsresult GetAccessForRequest(const DeviceStorageRequestType aRequestType, nsACString& aAccessResult); @@ -204,8 +225,4 @@ nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile); JS::Value InterfaceToJsval(nsPIDOMWindow* aWindow, nsISupports* aObject, const nsIID* aIID); -#ifdef MOZ_WIDGET_GONK -nsresult GetSDCardStatus(nsAString& aPath, nsAString& aState); -#endif - #endif diff --git a/dom/devicestorage/test/test_823965.html b/dom/devicestorage/test/test_823965.html index c751168e432..f51df07d5c8 100644 --- a/dom/devicestorage/test/test_823965.html +++ b/dom/devicestorage/test/test_823965.html @@ -61,7 +61,19 @@ function getError(e) { function addSuccess(e) { - ok(e.target.result == gFileName, "File name should match"); + var filename = e.target.result; + if (filename[0] == "/") { + // We got /storgaeName/prefix/filename + // Remove the storageName (this shows up on FirefoxOS) + filename = filename.substring(1); // Remove leading slash + var slashIndex = filename.indexOf("/"); + if (slashIndex >= 0) { + filename = filename.substring(slashIndex + 1); // Remove storageName + } + } + ok(filename == gFileName, "File name should match"); + // Since we now have the fully qualified name, change gFileName to that. + gFileName = e.target.result; var storage = navigator.getDeviceStorage("pictures"); request = storage.get(gFileName); diff --git a/dom/devicestorage/test/test_enumerate.html b/dom/devicestorage/test/test_enumerate.html index 8b486463c30..61ca591d5a5 100644 --- a/dom/devicestorage/test/test_enumerate.html +++ b/dom/devicestorage/test/test_enumerate.html @@ -34,6 +34,18 @@ function enumerateSuccess(e) { } var filename = e.target.result.name; + if (filename[0] == "/") { + // We got /storgaeName/prefix/filename + // Remove the storageName (this shows up on FirefoxOS) + filename = filename.substring(1); // Remove leading slash + var slashIndex = filename.indexOf("/"); + if (slashIndex >= 0) { + filename = filename.substring(slashIndex + 1); // Remove storageName + } + } + if (filename.startsWith(prefix)) { + filename = filename.substring(prefix.length + 1); // Remove prefix + } var index = files.indexOf(filename); files.remove(index); @@ -41,7 +53,7 @@ function enumerateSuccess(e) { ok(index > -1, "filename should be in the enumeration : " + e.target.result.name); // clean up - var cleanup = storage.delete(prefix + "/" + filename); + var cleanup = storage.delete(e.target.result.name); cleanup.onsuccess = function(e) {} // todo - can i remove this? e.target.continue(); diff --git a/dom/devicestorage/test/test_enumerateNoParam.html b/dom/devicestorage/test/test_enumerateNoParam.html index fec6bf75aa2..334061e7dee 100644 --- a/dom/devicestorage/test/test_enumerateNoParam.html +++ b/dom/devicestorage/test/test_enumerateNoParam.html @@ -36,16 +36,30 @@ function enumerateSuccess(e) { if (e.target.result == null) { ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array") devicestorage_cleanup(); + return; } var filename = e.target.result.name; - var index = files.indexOf(filename); + if (filename[0] == "/") { + // We got /storgaeName/prefix/filename + // Remove the storageName (this shows up on FirefoxOS) + filename = filename.substring(1); // Remove leading slash + var slashIndex = filename.indexOf("/"); + if (slashIndex >= 0) { + filename = filename.substring(slashIndex + 1); // Remove storageName + } + } + if (filename.startsWith(prefix)) { + filename = filename.substring(prefix.length + 1); // Remove prefix + } + + var index = files.indexOf(enumFilename); files.remove(index); ok(index > -1, "filename should be in the enumeration : " + e.target.result.name); // clean up - var cleanup = storage.delete(prefix + "/" + filename); + var cleanup = storage.delete(e.target.result.name); cleanup.onsuccess = function(e) {} // todo - can i remove this? e.target.continue(); diff --git a/dom/devicestorage/test/test_lastModificationFilter.html b/dom/devicestorage/test/test_lastModificationFilter.html index 20d3342a995..baa10e64d60 100644 --- a/dom/devicestorage/test/test_lastModificationFilter.html +++ b/dom/devicestorage/test/test_lastModificationFilter.html @@ -35,6 +35,18 @@ function verifyAndDelete(prefix, files, e) { } var filename = e.target.result.name; + if (filename[0] == "/") { + // We got /storgaeName/prefix/filename + // Remove the storageName (this shows up on FirefoxOS) + filename = filename.substring(1); // Remove leading slash + var slashIndex = filename.indexOf("/"); + if (slashIndex >= 0) { + filename = filename.substring(slashIndex + 1); // Remove storageName + } + } + if (filename.startsWith(prefix)) { + filename = filename.substring(prefix.length + 1); // Remove prefix + } var index = files.indexOf(filename); ok(index > -1, "filename should be in the enumeration : " + e.target.result.name); diff --git a/dom/devicestorage/test/test_watch.html b/dom/devicestorage/test/test_watch.html index b2b9343a353..dd6ca40a884 100644 --- a/dom/devicestorage/test/test_watch.html +++ b/dom/devicestorage/test/test_watch.html @@ -38,7 +38,17 @@ function onChange(e) { dump("we saw: " + e.path + " " + e.reason + "\n"); - if (e.path == gFileName) { + var filename = e.path; + if (filename[0] == "/") { + // We got /storgaeName/prefix/filename + // Remove the storageName (this shows up on FirefoxOS) + filename = filename.substring(1); // Remove leading slash + var slashIndex = filename.indexOf("/"); + if (slashIndex >= 0) { + filename = filename.substring(slashIndex + 1); // Remove storageName + } + } + if (filename == gFileName) { ok(true, "we saw the file get created"); storage.removeEventListener("change", onChange); devicestorage_cleanup(); diff --git a/dom/devicestorage/test/test_watchOther.html b/dom/devicestorage/test/test_watchOther.html index d975cafc323..24fca9cd838 100644 --- a/dom/devicestorage/test/test_watchOther.html +++ b/dom/devicestorage/test/test_watchOther.html @@ -38,7 +38,17 @@ function onChange(e) { dump("we saw: " + e.path + " " + e.reason + "\n"); - if (e.path == gFileName) { + var filename = e.path; + if (filename[0] == "/") { + // We got /storgaeName/prefix/filename + // Remove the storageName (this shows up on FirefoxOS) + filename = filename.substring(1); // Remove leading slash + var slashIndex = filename.indexOf("/"); + if (slashIndex >= 0) { + filename = filename.substring(slashIndex + 1); // Remove storageName + } + } + if (filename == gFileName) { ok(true, "we saw the file get created"); storage.removeEventListener("change", onChange); devicestorage_cleanup(); diff --git a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl index 38342fc086f..b2322e36017 100644 --- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl +++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl @@ -16,7 +16,7 @@ dictionary DeviceStorageEnumerationParameters jsval since; }; -[scriptable, uuid(b6274c63-daa2-4c7a-aa45-b72e45b5f2d2), builtinclass] +[scriptable, uuid(3d336180-b130-4b54-b205-ce74e36e733f), builtinclass] interface nsIDOMDeviceStorage : nsIDOMEventTarget { [implicit_jscontext] attribute jsval onchange; @@ -44,9 +44,9 @@ interface nsIDOMDeviceStorage : nsIDOMEventTarget nsIDOMDOMRequest available(); - // Note that the volumeName is just a name (like sdcard), and doesn't + // Note that the storageName is just a name (like sdcard), and doesn't // include any path information. - readonly attribute DOMString volumeName; + readonly attribute DOMString storageName; [noscript] readonly attribute nsIFile rootDirectory; }; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index f32d7a78bfe..e49d1a12baa 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1162,9 +1162,12 @@ ContentChild::RecvLastPrivateDocShellDestroyed() } bool -ContentChild::RecvFilePathUpdate(const nsString& type, const nsString& path, const nsCString& aReason) +ContentChild::RecvFilePathUpdate(const nsString& aStorageType, + const nsString& aStorageName, + const nsString& aPath, + const nsCString& aReason) { - nsRefPtr dsf = new DeviceStorageFile(type, path); + nsRefPtr dsf = new DeviceStorageFile(aStorageType, aStorageName, aPath); nsString reason; CopyASCIItoUTF16(aReason, reason); @@ -1175,12 +1178,12 @@ ContentChild::RecvFilePathUpdate(const nsString& type, const nsString& path, con bool ContentChild::RecvFileSystemUpdate(const nsString& aFsName, - const nsString& aName, + const nsString& aVolumeName, const int32_t& aState, const int32_t& aMountGeneration) { #ifdef MOZ_WIDGET_GONK - nsRefPtr volume = new nsVolume(aFsName, aName, aState, + nsRefPtr volume = new nsVolume(aFsName, aVolumeName, aState, aMountGeneration); nsCOMPtr obs = mozilla::services::GetObserverService(); @@ -1189,7 +1192,7 @@ ContentChild::RecvFileSystemUpdate(const nsString& aFsName, #else // Remove warnings about unused arguments unused << aFsName; - unused << aName; + unused << aVolumeName; unused << aState; unused << aMountGeneration; #endif diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 0fca5fc8155..c22a356d7fb 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -186,9 +186,12 @@ public: virtual bool RecvLastPrivateDocShellDestroyed(); - virtual bool RecvFilePathUpdate(const nsString& type, const nsString& path, const nsCString& reason); + virtual bool RecvFilePathUpdate(const nsString& aStorageType, + const nsString& aStorageName, + const nsString& aPath, + const nsCString& aReason); virtual bool RecvFileSystemUpdate(const nsString& aFsName, - const nsString& aName, + const nsString& aVolumeName, const int32_t& aState, const int32_t& aMountGeneration); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 6269c0f3b47..0dbf036bcee 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1510,7 +1510,7 @@ ContentParent::Observe(nsISupports* aSubject, CopyUTF16toUTF8(aData, creason); DeviceStorageFile* file = static_cast(aSubject); - unused << SendFilePathUpdate(file->mStorageType, file->mPath, creason); + unused << SendFilePathUpdate(file->mStorageType, file->mStorageName, file->mPath, creason); } #ifdef MOZ_WIDGET_GONK else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) { @@ -2350,16 +2350,20 @@ ContentParent::RecvAsyncMessage(const nsString& aMsg, bool ContentParent::RecvFilePathUpdateNotify(const nsString& aType, + const nsString& aStorageName, const nsString& aFilePath, const nsCString& aReason) { - nsRefPtr dsf = new DeviceStorageFile(aType, aFilePath); + nsRefPtr dsf = new DeviceStorageFile(aType, + aStorageName, + aFilePath); nsCOMPtr obs = mozilla::services::GetObserverService(); if (!obs) { return false; } - obs->NotifyObservers(dsf, "file-watcher-update", NS_ConvertASCIItoUTF16(aReason).get()); + obs->NotifyObservers(dsf, "file-watcher-update", + NS_ConvertASCIItoUTF16(aReason).get()); return true; } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index fe00d644353..f2e65b0643a 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -367,6 +367,7 @@ private: const ClonedMessageData& aData); virtual bool RecvFilePathUpdateNotify(const nsString& aType, + const nsString& aStorageName, const nsString& aFilePath, const nsCString& aReason); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index bf6545dd96f..8c556ee1e70 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -65,24 +65,25 @@ struct FontListEntry { struct DeviceStorageFreeSpaceParams { nsString type; - nsString relpath; + nsString storageName; }; struct DeviceStorageUsedSpaceParams { nsString type; - nsString relpath; + nsString storageName; }; struct DeviceStorageAvailableParams { nsString type; - nsString relpath; + nsString storageName; }; struct DeviceStorageAddParams { nsString type; + nsString storageName; nsString relpath; PBlob blob; }; @@ -90,6 +91,7 @@ struct DeviceStorageAddParams struct DeviceStorageGetParams { nsString type; + nsString storageName; nsString rootDir; nsString relpath; }; @@ -97,13 +99,15 @@ struct DeviceStorageGetParams struct DeviceStorageDeleteParams { nsString type; + nsString storageName; nsString relpath; }; struct DeviceStorageEnumerationParams { nsString type; - nsString relpath; + nsString storageName; + nsString rootdir; uint64_t since; }; @@ -346,7 +350,8 @@ child: // Notify child that last-pb-context-exited notification was observed LastPrivateDocShellDestroyed(); - FilePathUpdate(nsString type, nsString filepath, nsCString reasons); + FilePathUpdate(nsString storageType, nsString storageName, nsString filepath, + nsCString reasons); FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState, int32_t mountGeneration); @@ -494,6 +499,7 @@ parent: async AudioChannelChangedNotification(); async FilePathUpdateNotify(nsString aType, + nsString aStorageName, nsString aFilepath, nsCString aReason); // get nsIVolumeService to broadcast volume information diff --git a/dom/system/gonk/nsVolumeService.cpp b/dom/system/gonk/nsVolumeService.cpp index 9e08903e6c3..d34163b57cf 100644 --- a/dom/system/gonk/nsVolumeService.cpp +++ b/dom/system/gonk/nsVolumeService.cpp @@ -196,9 +196,21 @@ nsVolumeService::GetVolumeByPath(const nsAString& aPath, nsIVolume **aResult) nsCString utf8Path = NS_ConvertUTF16toUTF8(aPath); char realPathBuf[PATH_MAX]; - if (!realpath(utf8Path.get(), realPathBuf)) { - ERR("GetVolumeByPath: realpath on '%s' failed: %d", utf8Path.get(), errno); - return NSRESULT_FOR_ERRNO(); + while (realpath(utf8Path.get(), realPathBuf) < 0) { + if (errno != ENOENT) { + ERR("GetVolumeByPath: realpath on '%s' failed: %d", utf8Path.get(), errno); + return NSRESULT_FOR_ERRNO(); + } + // The pathname we were passed doesn't exist, so we try stripping off trailing + // components until we get a successful call to realpath, or until we run out + // of components (if we finally get to /something then we also stop). + int32_t slashIndex = utf8Path.RFindChar('/'); + if ((slashIndex == kNotFound) || (slashIndex == 0)) { + errno = ENOENT; + ERR("GetVolumeByPath: realpath on '%s' failed.", utf8Path.get()); + return NSRESULT_FOR_ERRNO(); + } + utf8Path = Substring(utf8Path, 0, slashIndex); } // The volume mount point is always a directory. Something like /mnt/sdcard