michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsDeviceStorage.h" michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/ClearOnShutdown.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/DeviceStorageBinding.h" michael@0: #include "mozilla/dom/DeviceStorageFileSystem.h" michael@0: #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h" michael@0: #include "mozilla/dom/Directory.h" michael@0: #include "mozilla/dom/FileSystemUtils.h" michael@0: #include "mozilla/dom/ipc/Blob.h" michael@0: #include "mozilla/dom/PBrowserChild.h" michael@0: #include "mozilla/dom/PContentPermissionRequestChild.h" michael@0: #include "mozilla/dom/PermissionMessageUtils.h" michael@0: #include "mozilla/dom/Promise.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/EventListenerManager.h" michael@0: #include "mozilla/LazyIdleThread.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Scoped.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: #include "nsAutoPtr.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIDirectoryEnumerator.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsIDOMFile.h" michael@0: #include "nsDOMBlobBuilder.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "TabChild.h" michael@0: #include "DeviceStorageFileDescriptor.h" michael@0: #include "DeviceStorageRequestChild.h" michael@0: #include "nsIDOMDeviceStorageChangeEvent.h" michael@0: #include "nsCRT.h" michael@0: #include "nsIObserverService.h" michael@0: #include "GeneratedEvents.h" michael@0: #include "nsIMIMEService.h" michael@0: #include "nsCExternalHandlerService.h" michael@0: #include "nsIPermissionManager.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsPrintfCString.h" michael@0: #include michael@0: #include "private/pprio.h" michael@0: #include "nsContentPermissionHelper.h" michael@0: michael@0: #include "mozilla/dom/DeviceStorageBinding.h" michael@0: michael@0: // Microsoft's API Name hackery sucks michael@0: #undef CreateEvent michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: #include "nsIVolume.h" michael@0: #include "nsIVolumeService.h" michael@0: #endif michael@0: michael@0: #define DEVICESTORAGE_PROPERTIES \ michael@0: "chrome://global/content/devicestorage.properties" michael@0: #define DEFAULT_THREAD_TIMEOUT_MS 30000 michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::dom::devicestorage; michael@0: using namespace mozilla::ipc; michael@0: michael@0: #include "nsDirectoryServiceDefs.h" michael@0: michael@0: namespace mozilla { michael@0: MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close); michael@0: } michael@0: michael@0: StaticAutoPtr michael@0: DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache; michael@0: michael@0: DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: mIOThread = new LazyIdleThread( michael@0: DEFAULT_THREAD_TIMEOUT_MS, michael@0: NS_LITERAL_CSTRING("DeviceStorageUsedSpaceCache I/O")); michael@0: michael@0: } michael@0: michael@0: DeviceStorageUsedSpaceCache::~DeviceStorageUsedSpaceCache() michael@0: { michael@0: } michael@0: michael@0: DeviceStorageUsedSpaceCache* michael@0: DeviceStorageUsedSpaceCache::CreateOrGet() michael@0: { michael@0: if (sDeviceStorageUsedSpaceCache) { michael@0: return sDeviceStorageUsedSpaceCache; michael@0: } michael@0: michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: sDeviceStorageUsedSpaceCache = new DeviceStorageUsedSpaceCache(); michael@0: ClearOnShutdown(&sDeviceStorageUsedSpaceCache); michael@0: return sDeviceStorageUsedSpaceCache; michael@0: } michael@0: michael@0: already_AddRefed michael@0: DeviceStorageUsedSpaceCache::GetCacheEntry(const nsAString& aStorageName) michael@0: { michael@0: nsTArray>::size_type numEntries = mCacheEntries.Length(); michael@0: nsTArray>::index_type i; michael@0: for (i = 0; i < numEntries; i++) { michael@0: nsRefPtr& cacheEntry = mCacheEntries[i]; michael@0: if (cacheEntry->mStorageName.Equals(aStorageName)) { michael@0: nsRefPtr addRefedCacheEntry = cacheEntry; michael@0: return addRefedCacheEntry.forget(); michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: static int64_t michael@0: GetFreeBytes(const nsAString& aStorageName) michael@0: { michael@0: // This function makes the assumption that the various types michael@0: // are all stored on the same filesystem. So we use pictures. michael@0: michael@0: DeviceStorageFile dsf(NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), michael@0: aStorageName); michael@0: int64_t freeBytes = 0; michael@0: dsf.GetDiskFreeSpace(&freeBytes); michael@0: return freeBytes; michael@0: } michael@0: michael@0: nsresult michael@0: DeviceStorageUsedSpaceCache::AccumUsedSizes(const nsAString& aStorageName, michael@0: uint64_t* aPicturesSoFar, michael@0: uint64_t* aVideosSoFar, michael@0: uint64_t* aMusicSoFar, michael@0: uint64_t* aTotalSoFar) michael@0: { michael@0: nsRefPtr cacheEntry = GetCacheEntry(aStorageName); michael@0: if (!cacheEntry || cacheEntry->mDirty) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: int64_t freeBytes = GetFreeBytes(cacheEntry->mStorageName); michael@0: if (freeBytes != cacheEntry->mFreeBytes) { michael@0: // Free space changed, so our cached results are no longer valid. michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: *aPicturesSoFar += cacheEntry->mPicturesUsedSize; michael@0: *aVideosSoFar += cacheEntry->mVideosUsedSize; michael@0: *aMusicSoFar += cacheEntry->mMusicUsedSize; michael@0: *aTotalSoFar += cacheEntry->mTotalUsedSize; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: DeviceStorageUsedSpaceCache::SetUsedSizes(const nsAString& aStorageName, michael@0: uint64_t aPictureSize, michael@0: uint64_t aVideosSize, michael@0: uint64_t aMusicSize, michael@0: uint64_t aTotalUsedSize) michael@0: { michael@0: nsRefPtr cacheEntry = GetCacheEntry(aStorageName); michael@0: if (!cacheEntry) { michael@0: cacheEntry = new CacheEntry; michael@0: cacheEntry->mStorageName = aStorageName; michael@0: mCacheEntries.AppendElement(cacheEntry); michael@0: } michael@0: cacheEntry->mFreeBytes = GetFreeBytes(cacheEntry->mStorageName); michael@0: michael@0: cacheEntry->mPicturesUsedSize = aPictureSize; michael@0: cacheEntry->mVideosUsedSize = aVideosSize; michael@0: cacheEntry->mMusicUsedSize = aMusicSize; michael@0: cacheEntry->mTotalUsedSize = aTotalUsedSize; michael@0: cacheEntry->mDirty = false; michael@0: } michael@0: michael@0: class GlobalDirs michael@0: { michael@0: public: michael@0: NS_INLINE_DECL_REFCOUNTING(GlobalDirs) michael@0: #if !defined(MOZ_WIDGET_GONK) michael@0: nsCOMPtr pictures; michael@0: nsCOMPtr videos; michael@0: nsCOMPtr music; michael@0: nsCOMPtr sdcard; michael@0: #endif michael@0: nsCOMPtr apps; michael@0: nsCOMPtr crashes; michael@0: nsCOMPtr overrideRootDir; michael@0: }; michael@0: michael@0: static StaticRefPtr sDirs; michael@0: michael@0: StaticAutoPtr michael@0: DeviceStorageTypeChecker::sDeviceStorageTypeChecker; michael@0: michael@0: DeviceStorageTypeChecker::DeviceStorageTypeChecker() michael@0: { michael@0: } michael@0: michael@0: DeviceStorageTypeChecker::~DeviceStorageTypeChecker() michael@0: { michael@0: } michael@0: michael@0: DeviceStorageTypeChecker* michael@0: DeviceStorageTypeChecker::CreateOrGet() michael@0: { michael@0: if (sDeviceStorageTypeChecker) { michael@0: return sDeviceStorageTypeChecker; michael@0: } michael@0: michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr stringService michael@0: = mozilla::services::GetStringBundleService(); michael@0: if (!stringService) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr filterBundle; michael@0: if (NS_FAILED(stringService->CreateBundle(DEVICESTORAGE_PROPERTIES, michael@0: getter_AddRefs(filterBundle)))) { michael@0: return nullptr; michael@0: } michael@0: michael@0: DeviceStorageTypeChecker* result = new DeviceStorageTypeChecker(); michael@0: result->InitFromBundle(filterBundle); michael@0: michael@0: sDeviceStorageTypeChecker = result; michael@0: ClearOnShutdown(&sDeviceStorageTypeChecker); michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: DeviceStorageTypeChecker::InitFromBundle(nsIStringBundle* aBundle) michael@0: { michael@0: aBundle->GetStringFromName( michael@0: NS_ConvertASCIItoUTF16(DEVICESTORAGE_PICTURES).get(), michael@0: getter_Copies(mPicturesExtensions)); michael@0: aBundle->GetStringFromName( michael@0: NS_ConvertASCIItoUTF16(DEVICESTORAGE_MUSIC).get(), michael@0: getter_Copies(mMusicExtensions)); michael@0: aBundle->GetStringFromName( michael@0: NS_ConvertASCIItoUTF16(DEVICESTORAGE_VIDEOS).get(), michael@0: getter_Copies(mVideosExtensions)); michael@0: } michael@0: michael@0: michael@0: bool michael@0: DeviceStorageTypeChecker::Check(const nsAString& aType, nsIDOMBlob* aBlob) michael@0: { michael@0: MOZ_ASSERT(aBlob); michael@0: michael@0: nsString mimeType; michael@0: if (NS_FAILED(aBlob->GetType(mimeType))) { michael@0: return false; michael@0: } michael@0: michael@0: if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { michael@0: return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/")); michael@0: } michael@0: michael@0: if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { michael@0: return StringBeginsWith(mimeType, NS_LITERAL_STRING("video/")); michael@0: } michael@0: michael@0: if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { michael@0: return StringBeginsWith(mimeType, NS_LITERAL_STRING("audio/")); michael@0: } michael@0: michael@0: if (aType.EqualsLiteral(DEVICESTORAGE_APPS) || michael@0: aType.EqualsLiteral(DEVICESTORAGE_SDCARD) || michael@0: aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) { michael@0: // Apps, crashes and sdcard have no restriction on mime types michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: DeviceStorageTypeChecker::Check(const nsAString& aType, nsIFile* aFile) michael@0: { michael@0: MOZ_ASSERT(aFile); michael@0: michael@0: if (aType.EqualsLiteral(DEVICESTORAGE_APPS) || michael@0: aType.EqualsLiteral(DEVICESTORAGE_SDCARD) || michael@0: aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) { michael@0: // Apps, crashes and sdcard have no restrictions on what file extensions used. michael@0: return true; michael@0: } michael@0: michael@0: nsString path; michael@0: aFile->GetPath(path); michael@0: michael@0: int32_t dotIdx = path.RFindChar(char16_t('.')); michael@0: if (dotIdx == kNotFound) { michael@0: return false; michael@0: } michael@0: michael@0: nsAutoString extensionMatch; michael@0: extensionMatch.AssignLiteral("*"); michael@0: extensionMatch.Append(Substring(path, dotIdx)); michael@0: extensionMatch.AppendLiteral(";"); michael@0: michael@0: if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { michael@0: return CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions); michael@0: } michael@0: michael@0: if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { michael@0: return CaseInsensitiveFindInReadable(extensionMatch, mVideosExtensions); michael@0: } michael@0: michael@0: if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { michael@0: return CaseInsensitiveFindInReadable(extensionMatch, mMusicExtensions); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: DeviceStorageTypeChecker::GetTypeFromFile(nsIFile* aFile, nsAString& aType) michael@0: { michael@0: MOZ_ASSERT(aFile); michael@0: michael@0: nsString path; michael@0: aFile->GetPath(path); michael@0: michael@0: GetTypeFromFileName(path, aType); michael@0: } michael@0: michael@0: void michael@0: DeviceStorageTypeChecker::GetTypeFromFileName(const nsAString& aFileName, michael@0: nsAString& aType) michael@0: { michael@0: aType.AssignLiteral(DEVICESTORAGE_SDCARD); michael@0: michael@0: nsString fileName(aFileName); michael@0: int32_t dotIdx = fileName.RFindChar(char16_t('.')); michael@0: if (dotIdx == kNotFound) { michael@0: return; michael@0: } michael@0: michael@0: nsAutoString extensionMatch; michael@0: extensionMatch.AssignLiteral("*"); michael@0: extensionMatch.Append(Substring(aFileName, dotIdx)); michael@0: extensionMatch.AppendLiteral(";"); michael@0: michael@0: if (CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions)) { michael@0: aType.AssignLiteral(DEVICESTORAGE_PICTURES); michael@0: } michael@0: else if (CaseInsensitiveFindInReadable(extensionMatch, mVideosExtensions)) { michael@0: aType.AssignLiteral(DEVICESTORAGE_VIDEOS); michael@0: } michael@0: else if (CaseInsensitiveFindInReadable(extensionMatch, mMusicExtensions)) { michael@0: aType.AssignLiteral(DEVICESTORAGE_MUSIC); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: DeviceStorageTypeChecker::GetPermissionForType(const nsAString& aType, michael@0: nsACString& aPermissionResult) michael@0: { michael@0: if (!aType.EqualsLiteral(DEVICESTORAGE_PICTURES) && michael@0: !aType.EqualsLiteral(DEVICESTORAGE_VIDEOS) && michael@0: !aType.EqualsLiteral(DEVICESTORAGE_MUSIC) && michael@0: !aType.EqualsLiteral(DEVICESTORAGE_APPS) && michael@0: !aType.EqualsLiteral(DEVICESTORAGE_SDCARD) && michael@0: !aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) { michael@0: // unknown type michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aPermissionResult.AssignLiteral("device-storage:"); michael@0: aPermissionResult.Append(NS_ConvertUTF16toUTF8(aType)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: DeviceStorageTypeChecker::GetAccessForRequest( michael@0: const DeviceStorageRequestType aRequestType, nsACString& aAccessResult) michael@0: { michael@0: switch(aRequestType) { michael@0: case DEVICE_STORAGE_REQUEST_READ: michael@0: case DEVICE_STORAGE_REQUEST_WATCH: michael@0: case DEVICE_STORAGE_REQUEST_FREE_SPACE: michael@0: case DEVICE_STORAGE_REQUEST_USED_SPACE: michael@0: case DEVICE_STORAGE_REQUEST_AVAILABLE: michael@0: case DEVICE_STORAGE_REQUEST_STATUS: michael@0: aAccessResult.AssignLiteral("read"); michael@0: break; michael@0: case DEVICE_STORAGE_REQUEST_WRITE: michael@0: case DEVICE_STORAGE_REQUEST_DELETE: michael@0: case DEVICE_STORAGE_REQUEST_FORMAT: michael@0: case DEVICE_STORAGE_REQUEST_MOUNT: michael@0: case DEVICE_STORAGE_REQUEST_UNMOUNT: michael@0: aAccessResult.AssignLiteral("write"); michael@0: break; michael@0: case DEVICE_STORAGE_REQUEST_CREATE: michael@0: case DEVICE_STORAGE_REQUEST_CREATEFD: michael@0: aAccessResult.AssignLiteral("create"); michael@0: break; michael@0: default: michael@0: aAccessResult.AssignLiteral("undefined"); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: //static michael@0: bool michael@0: DeviceStorageTypeChecker::IsVolumeBased(const nsAString& aType) michael@0: { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: // The apps and crashes aren't stored in the same place as the media, so michael@0: // we only ever return a single apps object, and not an array michael@0: // with one per volume (as is the case for the remaining michael@0: // storage types). michael@0: return !aType.EqualsLiteral(DEVICESTORAGE_APPS) && michael@0: !aType.EqualsLiteral(DEVICESTORAGE_CRASHES); michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(FileUpdateDispatcher, nsIObserver) michael@0: michael@0: mozilla::StaticRefPtr FileUpdateDispatcher::sSingleton; michael@0: michael@0: FileUpdateDispatcher* michael@0: FileUpdateDispatcher::GetSingleton() michael@0: { michael@0: if (sSingleton) { michael@0: return sSingleton; michael@0: } michael@0: michael@0: sSingleton = new FileUpdateDispatcher(); michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->AddObserver(sSingleton, "file-watcher-notify", false); michael@0: ClearOnShutdown(&sSingleton); michael@0: michael@0: return sSingleton; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FileUpdateDispatcher::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: michael@0: DeviceStorageFile* file = static_cast(aSubject); michael@0: if (!file || !file->mFile) { michael@0: NS_WARNING("Device storage file looks invalid!"); michael@0: return NS_OK; michael@0: } michael@0: ContentChild::GetSingleton() michael@0: ->SendFilePathUpdateNotify(file->mStorageType, michael@0: file->mStorageName, michael@0: file->mPath, michael@0: NS_ConvertUTF16toUTF8(aData)); michael@0: } else { michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->NotifyObservers(aSubject, "file-watcher-update", aData); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: class IOEventComplete : public nsRunnable michael@0: { michael@0: public: michael@0: IOEventComplete(DeviceStorageFile *aFile, const char *aType) michael@0: : mFile(aFile) michael@0: , mType(aType) michael@0: { michael@0: } michael@0: michael@0: ~IOEventComplete() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsString data; michael@0: CopyASCIItoUTF16(mType, data); michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: michael@0: obs->NotifyObservers(mFile, "file-watcher-notify", data.get()); michael@0: michael@0: DeviceStorageUsedSpaceCache* usedSpaceCache michael@0: = DeviceStorageUsedSpaceCache::CreateOrGet(); michael@0: MOZ_ASSERT(usedSpaceCache); michael@0: usedSpaceCache->Invalidate(mFile->mStorageName); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsCString mType; michael@0: }; michael@0: michael@0: DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, michael@0: const nsAString& aStorageName, michael@0: const nsAString& aRootDir, michael@0: const nsAString& aPath) michael@0: : mStorageType(aStorageType) michael@0: , mStorageName(aStorageName) michael@0: , mRootDir(aRootDir) michael@0: , mPath(aPath) michael@0: , mEditable(false) michael@0: , mLength(UINT64_MAX) michael@0: , mLastModifiedDate(UINT64_MAX) michael@0: { michael@0: Init(); michael@0: AppendRelativePath(mRootDir); michael@0: if (!mPath.EqualsLiteral("")) { michael@0: AppendRelativePath(mPath); michael@0: } michael@0: NormalizeFilePath(); michael@0: } michael@0: michael@0: DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, michael@0: const nsAString& aStorageName, michael@0: const nsAString& aPath) michael@0: : mStorageType(aStorageType) michael@0: , mStorageName(aStorageName) michael@0: , mPath(aPath) michael@0: , mEditable(false) michael@0: , mLength(UINT64_MAX) michael@0: , mLastModifiedDate(UINT64_MAX) michael@0: { michael@0: Init(); michael@0: AppendRelativePath(aPath); michael@0: NormalizeFilePath(); michael@0: } michael@0: michael@0: DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, michael@0: const nsAString& aStorageName) michael@0: : mStorageType(aStorageType) michael@0: , mStorageName(aStorageName) michael@0: , mEditable(false) michael@0: , mLength(UINT64_MAX) michael@0: , mLastModifiedDate(UINT64_MAX) michael@0: { michael@0: Init(); michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::Dump(const char* label) michael@0: { michael@0: nsString path; michael@0: if (mFile) { michael@0: mFile->GetPath(path); michael@0: } else { michael@0: path = NS_LITERAL_STRING("(null)"); michael@0: } michael@0: const char* ptStr; michael@0: if (XRE_GetProcessType() == GeckoProcessType_Default) { michael@0: ptStr = "parent"; michael@0: } else { michael@0: ptStr = "child"; michael@0: } michael@0: michael@0: printf_stderr("DSF (%s) %s: mStorageType '%s' mStorageName '%s' " michael@0: "mRootDir '%s' mPath '%s' mFile->GetPath '%s'\n", michael@0: ptStr, label, michael@0: NS_LossyConvertUTF16toASCII(mStorageType).get(), michael@0: NS_LossyConvertUTF16toASCII(mStorageName).get(), michael@0: NS_LossyConvertUTF16toASCII(mRootDir).get(), michael@0: NS_LossyConvertUTF16toASCII(mPath).get(), michael@0: NS_LossyConvertUTF16toASCII(path).get()); michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::Init() michael@0: { michael@0: DeviceStorageFile::GetRootDirectoryForType(mStorageType, michael@0: mStorageName, michael@0: getter_AddRefs(mFile)); michael@0: michael@0: DebugOnly typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: MOZ_ASSERT(typeChecker); michael@0: } michael@0: michael@0: // The OverrideRootDir is needed to facilitate testing of the michael@0: // device.storage.overrideRootDir preference. The preference is normally michael@0: // only read once during initialization, but since the test environment has michael@0: // no convenient way to restart, we use a pref watcher instead. michael@0: class OverrideRootDir MOZ_FINAL : public nsIObserver michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: static OverrideRootDir* GetSingleton(); michael@0: ~OverrideRootDir(); michael@0: void Init(); michael@0: private: michael@0: static mozilla::StaticRefPtr sSingleton; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(OverrideRootDir, nsIObserver) michael@0: michael@0: mozilla::StaticRefPtr michael@0: OverrideRootDir::sSingleton; michael@0: michael@0: OverrideRootDir* michael@0: OverrideRootDir::GetSingleton() michael@0: { michael@0: if (sSingleton) { michael@0: return sSingleton; michael@0: } michael@0: // Preference changes are automatically forwarded from parent to child michael@0: // in ContentParent::Observe, so we'll see the change in both the parent michael@0: // and the child process. michael@0: michael@0: sSingleton = new OverrideRootDir(); michael@0: Preferences::AddStrongObserver(sSingleton, "device.storage.overrideRootDir"); michael@0: ClearOnShutdown(&sSingleton); michael@0: michael@0: return sSingleton; michael@0: } michael@0: michael@0: OverrideRootDir::~OverrideRootDir() michael@0: { michael@0: Preferences::RemoveObserver(this, "device.storage.overrideRootDir"); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: OverrideRootDir::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (sSingleton) { michael@0: sSingleton->Init(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: OverrideRootDir::Init() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!sDirs) { michael@0: return; michael@0: } michael@0: michael@0: if (mozilla::Preferences::GetBool("device.storage.testing", false)) { michael@0: nsCOMPtr dirService michael@0: = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); michael@0: MOZ_ASSERT(dirService); michael@0: dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->overrideRootDir)); michael@0: if (sDirs->overrideRootDir) { michael@0: sDirs->overrideRootDir->AppendRelativeNativePath( michael@0: NS_LITERAL_CSTRING("device-storage-testing")); michael@0: } michael@0: } else { michael@0: // For users running on desktop, it's convenient to be able to override michael@0: // all of the directories to point to a single tree, much like what happens michael@0: // on a real device. michael@0: const nsAdoptingString& overrideRootDir = michael@0: mozilla::Preferences::GetString("device.storage.overrideRootDir"); michael@0: if (overrideRootDir && overrideRootDir.Length() > 0) { michael@0: NS_NewLocalFile(overrideRootDir, false, michael@0: getter_AddRefs(sDirs->overrideRootDir)); michael@0: } else { michael@0: sDirs->overrideRootDir = nullptr; michael@0: } michael@0: } michael@0: michael@0: if (sDirs->overrideRootDir) { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Default) { michael@0: // Only the parent process can create directories. In testing, because michael@0: // the preference is updated after startup, its entirely possible that michael@0: // the preference updated notification will be received by a child michael@0: // prior to the parent. michael@0: nsresult rv michael@0: = sDirs->overrideRootDir->Create(nsIFile::DIRECTORY_TYPE, 0777); michael@0: nsString path; michael@0: sDirs->overrideRootDir->GetPath(path); michael@0: if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) { michael@0: nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'", michael@0: NS_LossyConvertUTF16toASCII(path).get()); michael@0: NS_WARNING(msg.get()); michael@0: } michael@0: } michael@0: sDirs->overrideRootDir->Normalize(); michael@0: } michael@0: } michael@0: michael@0: // Directories which don't depend on a volume should be calculated once michael@0: // here. Directories which depend on the root directory of a volume michael@0: // should be calculated in DeviceStorageFile::GetRootDirectoryForType. michael@0: static void michael@0: InitDirs() michael@0: { michael@0: if (sDirs) { michael@0: return; michael@0: } michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: sDirs = new GlobalDirs; michael@0: ClearOnShutdown(&sDirs); michael@0: michael@0: nsCOMPtr dirService michael@0: = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); michael@0: MOZ_ASSERT(dirService); michael@0: michael@0: #if !defined(MOZ_WIDGET_GONK) michael@0: michael@0: #if defined (MOZ_WIDGET_COCOA) michael@0: dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->pictures)); michael@0: dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->videos)); michael@0: dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->music)); michael@0: #elif defined (XP_UNIX) michael@0: dirService->Get(NS_UNIX_XDG_PICTURES_DIR, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->pictures)); michael@0: dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->videos)); michael@0: dirService->Get(NS_UNIX_XDG_MUSIC_DIR, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->music)); michael@0: #elif defined (XP_WIN) michael@0: dirService->Get(NS_WIN_PICTURES_DIR, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->pictures)); michael@0: dirService->Get(NS_WIN_VIDEOS_DIR, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->videos)); michael@0: dirService->Get(NS_WIN_MUSIC_DIR, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->music)); michael@0: #endif michael@0: michael@0: // Eventually, on desktop, we want to do something smarter -- for example, michael@0: // detect when an sdcard is inserted, and use that instead of this. michael@0: dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->sdcard)); michael@0: if (sDirs->sdcard) { michael@0: sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard")); michael@0: } michael@0: #endif // !MOZ_WIDGET_GONK michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: NS_NewLocalFile(NS_LITERAL_STRING("/data"), michael@0: false, michael@0: getter_AddRefs(sDirs->apps)); michael@0: #else michael@0: dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(sDirs->apps)); michael@0: if (sDirs->apps) { michael@0: sDirs->apps->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps")); michael@0: } michael@0: #endif michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Default) { michael@0: NS_GetSpecialDirectory("UAppData", getter_AddRefs(sDirs->crashes)); michael@0: if (sDirs->crashes) { michael@0: sDirs->crashes->Append(NS_LITERAL_STRING("Crash Reports")); michael@0: } michael@0: } else { michael@0: // NS_GetSpecialDirectory("UAppData") fails in content processes because michael@0: // gAppData from toolkit/xre/nsAppRunner.cpp is not initialized. michael@0: #ifdef MOZ_WIDGET_GONK michael@0: NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"), michael@0: false, michael@0: getter_AddRefs(sDirs->crashes)); michael@0: #endif michael@0: } michael@0: michael@0: OverrideRootDir::GetSingleton()->Init(); michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::GetFullPath(nsAString &aFullPath) michael@0: { michael@0: aFullPath.Truncate(); michael@0: if (!mStorageName.EqualsLiteral("")) { michael@0: aFullPath.AppendLiteral("/"); michael@0: aFullPath.Append(mStorageName); michael@0: aFullPath.AppendLiteral("/"); michael@0: } michael@0: if (!mRootDir.EqualsLiteral("")) { michael@0: aFullPath.Append(mRootDir); michael@0: aFullPath.AppendLiteral("/"); michael@0: } michael@0: aFullPath.Append(mPath); michael@0: } michael@0: michael@0: michael@0: // Directories which don't depend on a volume should be calculated once michael@0: // in InitDirs. Directories which depend on the root directory of a volume michael@0: // should be calculated in this method. michael@0: void michael@0: DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType, michael@0: const nsAString& aStorageName, michael@0: nsIFile** aFile) michael@0: { michael@0: nsCOMPtr f; michael@0: *aFile = nullptr; michael@0: bool allowOverride = true; michael@0: michael@0: InitDirs(); michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsString volMountPoint; michael@0: if (DeviceStorageTypeChecker::IsVolumeBased(aStorageType)) { michael@0: nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE_VOID(vs); michael@0: nsresult rv; michael@0: nsCOMPtr vol; michael@0: rv = vs->GetVolumeByName(aStorageName, getter_AddRefs(vol)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: vol->GetMountPoint(volMountPoint); michael@0: } michael@0: #endif michael@0: michael@0: // Picture directory michael@0: if (aStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); michael@0: #else michael@0: f = sDirs->pictures; michael@0: #endif michael@0: } michael@0: michael@0: // Video directory michael@0: else if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); michael@0: #else michael@0: f = sDirs->videos; michael@0: #endif michael@0: } michael@0: michael@0: // Music directory michael@0: else if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); michael@0: #else michael@0: f = sDirs->music; michael@0: #endif michael@0: } michael@0: michael@0: // Apps directory michael@0: else if (aStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) { michael@0: f = sDirs->apps; michael@0: allowOverride = false; michael@0: } michael@0: michael@0: // default SDCard michael@0: else if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); michael@0: #else michael@0: f = sDirs->sdcard; michael@0: #endif michael@0: } michael@0: michael@0: // crash reports directory. michael@0: else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) { michael@0: f = sDirs->crashes; michael@0: allowOverride = false; michael@0: } else { michael@0: // Not a storage type that we recognize. Return null michael@0: return; michael@0: } michael@0: michael@0: // In testing, we default all device storage types to a temp directory. michael@0: // sDirs->overrideRootDir will only have been initialized (in InitDirs) michael@0: // if the preference device.storage.testing was set to true, or if michael@0: // device.storage.overrideRootDir is set. We can't test the preferences michael@0: // directly here, since we may not be on the main thread. michael@0: if (allowOverride && sDirs->overrideRootDir) { michael@0: f = sDirs->overrideRootDir; michael@0: } michael@0: michael@0: if (f) { michael@0: f->Clone(aFile); michael@0: } michael@0: } michael@0: michael@0: //static michael@0: already_AddRefed michael@0: DeviceStorageFile::CreateUnique(nsAString& aFileName, michael@0: uint32_t aFileType, michael@0: uint32_t aFileAttributes) michael@0: { michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: MOZ_ASSERT(typeChecker); michael@0: michael@0: nsString storageType; michael@0: typeChecker->GetTypeFromFileName(aFileName, storageType); michael@0: michael@0: nsString storageName; michael@0: nsString storagePath; michael@0: if (!nsDOMDeviceStorage::ParseFullPath(aFileName, storageName, storagePath)) { michael@0: return nullptr; michael@0: } michael@0: if (storageName.IsEmpty()) { michael@0: nsDOMDeviceStorage::GetDefaultStorageName(storageType, storageName); michael@0: } michael@0: nsRefPtr dsf = michael@0: new DeviceStorageFile(storageType, storageName, storagePath); michael@0: if (!dsf->mFile) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsresult rv = dsf->mFile->CreateUnique(aFileType, aFileAttributes); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: // CreateUnique may cause the filename to change. So we need to update mPath michael@0: // to reflect that. michael@0: nsString leafName; michael@0: dsf->mFile->GetLeafName(leafName); michael@0: michael@0: int32_t lastSlashIndex = dsf->mPath.RFindChar('/'); michael@0: if (lastSlashIndex == kNotFound) { michael@0: dsf->mPath.Assign(leafName); michael@0: } else { michael@0: // Include the last '/' michael@0: dsf->mPath = Substring(dsf->mPath, 0, lastSlashIndex + 1); michael@0: dsf->mPath.Append(leafName); michael@0: } michael@0: michael@0: return dsf.forget(); michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::SetPath(const nsAString& aPath) { michael@0: mPath.Assign(aPath); michael@0: NormalizeFilePath(); michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::SetEditable(bool aEditable) { michael@0: mEditable = aEditable; michael@0: } michael@0: michael@0: // we want to make sure that the names of file can't reach michael@0: // outside of the type of storage the user asked for. michael@0: bool michael@0: DeviceStorageFile::IsSafePath() michael@0: { michael@0: return IsSafePath(mRootDir) && IsSafePath(mPath); michael@0: } michael@0: michael@0: bool michael@0: DeviceStorageFile::IsSafePath(const nsAString& aPath) michael@0: { michael@0: nsAString::const_iterator start, end; michael@0: aPath.BeginReading(start); michael@0: aPath.EndReading(end); michael@0: michael@0: // if the path is a '~' or starts with '~/', return false. michael@0: NS_NAMED_LITERAL_STRING(tilde, "~"); michael@0: NS_NAMED_LITERAL_STRING(tildeSlash, "~/"); michael@0: if (aPath.Equals(tilde) || michael@0: StringBeginsWith(aPath, tildeSlash)) { michael@0: NS_WARNING("Path name starts with tilde!"); michael@0: return false; michael@0: } michael@0: // split on /. if any token is "", ., or .., return false. michael@0: NS_ConvertUTF16toUTF8 cname(aPath); michael@0: char* buffer = cname.BeginWriting(); michael@0: const char* token; michael@0: michael@0: while ((token = nsCRT::strtok(buffer, "/", &buffer))) { michael@0: if (PL_strcmp(token, "") == 0 || michael@0: PL_strcmp(token, ".") == 0 || michael@0: PL_strcmp(token, "..") == 0 ) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::NormalizeFilePath() { michael@0: FileSystemUtils::LocalPathToNormalizedPath(mPath, mPath); michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::AppendRelativePath(const nsAString& aPath) { michael@0: if (!mFile) { michael@0: return; michael@0: } michael@0: if (!IsSafePath(aPath)) { michael@0: // All of the APIs (in the child) do checks to verify that the path is michael@0: // valid and return PERMISSION_DENIED if a non-safe path is entered. michael@0: // This check is done in the parent and prevents a compromised michael@0: // child from bypassing the check. It shouldn't be possible for this michael@0: // code path to be taken with a non-compromised child. michael@0: NS_WARNING("Unsafe path detected - ignoring"); michael@0: NS_WARNING(NS_LossyConvertUTF16toASCII(aPath).get()); michael@0: return; michael@0: } michael@0: nsString localPath; michael@0: FileSystemUtils::NormalizedPathToLocalPath(aPath, localPath); michael@0: mFile->AppendRelativePath(localPath); michael@0: } michael@0: michael@0: nsresult michael@0: DeviceStorageFile::CreateFileDescriptor(FileDescriptor& aFileDescriptor) michael@0: { michael@0: ScopedPRFileDesc fd; michael@0: nsresult rv = mFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, michael@0: 0660, &fd.rwget()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // NOTE: The FileDescriptor::PlatformHandleType constructor returns a dup of michael@0: // the file descriptor, so we don't need the original fd after this. michael@0: // Our scoped file descriptor will automatically close fd. michael@0: aFileDescriptor = michael@0: FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: DeviceStorageFile::Write(nsIInputStream* aInputStream) michael@0: { michael@0: if (!aInputStream || !mFile) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr iocomplete = new IOEventComplete(this, "created"); michael@0: rv = NS_DispatchToMainThread(iocomplete); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: uint64_t bufSize = 0; michael@0: aInputStream->Available(&bufSize); michael@0: michael@0: nsCOMPtr outputStream; michael@0: NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile); michael@0: michael@0: if (!outputStream) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr bufferedOutputStream; michael@0: rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), michael@0: outputStream, michael@0: 4096*4); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: while (bufSize) { michael@0: uint32_t wrote; michael@0: rv = bufferedOutputStream->WriteFrom( michael@0: aInputStream, michael@0: static_cast(std::min(bufSize, UINT32_MAX)), michael@0: &wrote); michael@0: if (NS_FAILED(rv)) { michael@0: break; michael@0: } michael@0: bufSize -= wrote; michael@0: } michael@0: michael@0: iocomplete = new IOEventComplete(this, "modified"); michael@0: rv = NS_DispatchToMainThread(iocomplete); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: bufferedOutputStream->Close(); michael@0: outputStream->Close(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: DeviceStorageFile::Write(InfallibleTArray& aBits) michael@0: { michael@0: if (!mFile) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr iocomplete = new IOEventComplete(this, "created"); michael@0: rv = NS_DispatchToMainThread(iocomplete); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr outputStream; michael@0: NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile); michael@0: michael@0: if (!outputStream) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: uint32_t wrote; michael@0: outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote); michael@0: outputStream->Close(); michael@0: michael@0: iocomplete = new IOEventComplete(this, "modified"); michael@0: rv = NS_DispatchToMainThread(iocomplete); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: if (aBits.Length() != wrote) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: DeviceStorageFile::Remove() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: if (!mFile) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: bool check; michael@0: nsresult rv = mFile->Exists(&check); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: if (!check) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: rv = mFile->Remove(true); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr iocomplete = new IOEventComplete(this, "deleted"); michael@0: return NS_DispatchToMainThread(iocomplete); michael@0: } michael@0: michael@0: nsresult michael@0: DeviceStorageFile::CalculateMimeType() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsAutoCString mimeType; michael@0: nsCOMPtr mimeService = michael@0: do_GetService(NS_MIMESERVICE_CONTRACTID); michael@0: if (mimeService) { michael@0: nsresult rv = mimeService->GetTypeFromFile(mFile, mimeType); michael@0: if (NS_FAILED(rv)) { michael@0: mimeType.Truncate(); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: mMimeType = NS_ConvertUTF8toUTF16(mimeType); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: DeviceStorageFile::CalculateSizeAndModifiedDate() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: int64_t fileSize; michael@0: nsresult rv = mFile->GetFileSize(&fileSize); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mLength = fileSize; michael@0: michael@0: PRTime modDate; michael@0: rv = mFile->GetLastModifiedTime(&modDate); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mLastModifiedDate = modDate; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::CollectFiles(nsTArray > &aFiles, michael@0: PRTime aSince) michael@0: { michael@0: nsString fullRootPath; michael@0: mFile->GetPath(fullRootPath); michael@0: collectFilesInternal(aFiles, aSince, fullRootPath); michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::collectFilesInternal( michael@0: nsTArray > &aFiles, michael@0: PRTime aSince, michael@0: nsAString& aRootPath) michael@0: { michael@0: if (!mFile || !IsAvailable()) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr e; michael@0: mFile->GetDirectoryEntries(getter_AddRefs(e)); michael@0: michael@0: if (!e) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr files = do_QueryInterface(e); michael@0: nsCOMPtr f; michael@0: michael@0: while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) { michael@0: michael@0: PRTime msecs; michael@0: f->GetLastModifiedTime(&msecs); michael@0: michael@0: if (msecs < aSince) { michael@0: continue; michael@0: } michael@0: michael@0: bool isDir; michael@0: f->IsDirectory(&isDir); michael@0: michael@0: bool isFile; michael@0: f->IsFile(&isFile); michael@0: michael@0: nsString fullpath; michael@0: nsresult rv = f->GetPath(fullpath); michael@0: if (NS_FAILED(rv)) { michael@0: continue; michael@0: } michael@0: michael@0: if (!StringBeginsWith(fullpath, aRootPath)) { michael@0: NS_ERROR("collectFiles returned a path that does not belong!"); michael@0: continue; michael@0: } michael@0: michael@0: nsAString::size_type len = aRootPath.Length() + 1; // +1 for the trailing / michael@0: nsDependentSubstring newPath = Substring(fullpath, len); michael@0: michael@0: if (isDir) { michael@0: DeviceStorageFile dsf(mStorageType, mStorageName, mRootDir, newPath); michael@0: dsf.collectFilesInternal(aFiles, aSince, aRootPath); michael@0: } else if (isFile) { michael@0: nsRefPtr dsf = michael@0: new DeviceStorageFile(mStorageType, mStorageName, mRootDir, newPath); michael@0: dsf->CalculateSizeAndModifiedDate(); michael@0: aFiles.AppendElement(dsf); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::AccumDiskUsage(uint64_t* aPicturesSoFar, michael@0: uint64_t* aVideosSoFar, michael@0: uint64_t* aMusicSoFar, michael@0: uint64_t* aTotalSoFar) michael@0: { michael@0: if (!IsAvailable()) { michael@0: return; michael@0: } michael@0: michael@0: uint64_t pictureUsage = 0, videoUsage = 0, musicUsage = 0, totalUsage = 0; michael@0: michael@0: if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) { michael@0: DeviceStorageUsedSpaceCache* usedSpaceCache = michael@0: DeviceStorageUsedSpaceCache::CreateOrGet(); michael@0: MOZ_ASSERT(usedSpaceCache); michael@0: nsresult rv = usedSpaceCache->AccumUsedSizes(mStorageName, michael@0: aPicturesSoFar, aVideosSoFar, michael@0: aMusicSoFar, aTotalSoFar); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return; michael@0: } michael@0: AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage, michael@0: &musicUsage, &totalUsage); michael@0: usedSpaceCache->SetUsedSizes(mStorageName, pictureUsage, videoUsage, michael@0: musicUsage, totalUsage); michael@0: } else { michael@0: AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage, michael@0: &musicUsage, &totalUsage); michael@0: } michael@0: michael@0: *aPicturesSoFar += pictureUsage; michael@0: *aVideosSoFar += videoUsage; michael@0: *aMusicSoFar += musicUsage; michael@0: *aTotalSoFar += totalUsage; michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::AccumDirectoryUsage(nsIFile* aFile, michael@0: uint64_t* aPicturesSoFar, michael@0: uint64_t* aVideosSoFar, michael@0: uint64_t* aMusicSoFar, michael@0: uint64_t* aTotalSoFar) michael@0: { michael@0: if (!aFile) { michael@0: return; michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr e; michael@0: rv = aFile->GetDirectoryEntries(getter_AddRefs(e)); michael@0: michael@0: if (NS_FAILED(rv) || !e) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr files = do_QueryInterface(e); michael@0: MOZ_ASSERT(files); michael@0: michael@0: nsCOMPtr f; michael@0: while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) { michael@0: bool isDir; michael@0: rv = f->IsDirectory(&isDir); michael@0: if (NS_FAILED(rv)) { michael@0: continue; michael@0: } michael@0: michael@0: bool isFile; michael@0: rv = f->IsFile(&isFile); michael@0: if (NS_FAILED(rv)) { michael@0: continue; michael@0: } michael@0: michael@0: bool isLink; michael@0: rv = f->IsSymlink(&isLink); michael@0: if (NS_FAILED(rv)) { michael@0: continue; michael@0: } michael@0: michael@0: if (isLink) { michael@0: // for now, lets just totally ignore symlinks. michael@0: NS_WARNING("DirectoryDiskUsage ignores symlinks"); michael@0: } else if (isDir) { michael@0: AccumDirectoryUsage(f, aPicturesSoFar, aVideosSoFar, michael@0: aMusicSoFar, aTotalSoFar); michael@0: } else if (isFile) { michael@0: michael@0: int64_t size; michael@0: rv = f->GetFileSize(&size); michael@0: if (NS_FAILED(rv)) { michael@0: continue; michael@0: } michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: MOZ_ASSERT(typeChecker); michael@0: nsString type; michael@0: typeChecker->GetTypeFromFile(f, type); michael@0: michael@0: if (type.EqualsLiteral(DEVICESTORAGE_PICTURES)) { michael@0: *aPicturesSoFar += size; michael@0: } michael@0: else if (type.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { michael@0: *aVideosSoFar += size; michael@0: } michael@0: else if (type.EqualsLiteral(DEVICESTORAGE_MUSIC)) { michael@0: *aMusicSoFar += size; michael@0: } michael@0: *aTotalSoFar += size; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::GetDiskFreeSpace(int64_t* aSoFar) michael@0: { michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return; michael@0: } michael@0: if (!mFile || !IsAvailable()) { michael@0: return; michael@0: } michael@0: michael@0: int64_t storageAvail = 0; michael@0: nsresult rv = mFile->GetDiskSpaceAvailable(&storageAvail); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: *aSoFar += storageAvail; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: DeviceStorageFile::IsAvailable() michael@0: { michael@0: nsString status; michael@0: GetStatus(status); michael@0: return status.EqualsLiteral("available"); michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::DoFormat(nsAString& aStatus) michael@0: { michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return; michael@0: } michael@0: if (!typeChecker->IsVolumeBased(mStorageType)) { michael@0: aStatus.AssignLiteral("notVolume"); michael@0: return; michael@0: } michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE_VOID(vs); michael@0: michael@0: nsCOMPtr vol; michael@0: nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (!vol) { michael@0: return; michael@0: } michael@0: michael@0: vol->Format(); michael@0: michael@0: aStatus.AssignLiteral("formatting"); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::DoMount(nsAString& aStatus) michael@0: { michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return; michael@0: } michael@0: if (!typeChecker->IsVolumeBased(mStorageType)) { michael@0: aStatus.AssignLiteral("notVolume"); michael@0: return; michael@0: } michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE_VOID(vs); michael@0: michael@0: nsCOMPtr vol; michael@0: nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (!vol) { michael@0: return; michael@0: } michael@0: michael@0: vol->Mount(); michael@0: michael@0: aStatus.AssignLiteral("mounting"); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::DoUnmount(nsAString& aStatus) michael@0: { michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return; michael@0: } michael@0: if (!typeChecker->IsVolumeBased(mStorageType)) { michael@0: aStatus.AssignLiteral("notVolume"); michael@0: return; michael@0: } michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE_VOID(vs); michael@0: michael@0: nsCOMPtr vol; michael@0: nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (!vol) { michael@0: return; michael@0: } michael@0: michael@0: vol->Unmount(); michael@0: michael@0: aStatus.AssignLiteral("unmounting"); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::GetStatus(nsAString& aStatus) michael@0: { michael@0: aStatus.AssignLiteral("unavailable"); michael@0: michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return; michael@0: } michael@0: if (!typeChecker->IsVolumeBased(mStorageType)) { michael@0: aStatus.AssignLiteral("available"); michael@0: return; michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE_VOID(vs); michael@0: michael@0: nsCOMPtr vol; michael@0: nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (!vol) { michael@0: return; michael@0: } michael@0: bool isMediaPresent; michael@0: rv = vol->GetIsMediaPresent(&isMediaPresent); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (!isMediaPresent) { michael@0: return; michael@0: } michael@0: bool isSharing; michael@0: rv = vol->GetIsSharing(&isSharing); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (isSharing) { michael@0: aStatus.AssignLiteral("shared"); michael@0: return; michael@0: } michael@0: bool isFormatting; michael@0: rv = vol->GetIsFormatting(&isFormatting); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (isFormatting) { michael@0: aStatus.AssignLiteral("unavailable"); michael@0: return; michael@0: } michael@0: int32_t volState; michael@0: rv = vol->GetState(&volState); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (volState == nsIVolume::STATE_MOUNTED) { michael@0: aStatus.AssignLiteral("available"); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: DeviceStorageFile::GetStorageStatus(nsAString& aStatus) michael@0: { michael@0: aStatus.AssignLiteral("undefined"); michael@0: michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return; michael@0: } michael@0: if (!typeChecker->IsVolumeBased(mStorageType)) { michael@0: aStatus.AssignLiteral("available"); michael@0: return; michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE_VOID(vs); michael@0: michael@0: nsCOMPtr vol; michael@0: nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol)); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: if (!vol) { michael@0: return; michael@0: } michael@0: michael@0: int32_t volState; michael@0: rv = vol->GetState(&volState); michael@0: NS_ENSURE_SUCCESS_VOID(rv); michael@0: aStatus.AssignASCII(mozilla::system::NS_VolumeStateStr(volState)); michael@0: #endif michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS0(DeviceStorageFile) michael@0: michael@0: static void michael@0: RegisterForSDCardChanges(nsIObserver* aObserver) michael@0: { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->AddObserver(aObserver, NS_VOLUME_STATE_CHANGED, false); michael@0: #endif michael@0: } michael@0: michael@0: static void michael@0: UnregisterForSDCardChanges(nsIObserver* aObserver) michael@0: { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->RemoveObserver(aObserver, NS_VOLUME_STATE_CHANGED); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aStorageType, michael@0: const nsAString& aStorageName) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr f; michael@0: DeviceStorageFile::GetRootDirectoryForType(aStorageType, michael@0: aStorageName, michael@0: getter_AddRefs(f)); michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->AddObserver(this, "file-watcher-update", false); michael@0: obs->AddObserver(this, "disk-space-watcher", false); michael@0: mRootDirectory = f; michael@0: mStorageType = aStorageType; michael@0: mStorageName = aStorageName; michael@0: } michael@0: michael@0: JS::Value michael@0: InterfaceToJsval(nsPIDOMWindow* aWindow, michael@0: nsISupports* aObject, michael@0: const nsIID* aIID) michael@0: { michael@0: nsCOMPtr sgo = do_QueryInterface(aWindow); michael@0: if (!sgo) { michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: JSObject *unrootedScopeObj = sgo->GetGlobalJSObject(); michael@0: NS_ENSURE_TRUE(unrootedScopeObj, JS::NullValue()); michael@0: JSRuntime *runtime = JS_GetObjectRuntime(unrootedScopeObj); michael@0: JS::Rooted someJsVal(runtime); michael@0: nsresult rv; michael@0: michael@0: { // Protect someJsVal from moving GC in ~JSAutoCompartment michael@0: AutoJSContext cx; michael@0: michael@0: JS::Rooted scopeObj(cx, unrootedScopeObj); michael@0: JSAutoCompartment ac(cx, scopeObj); michael@0: michael@0: rv = nsContentUtils::WrapNative(cx, aObject, aIID, &someJsVal); michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: return JS::NullValue(); michael@0: } michael@0: michael@0: return someJsVal; michael@0: } michael@0: michael@0: JS::Value michael@0: nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aWindow); michael@0: michael@0: if (!aFile) { michael@0: return JSVAL_NULL; michael@0: } michael@0: michael@0: if (aFile->mEditable) { michael@0: // TODO - needs janv's file handle support. michael@0: return JSVAL_NULL; michael@0: } michael@0: michael@0: nsString fullPath; michael@0: aFile->GetFullPath(fullPath); michael@0: michael@0: // This check is useful to know if somewhere the DeviceStorageFile michael@0: // has not been properly set. Mimetype is not checked because it can be michael@0: // empty. michael@0: MOZ_ASSERT(aFile->mLength != UINT64_MAX); michael@0: MOZ_ASSERT(aFile->mLastModifiedDate != UINT64_MAX); michael@0: michael@0: nsCOMPtr blob = new nsDOMFileFile(fullPath, aFile->mMimeType, michael@0: aFile->mLength, aFile->mFile, michael@0: aFile->mLastModifiedDate); michael@0: return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob)); michael@0: } michael@0: michael@0: JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aWindow); michael@0: michael@0: nsCOMPtr sgo = do_QueryInterface(aWindow); michael@0: if (!sgo) { michael@0: return JSVAL_NULL; michael@0: } michael@0: michael@0: nsIScriptContext *scriptContext = sgo->GetScriptContext(); michael@0: if (!scriptContext) { michael@0: return JSVAL_NULL; michael@0: } michael@0: michael@0: AutoPushJSContext cx(scriptContext->GetNativeContext()); michael@0: if (!cx) { michael@0: return JSVAL_NULL; michael@0: } michael@0: michael@0: JS::Rooted result(cx); michael@0: if (!xpc::StringToJsval(cx, aString, &result)) { michael@0: return JSVAL_NULL; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: class DeviceStorageCursorRequest MOZ_FINAL michael@0: : public nsIContentPermissionRequest michael@0: , public PCOMContentPermissionRequestChild michael@0: { michael@0: public: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageCursorRequest, michael@0: nsIContentPermissionRequest) michael@0: michael@0: NS_FORWARD_NSICONTENTPERMISSIONREQUEST(mCursor->); michael@0: michael@0: DeviceStorageCursorRequest(nsDOMDeviceStorageCursor* aCursor) michael@0: : mCursor(aCursor) { } michael@0: michael@0: ~DeviceStorageCursorRequest() {} michael@0: michael@0: bool Recv__delete__(const bool& allow, michael@0: const InfallibleTArray& choices) michael@0: { michael@0: MOZ_ASSERT(choices.IsEmpty(), "DeviceStorageCursor doesn't support permission choice"); michael@0: if (allow) { michael@0: Allow(JS::UndefinedHandleValue); michael@0: } michael@0: else { michael@0: Cancel(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void IPDLRelease() michael@0: { michael@0: Release(); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mCursor; michael@0: }; michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageCursorRequest) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageCursorRequest) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageCursorRequest) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(DeviceStorageCursorRequest, michael@0: mCursor) michael@0: michael@0: michael@0: class PostErrorEvent : public nsRunnable michael@0: { michael@0: public: michael@0: PostErrorEvent(already_AddRefed aRequest, const char* aMessage) michael@0: : mRequest(aRequest) michael@0: { michael@0: CopyASCIItoUTF16(aMessage, mError); michael@0: } michael@0: michael@0: PostErrorEvent(DOMRequest* aRequest, const char* aMessage) michael@0: : mRequest(aRequest) michael@0: { michael@0: CopyASCIItoUTF16(aMessage, mError); michael@0: } michael@0: michael@0: ~PostErrorEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (!mRequest->GetOwner()) { michael@0: return NS_OK; michael@0: } michael@0: mRequest->FireError(mError); michael@0: mRequest = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mRequest; michael@0: nsString mError; michael@0: }; michael@0: michael@0: ContinueCursorEvent::ContinueCursorEvent(already_AddRefed aRequest) michael@0: : mRequest(aRequest) michael@0: { michael@0: } michael@0: michael@0: ContinueCursorEvent::ContinueCursorEvent(DOMRequest* aRequest) michael@0: : mRequest(aRequest) michael@0: { michael@0: } michael@0: michael@0: already_AddRefed michael@0: ContinueCursorEvent::GetNextFile() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsDOMDeviceStorageCursor* cursor michael@0: = static_cast(mRequest.get()); michael@0: nsString cursorStorageType; michael@0: cursor->GetStorageType(cursorStorageType); michael@0: michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return nullptr; michael@0: } michael@0: michael@0: while (cursor->mFiles.Length() > 0) { michael@0: nsRefPtr file = cursor->mFiles[0]; michael@0: cursor->mFiles.RemoveElementAt(0); michael@0: if (!typeChecker->Check(cursorStorageType, file->mFile)) { michael@0: continue; michael@0: } michael@0: michael@0: file->CalculateMimeType(); michael@0: return file.forget(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: ContinueCursorEvent::~ContinueCursorEvent() {} michael@0: michael@0: void michael@0: ContinueCursorEvent::Continue() michael@0: { michael@0: if (XRE_GetProcessType() == GeckoProcessType_Default) { michael@0: DebugOnly rv = NS_DispatchToMainThread(this); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr file = GetNextFile(); michael@0: michael@0: if (!file) { michael@0: // done with enumeration. michael@0: DebugOnly rv = NS_DispatchToMainThread(this); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: return; michael@0: } michael@0: michael@0: nsDOMDeviceStorageCursor* cursor michael@0: = static_cast(mRequest.get()); michael@0: nsString cursorStorageType; michael@0: cursor->GetStorageType(cursorStorageType); michael@0: michael@0: DeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, file); michael@0: child->SetCallback(cursor); michael@0: DeviceStorageGetParams params(cursorStorageType, michael@0: file->mStorageName, michael@0: file->mRootDir, michael@0: file->mPath); michael@0: ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, michael@0: params); michael@0: mRequest = nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ContinueCursorEvent::Run() michael@0: { michael@0: nsCOMPtr window = mRequest->GetOwner(); michael@0: if (!window) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr file = GetNextFile(); michael@0: michael@0: nsDOMDeviceStorageCursor* cursor michael@0: = static_cast(mRequest.get()); michael@0: michael@0: AutoJSContext cx; michael@0: JS::Rooted val(cx, nsIFileToJsval(window, file)); michael@0: michael@0: if (file) { michael@0: cursor->mOkToCallContinue = true; michael@0: cursor->FireSuccess(val); michael@0: } else { michael@0: cursor->FireDone(); michael@0: } michael@0: mRequest = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: class InitCursorEvent : public nsRunnable michael@0: { michael@0: public: michael@0: InitCursorEvent(DOMRequest* aRequest, DeviceStorageFile* aFile) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: } michael@0: michael@0: ~InitCursorEvent() {} michael@0: michael@0: NS_IMETHOD Run() { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: if (mFile->mFile) { michael@0: bool check; michael@0: mFile->mFile->IsDirectory(&check); michael@0: if (!check) { michael@0: nsCOMPtr event = michael@0: new PostErrorEvent(mRequest.forget(), michael@0: POST_ERROR_EVENT_FILE_NOT_ENUMERABLE); michael@0: return NS_DispatchToMainThread(event); michael@0: } michael@0: } michael@0: michael@0: nsDOMDeviceStorageCursor* cursor michael@0: = static_cast(mRequest.get()); michael@0: mFile->CollectFiles(cursor->mFiles, cursor->mSince); michael@0: michael@0: nsRefPtr event michael@0: = new ContinueCursorEvent(mRequest.forget()); michael@0: event->Continue(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorageCursor) michael@0: NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMCursor) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorageCursor, DOMCursor) michael@0: NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorageCursor, DOMCursor) michael@0: michael@0: nsDOMDeviceStorageCursor::nsDOMDeviceStorageCursor(nsPIDOMWindow* aWindow, michael@0: nsIPrincipal* aPrincipal, michael@0: DeviceStorageFile* aFile, michael@0: PRTime aSince) michael@0: : DOMCursor(aWindow, nullptr) michael@0: , mOkToCallContinue(false) michael@0: , mSince(aSince) michael@0: , mFile(aFile) michael@0: , mPrincipal(aPrincipal) michael@0: { michael@0: } michael@0: michael@0: nsDOMDeviceStorageCursor::~nsDOMDeviceStorageCursor() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType) michael@0: { michael@0: aType = mFile->mStorageType; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorageCursor::GetTypes(nsIArray** aTypes) michael@0: { michael@0: nsCString type; michael@0: nsresult rv = michael@0: DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsTArray emptyOptions; michael@0: return CreatePermissionArray(type, michael@0: NS_LITERAL_CSTRING("read"), michael@0: emptyOptions, michael@0: aTypes); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorageCursor::GetPrincipal(nsIPrincipal * *aRequestingPrincipal) michael@0: { michael@0: NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorageCursor::GetWindow(nsIDOMWindow * *aRequestingWindow) michael@0: { michael@0: NS_IF_ADDREF(*aRequestingWindow = GetOwner()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorageCursor::GetElement(nsIDOMElement * *aRequestingElement) michael@0: { michael@0: *aRequestingElement = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorageCursor::Cancel() michael@0: { michael@0: nsCOMPtr event michael@0: = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED); michael@0: return NS_DispatchToMainThread(event); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorageCursor::Allow(JS::HandleValue aChoices) michael@0: { michael@0: MOZ_ASSERT(aChoices.isUndefined()); michael@0: michael@0: if (!mFile->IsSafePath()) { michael@0: nsCOMPtr r michael@0: = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED); michael@0: return NS_DispatchToMainThread(r); michael@0: } michael@0: michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(this, mFile); michael@0: DeviceStorageEnumerationParams params(mFile->mStorageType, michael@0: mFile->mStorageName, michael@0: mFile->mRootDir, michael@0: mSince); michael@0: ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, michael@0: params); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr target michael@0: = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); michael@0: MOZ_ASSERT(target); michael@0: michael@0: nsCOMPtr event = new InitCursorEvent(this, mFile); michael@0: target->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorageCursor::Continue(ErrorResult& aRv) michael@0: { michael@0: if (!mOkToCallContinue) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return; michael@0: } michael@0: michael@0: if (mResult != JSVAL_VOID) { michael@0: // We call onsuccess multiple times. Clear the last michael@0: // result. michael@0: mResult = JSVAL_VOID; michael@0: mDone = false; michael@0: } michael@0: michael@0: nsRefPtr event = new ContinueCursorEvent(this); michael@0: event->Continue(); michael@0: michael@0: mOkToCallContinue = false; michael@0: } michael@0: michael@0: bool michael@0: nsDOMDeviceStorageCursor::Recv__delete__(const bool& allow, michael@0: const InfallibleTArray& choices) michael@0: { michael@0: MOZ_ASSERT(choices.IsEmpty(), "DeviceStorageCursor doesn't support permission choice"); michael@0: michael@0: if (allow) { michael@0: Allow(JS::UndefinedHandleValue); michael@0: } michael@0: else { michael@0: Cancel(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorageCursor::IPDLRelease() michael@0: { michael@0: Release(); michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorageCursor::RequestComplete() michael@0: { michael@0: MOZ_ASSERT(!mOkToCallContinue); michael@0: mOkToCallContinue = true; michael@0: } michael@0: michael@0: class PostAvailableResultEvent : public nsRunnable michael@0: { michael@0: public: michael@0: PostAvailableResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~PostAvailableResultEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsCOMPtr window = mRequest->GetOwner(); michael@0: if (!window) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsString state = NS_LITERAL_STRING("unavailable"); michael@0: if (mFile) { michael@0: mFile->GetStatus(state); michael@0: } michael@0: michael@0: AutoJSContext cx; michael@0: JS::Rooted result(cx, StringToJsval(window, state)); michael@0: mRequest->FireSuccess(result); michael@0: mRequest = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class PostStatusResultEvent : public nsRunnable michael@0: { michael@0: public: michael@0: PostStatusResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~PostStatusResultEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsCOMPtr window = mRequest->GetOwner(); michael@0: if (!window) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsString state = NS_LITERAL_STRING("undefined"); michael@0: if (mFile) { michael@0: mFile->GetStorageStatus(state); michael@0: } michael@0: michael@0: AutoJSContext cx; michael@0: JS::Rooted result(cx, StringToJsval(window, state)); michael@0: mRequest->FireSuccess(result); michael@0: mRequest = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class PostFormatResultEvent : public nsRunnable michael@0: { michael@0: public: michael@0: PostFormatResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~PostFormatResultEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsCOMPtr window = mRequest->GetOwner(); michael@0: if (!window) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsString state = NS_LITERAL_STRING("unavailable"); michael@0: if (mFile) { michael@0: mFile->DoFormat(state); michael@0: } michael@0: michael@0: AutoJSContext cx; michael@0: JS::Rooted result(cx, StringToJsval(window, state)); michael@0: mRequest->FireSuccess(result); michael@0: mRequest = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class PostMountResultEvent : public nsRunnable michael@0: { michael@0: public: michael@0: PostMountResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~PostMountResultEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsCOMPtr window = mRequest->GetOwner(); michael@0: if (!window) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsString state = NS_LITERAL_STRING("unavailable"); michael@0: if (mFile) { michael@0: mFile->DoMount(state); michael@0: } michael@0: michael@0: AutoJSContext cx; michael@0: JS::Rooted result(cx, StringToJsval(window, state)); michael@0: mRequest->FireSuccess(result); michael@0: mRequest = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class PostUnmountResultEvent : public nsRunnable michael@0: { michael@0: public: michael@0: PostUnmountResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~PostUnmountResultEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsCOMPtr window = mRequest->GetOwner(); michael@0: if (!window) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsString state = NS_LITERAL_STRING("unavailable"); michael@0: if (mFile) { michael@0: mFile->DoUnmount(state); michael@0: } michael@0: michael@0: AutoJSContext cx; michael@0: JS::Rooted result(cx, StringToJsval(window, state)); michael@0: mRequest->FireSuccess(result); michael@0: mRequest = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class PostResultEvent : public nsRunnable michael@0: { michael@0: public: michael@0: PostResultEvent(already_AddRefed aRequest, michael@0: DeviceStorageFile* aFile) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: PostResultEvent(already_AddRefed aRequest, michael@0: const nsAString & aPath) michael@0: : mPath(aPath) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: PostResultEvent(already_AddRefed aRequest, michael@0: const uint64_t aValue) michael@0: : mValue(aValue) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~PostResultEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsCOMPtr window = mRequest->GetOwner(); michael@0: if (!window) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: AutoJSContext cx; michael@0: JS::Rooted result(cx, JSVAL_NULL); michael@0: michael@0: if (mFile) { michael@0: result = nsIFileToJsval(window, mFile); michael@0: } else if (mPath.Length()) { michael@0: result = StringToJsval(window, mPath); michael@0: } michael@0: else { michael@0: result = JS_NumberValue(double(mValue)); michael@0: } michael@0: michael@0: mRequest->FireSuccess(result); michael@0: mRequest = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsString mPath; michael@0: uint64_t mValue; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class CreateFdEvent : public nsRunnable michael@0: { michael@0: public: michael@0: CreateFdEvent(DeviceStorageFileDescriptor* aDSFileDescriptor, michael@0: already_AddRefed aRequest) michael@0: : mDSFileDescriptor(aDSFileDescriptor) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mDSFileDescriptor); michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: DeviceStorageFile* dsFile = mDSFileDescriptor->mDSFile; michael@0: MOZ_ASSERT(dsFile); michael@0: michael@0: nsString fullPath; michael@0: dsFile->GetFullPath(fullPath); michael@0: MOZ_ASSERT(!fullPath.IsEmpty()); michael@0: michael@0: bool check = false; michael@0: dsFile->mFile->Exists(&check); michael@0: if (check) { michael@0: nsCOMPtr event = michael@0: new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS); michael@0: return NS_DispatchToMainThread(event); michael@0: } michael@0: michael@0: nsresult rv = dsFile->CreateFileDescriptor(mDSFileDescriptor->mFileDescriptor); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: dsFile->mFile->Remove(false); michael@0: michael@0: nsCOMPtr event = michael@0: new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN); michael@0: return NS_DispatchToMainThread(event); michael@0: } michael@0: michael@0: nsCOMPtr event = michael@0: new PostResultEvent(mRequest.forget(), fullPath); michael@0: return NS_DispatchToMainThread(event); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mDSFileDescriptor; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class WriteFileEvent : public nsRunnable michael@0: { michael@0: public: michael@0: WriteFileEvent(nsIDOMBlob* aBlob, michael@0: DeviceStorageFile *aFile, michael@0: already_AddRefed aRequest) michael@0: : mBlob(aBlob) michael@0: , mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mFile); michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~WriteFileEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: nsCOMPtr stream; michael@0: mBlob->GetInternalStream(getter_AddRefs(stream)); michael@0: michael@0: bool check = false; michael@0: mFile->mFile->Exists(&check); michael@0: if (check) { michael@0: nsCOMPtr event = michael@0: new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS); michael@0: return NS_DispatchToMainThread(event); michael@0: } michael@0: michael@0: nsresult rv = mFile->Write(stream); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: mFile->mFile->Remove(false); michael@0: michael@0: nsCOMPtr event = michael@0: new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN); michael@0: return NS_DispatchToMainThread(event); michael@0: } michael@0: michael@0: nsString fullPath; michael@0: mFile->GetFullPath(fullPath); michael@0: nsCOMPtr event = michael@0: new PostResultEvent(mRequest.forget(), fullPath); michael@0: return NS_DispatchToMainThread(event); michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mBlob; michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class ReadFileEvent : public nsRunnable michael@0: { michael@0: public: michael@0: ReadFileEvent(DeviceStorageFile* aFile, michael@0: already_AddRefed aRequest) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mFile); michael@0: MOZ_ASSERT(mRequest); michael@0: mFile->CalculateMimeType(); michael@0: } michael@0: michael@0: ~ReadFileEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: nsCOMPtr r; michael@0: if (!mFile->mEditable) { michael@0: bool check = false; michael@0: mFile->mFile->Exists(&check); michael@0: if (!check) { michael@0: r = new PostErrorEvent(mRequest.forget(), michael@0: POST_ERROR_EVENT_FILE_DOES_NOT_EXIST); michael@0: } michael@0: } michael@0: michael@0: if (!r) { michael@0: nsresult rv = mFile->CalculateSizeAndModifiedDate(); michael@0: if (NS_FAILED(rv)) { michael@0: r = new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN); michael@0: } michael@0: } michael@0: michael@0: if (!r) { michael@0: r = new PostResultEvent(mRequest.forget(), mFile); michael@0: } michael@0: return NS_DispatchToMainThread(r); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class DeleteFileEvent : public nsRunnable michael@0: { michael@0: public: michael@0: DeleteFileEvent(DeviceStorageFile* aFile, michael@0: already_AddRefed aRequest) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mFile); michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~DeleteFileEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: mFile->Remove(); michael@0: michael@0: nsCOMPtr r; michael@0: bool check = false; michael@0: mFile->mFile->Exists(&check); michael@0: if (check) { michael@0: r = new PostErrorEvent(mRequest.forget(), michael@0: POST_ERROR_EVENT_FILE_DOES_NOT_EXIST); michael@0: } michael@0: else { michael@0: nsString fullPath; michael@0: mFile->GetFullPath(fullPath); michael@0: r = new PostResultEvent(mRequest.forget(), fullPath); michael@0: } michael@0: return NS_DispatchToMainThread(r); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class UsedSpaceFileEvent : public nsRunnable michael@0: { michael@0: public: michael@0: UsedSpaceFileEvent(DeviceStorageFile* aFile, michael@0: already_AddRefed aRequest) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mFile); michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~UsedSpaceFileEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0; michael@0: mFile->AccumDiskUsage(&picturesUsage, &videosUsage, michael@0: &musicUsage, &totalUsage); michael@0: nsCOMPtr r; michael@0: if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { michael@0: r = new PostResultEvent(mRequest.forget(), picturesUsage); michael@0: } michael@0: else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { michael@0: r = new PostResultEvent(mRequest.forget(), videosUsage); michael@0: } michael@0: else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { michael@0: r = new PostResultEvent(mRequest.forget(), musicUsage); michael@0: } else { michael@0: r = new PostResultEvent(mRequest.forget(), totalUsage); michael@0: } michael@0: return NS_DispatchToMainThread(r); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class FreeSpaceFileEvent : public nsRunnable michael@0: { michael@0: public: michael@0: FreeSpaceFileEvent(DeviceStorageFile* aFile, michael@0: already_AddRefed aRequest) michael@0: : mFile(aFile) michael@0: , mRequest(aRequest) michael@0: { michael@0: MOZ_ASSERT(mFile); michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: ~FreeSpaceFileEvent() {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: int64_t freeSpace = 0; michael@0: if (mFile) { michael@0: mFile->GetDiskFreeSpace(&freeSpace); michael@0: } michael@0: michael@0: nsCOMPtr r; michael@0: r = new PostResultEvent(mRequest.forget(), michael@0: static_cast(freeSpace)); michael@0: return NS_DispatchToMainThread(r); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class DeviceStorageRequest MOZ_FINAL michael@0: : public nsIContentPermissionRequest michael@0: , public nsIRunnable michael@0: , public PCOMContentPermissionRequestChild michael@0: { michael@0: public: michael@0: michael@0: DeviceStorageRequest(const DeviceStorageRequestType aRequestType, michael@0: nsPIDOMWindow* aWindow, michael@0: nsIPrincipal* aPrincipal, michael@0: DeviceStorageFile* aFile, michael@0: DOMRequest* aRequest, michael@0: nsDOMDeviceStorage* aDeviceStorage) michael@0: : mRequestType(aRequestType) michael@0: , mWindow(aWindow) michael@0: , mPrincipal(aPrincipal) michael@0: , mFile(aFile) michael@0: , mRequest(aRequest) michael@0: , mDeviceStorage(aDeviceStorage) michael@0: { michael@0: MOZ_ASSERT(mWindow); michael@0: MOZ_ASSERT(mPrincipal); michael@0: MOZ_ASSERT(mFile); michael@0: MOZ_ASSERT(mRequest); michael@0: MOZ_ASSERT(mDeviceStorage); michael@0: } michael@0: michael@0: DeviceStorageRequest(const DeviceStorageRequestType aRequestType, michael@0: nsPIDOMWindow* aWindow, michael@0: nsIPrincipal* aPrincipal, michael@0: DeviceStorageFile* aFile, michael@0: DOMRequest* aRequest, michael@0: nsIDOMBlob* aBlob = nullptr) michael@0: : mRequestType(aRequestType) michael@0: , mWindow(aWindow) michael@0: , mPrincipal(aPrincipal) michael@0: , mFile(aFile) michael@0: , mRequest(aRequest) michael@0: , mBlob(aBlob) michael@0: { michael@0: MOZ_ASSERT(mWindow); michael@0: MOZ_ASSERT(mPrincipal); michael@0: MOZ_ASSERT(mFile); michael@0: MOZ_ASSERT(mRequest); michael@0: } michael@0: michael@0: DeviceStorageRequest(const DeviceStorageRequestType aRequestType, michael@0: nsPIDOMWindow* aWindow, michael@0: nsIPrincipal* aPrincipal, michael@0: DeviceStorageFile* aFile, michael@0: DOMRequest* aRequest, michael@0: DeviceStorageFileDescriptor* aDSFileDescriptor) michael@0: : mRequestType(aRequestType) michael@0: , mWindow(aWindow) michael@0: , mPrincipal(aPrincipal) michael@0: , mFile(aFile) michael@0: , mRequest(aRequest) michael@0: , mDSFileDescriptor(aDSFileDescriptor) michael@0: { michael@0: MOZ_ASSERT(mRequestType == DEVICE_STORAGE_REQUEST_CREATEFD); michael@0: MOZ_ASSERT(mWindow); michael@0: MOZ_ASSERT(mPrincipal); michael@0: MOZ_ASSERT(mFile); michael@0: MOZ_ASSERT(mRequest); michael@0: MOZ_ASSERT(mDSFileDescriptor); michael@0: } michael@0: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageRequest, michael@0: nsIContentPermissionRequest) michael@0: michael@0: NS_IMETHOD Run() { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) { michael@0: Allow(JS::UndefinedHandleValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: michael@0: // because owner implements nsITabChild, we can assume that it is michael@0: // the one and only TabChild. michael@0: TabChild* child = TabChild::GetFrom(mWindow->GetDocShell()); michael@0: if (!child) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Retain a reference so the object isn't deleted without IPDL's michael@0: // knowledge. Corresponding release occurs in michael@0: // DeallocPContentPermissionRequest. michael@0: AddRef(); michael@0: michael@0: nsCString type; michael@0: nsresult rv = DeviceStorageTypeChecker::GetPermissionForType( michael@0: mFile->mStorageType, type); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: nsCString access; michael@0: rv = DeviceStorageTypeChecker::GetAccessForRequest( michael@0: DeviceStorageRequestType(mRequestType), access); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: nsTArray permArray; michael@0: nsTArray emptyOptions; michael@0: permArray.AppendElement(PermissionRequest(type, access, emptyOptions)); michael@0: child->SendPContentPermissionRequestConstructor( michael@0: this, permArray, IPC::Principal(mPrincipal)); michael@0: michael@0: Sendprompt(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr prompt michael@0: = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); michael@0: if (prompt) { michael@0: prompt->Prompt(this); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP GetTypes(nsIArray** aTypes) michael@0: { michael@0: nsCString type; michael@0: nsresult rv = michael@0: DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCString access; michael@0: rv = DeviceStorageTypeChecker::GetAccessForRequest( michael@0: DeviceStorageRequestType(mRequestType), access); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsTArray emptyOptions; michael@0: return CreatePermissionArray(type, access, emptyOptions, aTypes); michael@0: } michael@0: michael@0: NS_IMETHOD GetPrincipal(nsIPrincipal * *aRequestingPrincipal) michael@0: { michael@0: NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD GetWindow(nsIDOMWindow * *aRequestingWindow) michael@0: { michael@0: NS_IF_ADDREF(*aRequestingWindow = mWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD GetElement(nsIDOMElement * *aRequestingElement) michael@0: { michael@0: *aRequestingElement = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD Cancel() michael@0: { michael@0: nsCOMPtr event michael@0: = new PostErrorEvent(mRequest.forget(), michael@0: POST_ERROR_EVENT_PERMISSION_DENIED); michael@0: return NS_DispatchToMainThread(event); michael@0: } michael@0: michael@0: NS_IMETHOD Allow(JS::HandleValue aChoices) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aChoices.isUndefined()); michael@0: michael@0: if (!mRequest) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr r; michael@0: michael@0: switch(mRequestType) { michael@0: case DEVICE_STORAGE_REQUEST_CREATEFD: michael@0: { michael@0: if (!mFile->mFile) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) { michael@0: r = new PostErrorEvent(mRequest.forget(), michael@0: POST_ERROR_EVENT_ILLEGAL_TYPE); michael@0: return NS_DispatchToCurrentThread(r); michael@0: } michael@0: michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: michael@0: DeviceStorageCreateFdParams params; michael@0: params.type() = mFile->mStorageType; michael@0: params.storageName() = mFile->mStorageName; michael@0: params.relpath() = mFile->mPath; michael@0: michael@0: mFile->Dump("DeviceStorageCreateFdParams"); michael@0: michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile, michael@0: mDSFileDescriptor.get()); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: mDSFileDescriptor->mDSFile = mFile; michael@0: r = new CreateFdEvent(mDSFileDescriptor.get(), mRequest.forget()); michael@0: break; michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_CREATE: michael@0: { michael@0: if (!mBlob || !mFile->mFile) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!typeChecker->Check(mFile->mStorageType, mFile->mFile) || michael@0: !typeChecker->Check(mFile->mStorageType, mBlob)) { michael@0: r = new PostErrorEvent(mRequest.forget(), michael@0: POST_ERROR_EVENT_ILLEGAL_TYPE); michael@0: return NS_DispatchToCurrentThread(r); michael@0: } michael@0: michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: BlobChild* actor michael@0: = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlob); michael@0: if (!actor) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DeviceStorageAddParams params; michael@0: params.blobChild() = actor; michael@0: params.type() = mFile->mStorageType; michael@0: params.storageName() = mFile->mStorageName; michael@0: params.relpath() = mFile->mPath; michael@0: michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: r = new WriteFileEvent(mBlob, mFile, mRequest.forget()); michael@0: break; michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_READ: michael@0: case DEVICE_STORAGE_REQUEST_WRITE: michael@0: { michael@0: if (!mFile->mFile) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) { michael@0: r = new PostErrorEvent(mRequest.forget(), michael@0: POST_ERROR_EVENT_ILLEGAL_TYPE); michael@0: return NS_DispatchToCurrentThread(r); michael@0: } michael@0: michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: DeviceStorageGetParams params(mFile->mStorageType, michael@0: mFile->mStorageName, michael@0: mFile->mRootDir, michael@0: mFile->mPath); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: michael@0: r = new ReadFileEvent(mFile, mRequest.forget()); michael@0: break; michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_DELETE: michael@0: { michael@0: if (!mFile->mFile) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) { michael@0: r = new PostErrorEvent(mRequest.forget(), michael@0: POST_ERROR_EVENT_ILLEGAL_TYPE); michael@0: return NS_DispatchToCurrentThread(r); michael@0: } michael@0: michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: DeviceStorageDeleteParams params(mFile->mStorageType, michael@0: mFile->mStorageName, michael@0: mFile->mPath); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: r = new DeleteFileEvent(mFile, mRequest.forget()); michael@0: break; michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_FREE_SPACE: michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: DeviceStorageFreeSpaceParams params(mFile->mStorageType, michael@0: mFile->mStorageName); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: r = new FreeSpaceFileEvent(mFile, mRequest.forget()); michael@0: break; michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_USED_SPACE: michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: DeviceStorageUsedSpaceParams params(mFile->mStorageType, michael@0: mFile->mStorageName); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: // this needs to be dispatched to only one (1) michael@0: // thread or we will do more work than required. michael@0: DeviceStorageUsedSpaceCache* usedSpaceCache michael@0: = DeviceStorageUsedSpaceCache::CreateOrGet(); michael@0: MOZ_ASSERT(usedSpaceCache); michael@0: r = new UsedSpaceFileEvent(mFile, mRequest.forget()); michael@0: usedSpaceCache->Dispatch(r); michael@0: return NS_OK; michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_AVAILABLE: michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: DeviceStorageAvailableParams params(mFile->mStorageType, michael@0: mFile->mStorageName); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: r = new PostAvailableResultEvent(mFile, mRequest); michael@0: return NS_DispatchToCurrentThread(r); michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_STATUS: michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: DeviceStorageStatusParams params(mFile->mStorageType, michael@0: mFile->mStorageName); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: r = new PostStatusResultEvent(mFile, mRequest); michael@0: return NS_DispatchToCurrentThread(r); michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_WATCH: michael@0: { michael@0: mDeviceStorage->mAllowedToWatchFile = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_FORMAT: michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: DeviceStorageFormatParams params(mFile->mStorageType, michael@0: mFile->mStorageName); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: r = new PostFormatResultEvent(mFile, mRequest); michael@0: return NS_DispatchToCurrentThread(r); michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_MOUNT: michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: DeviceStorageMountParams params(mFile->mStorageType, michael@0: mFile->mStorageName); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: r = new PostMountResultEvent(mFile, mRequest); michael@0: return NS_DispatchToCurrentThread(r); michael@0: } michael@0: michael@0: case DEVICE_STORAGE_REQUEST_UNMOUNT: michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: PDeviceStorageRequestChild* child michael@0: = new DeviceStorageRequestChild(mRequest, mFile); michael@0: DeviceStorageUnmountParams params(mFile->mStorageType, michael@0: mFile->mStorageName); michael@0: ContentChild::GetSingleton() michael@0: ->SendPDeviceStorageRequestConstructor(child, params); michael@0: return NS_OK; michael@0: } michael@0: r = new PostUnmountResultEvent(mFile, mRequest); michael@0: return NS_DispatchToCurrentThread(r); michael@0: } michael@0: } michael@0: michael@0: if (r) { michael@0: nsCOMPtr target michael@0: = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); michael@0: MOZ_ASSERT(target); michael@0: target->Dispatch(r, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool Recv__delete__(const bool& allow, michael@0: const InfallibleTArray& choices) michael@0: { michael@0: MOZ_ASSERT(choices.IsEmpty(), "DeviceStorage doesn't support permission choice"); michael@0: michael@0: if (allow) { michael@0: Allow(JS::UndefinedHandleValue); michael@0: } michael@0: else { michael@0: Cancel(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void IPDLRelease() michael@0: { michael@0: Release(); michael@0: } michael@0: michael@0: private: michael@0: int32_t mRequestType; michael@0: nsCOMPtr mWindow; michael@0: nsCOMPtr mPrincipal; michael@0: nsRefPtr mFile; michael@0: michael@0: nsRefPtr mRequest; michael@0: nsCOMPtr mBlob; michael@0: nsRefPtr mDeviceStorage; michael@0: nsRefPtr mDSFileDescriptor; michael@0: }; michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageRequest) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRunnable) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageRequest) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageRequest) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(DeviceStorageRequest, michael@0: mRequest, michael@0: mWindow, michael@0: mBlob, michael@0: mDeviceStorage) michael@0: michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsDOMDeviceStorage) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorage) michael@0: NS_INTERFACE_MAP_ENTRY(nsIObserver) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper) michael@0: michael@0: nsDOMDeviceStorage::nsDOMDeviceStorage(nsPIDOMWindow* aWindow) michael@0: : DOMEventTargetHelper(aWindow) michael@0: , mIsWatchingFile(false) michael@0: , mAllowedToWatchFile(false) michael@0: { michael@0: } michael@0: michael@0: /* virtual */ JSObject* michael@0: nsDOMDeviceStorage::WrapObject(JSContext* aCx) michael@0: { michael@0: return DeviceStorageBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: nsresult michael@0: nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType, michael@0: const nsAString &aVolName) michael@0: { michael@0: DebugOnly observer michael@0: = FileUpdateDispatcher::GetSingleton(); michael@0: MOZ_ASSERT(observer); michael@0: michael@0: MOZ_ASSERT(aWindow); michael@0: michael@0: SetRootDirectoryForType(aType, aVolName); michael@0: if (!mRootDirectory) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: if (!mStorageName.IsEmpty()) { michael@0: RegisterForSDCardChanges(this); michael@0: } michael@0: michael@0: // Grab the principal of the document michael@0: nsCOMPtr doc = aWindow->GetDoc(); michael@0: if (!doc) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: mPrincipal = doc->NodePrincipal(); michael@0: michael@0: // the 'apps' type is special. We only want this exposed michael@0: // if the caller has the "webapps-manage" permission. michael@0: if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) { michael@0: nsCOMPtr permissionManager michael@0: = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); michael@0: NS_ENSURE_TRUE(permissionManager, NS_ERROR_FAILURE); michael@0: michael@0: uint32_t permission; michael@0: nsresult rv michael@0: = permissionManager->TestPermissionFromPrincipal(mPrincipal, michael@0: "webapps-manage", michael@0: &permission); michael@0: michael@0: if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsDOMDeviceStorage::~nsDOMDeviceStorage() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorage::Shutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (mFileSystem) { michael@0: mFileSystem->Shutdown(); michael@0: mFileSystem = nullptr; michael@0: } michael@0: michael@0: if (!mStorageName.IsEmpty()) { michael@0: UnregisterForSDCardChanges(this); michael@0: } michael@0: michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->RemoveObserver(this, "file-watcher-update"); michael@0: obs->RemoveObserver(this, "disk-space-watcher"); michael@0: } michael@0: michael@0: StaticAutoPtr> nsDOMDeviceStorage::sVolumeNameCache; michael@0: michael@0: // static michael@0: void michael@0: nsDOMDeviceStorage::GetOrderedVolumeNames( michael@0: nsDOMDeviceStorage::VolumeNameArray &aVolumeNames) michael@0: { michael@0: if (sVolumeNameCache && sVolumeNameCache->Length() > 0) { michael@0: aVolumeNames.AppendElements(*sVolumeNameCache); michael@0: return; michael@0: } michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); michael@0: if (vs) { michael@0: vs->GetVolumeNames(aVolumeNames); michael@0: michael@0: // If the volume sdcard exists, then we want it to be first. michael@0: michael@0: VolumeNameArray::index_type sdcardIndex; michael@0: sdcardIndex = aVolumeNames.IndexOf(NS_LITERAL_STRING("sdcard")); michael@0: if (sdcardIndex != VolumeNameArray::NoIndex && sdcardIndex > 0) { michael@0: aVolumeNames.RemoveElementAt(sdcardIndex); michael@0: aVolumeNames.InsertElementAt(0, NS_LITERAL_STRING("sdcard")); michael@0: } michael@0: } michael@0: #endif michael@0: if (aVolumeNames.IsEmpty()) { michael@0: aVolumeNames.AppendElement(EmptyString()); michael@0: } michael@0: sVolumeNameCache = new nsTArray; michael@0: sVolumeNameCache->AppendElements(aVolumeNames); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsDOMDeviceStorage::CreateDeviceStorageFor(nsPIDOMWindow* aWin, michael@0: const nsAString &aType, michael@0: nsDOMDeviceStorage** aStore) michael@0: { michael@0: nsString storageName; michael@0: if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) { michael@0: // The storage name will be the empty string michael@0: storageName.Truncate(); michael@0: } else { michael@0: GetDefaultStorageName(aType, storageName); michael@0: } michael@0: michael@0: nsRefPtr ds = new nsDOMDeviceStorage(aWin); michael@0: if (NS_FAILED(ds->Init(aWin, aType, storageName))) { michael@0: *aStore = nullptr; michael@0: return; michael@0: } michael@0: NS_ADDREF(*aStore = ds.get()); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsDOMDeviceStorage::CreateDeviceStoragesFor( michael@0: nsPIDOMWindow* aWin, michael@0: const nsAString &aType, michael@0: nsTArray > &aStores) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) { michael@0: nsRefPtr storage = new nsDOMDeviceStorage(aWin); michael@0: rv = storage->Init(aWin, aType, EmptyString()); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: aStores.AppendElement(storage); michael@0: } michael@0: return; michael@0: } michael@0: VolumeNameArray volNames; michael@0: GetOrderedVolumeNames(volNames); michael@0: michael@0: VolumeNameArray::size_type numVolumeNames = volNames.Length(); michael@0: for (VolumeNameArray::index_type i = 0; i < numVolumeNames; i++) { michael@0: nsRefPtr storage = new nsDOMDeviceStorage(aWin); michael@0: rv = storage->Init(aWin, aType, volNames[i]); michael@0: if (NS_FAILED(rv)) { michael@0: break; michael@0: } michael@0: aStores.AppendElement(storage); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: nsDOMDeviceStorage::ParseFullPath(const nsAString& aFullPath, michael@0: nsAString& aOutStorageName, michael@0: nsAString& aOutStoragePath) michael@0: { michael@0: aOutStorageName.Truncate(); michael@0: aOutStoragePath.Truncate(); michael@0: michael@0: NS_NAMED_LITERAL_STRING(slash, "/"); michael@0: michael@0: nsDependentSubstring storageName; michael@0: michael@0: if (StringBeginsWith(aFullPath, slash)) { michael@0: int32_t slashIndex = aFullPath.FindChar('/', 1); michael@0: if (slashIndex == kNotFound) { michael@0: // names of the form /filename are illegal michael@0: return false; michael@0: } michael@0: storageName.Rebind(aFullPath, 1, slashIndex - 1); michael@0: aOutStoragePath = Substring(aFullPath, slashIndex + 1); michael@0: } else { michael@0: aOutStoragePath = aFullPath; michael@0: } michael@0: // If no volume name was specified in aFullPath, then aOutStorageName michael@0: // will wind up being the empty string. It's up to the caller to figure michael@0: // out which storage name to actually use. michael@0: aOutStorageName = storageName; michael@0: return true; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::GetStorage(const nsAString& aFullPath, michael@0: nsAString& aOutStoragePath) michael@0: { michael@0: nsString storageName; michael@0: if (!ParseFullPath(aFullPath, storageName, aOutStoragePath)) { michael@0: return nullptr; michael@0: } michael@0: nsRefPtr ds; michael@0: if (storageName.IsEmpty()) { michael@0: ds = this; michael@0: } else { michael@0: ds = GetStorageByName(storageName); michael@0: } michael@0: return ds.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::GetStorageByName(const nsAString& aStorageName) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr ds; michael@0: michael@0: if (mStorageName.Equals(aStorageName)) { michael@0: ds = this; michael@0: return ds.forget(); michael@0: } michael@0: VolumeNameArray volNames; michael@0: GetOrderedVolumeNames(volNames); michael@0: VolumeNameArray::size_type numVolumes = volNames.Length(); michael@0: VolumeNameArray::index_type i; michael@0: for (i = 0; i < numVolumes; i++) { michael@0: if (volNames[i].Equals(aStorageName)) { michael@0: ds = new nsDOMDeviceStorage(GetOwner()); michael@0: nsresult rv = ds->Init(GetOwner(), mStorageType, aStorageName); michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: return ds.forget(); michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsDOMDeviceStorage::GetDefaultStorageName(const nsAString& aStorageType, michael@0: nsAString& aStorageName) michael@0: { michael@0: // See if the preferred volume is available. michael@0: nsRefPtr ds; michael@0: nsAdoptingString prefStorageName = michael@0: mozilla::Preferences::GetString("device.storage.writable.name"); michael@0: if (prefStorageName) { michael@0: aStorageName = prefStorageName; michael@0: return; michael@0: } michael@0: michael@0: // No preferred storage, we'll use the first one (which should be sdcard). michael@0: michael@0: VolumeNameArray volNames; michael@0: GetOrderedVolumeNames(volNames); michael@0: if (volNames.Length() > 0) { michael@0: aStorageName = volNames[0]; michael@0: return; michael@0: } michael@0: michael@0: // No volumes available, return the empty string. This is normal for michael@0: // b2g-desktop. michael@0: aStorageName.Truncate(); michael@0: } michael@0: michael@0: bool michael@0: nsDOMDeviceStorage::IsAvailable() michael@0: { michael@0: DeviceStorageFile dsf(mStorageType, mStorageName); michael@0: return dsf.IsAvailable(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr request = Add(aBlob, rv); michael@0: request.forget(_retval); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::Add(nsIDOMBlob* aBlob, ErrorResult& aRv) michael@0: { michael@0: if (!aBlob) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID); michael@0: if (!mimeSvc) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: // if mimeType isn't set, we will not get a correct michael@0: // extension, and AddNamed() will fail. This will post an michael@0: // onerror to the requestee. michael@0: nsString mimeType; michael@0: aBlob->GetType(mimeType); michael@0: michael@0: nsCString extension; michael@0: mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType), michael@0: EmptyCString(), extension); michael@0: // if extension is null here, we will ignore it for now. michael@0: // AddNamed() will check the file path and fail. This michael@0: // will post an onerror to the requestee. michael@0: michael@0: // possible race here w/ unique filename michael@0: char buffer[32]; michael@0: NS_MakeRandomString(buffer, ArrayLength(buffer) - 1); michael@0: michael@0: nsAutoCString path; michael@0: path.Assign(nsDependentCString(buffer)); michael@0: path.Append("."); michael@0: path.Append(extension); michael@0: michael@0: return AddNamed(aBlob, NS_ConvertASCIItoUTF16(path), aRv); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob, michael@0: const nsAString & aPath, michael@0: nsIDOMDOMRequest * *_retval) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr request = AddNamed(aBlob, aPath, rv); michael@0: request.forget(_retval); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // if the blob is null here, bail michael@0: if (!aBlob) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr r; michael@0: nsresult rv; michael@0: michael@0: if (IsFullPath(aPath)) { michael@0: nsString storagePath; michael@0: nsRefPtr ds = GetStorage(aPath, storagePath); michael@0: if (!ds) { michael@0: nsRefPtr request = new DOMRequest(win); michael@0: r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN); michael@0: rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: return ds->AddNamed(aBlob, storagePath, aRv); michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName, michael@0: aPath); michael@0: if (!dsf->IsSafePath()) { michael@0: r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED); michael@0: } else if (!typeChecker->Check(mStorageType, dsf->mFile) || michael@0: !typeChecker->Check(mStorageType, aBlob)) { michael@0: r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE); michael@0: } else { michael@0: r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATE, michael@0: win, mPrincipal, dsf, request, aBlob); michael@0: } michael@0: michael@0: rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::Get(const nsAString& aPath, nsIDOMDOMRequest** aRetval) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr request = Get(aPath, rv); michael@0: request.forget(aRetval); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::GetEditable(const nsAString& aPath, michael@0: nsIDOMDOMRequest** aRetval) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr request = GetEditable(aPath, rv); michael@0: request.forget(aRetval); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::GetInternal(const nsAString& aPath, bool aEditable, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: if (IsFullPath(aPath)) { michael@0: nsString storagePath; michael@0: nsRefPtr ds = GetStorage(aPath, storagePath); michael@0: if (!ds) { michael@0: nsCOMPtr r = michael@0: new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: ds->GetInternal(win, storagePath, request, aEditable); michael@0: return request.forget(); michael@0: } michael@0: GetInternal(win, aPath, request, aEditable); michael@0: return request.forget(); michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorage::GetInternal(nsPIDOMWindow *aWin, michael@0: const nsAString& aPath, michael@0: DOMRequest* aRequest, michael@0: bool aEditable) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName, michael@0: aPath); michael@0: dsf->SetEditable(aEditable); michael@0: michael@0: nsCOMPtr r; michael@0: if (!dsf->IsSafePath()) { michael@0: r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED); michael@0: } else { michael@0: r = new DeviceStorageRequest(aEditable ? DEVICE_STORAGE_REQUEST_WRITE michael@0: : DEVICE_STORAGE_REQUEST_READ, michael@0: aWin, mPrincipal, dsf, aRequest); michael@0: } michael@0: DebugOnly rv = NS_DispatchToCurrentThread(r); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::Delete(const nsAString& aPath, nsIDOMDOMRequest** aRetval) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr request = Delete(aPath, rv); michael@0: request.forget(aRetval); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::Delete(const nsAString& aPath, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: if (IsFullPath(aPath)) { michael@0: nsString storagePath; michael@0: nsRefPtr ds = GetStorage(aPath, storagePath); michael@0: if (!ds) { michael@0: nsCOMPtr r = michael@0: new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: ds->DeleteInternal(win, storagePath, request); michael@0: return request.forget(); michael@0: } michael@0: DeleteInternal(win, aPath, request); michael@0: return request.forget(); michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorage::DeleteInternal(nsPIDOMWindow *aWin, michael@0: const nsAString& aPath, michael@0: DOMRequest* aRequest) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr r; michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName, michael@0: aPath); michael@0: if (!dsf->IsSafePath()) { michael@0: r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED); michael@0: } else { michael@0: r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE, michael@0: aWin, mPrincipal, dsf, aRequest); michael@0: } michael@0: DebugOnly rv = NS_DispatchToCurrentThread(r); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::FreeSpace(nsIDOMDOMRequest** aRetval) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr request = FreeSpace(rv); michael@0: request.forget(aRetval); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::FreeSpace(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName); michael@0: nsCOMPtr r michael@0: = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE, michael@0: win, mPrincipal, dsf, request); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::UsedSpace(nsIDOMDOMRequest** aRetval) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr request = UsedSpace(rv); michael@0: request.forget(aRetval); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::UsedSpace(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: DebugOnly usedSpaceCache michael@0: = DeviceStorageUsedSpaceCache::CreateOrGet(); michael@0: MOZ_ASSERT(usedSpaceCache); michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName); michael@0: nsCOMPtr r michael@0: = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE, michael@0: win, mPrincipal, dsf, request); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::Available(nsIDOMDOMRequest** aRetval) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr request = Available(rv); michael@0: request.forget(aRetval); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::Available(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName); michael@0: nsCOMPtr r michael@0: = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE, michael@0: win, mPrincipal, dsf, request); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::StorageStatus(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName); michael@0: nsCOMPtr r michael@0: = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_STATUS, michael@0: win, mPrincipal, dsf, request); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::Format(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName); michael@0: nsCOMPtr r michael@0: = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FORMAT, michael@0: win, mPrincipal, dsf, request); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::Mount(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName); michael@0: nsCOMPtr r michael@0: = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_MOUNT, michael@0: win, mPrincipal, dsf, request); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::Unmount(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName); michael@0: nsCOMPtr r michael@0: = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_UNMOUNT, michael@0: win, mPrincipal, dsf, request); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return request.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::CreateFileDescriptor(const nsAString& aPath, michael@0: DeviceStorageFileDescriptor* aDSFileDescriptor, michael@0: nsIDOMDOMRequest** aRequest) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aDSFileDescriptor); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: DeviceStorageTypeChecker* typeChecker michael@0: = DeviceStorageTypeChecker::CreateOrGet(); michael@0: if (!typeChecker) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr r; michael@0: nsresult rv; michael@0: michael@0: if (IsFullPath(aPath)) { michael@0: nsString storagePath; michael@0: nsRefPtr ds = GetStorage(aPath, storagePath); michael@0: if (!ds) { michael@0: nsRefPtr request = new DOMRequest(win); michael@0: r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN); michael@0: rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: request.forget(aRequest); michael@0: return NS_OK; michael@0: } michael@0: return ds->CreateFileDescriptor(storagePath, aDSFileDescriptor, aRequest); michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName, michael@0: aPath); michael@0: if (!dsf->IsSafePath()) { michael@0: r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED); michael@0: } else if (!typeChecker->Check(mStorageType, dsf->mFile)) { michael@0: r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE); michael@0: } else { michael@0: r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATEFD, michael@0: win, mPrincipal, dsf, request, michael@0: aDSFileDescriptor); michael@0: } michael@0: michael@0: rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: request.forget(aRequest); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsDOMDeviceStorage::Default() michael@0: { michael@0: nsString defaultStorageName; michael@0: GetDefaultStorageName(mStorageType, defaultStorageName); michael@0: return mStorageName.Equals(defaultStorageName); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::GetRoot() michael@0: { michael@0: if (!mFileSystem) { michael@0: mFileSystem = new DeviceStorageFileSystem(mStorageType, mStorageName); michael@0: mFileSystem->Init(this); michael@0: } michael@0: return mozilla::dom::Directory::GetRoot(mFileSystem); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::GetDefault(bool* aDefault) michael@0: { michael@0: *aDefault = Default(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::GetStorageName(nsAString& aStorageName) michael@0: { michael@0: aStorageName = mStorageName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::Enumerate(const nsAString& aPath, michael@0: const EnumerationParameters& aOptions, michael@0: ErrorResult& aRv) michael@0: { michael@0: return EnumerateInternal(aPath, aOptions, false, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::EnumerateEditable(const nsAString& aPath, michael@0: const EnumerationParameters& aOptions, michael@0: ErrorResult& aRv) michael@0: { michael@0: return EnumerateInternal(aPath, aOptions, true, aRv); michael@0: } michael@0: michael@0: michael@0: already_AddRefed michael@0: nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath, michael@0: const EnumerationParameters& aOptions, michael@0: bool aEditable, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: PRTime since = 0; michael@0: if (aOptions.mSince.WasPassed() && !aOptions.mSince.Value().IsUndefined()) { michael@0: since = PRTime(aOptions.mSince.Value().TimeStamp()); michael@0: } michael@0: michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName, michael@0: aPath, michael@0: EmptyString()); michael@0: dsf->SetEditable(aEditable); michael@0: michael@0: nsRefPtr cursor michael@0: = new nsDOMDeviceStorageCursor(win, mPrincipal, dsf, since); michael@0: nsRefPtr r michael@0: = new DeviceStorageCursorRequest(cursor); michael@0: michael@0: if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) { michael@0: r->Allow(JS::UndefinedHandleValue); michael@0: return cursor.forget(); michael@0: } michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: // because owner implements nsITabChild, we can assume that it is michael@0: // the one and only TabChild. michael@0: TabChild* child = TabChild::GetFrom(win->GetDocShell()); michael@0: if (!child) { michael@0: return cursor.forget(); michael@0: } michael@0: michael@0: // Retain a reference so the object isn't deleted without IPDL's knowledge. michael@0: // Corresponding release occurs in DeallocPContentPermissionRequest. michael@0: r->AddRef(); michael@0: michael@0: nsCString type; michael@0: aRv = DeviceStorageTypeChecker::GetPermissionForType(mStorageType, type); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: nsTArray permArray; michael@0: nsTArray emptyOptions; michael@0: permArray.AppendElement(PermissionRequest(type, michael@0: NS_LITERAL_CSTRING("read"), michael@0: emptyOptions)); michael@0: child->SendPContentPermissionRequestConstructor(r, michael@0: permArray, michael@0: IPC::Principal(mPrincipal)); michael@0: michael@0: r->Sendprompt(); michael@0: michael@0: return cursor.forget(); michael@0: } michael@0: michael@0: nsCOMPtr prompt michael@0: = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); michael@0: if (prompt) { michael@0: prompt->Prompt(r); michael@0: } michael@0: michael@0: return cursor.forget(); michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: void michael@0: nsDOMDeviceStorage::DispatchMountChangeEvent(nsAString& aVolumeStatus) michael@0: { michael@0: if (aVolumeStatus == mLastStatus) { michael@0: // We've already sent this status, don't bother sending it again. michael@0: return; michael@0: } michael@0: mLastStatus = aVolumeStatus; michael@0: michael@0: nsCOMPtr event; michael@0: NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this, michael@0: nullptr, nullptr); michael@0: michael@0: nsCOMPtr ce = do_QueryInterface(event); michael@0: nsresult rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"), michael@0: true, false, michael@0: mStorageName, michael@0: aVolumeStatus); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: bool ignore; michael@0: DispatchEvent(ce, &ignore); michael@0: } michael@0: #endif michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!strcmp(aTopic, "file-watcher-update")) { michael@0: michael@0: DeviceStorageFile* file = static_cast(aSubject); michael@0: Notify(NS_ConvertUTF16toUTF8(aData).get(), file); michael@0: return NS_OK; michael@0: } michael@0: if (!strcmp(aTopic, "disk-space-watcher")) { michael@0: // 'disk-space-watcher' notifications are sent when there is a modification michael@0: // of a file in a specific location while a low device storage situation michael@0: // exists or after recovery of a low storage situation. For Firefox OS, michael@0: // these notifications are specific for apps storage. michael@0: nsRefPtr file = michael@0: new DeviceStorageFile(mStorageType, mStorageName); michael@0: if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "full")) { michael@0: Notify("low-disk-space", file); michael@0: } else if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "free")) { michael@0: Notify("available-disk-space", file); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: else if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) { michael@0: // We invalidate the used space cache for the volume that actually changed michael@0: // state. michael@0: nsCOMPtr vol = do_QueryInterface(aSubject); michael@0: if (!vol) { michael@0: return NS_OK; michael@0: } michael@0: nsString volName; michael@0: vol->GetName(volName); michael@0: michael@0: DeviceStorageUsedSpaceCache* usedSpaceCache michael@0: = DeviceStorageUsedSpaceCache::CreateOrGet(); michael@0: MOZ_ASSERT(usedSpaceCache); michael@0: usedSpaceCache->Invalidate(volName); michael@0: michael@0: if (!volName.Equals(mStorageName)) { michael@0: // Not our volume - we can ignore. michael@0: return NS_OK; michael@0: } michael@0: michael@0: DeviceStorageFile dsf(mStorageType, mStorageName); michael@0: nsString status; michael@0: dsf.GetStatus(status); michael@0: DispatchMountChangeEvent(status); michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsDOMDeviceStorage::Notify(const char* aReason, DeviceStorageFile* aFile) michael@0: { michael@0: if (!mAllowedToWatchFile) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mStorageType.Equals(aFile->mStorageType) || michael@0: !mStorageName.Equals(aFile->mStorageName)) { michael@0: // Ignore this michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr event; michael@0: NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this, michael@0: nullptr, nullptr); michael@0: michael@0: nsCOMPtr ce = do_QueryInterface(event); michael@0: michael@0: nsString reason; michael@0: reason.AssignWithConversion(aReason); michael@0: michael@0: nsString fullPath; michael@0: aFile->GetFullPath(fullPath); michael@0: nsresult rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"), michael@0: true, false, fullPath, michael@0: reason); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool ignore; michael@0: DispatchEvent(ce, &ignore); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::AddEventListener(const nsAString & aType, michael@0: nsIDOMEventListener *aListener, michael@0: bool aUseCapture, michael@0: bool aWantsUntrusted, michael@0: uint8_t aArgc) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName); michael@0: nsCOMPtr r michael@0: = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH, michael@0: win, mPrincipal, dsf, request, this); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: return DOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture, michael@0: aWantsUntrusted, aArgc); michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorage::AddEventListener(const nsAString & aType, michael@0: EventListener *aListener, michael@0: bool aUseCapture, michael@0: const Nullable& aWantsUntrusted, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr request = new DOMRequest(win); michael@0: nsRefPtr dsf = new DeviceStorageFile(mStorageType, michael@0: mStorageName); michael@0: nsCOMPtr r michael@0: = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH, michael@0: win, mPrincipal, dsf, request, this); michael@0: nsresult rv = NS_DispatchToCurrentThread(r); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return; michael@0: } michael@0: DOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture, michael@0: aWantsUntrusted, aRv); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType, michael@0: nsIDOMEventListener *aListener, michael@0: bool aUseCapture, michael@0: bool aWantsUntrusted, michael@0: uint8_t aArgc) michael@0: { michael@0: if (!mIsWatchingFile) { michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->AddObserver(this, "file-watcher-update", false); michael@0: mIsWatchingFile = true; michael@0: } michael@0: michael@0: return nsDOMDeviceStorage::AddEventListener(aType, aListener, aUseCapture, michael@0: aWantsUntrusted, aArgc); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::RemoveEventListener(const nsAString & aType, michael@0: nsIDOMEventListener *aListener, michael@0: bool aUseCapture) michael@0: { michael@0: DOMEventTargetHelper::RemoveEventListener(aType, aListener, false); michael@0: michael@0: if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) { michael@0: mIsWatchingFile = false; michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->RemoveObserver(this, "file-watcher-update"); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsDOMDeviceStorage::RemoveEventListener(const nsAString& aType, michael@0: EventListener* aListener, michael@0: bool aCapture, michael@0: ErrorResult& aRv) michael@0: { michael@0: DOMEventTargetHelper::RemoveEventListener(aType, aListener, aCapture, aRv); michael@0: michael@0: if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) { michael@0: mIsWatchingFile = false; michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->RemoveObserver(this, "file-watcher-update"); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::RemoveSystemEventListener(const nsAString & aType, michael@0: nsIDOMEventListener *aListener, michael@0: bool aUseCapture) michael@0: { michael@0: return nsDOMDeviceStorage::RemoveEventListener(aType, aListener, aUseCapture); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMDeviceStorage::DispatchEvent(nsIDOMEvent *aEvt, michael@0: bool *aRetval) michael@0: { michael@0: return DOMEventTargetHelper::DispatchEvent(aEvt, aRetval); michael@0: } michael@0: michael@0: EventTarget* michael@0: nsDOMDeviceStorage::GetTargetForDOMEvent() michael@0: { michael@0: return DOMEventTargetHelper::GetTargetForDOMEvent(); michael@0: } michael@0: michael@0: EventTarget * michael@0: nsDOMDeviceStorage::GetTargetForEventTargetChain() michael@0: { michael@0: return DOMEventTargetHelper::GetTargetForEventTargetChain(); michael@0: } michael@0: michael@0: nsresult michael@0: nsDOMDeviceStorage::PreHandleEvent(EventChainPreVisitor& aVisitor) michael@0: { michael@0: return DOMEventTargetHelper::PreHandleEvent(aVisitor); michael@0: } michael@0: michael@0: nsresult michael@0: nsDOMDeviceStorage::WillHandleEvent(EventChainPostVisitor& aVisitor) michael@0: { michael@0: return DOMEventTargetHelper::WillHandleEvent(aVisitor); michael@0: } michael@0: michael@0: nsresult michael@0: nsDOMDeviceStorage::PostHandleEvent(EventChainPostVisitor& aVisitor) michael@0: { michael@0: return DOMEventTargetHelper::PostHandleEvent(aVisitor); michael@0: } michael@0: michael@0: nsresult michael@0: nsDOMDeviceStorage::DispatchDOMEvent(WidgetEvent* aEvent, michael@0: nsIDOMEvent* aDOMEvent, michael@0: nsPresContext* aPresContext, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: return DOMEventTargetHelper::DispatchDOMEvent(aEvent, michael@0: aDOMEvent, michael@0: aPresContext, michael@0: aEventStatus); michael@0: } michael@0: michael@0: EventListenerManager* michael@0: nsDOMDeviceStorage::GetOrCreateListenerManager() michael@0: { michael@0: return DOMEventTargetHelper::GetOrCreateListenerManager(); michael@0: } michael@0: michael@0: EventListenerManager* michael@0: nsDOMDeviceStorage::GetExistingListenerManager() const michael@0: { michael@0: return DOMEventTargetHelper::GetExistingListenerManager(); michael@0: } michael@0: michael@0: nsIScriptContext * michael@0: nsDOMDeviceStorage::GetContextForEventHandlers(nsresult *aRv) michael@0: { michael@0: return DOMEventTargetHelper::GetContextForEventHandlers(aRv); michael@0: } michael@0: michael@0: JSContext * michael@0: nsDOMDeviceStorage::GetJSContextForEventHandlers() michael@0: { michael@0: return DOMEventTargetHelper::GetJSContextForEventHandlers(); michael@0: } michael@0: michael@0: NS_IMPL_EVENT_HANDLER(nsDOMDeviceStorage, change)