dom/devicestorage/nsDeviceStorage.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 sw=2 et tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsDeviceStorage.h"
     9 #include "mozilla/Attributes.h"
    10 #include "mozilla/ClearOnShutdown.h"
    11 #include "mozilla/DebugOnly.h"
    12 #include "mozilla/dom/ContentChild.h"
    13 #include "mozilla/dom/DeviceStorageBinding.h"
    14 #include "mozilla/dom/DeviceStorageFileSystem.h"
    15 #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
    16 #include "mozilla/dom/Directory.h"
    17 #include "mozilla/dom/FileSystemUtils.h"
    18 #include "mozilla/dom/ipc/Blob.h"
    19 #include "mozilla/dom/PBrowserChild.h"
    20 #include "mozilla/dom/PContentPermissionRequestChild.h"
    21 #include "mozilla/dom/PermissionMessageUtils.h"
    22 #include "mozilla/dom/Promise.h"
    23 #include "mozilla/EventDispatcher.h"
    24 #include "mozilla/EventListenerManager.h"
    25 #include "mozilla/LazyIdleThread.h"
    26 #include "mozilla/Preferences.h"
    27 #include "mozilla/Scoped.h"
    28 #include "mozilla/Services.h"
    30 #include "nsAutoPtr.h"
    31 #include "nsServiceManagerUtils.h"
    32 #include "nsIFile.h"
    33 #include "nsIDirectoryEnumerator.h"
    34 #include "nsAppDirectoryServiceDefs.h"
    35 #include "nsDirectoryServiceDefs.h"
    36 #include "nsIDOMFile.h"
    37 #include "nsDOMBlobBuilder.h"
    38 #include "nsNetUtil.h"
    39 #include "nsCycleCollectionParticipant.h"
    40 #include "nsIPrincipal.h"
    41 #include "nsJSUtils.h"
    42 #include "nsContentUtils.h"
    43 #include "nsCxPusher.h"
    44 #include "nsXULAppAPI.h"
    45 #include "TabChild.h"
    46 #include "DeviceStorageFileDescriptor.h"
    47 #include "DeviceStorageRequestChild.h"
    48 #include "nsIDOMDeviceStorageChangeEvent.h"
    49 #include "nsCRT.h"
    50 #include "nsIObserverService.h"
    51 #include "GeneratedEvents.h"
    52 #include "nsIMIMEService.h"
    53 #include "nsCExternalHandlerService.h"
    54 #include "nsIPermissionManager.h"
    55 #include "nsIStringBundle.h"
    56 #include "nsIDocument.h"
    57 #include "nsPrintfCString.h"
    58 #include <algorithm>
    59 #include "private/pprio.h"
    60 #include "nsContentPermissionHelper.h"
    62 #include "mozilla/dom/DeviceStorageBinding.h"
    64 // Microsoft's API Name hackery sucks
    65 #undef CreateEvent
    67 #ifdef MOZ_WIDGET_GONK
    68 #include "nsIVolume.h"
    69 #include "nsIVolumeService.h"
    70 #endif
    72 #define DEVICESTORAGE_PROPERTIES \
    73   "chrome://global/content/devicestorage.properties"
    74 #define DEFAULT_THREAD_TIMEOUT_MS 30000
    76 using namespace mozilla;
    77 using namespace mozilla::dom;
    78 using namespace mozilla::dom::devicestorage;
    79 using namespace mozilla::ipc;
    81 #include "nsDirectoryServiceDefs.h"
    83 namespace mozilla {
    84   MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
    85 }
    87 StaticAutoPtr<DeviceStorageUsedSpaceCache>
    88   DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
    90 DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache()
    91 {
    92   MOZ_ASSERT(NS_IsMainThread());
    94   mIOThread = new LazyIdleThread(
    95     DEFAULT_THREAD_TIMEOUT_MS,
    96     NS_LITERAL_CSTRING("DeviceStorageUsedSpaceCache I/O"));
    98 }
   100 DeviceStorageUsedSpaceCache::~DeviceStorageUsedSpaceCache()
   101 {
   102 }
   104 DeviceStorageUsedSpaceCache*
   105 DeviceStorageUsedSpaceCache::CreateOrGet()
   106 {
   107   if (sDeviceStorageUsedSpaceCache) {
   108     return sDeviceStorageUsedSpaceCache;
   109   }
   111   MOZ_ASSERT(NS_IsMainThread());
   113   sDeviceStorageUsedSpaceCache = new DeviceStorageUsedSpaceCache();
   114   ClearOnShutdown(&sDeviceStorageUsedSpaceCache);
   115   return sDeviceStorageUsedSpaceCache;
   116 }
   118 already_AddRefed<DeviceStorageUsedSpaceCache::CacheEntry>
   119 DeviceStorageUsedSpaceCache::GetCacheEntry(const nsAString& aStorageName)
   120 {
   121   nsTArray<nsRefPtr<CacheEntry>>::size_type numEntries = mCacheEntries.Length();
   122   nsTArray<nsRefPtr<CacheEntry>>::index_type i;
   123   for (i = 0; i < numEntries; i++) {
   124     nsRefPtr<CacheEntry>& cacheEntry = mCacheEntries[i];
   125     if (cacheEntry->mStorageName.Equals(aStorageName)) {
   126       nsRefPtr<CacheEntry> addRefedCacheEntry = cacheEntry;
   127       return addRefedCacheEntry.forget();
   128     }
   129   }
   130   return nullptr;
   131 }
   133 static int64_t
   134 GetFreeBytes(const nsAString& aStorageName)
   135 {
   136   // This function makes the assumption that the various types
   137   // are all stored on the same filesystem. So we use pictures.
   139   DeviceStorageFile dsf(NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
   140                         aStorageName);
   141   int64_t freeBytes = 0;
   142   dsf.GetDiskFreeSpace(&freeBytes);
   143   return freeBytes;
   144 }
   146 nsresult
   147 DeviceStorageUsedSpaceCache::AccumUsedSizes(const nsAString& aStorageName,
   148                                             uint64_t* aPicturesSoFar,
   149                                             uint64_t* aVideosSoFar,
   150                                             uint64_t* aMusicSoFar,
   151                                             uint64_t* aTotalSoFar)
   152 {
   153   nsRefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
   154   if (!cacheEntry || cacheEntry->mDirty) {
   155     return NS_ERROR_NOT_AVAILABLE;
   156   }
   157   int64_t freeBytes = GetFreeBytes(cacheEntry->mStorageName);
   158   if (freeBytes != cacheEntry->mFreeBytes) {
   159     // Free space changed, so our cached results are no longer valid.
   160     return NS_ERROR_NOT_AVAILABLE;
   161   }
   163   *aPicturesSoFar += cacheEntry->mPicturesUsedSize;
   164   *aVideosSoFar += cacheEntry->mVideosUsedSize;
   165   *aMusicSoFar += cacheEntry->mMusicUsedSize;
   166   *aTotalSoFar += cacheEntry->mTotalUsedSize;
   168   return NS_OK;
   169 }
   171 void
   172 DeviceStorageUsedSpaceCache::SetUsedSizes(const nsAString& aStorageName,
   173                                           uint64_t aPictureSize,
   174                                           uint64_t aVideosSize,
   175                                           uint64_t aMusicSize,
   176                                           uint64_t aTotalUsedSize)
   177 {
   178   nsRefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
   179   if (!cacheEntry) {
   180     cacheEntry = new CacheEntry;
   181     cacheEntry->mStorageName = aStorageName;
   182     mCacheEntries.AppendElement(cacheEntry);
   183   }
   184   cacheEntry->mFreeBytes = GetFreeBytes(cacheEntry->mStorageName);
   186   cacheEntry->mPicturesUsedSize = aPictureSize;
   187   cacheEntry->mVideosUsedSize = aVideosSize;
   188   cacheEntry->mMusicUsedSize = aMusicSize;
   189   cacheEntry->mTotalUsedSize = aTotalUsedSize;
   190   cacheEntry->mDirty = false;
   191 }
   193 class GlobalDirs
   194 {
   195 public:
   196   NS_INLINE_DECL_REFCOUNTING(GlobalDirs)
   197 #if !defined(MOZ_WIDGET_GONK)
   198   nsCOMPtr<nsIFile> pictures;
   199   nsCOMPtr<nsIFile> videos;
   200   nsCOMPtr<nsIFile> music;
   201   nsCOMPtr<nsIFile> sdcard;
   202 #endif
   203   nsCOMPtr<nsIFile> apps;
   204   nsCOMPtr<nsIFile> crashes;
   205   nsCOMPtr<nsIFile> overrideRootDir;
   206 };
   208 static StaticRefPtr<GlobalDirs> sDirs;
   210 StaticAutoPtr<DeviceStorageTypeChecker>
   211   DeviceStorageTypeChecker::sDeviceStorageTypeChecker;
   213 DeviceStorageTypeChecker::DeviceStorageTypeChecker()
   214 {
   215 }
   217 DeviceStorageTypeChecker::~DeviceStorageTypeChecker()
   218 {
   219 }
   221 DeviceStorageTypeChecker*
   222 DeviceStorageTypeChecker::CreateOrGet()
   223 {
   224   if (sDeviceStorageTypeChecker) {
   225     return sDeviceStorageTypeChecker;
   226   }
   228   MOZ_ASSERT(NS_IsMainThread());
   230   nsCOMPtr<nsIStringBundleService> stringService
   231     = mozilla::services::GetStringBundleService();
   232   if (!stringService) {
   233     return nullptr;
   234   }
   236   nsCOMPtr<nsIStringBundle> filterBundle;
   237   if (NS_FAILED(stringService->CreateBundle(DEVICESTORAGE_PROPERTIES,
   238                                             getter_AddRefs(filterBundle)))) {
   239     return nullptr;
   240   }
   242   DeviceStorageTypeChecker* result = new DeviceStorageTypeChecker();
   243   result->InitFromBundle(filterBundle);
   245   sDeviceStorageTypeChecker = result;
   246   ClearOnShutdown(&sDeviceStorageTypeChecker);
   247   return result;
   248 }
   250 void
   251 DeviceStorageTypeChecker::InitFromBundle(nsIStringBundle* aBundle)
   252 {
   253   aBundle->GetStringFromName(
   254     NS_ConvertASCIItoUTF16(DEVICESTORAGE_PICTURES).get(),
   255     getter_Copies(mPicturesExtensions));
   256   aBundle->GetStringFromName(
   257     NS_ConvertASCIItoUTF16(DEVICESTORAGE_MUSIC).get(),
   258     getter_Copies(mMusicExtensions));
   259   aBundle->GetStringFromName(
   260     NS_ConvertASCIItoUTF16(DEVICESTORAGE_VIDEOS).get(),
   261     getter_Copies(mVideosExtensions));
   262 }
   265 bool
   266 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIDOMBlob* aBlob)
   267 {
   268   MOZ_ASSERT(aBlob);
   270   nsString mimeType;
   271   if (NS_FAILED(aBlob->GetType(mimeType))) {
   272     return false;
   273   }
   275   if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
   276     return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/"));
   277   }
   279   if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
   280     return StringBeginsWith(mimeType, NS_LITERAL_STRING("video/"));
   281   }
   283   if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
   284     return StringBeginsWith(mimeType, NS_LITERAL_STRING("audio/"));
   285   }
   287   if (aType.EqualsLiteral(DEVICESTORAGE_APPS) ||
   288       aType.EqualsLiteral(DEVICESTORAGE_SDCARD) ||
   289       aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
   290     // Apps, crashes and sdcard have no restriction on mime types
   291     return true;
   292   }
   294   return false;
   295 }
   297 bool
   298 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIFile* aFile)
   299 {
   300   MOZ_ASSERT(aFile);
   302   if (aType.EqualsLiteral(DEVICESTORAGE_APPS) ||
   303       aType.EqualsLiteral(DEVICESTORAGE_SDCARD) ||
   304       aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
   305     // Apps, crashes and sdcard have no restrictions on what file extensions used.
   306     return true;
   307   }
   309   nsString path;
   310   aFile->GetPath(path);
   312   int32_t dotIdx = path.RFindChar(char16_t('.'));
   313   if (dotIdx == kNotFound) {
   314     return false;
   315   }
   317   nsAutoString extensionMatch;
   318   extensionMatch.AssignLiteral("*");
   319   extensionMatch.Append(Substring(path, dotIdx));
   320   extensionMatch.AppendLiteral(";");
   322   if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
   323     return CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions);
   324   }
   326   if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
   327     return CaseInsensitiveFindInReadable(extensionMatch, mVideosExtensions);
   328   }
   330   if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
   331     return CaseInsensitiveFindInReadable(extensionMatch, mMusicExtensions);
   332   }
   334   return false;
   335 }
   337 void
   338 DeviceStorageTypeChecker::GetTypeFromFile(nsIFile* aFile, nsAString& aType)
   339 {
   340   MOZ_ASSERT(aFile);
   342   nsString path;
   343   aFile->GetPath(path);
   345   GetTypeFromFileName(path, aType);
   346 }
   348 void
   349 DeviceStorageTypeChecker::GetTypeFromFileName(const nsAString& aFileName,
   350                                               nsAString& aType)
   351 {
   352   aType.AssignLiteral(DEVICESTORAGE_SDCARD);
   354   nsString fileName(aFileName);
   355   int32_t dotIdx = fileName.RFindChar(char16_t('.'));
   356   if (dotIdx == kNotFound) {
   357     return;
   358   }
   360   nsAutoString extensionMatch;
   361   extensionMatch.AssignLiteral("*");
   362   extensionMatch.Append(Substring(aFileName, dotIdx));
   363   extensionMatch.AppendLiteral(";");
   365   if (CaseInsensitiveFindInReadable(extensionMatch, mPicturesExtensions)) {
   366     aType.AssignLiteral(DEVICESTORAGE_PICTURES);
   367   }
   368   else if (CaseInsensitiveFindInReadable(extensionMatch, mVideosExtensions)) {
   369     aType.AssignLiteral(DEVICESTORAGE_VIDEOS);
   370   }
   371   else if (CaseInsensitiveFindInReadable(extensionMatch, mMusicExtensions)) {
   372     aType.AssignLiteral(DEVICESTORAGE_MUSIC);
   373   }
   374 }
   376 nsresult
   377 DeviceStorageTypeChecker::GetPermissionForType(const nsAString& aType,
   378                                                nsACString& aPermissionResult)
   379 {
   380   if (!aType.EqualsLiteral(DEVICESTORAGE_PICTURES) &&
   381       !aType.EqualsLiteral(DEVICESTORAGE_VIDEOS) &&
   382       !aType.EqualsLiteral(DEVICESTORAGE_MUSIC) &&
   383       !aType.EqualsLiteral(DEVICESTORAGE_APPS) &&
   384       !aType.EqualsLiteral(DEVICESTORAGE_SDCARD) &&
   385       !aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
   386     // unknown type
   387     return NS_ERROR_FAILURE;
   388   }
   390   aPermissionResult.AssignLiteral("device-storage:");
   391   aPermissionResult.Append(NS_ConvertUTF16toUTF8(aType));
   392   return NS_OK;
   393 }
   395 nsresult
   396 DeviceStorageTypeChecker::GetAccessForRequest(
   397   const DeviceStorageRequestType aRequestType, nsACString& aAccessResult)
   398 {
   399   switch(aRequestType) {
   400     case DEVICE_STORAGE_REQUEST_READ:
   401     case DEVICE_STORAGE_REQUEST_WATCH:
   402     case DEVICE_STORAGE_REQUEST_FREE_SPACE:
   403     case DEVICE_STORAGE_REQUEST_USED_SPACE:
   404     case DEVICE_STORAGE_REQUEST_AVAILABLE:
   405     case DEVICE_STORAGE_REQUEST_STATUS:
   406       aAccessResult.AssignLiteral("read");
   407       break;
   408     case DEVICE_STORAGE_REQUEST_WRITE:
   409     case DEVICE_STORAGE_REQUEST_DELETE:
   410     case DEVICE_STORAGE_REQUEST_FORMAT:
   411     case DEVICE_STORAGE_REQUEST_MOUNT:
   412     case DEVICE_STORAGE_REQUEST_UNMOUNT:
   413       aAccessResult.AssignLiteral("write");
   414       break;
   415     case DEVICE_STORAGE_REQUEST_CREATE:
   416     case DEVICE_STORAGE_REQUEST_CREATEFD:
   417       aAccessResult.AssignLiteral("create");
   418       break;
   419     default:
   420       aAccessResult.AssignLiteral("undefined");
   421   }
   422   return NS_OK;
   423 }
   425 //static
   426 bool
   427 DeviceStorageTypeChecker::IsVolumeBased(const nsAString& aType)
   428 {
   429 #ifdef MOZ_WIDGET_GONK
   430   // The apps and crashes aren't stored in the same place as the media, so
   431   // we only ever return a single apps object, and not an array
   432   // with one per volume (as is the case for the remaining
   433   // storage types).
   434   return !aType.EqualsLiteral(DEVICESTORAGE_APPS) &&
   435          !aType.EqualsLiteral(DEVICESTORAGE_CRASHES);
   436 #else
   437   return false;
   438 #endif
   439 }
   441 NS_IMPL_ISUPPORTS(FileUpdateDispatcher, nsIObserver)
   443 mozilla::StaticRefPtr<FileUpdateDispatcher> FileUpdateDispatcher::sSingleton;
   445 FileUpdateDispatcher*
   446 FileUpdateDispatcher::GetSingleton()
   447 {
   448   if (sSingleton) {
   449     return sSingleton;
   450   }
   452   sSingleton = new FileUpdateDispatcher();
   453   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   454   obs->AddObserver(sSingleton, "file-watcher-notify", false);
   455   ClearOnShutdown(&sSingleton);
   457   return sSingleton;
   458 }
   460 NS_IMETHODIMP
   461 FileUpdateDispatcher::Observe(nsISupports *aSubject,
   462                               const char *aTopic,
   463                               const char16_t *aData)
   464 {
   465   if (XRE_GetProcessType() != GeckoProcessType_Default) {
   467     DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
   468     if (!file || !file->mFile) {
   469       NS_WARNING("Device storage file looks invalid!");
   470       return NS_OK;
   471     }
   472     ContentChild::GetSingleton()
   473       ->SendFilePathUpdateNotify(file->mStorageType,
   474                                  file->mStorageName,
   475                                  file->mPath,
   476                                  NS_ConvertUTF16toUTF8(aData));
   477   } else {
   478     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   479     obs->NotifyObservers(aSubject, "file-watcher-update", aData);
   480   }
   481   return NS_OK;
   482 }
   484 class IOEventComplete : public nsRunnable
   485 {
   486 public:
   487   IOEventComplete(DeviceStorageFile *aFile, const char *aType)
   488     : mFile(aFile)
   489     , mType(aType)
   490   {
   491   }
   493   ~IOEventComplete() {}
   495   NS_IMETHOD Run()
   496   {
   497     MOZ_ASSERT(NS_IsMainThread());
   498     nsString data;
   499     CopyASCIItoUTF16(mType, data);
   500     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   502     obs->NotifyObservers(mFile, "file-watcher-notify", data.get());
   504     DeviceStorageUsedSpaceCache* usedSpaceCache
   505       = DeviceStorageUsedSpaceCache::CreateOrGet();
   506     MOZ_ASSERT(usedSpaceCache);
   507     usedSpaceCache->Invalidate(mFile->mStorageName);
   508     return NS_OK;
   509   }
   511 private:
   512   nsRefPtr<DeviceStorageFile> mFile;
   513   nsCString mType;
   514 };
   516 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
   517                                      const nsAString& aStorageName,
   518                                      const nsAString& aRootDir,
   519                                      const nsAString& aPath)
   520   : mStorageType(aStorageType)
   521   , mStorageName(aStorageName)
   522   , mRootDir(aRootDir)
   523   , mPath(aPath)
   524   , mEditable(false)
   525   , mLength(UINT64_MAX)
   526   , mLastModifiedDate(UINT64_MAX)
   527 {
   528   Init();
   529   AppendRelativePath(mRootDir);
   530   if (!mPath.EqualsLiteral("")) {
   531     AppendRelativePath(mPath);
   532   }
   533   NormalizeFilePath();
   534 }
   536 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
   537                                      const nsAString& aStorageName,
   538                                      const nsAString& aPath)
   539   : mStorageType(aStorageType)
   540   , mStorageName(aStorageName)
   541   , mPath(aPath)
   542   , mEditable(false)
   543   , mLength(UINT64_MAX)
   544   , mLastModifiedDate(UINT64_MAX)
   545 {
   546   Init();
   547   AppendRelativePath(aPath);
   548   NormalizeFilePath();
   549 }
   551 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
   552                                      const nsAString& aStorageName)
   553   : mStorageType(aStorageType)
   554   , mStorageName(aStorageName)
   555   , mEditable(false)
   556   , mLength(UINT64_MAX)
   557   , mLastModifiedDate(UINT64_MAX)
   558 {
   559   Init();
   560 }
   562 void
   563 DeviceStorageFile::Dump(const char* label)
   564 {
   565   nsString path;
   566   if (mFile) {
   567     mFile->GetPath(path);
   568   } else {
   569     path = NS_LITERAL_STRING("(null)");
   570   }
   571   const char* ptStr;
   572   if (XRE_GetProcessType() == GeckoProcessType_Default) {
   573     ptStr = "parent";
   574   } else {
   575     ptStr = "child";
   576   }
   578   printf_stderr("DSF (%s) %s: mStorageType '%s' mStorageName '%s' "
   579                 "mRootDir '%s' mPath '%s' mFile->GetPath '%s'\n",
   580                 ptStr, label,
   581                 NS_LossyConvertUTF16toASCII(mStorageType).get(),
   582                 NS_LossyConvertUTF16toASCII(mStorageName).get(),
   583                 NS_LossyConvertUTF16toASCII(mRootDir).get(),
   584                 NS_LossyConvertUTF16toASCII(mPath).get(),
   585                 NS_LossyConvertUTF16toASCII(path).get());
   586 }
   588 void
   589 DeviceStorageFile::Init()
   590 {
   591   DeviceStorageFile::GetRootDirectoryForType(mStorageType,
   592                                              mStorageName,
   593                                              getter_AddRefs(mFile));
   595   DebugOnly<DeviceStorageTypeChecker*> typeChecker
   596     = DeviceStorageTypeChecker::CreateOrGet();
   597   MOZ_ASSERT(typeChecker);
   598 }
   600 // The OverrideRootDir is needed to facilitate testing of the
   601 // device.storage.overrideRootDir preference. The preference is normally
   602 // only read once during initialization, but since the test environment has
   603 // no convenient way to restart, we use a pref watcher instead.
   604 class OverrideRootDir MOZ_FINAL : public nsIObserver
   605 {
   606 public:
   607   NS_DECL_ISUPPORTS
   608   NS_DECL_NSIOBSERVER
   610   static OverrideRootDir* GetSingleton();
   611   ~OverrideRootDir();
   612   void Init();
   613 private:
   614   static mozilla::StaticRefPtr<OverrideRootDir> sSingleton;
   615 };
   617 NS_IMPL_ISUPPORTS(OverrideRootDir, nsIObserver)
   619 mozilla::StaticRefPtr<OverrideRootDir>
   620   OverrideRootDir::sSingleton;
   622 OverrideRootDir*
   623 OverrideRootDir::GetSingleton()
   624 {
   625   if (sSingleton) {
   626     return sSingleton;
   627   }
   628   // Preference changes are automatically forwarded from parent to child
   629   // in ContentParent::Observe, so we'll see the change in both the parent
   630   // and the child process.
   632   sSingleton = new OverrideRootDir();
   633   Preferences::AddStrongObserver(sSingleton, "device.storage.overrideRootDir");
   634   ClearOnShutdown(&sSingleton);
   636   return sSingleton;
   637 }
   639 OverrideRootDir::~OverrideRootDir()
   640 {
   641   Preferences::RemoveObserver(this, "device.storage.overrideRootDir");
   642 }
   644 NS_IMETHODIMP
   645 OverrideRootDir::Observe(nsISupports *aSubject,
   646                               const char *aTopic,
   647                               const char16_t *aData)
   648 {
   649   MOZ_ASSERT(NS_IsMainThread());
   651   if (sSingleton) {
   652     sSingleton->Init();
   653   }
   654   return NS_OK;
   655 }
   657 void
   658 OverrideRootDir::Init()
   659 {
   660   MOZ_ASSERT(NS_IsMainThread());
   662   if (!sDirs) {
   663     return;
   664   }
   666   if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
   667     nsCOMPtr<nsIProperties> dirService
   668       = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   669     MOZ_ASSERT(dirService);
   670     dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile),
   671                     getter_AddRefs(sDirs->overrideRootDir));
   672     if (sDirs->overrideRootDir) {
   673       sDirs->overrideRootDir->AppendRelativeNativePath(
   674         NS_LITERAL_CSTRING("device-storage-testing"));
   675     }
   676   } else {
   677     // For users running on desktop, it's convenient to be able to override
   678     // all of the directories to point to a single tree, much like what happens
   679     // on a real device.
   680     const nsAdoptingString& overrideRootDir =
   681       mozilla::Preferences::GetString("device.storage.overrideRootDir");
   682     if (overrideRootDir && overrideRootDir.Length() > 0) {
   683       NS_NewLocalFile(overrideRootDir, false,
   684                       getter_AddRefs(sDirs->overrideRootDir));
   685     } else {
   686       sDirs->overrideRootDir = nullptr;
   687     }
   688   }
   690   if (sDirs->overrideRootDir) {
   691     if (XRE_GetProcessType() == GeckoProcessType_Default) {
   692       // Only the parent process can create directories. In testing, because
   693       // the preference is updated after startup, its entirely possible that
   694       // the preference updated notification will be received by a child
   695       // prior to the parent.
   696       nsresult rv
   697         = sDirs->overrideRootDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
   698       nsString path;
   699       sDirs->overrideRootDir->GetPath(path);
   700       if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
   701         nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
   702                             NS_LossyConvertUTF16toASCII(path).get());
   703         NS_WARNING(msg.get());
   704       }
   705     }
   706     sDirs->overrideRootDir->Normalize();
   707   }
   708 }
   710 // Directories which don't depend on a volume should be calculated once
   711 // here. Directories which depend on the root directory of a volume
   712 // should be calculated in DeviceStorageFile::GetRootDirectoryForType.
   713 static void
   714 InitDirs()
   715 {
   716   if (sDirs) {
   717     return;
   718   }
   719   MOZ_ASSERT(NS_IsMainThread());
   720   sDirs = new GlobalDirs;
   721   ClearOnShutdown(&sDirs);
   723   nsCOMPtr<nsIProperties> dirService
   724     = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   725   MOZ_ASSERT(dirService);
   727 #if !defined(MOZ_WIDGET_GONK)
   729 #if defined (MOZ_WIDGET_COCOA)
   730   dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR,
   731                   NS_GET_IID(nsIFile),
   732                   getter_AddRefs(sDirs->pictures));
   733   dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR,
   734                   NS_GET_IID(nsIFile),
   735                   getter_AddRefs(sDirs->videos));
   736   dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR,
   737                   NS_GET_IID(nsIFile),
   738                   getter_AddRefs(sDirs->music));
   739 #elif defined (XP_UNIX)
   740   dirService->Get(NS_UNIX_XDG_PICTURES_DIR,
   741                   NS_GET_IID(nsIFile),
   742                   getter_AddRefs(sDirs->pictures));
   743   dirService->Get(NS_UNIX_XDG_VIDEOS_DIR,
   744                   NS_GET_IID(nsIFile),
   745                   getter_AddRefs(sDirs->videos));
   746   dirService->Get(NS_UNIX_XDG_MUSIC_DIR,
   747                   NS_GET_IID(nsIFile),
   748                   getter_AddRefs(sDirs->music));
   749 #elif defined (XP_WIN)
   750   dirService->Get(NS_WIN_PICTURES_DIR,
   751                   NS_GET_IID(nsIFile),
   752                   getter_AddRefs(sDirs->pictures));
   753   dirService->Get(NS_WIN_VIDEOS_DIR,
   754                   NS_GET_IID(nsIFile),
   755                   getter_AddRefs(sDirs->videos));
   756   dirService->Get(NS_WIN_MUSIC_DIR,
   757                   NS_GET_IID(nsIFile),
   758                   getter_AddRefs(sDirs->music));
   759 #endif
   761   // Eventually, on desktop, we want to do something smarter -- for example,
   762   // detect when an sdcard is inserted, and use that instead of this.
   763   dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
   764                   getter_AddRefs(sDirs->sdcard));
   765   if (sDirs->sdcard) {
   766     sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
   767   }
   768 #endif // !MOZ_WIDGET_GONK
   770 #ifdef MOZ_WIDGET_GONK
   771   NS_NewLocalFile(NS_LITERAL_STRING("/data"),
   772                   false,
   773                   getter_AddRefs(sDirs->apps));
   774 #else
   775   dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
   776                   getter_AddRefs(sDirs->apps));
   777   if (sDirs->apps) {
   778     sDirs->apps->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
   779   }
   780 #endif
   782   if (XRE_GetProcessType() == GeckoProcessType_Default) {
   783     NS_GetSpecialDirectory("UAppData", getter_AddRefs(sDirs->crashes));
   784     if (sDirs->crashes) {
   785       sDirs->crashes->Append(NS_LITERAL_STRING("Crash Reports"));
   786     }
   787   } else {
   788     // NS_GetSpecialDirectory("UAppData") fails in content processes because
   789     // gAppData from toolkit/xre/nsAppRunner.cpp is not initialized.
   790 #ifdef MOZ_WIDGET_GONK
   791     NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"),
   792                                       false,
   793                                       getter_AddRefs(sDirs->crashes));
   794 #endif
   795   }
   797   OverrideRootDir::GetSingleton()->Init();
   798 }
   800 void
   801 DeviceStorageFile::GetFullPath(nsAString &aFullPath)
   802 {
   803   aFullPath.Truncate();
   804   if (!mStorageName.EqualsLiteral("")) {
   805     aFullPath.AppendLiteral("/");
   806     aFullPath.Append(mStorageName);
   807     aFullPath.AppendLiteral("/");
   808   }
   809   if (!mRootDir.EqualsLiteral("")) {
   810     aFullPath.Append(mRootDir);
   811     aFullPath.AppendLiteral("/");
   812   }
   813   aFullPath.Append(mPath);
   814 }
   817 // Directories which don't depend on a volume should be calculated once
   818 // in InitDirs. Directories which depend on the root directory of a volume
   819 // should be calculated in this method.
   820 void
   821 DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType,
   822                                            const nsAString& aStorageName,
   823                                            nsIFile** aFile)
   824 {
   825   nsCOMPtr<nsIFile> f;
   826   *aFile = nullptr;
   827   bool allowOverride = true;
   829   InitDirs();
   831 #ifdef MOZ_WIDGET_GONK
   832   nsString volMountPoint;
   833   if (DeviceStorageTypeChecker::IsVolumeBased(aStorageType)) {
   834     nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
   835     NS_ENSURE_TRUE_VOID(vs);
   836     nsresult rv;
   837     nsCOMPtr<nsIVolume> vol;
   838     rv = vs->GetVolumeByName(aStorageName, getter_AddRefs(vol));
   839     NS_ENSURE_SUCCESS_VOID(rv);
   840     vol->GetMountPoint(volMountPoint);
   841   }
   842 #endif
   844   // Picture directory
   845   if (aStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
   846 #ifdef MOZ_WIDGET_GONK
   847     NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
   848 #else
   849     f = sDirs->pictures;
   850 #endif
   851   }
   853   // Video directory
   854   else if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
   855 #ifdef MOZ_WIDGET_GONK
   856     NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
   857 #else
   858     f = sDirs->videos;
   859 #endif
   860   }
   862   // Music directory
   863   else if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
   864 #ifdef MOZ_WIDGET_GONK
   865     NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
   866 #else
   867     f = sDirs->music;
   868 #endif
   869   }
   871   // Apps directory
   872   else if (aStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) {
   873     f = sDirs->apps;
   874     allowOverride = false;
   875   }
   877    // default SDCard
   878    else if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
   879 #ifdef MOZ_WIDGET_GONK
   880      NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
   881 #else
   882      f = sDirs->sdcard;
   883 #endif
   884   }
   886   // crash reports directory.
   887   else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
   888     f = sDirs->crashes;
   889     allowOverride = false;
   890   } else {
   891     // Not a storage type that we recognize. Return null
   892     return;
   893   }
   895   // In testing, we default all device storage types to a temp directory.
   896   // sDirs->overrideRootDir will only have been initialized (in InitDirs)
   897   // if the preference device.storage.testing was set to true, or if
   898   // device.storage.overrideRootDir is set. We can't test the preferences
   899   // directly here, since we may not be on the main thread.
   900   if (allowOverride && sDirs->overrideRootDir) {
   901     f = sDirs->overrideRootDir;
   902   }
   904   if (f) {
   905     f->Clone(aFile);
   906   }
   907 }
   909 //static
   910 already_AddRefed<DeviceStorageFile>
   911 DeviceStorageFile::CreateUnique(nsAString& aFileName,
   912                                 uint32_t aFileType,
   913                                 uint32_t aFileAttributes)
   914 {
   915   DeviceStorageTypeChecker* typeChecker
   916     = DeviceStorageTypeChecker::CreateOrGet();
   917   MOZ_ASSERT(typeChecker);
   919   nsString storageType;
   920   typeChecker->GetTypeFromFileName(aFileName, storageType);
   922   nsString storageName;
   923   nsString storagePath;
   924   if (!nsDOMDeviceStorage::ParseFullPath(aFileName, storageName, storagePath)) {
   925     return nullptr;
   926   }
   927   if (storageName.IsEmpty()) {
   928     nsDOMDeviceStorage::GetDefaultStorageName(storageType, storageName);
   929   }
   930   nsRefPtr<DeviceStorageFile> dsf =
   931     new DeviceStorageFile(storageType, storageName, storagePath);
   932   if (!dsf->mFile) {
   933     return nullptr;
   934   }
   936   nsresult rv = dsf->mFile->CreateUnique(aFileType, aFileAttributes);
   937   NS_ENSURE_SUCCESS(rv, nullptr);
   939   // CreateUnique may cause the filename to change. So we need to update mPath
   940   // to reflect that.
   941   nsString leafName;
   942   dsf->mFile->GetLeafName(leafName);
   944   int32_t lastSlashIndex = dsf->mPath.RFindChar('/');
   945   if (lastSlashIndex == kNotFound) {
   946     dsf->mPath.Assign(leafName);
   947   } else {
   948     // Include the last '/'
   949     dsf->mPath = Substring(dsf->mPath, 0, lastSlashIndex + 1);
   950     dsf->mPath.Append(leafName);
   951   }
   953   return dsf.forget();
   954 }
   956 void
   957 DeviceStorageFile::SetPath(const nsAString& aPath) {
   958   mPath.Assign(aPath);
   959   NormalizeFilePath();
   960 }
   962 void
   963 DeviceStorageFile::SetEditable(bool aEditable) {
   964   mEditable = aEditable;
   965 }
   967 // we want to make sure that the names of file can't reach
   968 // outside of the type of storage the user asked for.
   969 bool
   970 DeviceStorageFile::IsSafePath()
   971 {
   972   return IsSafePath(mRootDir) && IsSafePath(mPath);
   973 }
   975 bool
   976 DeviceStorageFile::IsSafePath(const nsAString& aPath)
   977 {
   978   nsAString::const_iterator start, end;
   979   aPath.BeginReading(start);
   980   aPath.EndReading(end);
   982   // if the path is a '~' or starts with '~/', return false.
   983   NS_NAMED_LITERAL_STRING(tilde, "~");
   984   NS_NAMED_LITERAL_STRING(tildeSlash, "~/");
   985   if (aPath.Equals(tilde) ||
   986       StringBeginsWith(aPath, tildeSlash)) {
   987     NS_WARNING("Path name starts with tilde!");
   988     return false;
   989    }
   990   // split on /.  if any token is "", ., or .., return false.
   991   NS_ConvertUTF16toUTF8 cname(aPath);
   992   char* buffer = cname.BeginWriting();
   993   const char* token;
   995   while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
   996     if (PL_strcmp(token, "") == 0 ||
   997         PL_strcmp(token, ".") == 0 ||
   998         PL_strcmp(token, "..") == 0 ) {
   999       return false;
  1002   return true;
  1005 void
  1006 DeviceStorageFile::NormalizeFilePath() {
  1007   FileSystemUtils::LocalPathToNormalizedPath(mPath, mPath);
  1010 void
  1011 DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
  1012   if (!mFile) {
  1013     return;
  1015   if (!IsSafePath(aPath)) {
  1016     // All of the APIs (in the child) do checks to verify that the path is
  1017     // valid and return PERMISSION_DENIED if a non-safe path is entered.
  1018     // This check is done in the parent and prevents a compromised
  1019     // child from bypassing the check. It shouldn't be possible for this
  1020     // code path to be taken with a non-compromised child.
  1021     NS_WARNING("Unsafe path detected - ignoring");
  1022     NS_WARNING(NS_LossyConvertUTF16toASCII(aPath).get());
  1023     return;
  1025   nsString localPath;
  1026   FileSystemUtils::NormalizedPathToLocalPath(aPath, localPath);
  1027   mFile->AppendRelativePath(localPath);
  1030 nsresult
  1031 DeviceStorageFile::CreateFileDescriptor(FileDescriptor& aFileDescriptor)
  1033   ScopedPRFileDesc fd;
  1034   nsresult rv = mFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE,
  1035                                         0660, &fd.rwget());
  1036   NS_ENSURE_SUCCESS(rv, rv);
  1038   // NOTE: The FileDescriptor::PlatformHandleType constructor returns a dup of
  1039   //       the file descriptor, so we don't need the original fd after this.
  1040   //       Our scoped file descriptor will automatically close fd.
  1041   aFileDescriptor =
  1042     FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd));
  1043   return NS_OK;
  1046 nsresult
  1047 DeviceStorageFile::Write(nsIInputStream* aInputStream)
  1049   if (!aInputStream || !mFile) {
  1050     return NS_ERROR_FAILURE;
  1053   nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
  1054   if (NS_WARN_IF(NS_FAILED(rv))) {
  1055     return rv;
  1058   nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "created");
  1059   rv = NS_DispatchToMainThread(iocomplete);
  1060   if (NS_WARN_IF(NS_FAILED(rv))) {
  1061     return rv;
  1064   uint64_t bufSize = 0;
  1065   aInputStream->Available(&bufSize);
  1067   nsCOMPtr<nsIOutputStream> outputStream;
  1068   NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
  1070   if (!outputStream) {
  1071     return NS_ERROR_FAILURE;
  1074   nsCOMPtr<nsIOutputStream> bufferedOutputStream;
  1075   rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
  1076                                   outputStream,
  1077                                   4096*4);
  1078   NS_ENSURE_SUCCESS(rv, rv);
  1080   while (bufSize) {
  1081     uint32_t wrote;
  1082     rv = bufferedOutputStream->WriteFrom(
  1083       aInputStream,
  1084       static_cast<uint32_t>(std::min<uint64_t>(bufSize, UINT32_MAX)),
  1085       &wrote);
  1086     if (NS_FAILED(rv)) {
  1087       break;
  1089     bufSize -= wrote;
  1092   iocomplete = new IOEventComplete(this, "modified");
  1093   rv = NS_DispatchToMainThread(iocomplete);
  1094   if (NS_WARN_IF(NS_FAILED(rv))) {
  1095     return rv;
  1098   bufferedOutputStream->Close();
  1099   outputStream->Close();
  1100   if (NS_WARN_IF(NS_FAILED(rv))) {
  1101     return rv;
  1103   return NS_OK;
  1106 nsresult
  1107 DeviceStorageFile::Write(InfallibleTArray<uint8_t>& aBits)
  1109   if (!mFile) {
  1110     return NS_ERROR_FAILURE;
  1113   nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
  1114   if (NS_WARN_IF(NS_FAILED(rv))) {
  1115     return rv;
  1118   nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "created");
  1119   rv = NS_DispatchToMainThread(iocomplete);
  1120   if (NS_WARN_IF(NS_FAILED(rv))) {
  1121     return rv;
  1124   nsCOMPtr<nsIOutputStream> outputStream;
  1125   NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
  1127   if (!outputStream) {
  1128     return NS_ERROR_FAILURE;
  1131   uint32_t wrote;
  1132   outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote);
  1133   outputStream->Close();
  1135   iocomplete = new IOEventComplete(this, "modified");
  1136   rv = NS_DispatchToMainThread(iocomplete);
  1137   if (NS_WARN_IF(NS_FAILED(rv))) {
  1138     return rv;
  1141   if (aBits.Length() != wrote) {
  1142     return NS_ERROR_FAILURE;
  1144   return NS_OK;
  1147 nsresult
  1148 DeviceStorageFile::Remove()
  1150   MOZ_ASSERT(!NS_IsMainThread());
  1152   if (!mFile) {
  1153     return NS_ERROR_FAILURE;
  1156   bool check;
  1157   nsresult rv = mFile->Exists(&check);
  1158   if (NS_FAILED(rv)) {
  1159     return rv;
  1162   if (!check) {
  1163     return NS_OK;
  1166   rv = mFile->Remove(true);
  1167   if (NS_WARN_IF(NS_FAILED(rv))) {
  1168     return rv;
  1171   nsCOMPtr<nsIRunnable> iocomplete = new IOEventComplete(this, "deleted");
  1172   return NS_DispatchToMainThread(iocomplete);
  1175 nsresult
  1176 DeviceStorageFile::CalculateMimeType()
  1178   MOZ_ASSERT(NS_IsMainThread());
  1180   nsAutoCString mimeType;
  1181   nsCOMPtr<nsIMIMEService> mimeService =
  1182     do_GetService(NS_MIMESERVICE_CONTRACTID);
  1183   if (mimeService) {
  1184     nsresult rv = mimeService->GetTypeFromFile(mFile, mimeType);
  1185     if (NS_FAILED(rv)) {
  1186       mimeType.Truncate();
  1187       return rv;
  1191   mMimeType = NS_ConvertUTF8toUTF16(mimeType);
  1192   return NS_OK;
  1195 nsresult
  1196 DeviceStorageFile::CalculateSizeAndModifiedDate()
  1198   MOZ_ASSERT(!NS_IsMainThread());
  1200   int64_t fileSize;
  1201   nsresult rv = mFile->GetFileSize(&fileSize);
  1202   NS_ENSURE_SUCCESS(rv, rv);
  1204   mLength = fileSize;
  1206   PRTime modDate;
  1207   rv = mFile->GetLastModifiedTime(&modDate);
  1208   NS_ENSURE_SUCCESS(rv, rv);
  1210   mLastModifiedDate = modDate;
  1211   return NS_OK;
  1214 void
  1215 DeviceStorageFile::CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
  1216                                 PRTime aSince)
  1218   nsString fullRootPath;
  1219   mFile->GetPath(fullRootPath);
  1220   collectFilesInternal(aFiles, aSince, fullRootPath);
  1223 void
  1224 DeviceStorageFile::collectFilesInternal(
  1225   nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
  1226   PRTime aSince,
  1227   nsAString& aRootPath)
  1229   if (!mFile || !IsAvailable()) {
  1230     return;
  1233   nsCOMPtr<nsISimpleEnumerator> e;
  1234   mFile->GetDirectoryEntries(getter_AddRefs(e));
  1236   if (!e) {
  1237     return;
  1240   nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
  1241   nsCOMPtr<nsIFile> f;
  1243   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
  1245     PRTime msecs;
  1246     f->GetLastModifiedTime(&msecs);
  1248     if (msecs < aSince) {
  1249       continue;
  1252     bool isDir;
  1253     f->IsDirectory(&isDir);
  1255     bool isFile;
  1256     f->IsFile(&isFile);
  1258     nsString fullpath;
  1259     nsresult rv = f->GetPath(fullpath);
  1260     if (NS_FAILED(rv)) {
  1261       continue;
  1264     if (!StringBeginsWith(fullpath, aRootPath)) {
  1265       NS_ERROR("collectFiles returned a path that does not belong!");
  1266       continue;
  1269     nsAString::size_type len = aRootPath.Length() + 1; // +1 for the trailing /
  1270     nsDependentSubstring newPath = Substring(fullpath, len);
  1272     if (isDir) {
  1273       DeviceStorageFile dsf(mStorageType, mStorageName, mRootDir, newPath);
  1274       dsf.collectFilesInternal(aFiles, aSince, aRootPath);
  1275     } else if (isFile) {
  1276       nsRefPtr<DeviceStorageFile> dsf =
  1277         new DeviceStorageFile(mStorageType, mStorageName, mRootDir, newPath);
  1278       dsf->CalculateSizeAndModifiedDate();
  1279       aFiles.AppendElement(dsf);
  1284 void
  1285 DeviceStorageFile::AccumDiskUsage(uint64_t* aPicturesSoFar,
  1286                                   uint64_t* aVideosSoFar,
  1287                                   uint64_t* aMusicSoFar,
  1288                                   uint64_t* aTotalSoFar)
  1290   if (!IsAvailable()) {
  1291     return;
  1294   uint64_t pictureUsage = 0, videoUsage = 0, musicUsage = 0, totalUsage = 0;
  1296   if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) {
  1297     DeviceStorageUsedSpaceCache* usedSpaceCache =
  1298       DeviceStorageUsedSpaceCache::CreateOrGet();
  1299     MOZ_ASSERT(usedSpaceCache);
  1300     nsresult rv = usedSpaceCache->AccumUsedSizes(mStorageName,
  1301                                                  aPicturesSoFar, aVideosSoFar,
  1302                                                  aMusicSoFar, aTotalSoFar);
  1303     if (NS_SUCCEEDED(rv)) {
  1304       return;
  1306     AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage,
  1307                         &musicUsage, &totalUsage);
  1308     usedSpaceCache->SetUsedSizes(mStorageName, pictureUsage, videoUsage,
  1309                                  musicUsage, totalUsage);
  1310   } else {
  1311     AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage,
  1312                         &musicUsage, &totalUsage);
  1315   *aPicturesSoFar += pictureUsage;
  1316   *aVideosSoFar += videoUsage;
  1317   *aMusicSoFar += musicUsage;
  1318   *aTotalSoFar += totalUsage;
  1321 void
  1322 DeviceStorageFile::AccumDirectoryUsage(nsIFile* aFile,
  1323                                        uint64_t* aPicturesSoFar,
  1324                                        uint64_t* aVideosSoFar,
  1325                                        uint64_t* aMusicSoFar,
  1326                                        uint64_t* aTotalSoFar)
  1328   if (!aFile) {
  1329     return;
  1332   nsresult rv;
  1333   nsCOMPtr<nsISimpleEnumerator> e;
  1334   rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
  1336   if (NS_FAILED(rv) || !e) {
  1337     return;
  1340   nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
  1341   MOZ_ASSERT(files);
  1343   nsCOMPtr<nsIFile> f;
  1344   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
  1345     bool isDir;
  1346     rv = f->IsDirectory(&isDir);
  1347     if (NS_FAILED(rv)) {
  1348       continue;
  1351     bool isFile;
  1352     rv = f->IsFile(&isFile);
  1353     if (NS_FAILED(rv)) {
  1354       continue;
  1357     bool isLink;
  1358     rv = f->IsSymlink(&isLink);
  1359     if (NS_FAILED(rv)) {
  1360       continue;
  1363     if (isLink) {
  1364       // for now, lets just totally ignore symlinks.
  1365       NS_WARNING("DirectoryDiskUsage ignores symlinks");
  1366     } else if (isDir) {
  1367       AccumDirectoryUsage(f, aPicturesSoFar, aVideosSoFar,
  1368                           aMusicSoFar, aTotalSoFar);
  1369     } else if (isFile) {
  1371       int64_t size;
  1372       rv = f->GetFileSize(&size);
  1373       if (NS_FAILED(rv)) {
  1374         continue;
  1376       DeviceStorageTypeChecker* typeChecker
  1377         = DeviceStorageTypeChecker::CreateOrGet();
  1378       MOZ_ASSERT(typeChecker);
  1379       nsString type;
  1380       typeChecker->GetTypeFromFile(f, type);
  1382       if (type.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
  1383         *aPicturesSoFar += size;
  1385       else if (type.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
  1386         *aVideosSoFar += size;
  1388       else if (type.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
  1389         *aMusicSoFar += size;
  1391       *aTotalSoFar += size;
  1396 void
  1397 DeviceStorageFile::GetDiskFreeSpace(int64_t* aSoFar)
  1399   DeviceStorageTypeChecker* typeChecker
  1400     = DeviceStorageTypeChecker::CreateOrGet();
  1401   if (!typeChecker) {
  1402     return;
  1404   if (!mFile || !IsAvailable()) {
  1405     return;
  1408   int64_t storageAvail = 0;
  1409   nsresult rv = mFile->GetDiskSpaceAvailable(&storageAvail);
  1410   if (NS_SUCCEEDED(rv)) {
  1411     *aSoFar += storageAvail;
  1415 bool
  1416 DeviceStorageFile::IsAvailable()
  1418   nsString status;
  1419   GetStatus(status);
  1420   return status.EqualsLiteral("available");
  1423 void
  1424 DeviceStorageFile::DoFormat(nsAString& aStatus)
  1426   DeviceStorageTypeChecker* typeChecker
  1427     = DeviceStorageTypeChecker::CreateOrGet();
  1428   if (!typeChecker) {
  1429     return;
  1431   if (!typeChecker->IsVolumeBased(mStorageType)) {
  1432     aStatus.AssignLiteral("notVolume");
  1433     return;
  1435 #ifdef MOZ_WIDGET_GONK
  1436   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
  1437   NS_ENSURE_TRUE_VOID(vs);
  1439   nsCOMPtr<nsIVolume> vol;
  1440   nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
  1441   NS_ENSURE_SUCCESS_VOID(rv);
  1442   if (!vol) {
  1443     return;
  1446   vol->Format();
  1448   aStatus.AssignLiteral("formatting");
  1449 #endif
  1450   return;
  1453 void
  1454 DeviceStorageFile::DoMount(nsAString& aStatus)
  1456   DeviceStorageTypeChecker* typeChecker
  1457     = DeviceStorageTypeChecker::CreateOrGet();
  1458   if (!typeChecker) {
  1459     return;
  1461   if (!typeChecker->IsVolumeBased(mStorageType)) {
  1462     aStatus.AssignLiteral("notVolume");
  1463     return;
  1465 #ifdef MOZ_WIDGET_GONK
  1466   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
  1467   NS_ENSURE_TRUE_VOID(vs);
  1469   nsCOMPtr<nsIVolume> vol;
  1470   nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
  1471   NS_ENSURE_SUCCESS_VOID(rv);
  1472   if (!vol) {
  1473     return;
  1476   vol->Mount();
  1478   aStatus.AssignLiteral("mounting");
  1479 #endif
  1480   return;
  1483 void
  1484 DeviceStorageFile::DoUnmount(nsAString& aStatus)
  1486   DeviceStorageTypeChecker* typeChecker
  1487     = DeviceStorageTypeChecker::CreateOrGet();
  1488   if (!typeChecker) {
  1489     return;
  1491   if (!typeChecker->IsVolumeBased(mStorageType)) {
  1492     aStatus.AssignLiteral("notVolume");
  1493     return;
  1495 #ifdef MOZ_WIDGET_GONK
  1496   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
  1497   NS_ENSURE_TRUE_VOID(vs);
  1499   nsCOMPtr<nsIVolume> vol;
  1500   nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
  1501   NS_ENSURE_SUCCESS_VOID(rv);
  1502   if (!vol) {
  1503     return;
  1506   vol->Unmount();
  1508   aStatus.AssignLiteral("unmounting");
  1509 #endif
  1510   return;
  1513 void
  1514 DeviceStorageFile::GetStatus(nsAString& aStatus)
  1516   aStatus.AssignLiteral("unavailable");
  1518   DeviceStorageTypeChecker* typeChecker
  1519     = DeviceStorageTypeChecker::CreateOrGet();
  1520   if (!typeChecker) {
  1521     return;
  1523   if (!typeChecker->IsVolumeBased(mStorageType)) {
  1524     aStatus.AssignLiteral("available");
  1525     return;
  1528 #ifdef MOZ_WIDGET_GONK
  1529   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
  1530   NS_ENSURE_TRUE_VOID(vs);
  1532   nsCOMPtr<nsIVolume> vol;
  1533   nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
  1534   NS_ENSURE_SUCCESS_VOID(rv);
  1535   if (!vol) {
  1536     return;
  1538   bool isMediaPresent;
  1539   rv = vol->GetIsMediaPresent(&isMediaPresent);
  1540   NS_ENSURE_SUCCESS_VOID(rv);
  1541   if (!isMediaPresent) {
  1542     return;
  1544   bool isSharing;
  1545   rv = vol->GetIsSharing(&isSharing);
  1546   NS_ENSURE_SUCCESS_VOID(rv);
  1547   if (isSharing) {
  1548     aStatus.AssignLiteral("shared");
  1549     return;
  1551   bool isFormatting;
  1552   rv = vol->GetIsFormatting(&isFormatting);
  1553   NS_ENSURE_SUCCESS_VOID(rv);
  1554   if (isFormatting) {
  1555     aStatus.AssignLiteral("unavailable");
  1556     return;
  1558   int32_t volState;
  1559   rv = vol->GetState(&volState);
  1560   NS_ENSURE_SUCCESS_VOID(rv);
  1561   if (volState == nsIVolume::STATE_MOUNTED) {
  1562     aStatus.AssignLiteral("available");
  1564 #endif
  1567 void
  1568 DeviceStorageFile::GetStorageStatus(nsAString& aStatus)
  1570   aStatus.AssignLiteral("undefined");
  1572   DeviceStorageTypeChecker* typeChecker
  1573     = DeviceStorageTypeChecker::CreateOrGet();
  1574   if (!typeChecker) {
  1575     return;
  1577   if (!typeChecker->IsVolumeBased(mStorageType)) {
  1578     aStatus.AssignLiteral("available");
  1579     return;
  1582 #ifdef MOZ_WIDGET_GONK
  1583   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
  1584   NS_ENSURE_TRUE_VOID(vs);
  1586   nsCOMPtr<nsIVolume> vol;
  1587   nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
  1588   NS_ENSURE_SUCCESS_VOID(rv);
  1589   if (!vol) {
  1590     return;
  1593   int32_t volState;
  1594   rv = vol->GetState(&volState);
  1595   NS_ENSURE_SUCCESS_VOID(rv);
  1596   aStatus.AssignASCII(mozilla::system::NS_VolumeStateStr(volState));
  1597 #endif
  1600 NS_IMPL_ISUPPORTS0(DeviceStorageFile)
  1602 static void
  1603 RegisterForSDCardChanges(nsIObserver* aObserver)
  1605 #ifdef MOZ_WIDGET_GONK
  1606   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  1607   obs->AddObserver(aObserver, NS_VOLUME_STATE_CHANGED, false);
  1608 #endif
  1611 static void
  1612 UnregisterForSDCardChanges(nsIObserver* aObserver)
  1614 #ifdef MOZ_WIDGET_GONK
  1615   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  1616   obs->RemoveObserver(aObserver, NS_VOLUME_STATE_CHANGED);
  1617 #endif
  1620 void
  1621 nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aStorageType,
  1622                                             const nsAString& aStorageName)
  1624   MOZ_ASSERT(NS_IsMainThread());
  1626   nsCOMPtr<nsIFile> f;
  1627   DeviceStorageFile::GetRootDirectoryForType(aStorageType,
  1628                                              aStorageName,
  1629                                              getter_AddRefs(f));
  1630   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  1631   obs->AddObserver(this, "file-watcher-update", false);
  1632   obs->AddObserver(this, "disk-space-watcher", false);
  1633   mRootDirectory = f;
  1634   mStorageType = aStorageType;
  1635   mStorageName = aStorageName;
  1638 JS::Value
  1639 InterfaceToJsval(nsPIDOMWindow* aWindow,
  1640                  nsISupports* aObject,
  1641                  const nsIID* aIID)
  1643   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
  1644   if (!sgo) {
  1645     return JS::NullValue();
  1648   JSObject *unrootedScopeObj = sgo->GetGlobalJSObject();
  1649   NS_ENSURE_TRUE(unrootedScopeObj, JS::NullValue());
  1650   JSRuntime *runtime = JS_GetObjectRuntime(unrootedScopeObj);
  1651   JS::Rooted<JS::Value> someJsVal(runtime);
  1652   nsresult rv;
  1654   { // Protect someJsVal from moving GC in ~JSAutoCompartment
  1655     AutoJSContext cx;
  1657     JS::Rooted<JSObject*> scopeObj(cx, unrootedScopeObj);
  1658     JSAutoCompartment ac(cx, scopeObj);
  1660     rv = nsContentUtils::WrapNative(cx, aObject, aIID, &someJsVal);
  1662   if (NS_FAILED(rv)) {
  1663     return JS::NullValue();
  1666   return someJsVal;
  1669 JS::Value
  1670 nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
  1672   MOZ_ASSERT(NS_IsMainThread());
  1673   MOZ_ASSERT(aWindow);
  1675   if (!aFile) {
  1676     return JSVAL_NULL;
  1679   if (aFile->mEditable) {
  1680     // TODO - needs janv's file handle support.
  1681     return JSVAL_NULL;
  1684   nsString fullPath;
  1685   aFile->GetFullPath(fullPath);
  1687   // This check is useful to know if somewhere the DeviceStorageFile
  1688   // has not been properly set. Mimetype is not checked because it can be
  1689   // empty.
  1690   MOZ_ASSERT(aFile->mLength != UINT64_MAX);
  1691   MOZ_ASSERT(aFile->mLastModifiedDate != UINT64_MAX);
  1693   nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(fullPath, aFile->mMimeType,
  1694                                                 aFile->mLength, aFile->mFile,
  1695                                                 aFile->mLastModifiedDate);
  1696   return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
  1699 JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
  1701   MOZ_ASSERT(NS_IsMainThread());
  1702   MOZ_ASSERT(aWindow);
  1704   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
  1705   if (!sgo) {
  1706     return JSVAL_NULL;
  1709   nsIScriptContext *scriptContext = sgo->GetScriptContext();
  1710   if (!scriptContext) {
  1711     return JSVAL_NULL;
  1714   AutoPushJSContext cx(scriptContext->GetNativeContext());
  1715   if (!cx) {
  1716     return JSVAL_NULL;
  1719   JS::Rooted<JS::Value> result(cx);
  1720   if (!xpc::StringToJsval(cx, aString, &result)) {
  1721     return JSVAL_NULL;
  1724   return result;
  1727 class DeviceStorageCursorRequest MOZ_FINAL
  1728   : public nsIContentPermissionRequest
  1729   , public PCOMContentPermissionRequestChild
  1731 public:
  1732   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  1733   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageCursorRequest,
  1734                                            nsIContentPermissionRequest)
  1736   NS_FORWARD_NSICONTENTPERMISSIONREQUEST(mCursor->);
  1738   DeviceStorageCursorRequest(nsDOMDeviceStorageCursor* aCursor)
  1739     : mCursor(aCursor) { }
  1741   ~DeviceStorageCursorRequest() {}
  1743   bool Recv__delete__(const bool& allow,
  1744                       const InfallibleTArray<PermissionChoice>& choices)
  1746     MOZ_ASSERT(choices.IsEmpty(), "DeviceStorageCursor doesn't support permission choice");
  1747     if (allow) {
  1748       Allow(JS::UndefinedHandleValue);
  1750     else {
  1751       Cancel();
  1753     return true;
  1756   void IPDLRelease()
  1758     Release();
  1761 private:
  1762   nsRefPtr<nsDOMDeviceStorageCursor> mCursor;
  1763 };
  1765 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageCursorRequest)
  1766   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
  1767   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
  1768 NS_INTERFACE_MAP_END
  1770 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageCursorRequest)
  1771 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageCursorRequest)
  1773 NS_IMPL_CYCLE_COLLECTION(DeviceStorageCursorRequest,
  1774                          mCursor)
  1777 class PostErrorEvent : public nsRunnable
  1779 public:
  1780   PostErrorEvent(already_AddRefed<DOMRequest> aRequest, const char* aMessage)
  1781     : mRequest(aRequest)
  1783     CopyASCIItoUTF16(aMessage, mError);
  1786   PostErrorEvent(DOMRequest* aRequest, const char* aMessage)
  1787     : mRequest(aRequest)
  1789     CopyASCIItoUTF16(aMessage, mError);
  1792   ~PostErrorEvent() {}
  1794   NS_IMETHOD Run()
  1796     MOZ_ASSERT(NS_IsMainThread());
  1797     if (!mRequest->GetOwner()) {
  1798       return NS_OK;
  1800     mRequest->FireError(mError);
  1801     mRequest = nullptr;
  1802     return NS_OK;
  1805 private:
  1806   nsRefPtr<DOMRequest> mRequest;
  1807   nsString mError;
  1808 };
  1810 ContinueCursorEvent::ContinueCursorEvent(already_AddRefed<DOMRequest> aRequest)
  1811   : mRequest(aRequest)
  1815 ContinueCursorEvent::ContinueCursorEvent(DOMRequest* aRequest)
  1816   : mRequest(aRequest)
  1820 already_AddRefed<DeviceStorageFile>
  1821 ContinueCursorEvent::GetNextFile()
  1823   MOZ_ASSERT(NS_IsMainThread());
  1825   nsDOMDeviceStorageCursor* cursor
  1826     = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
  1827   nsString cursorStorageType;
  1828   cursor->GetStorageType(cursorStorageType);
  1830   DeviceStorageTypeChecker* typeChecker
  1831     = DeviceStorageTypeChecker::CreateOrGet();
  1832   if (!typeChecker) {
  1833     return nullptr;
  1836   while (cursor->mFiles.Length() > 0) {
  1837     nsRefPtr<DeviceStorageFile> file = cursor->mFiles[0];
  1838     cursor->mFiles.RemoveElementAt(0);
  1839     if (!typeChecker->Check(cursorStorageType, file->mFile)) {
  1840       continue;
  1843     file->CalculateMimeType();
  1844     return file.forget();
  1847   return nullptr;
  1850 ContinueCursorEvent::~ContinueCursorEvent() {}
  1852 void
  1853 ContinueCursorEvent::Continue()
  1855   if (XRE_GetProcessType() == GeckoProcessType_Default) {
  1856     DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
  1857     MOZ_ASSERT(NS_SUCCEEDED(rv));
  1858     return;
  1861   nsRefPtr<DeviceStorageFile> file = GetNextFile();
  1863   if (!file) {
  1864     // done with enumeration.
  1865     DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
  1866     MOZ_ASSERT(NS_SUCCEEDED(rv));
  1867     return;
  1870   nsDOMDeviceStorageCursor* cursor
  1871     = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
  1872   nsString cursorStorageType;
  1873   cursor->GetStorageType(cursorStorageType);
  1875   DeviceStorageRequestChild* child
  1876     = new DeviceStorageRequestChild(mRequest, file);
  1877   child->SetCallback(cursor);
  1878   DeviceStorageGetParams params(cursorStorageType,
  1879                                 file->mStorageName,
  1880                                 file->mRootDir,
  1881                                 file->mPath);
  1882   ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child,
  1883                                                                      params);
  1884   mRequest = nullptr;
  1887 NS_IMETHODIMP
  1888 ContinueCursorEvent::Run()
  1890   nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
  1891   if (!window) {
  1892     return NS_OK;
  1895   nsRefPtr<DeviceStorageFile> file = GetNextFile();
  1897   nsDOMDeviceStorageCursor* cursor
  1898     = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
  1900   AutoJSContext cx;
  1901   JS::Rooted<JS::Value> val(cx, nsIFileToJsval(window, file));
  1903   if (file) {
  1904     cursor->mOkToCallContinue = true;
  1905     cursor->FireSuccess(val);
  1906   } else {
  1907     cursor->FireDone();
  1909   mRequest = nullptr;
  1910   return NS_OK;
  1913 class InitCursorEvent : public nsRunnable
  1915 public:
  1916     InitCursorEvent(DOMRequest* aRequest, DeviceStorageFile* aFile)
  1917     : mFile(aFile)
  1918     , mRequest(aRequest)
  1922   ~InitCursorEvent() {}
  1924   NS_IMETHOD Run() {
  1925     MOZ_ASSERT(!NS_IsMainThread());
  1927     if (mFile->mFile) {
  1928       bool check;
  1929       mFile->mFile->IsDirectory(&check);
  1930       if (!check) {
  1931         nsCOMPtr<nsIRunnable> event =
  1932           new PostErrorEvent(mRequest.forget(),
  1933                              POST_ERROR_EVENT_FILE_NOT_ENUMERABLE);
  1934         return NS_DispatchToMainThread(event);
  1938     nsDOMDeviceStorageCursor* cursor
  1939       = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
  1940     mFile->CollectFiles(cursor->mFiles, cursor->mSince);
  1942     nsRefPtr<ContinueCursorEvent> event
  1943       = new ContinueCursorEvent(mRequest.forget());
  1944     event->Continue();
  1946     return NS_OK;
  1950 private:
  1951   nsRefPtr<DeviceStorageFile> mFile;
  1952   nsRefPtr<DOMRequest> mRequest;
  1953 };
  1955 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorageCursor)
  1956   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
  1957 NS_INTERFACE_MAP_END_INHERITING(DOMCursor)
  1959 NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorageCursor, DOMCursor)
  1960 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorageCursor, DOMCursor)
  1962 nsDOMDeviceStorageCursor::nsDOMDeviceStorageCursor(nsPIDOMWindow* aWindow,
  1963                                                    nsIPrincipal* aPrincipal,
  1964                                                    DeviceStorageFile* aFile,
  1965                                                    PRTime aSince)
  1966   : DOMCursor(aWindow, nullptr)
  1967   , mOkToCallContinue(false)
  1968   , mSince(aSince)
  1969   , mFile(aFile)
  1970   , mPrincipal(aPrincipal)
  1974 nsDOMDeviceStorageCursor::~nsDOMDeviceStorageCursor()
  1978 void
  1979 nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType)
  1981   aType = mFile->mStorageType;
  1984 NS_IMETHODIMP
  1985 nsDOMDeviceStorageCursor::GetTypes(nsIArray** aTypes)
  1987   nsCString type;
  1988   nsresult rv =
  1989     DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
  1990   NS_ENSURE_SUCCESS(rv, rv);
  1992   nsTArray<nsString> emptyOptions;
  1993   return CreatePermissionArray(type,
  1994                                NS_LITERAL_CSTRING("read"),
  1995                                emptyOptions,
  1996                                aTypes);
  1999 NS_IMETHODIMP
  2000 nsDOMDeviceStorageCursor::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
  2002   NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
  2003   return NS_OK;
  2006 NS_IMETHODIMP
  2007 nsDOMDeviceStorageCursor::GetWindow(nsIDOMWindow * *aRequestingWindow)
  2009   NS_IF_ADDREF(*aRequestingWindow = GetOwner());
  2010   return NS_OK;
  2013 NS_IMETHODIMP
  2014 nsDOMDeviceStorageCursor::GetElement(nsIDOMElement * *aRequestingElement)
  2016   *aRequestingElement = nullptr;
  2017   return NS_OK;
  2020 NS_IMETHODIMP
  2021 nsDOMDeviceStorageCursor::Cancel()
  2023   nsCOMPtr<nsIRunnable> event
  2024     = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
  2025   return NS_DispatchToMainThread(event);
  2028 NS_IMETHODIMP
  2029 nsDOMDeviceStorageCursor::Allow(JS::HandleValue aChoices)
  2031   MOZ_ASSERT(aChoices.isUndefined());
  2033   if (!mFile->IsSafePath()) {
  2034     nsCOMPtr<nsIRunnable> r
  2035       = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
  2036     return NS_DispatchToMainThread(r);
  2039   if (XRE_GetProcessType() != GeckoProcessType_Default) {
  2040     PDeviceStorageRequestChild* child
  2041       = new DeviceStorageRequestChild(this, mFile);
  2042     DeviceStorageEnumerationParams params(mFile->mStorageType,
  2043                                           mFile->mStorageName,
  2044                                           mFile->mRootDir,
  2045                                           mSince);
  2046     ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child,
  2047                                                                        params);
  2048     return NS_OK;
  2051   nsCOMPtr<nsIEventTarget> target
  2052     = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  2053   MOZ_ASSERT(target);
  2055   nsCOMPtr<nsIRunnable> event = new InitCursorEvent(this, mFile);
  2056   target->Dispatch(event, NS_DISPATCH_NORMAL);
  2057   return NS_OK;
  2060 void
  2061 nsDOMDeviceStorageCursor::Continue(ErrorResult& aRv)
  2063   if (!mOkToCallContinue) {
  2064     aRv.Throw(NS_ERROR_UNEXPECTED);
  2065     return;
  2068   if (mResult != JSVAL_VOID) {
  2069     // We call onsuccess multiple times. Clear the last
  2070     // result.
  2071     mResult = JSVAL_VOID;
  2072     mDone = false;
  2075   nsRefPtr<ContinueCursorEvent> event = new ContinueCursorEvent(this);
  2076   event->Continue();
  2078   mOkToCallContinue = false;
  2081 bool
  2082 nsDOMDeviceStorageCursor::Recv__delete__(const bool& allow,
  2083                                          const InfallibleTArray<PermissionChoice>& choices)
  2085   MOZ_ASSERT(choices.IsEmpty(), "DeviceStorageCursor doesn't support permission choice");
  2087   if (allow) {
  2088     Allow(JS::UndefinedHandleValue);
  2090   else {
  2091     Cancel();
  2093   return true;
  2096 void
  2097 nsDOMDeviceStorageCursor::IPDLRelease()
  2099   Release();
  2102 void
  2103 nsDOMDeviceStorageCursor::RequestComplete()
  2105   MOZ_ASSERT(!mOkToCallContinue);
  2106   mOkToCallContinue = true;
  2109 class PostAvailableResultEvent : public nsRunnable
  2111 public:
  2112   PostAvailableResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
  2113     : mFile(aFile)
  2114     , mRequest(aRequest)
  2116     MOZ_ASSERT(mRequest);
  2119   ~PostAvailableResultEvent() {}
  2121   NS_IMETHOD Run()
  2123     MOZ_ASSERT(NS_IsMainThread());
  2124     nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
  2125     if (!window) {
  2126       return NS_OK;
  2129     nsString state = NS_LITERAL_STRING("unavailable");
  2130     if (mFile) {
  2131       mFile->GetStatus(state);
  2134     AutoJSContext cx;
  2135     JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
  2136     mRequest->FireSuccess(result);
  2137     mRequest = nullptr;
  2138     return NS_OK;
  2141 private:
  2142   nsRefPtr<DeviceStorageFile> mFile;
  2143   nsRefPtr<DOMRequest> mRequest;
  2144 };
  2146 class PostStatusResultEvent : public nsRunnable
  2148 public:
  2149   PostStatusResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
  2150     : mFile(aFile)
  2151     , mRequest(aRequest)
  2153     MOZ_ASSERT(mRequest);
  2156   ~PostStatusResultEvent() {}
  2158   NS_IMETHOD Run()
  2160     MOZ_ASSERT(NS_IsMainThread());
  2161     nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
  2162     if (!window) {
  2163       return NS_OK;
  2166     nsString state = NS_LITERAL_STRING("undefined");
  2167     if (mFile) {
  2168       mFile->GetStorageStatus(state);
  2171     AutoJSContext cx;
  2172     JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
  2173     mRequest->FireSuccess(result);
  2174     mRequest = nullptr;
  2175     return NS_OK;
  2178 private:
  2179   nsRefPtr<DeviceStorageFile> mFile;
  2180   nsRefPtr<DOMRequest> mRequest;
  2181 };
  2183 class PostFormatResultEvent : public nsRunnable
  2185 public:
  2186   PostFormatResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
  2187     : mFile(aFile)
  2188     , mRequest(aRequest)
  2190     MOZ_ASSERT(mRequest);
  2193   ~PostFormatResultEvent() {}
  2195   NS_IMETHOD Run()
  2197     MOZ_ASSERT(NS_IsMainThread());
  2198     nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
  2199     if (!window) {
  2200       return NS_OK;
  2203     nsString state = NS_LITERAL_STRING("unavailable");
  2204     if (mFile) {
  2205       mFile->DoFormat(state);
  2208     AutoJSContext cx;
  2209     JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
  2210     mRequest->FireSuccess(result);
  2211     mRequest = nullptr;
  2212     return NS_OK;
  2215 private:
  2216   nsRefPtr<DeviceStorageFile> mFile;
  2217   nsRefPtr<DOMRequest> mRequest;
  2218 };
  2220 class PostMountResultEvent : public nsRunnable
  2222 public:
  2223   PostMountResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
  2224     : mFile(aFile)
  2225     , mRequest(aRequest)
  2227     MOZ_ASSERT(mRequest);
  2230   ~PostMountResultEvent() {}
  2232   NS_IMETHOD Run()
  2234     MOZ_ASSERT(NS_IsMainThread());
  2235     nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
  2236     if (!window) {
  2237       return NS_OK;
  2240     nsString state = NS_LITERAL_STRING("unavailable");
  2241     if (mFile) {
  2242       mFile->DoMount(state);
  2245     AutoJSContext cx;
  2246     JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
  2247     mRequest->FireSuccess(result);
  2248     mRequest = nullptr;
  2249     return NS_OK;
  2252 private:
  2253   nsRefPtr<DeviceStorageFile> mFile;
  2254   nsRefPtr<DOMRequest> mRequest;
  2255 };
  2257 class PostUnmountResultEvent : public nsRunnable
  2259 public:
  2260   PostUnmountResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
  2261     : mFile(aFile)
  2262     , mRequest(aRequest)
  2264     MOZ_ASSERT(mRequest);
  2267   ~PostUnmountResultEvent() {}
  2269   NS_IMETHOD Run()
  2271     MOZ_ASSERT(NS_IsMainThread());
  2272     nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
  2273     if (!window) {
  2274       return NS_OK;
  2277     nsString state = NS_LITERAL_STRING("unavailable");
  2278     if (mFile) {
  2279       mFile->DoUnmount(state);
  2282     AutoJSContext cx;
  2283     JS::Rooted<JS::Value> result(cx, StringToJsval(window, state));
  2284     mRequest->FireSuccess(result);
  2285     mRequest = nullptr;
  2286     return NS_OK;
  2289 private:
  2290   nsRefPtr<DeviceStorageFile> mFile;
  2291   nsRefPtr<DOMRequest> mRequest;
  2292 };
  2294 class PostResultEvent : public nsRunnable
  2296 public:
  2297   PostResultEvent(already_AddRefed<DOMRequest> aRequest,
  2298                   DeviceStorageFile* aFile)
  2299     : mFile(aFile)
  2300     , mRequest(aRequest)
  2302     MOZ_ASSERT(mRequest);
  2305   PostResultEvent(already_AddRefed<DOMRequest> aRequest,
  2306                   const nsAString & aPath)
  2307     : mPath(aPath)
  2308     , mRequest(aRequest)
  2310     MOZ_ASSERT(mRequest);
  2313   PostResultEvent(already_AddRefed<DOMRequest> aRequest,
  2314                   const uint64_t aValue)
  2315     : mValue(aValue)
  2316     , mRequest(aRequest)
  2318     MOZ_ASSERT(mRequest);
  2321   ~PostResultEvent() {}
  2323   NS_IMETHOD Run()
  2325     MOZ_ASSERT(NS_IsMainThread());
  2326     nsCOMPtr<nsPIDOMWindow> window = mRequest->GetOwner();
  2327     if (!window) {
  2328       return NS_OK;
  2331     AutoJSContext cx;
  2332     JS::Rooted<JS::Value> result(cx, JSVAL_NULL);
  2334     if (mFile) {
  2335       result = nsIFileToJsval(window, mFile);
  2336     } else if (mPath.Length()) {
  2337       result = StringToJsval(window, mPath);
  2339     else {
  2340       result = JS_NumberValue(double(mValue));
  2343     mRequest->FireSuccess(result);
  2344     mRequest = nullptr;
  2345     return NS_OK;
  2348 private:
  2349   nsRefPtr<DeviceStorageFile> mFile;
  2350   nsString mPath;
  2351   uint64_t mValue;
  2352   nsRefPtr<DOMRequest> mRequest;
  2353 };
  2355 class CreateFdEvent : public nsRunnable
  2357 public:
  2358   CreateFdEvent(DeviceStorageFileDescriptor* aDSFileDescriptor,
  2359                 already_AddRefed<DOMRequest> aRequest)
  2360     : mDSFileDescriptor(aDSFileDescriptor)
  2361     , mRequest(aRequest)
  2363     MOZ_ASSERT(mDSFileDescriptor);
  2364     MOZ_ASSERT(mRequest);
  2367   NS_IMETHOD Run()
  2369     MOZ_ASSERT(!NS_IsMainThread());
  2371     DeviceStorageFile* dsFile = mDSFileDescriptor->mDSFile;
  2372     MOZ_ASSERT(dsFile);
  2374     nsString fullPath;
  2375     dsFile->GetFullPath(fullPath);
  2376     MOZ_ASSERT(!fullPath.IsEmpty());
  2378     bool check = false;
  2379     dsFile->mFile->Exists(&check);
  2380     if (check) {
  2381       nsCOMPtr<nsIRunnable> event =
  2382         new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
  2383       return NS_DispatchToMainThread(event);
  2386     nsresult rv = dsFile->CreateFileDescriptor(mDSFileDescriptor->mFileDescriptor);
  2388     if (NS_FAILED(rv)) {
  2389       dsFile->mFile->Remove(false);
  2391       nsCOMPtr<nsIRunnable> event =
  2392         new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
  2393       return NS_DispatchToMainThread(event);
  2396     nsCOMPtr<nsIRunnable> event =
  2397       new PostResultEvent(mRequest.forget(), fullPath);
  2398     return NS_DispatchToMainThread(event);
  2401 private:
  2402   nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
  2403   nsRefPtr<DOMRequest> mRequest;
  2404 };
  2406 class WriteFileEvent : public nsRunnable
  2408 public:
  2409   WriteFileEvent(nsIDOMBlob* aBlob,
  2410                  DeviceStorageFile *aFile,
  2411                  already_AddRefed<DOMRequest> aRequest)
  2412     : mBlob(aBlob)
  2413     , mFile(aFile)
  2414     , mRequest(aRequest)
  2416     MOZ_ASSERT(mFile);
  2417     MOZ_ASSERT(mRequest);
  2420   ~WriteFileEvent() {}
  2422   NS_IMETHOD Run()
  2424     MOZ_ASSERT(!NS_IsMainThread());
  2426     nsCOMPtr<nsIInputStream> stream;
  2427     mBlob->GetInternalStream(getter_AddRefs(stream));
  2429     bool check = false;
  2430     mFile->mFile->Exists(&check);
  2431     if (check) {
  2432       nsCOMPtr<nsIRunnable> event =
  2433         new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
  2434       return NS_DispatchToMainThread(event);
  2437     nsresult rv = mFile->Write(stream);
  2439     if (NS_FAILED(rv)) {
  2440       mFile->mFile->Remove(false);
  2442       nsCOMPtr<nsIRunnable> event =
  2443         new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
  2444       return NS_DispatchToMainThread(event);
  2447     nsString fullPath;
  2448     mFile->GetFullPath(fullPath);
  2449     nsCOMPtr<nsIRunnable> event =
  2450       new PostResultEvent(mRequest.forget(), fullPath);
  2451     return NS_DispatchToMainThread(event);
  2454 private:
  2455   nsCOMPtr<nsIDOMBlob> mBlob;
  2456   nsRefPtr<DeviceStorageFile> mFile;
  2457   nsRefPtr<DOMRequest> mRequest;
  2458 };
  2460 class ReadFileEvent : public nsRunnable
  2462 public:
  2463   ReadFileEvent(DeviceStorageFile* aFile,
  2464                 already_AddRefed<DOMRequest> aRequest)
  2465     : mFile(aFile)
  2466     , mRequest(aRequest)
  2468     MOZ_ASSERT(mFile);
  2469     MOZ_ASSERT(mRequest);
  2470     mFile->CalculateMimeType();
  2473   ~ReadFileEvent() {}
  2475   NS_IMETHOD Run()
  2477     MOZ_ASSERT(!NS_IsMainThread());
  2479     nsCOMPtr<nsIRunnable> r;
  2480     if (!mFile->mEditable) {
  2481       bool check = false;
  2482       mFile->mFile->Exists(&check);
  2483       if (!check) {
  2484         r = new PostErrorEvent(mRequest.forget(),
  2485                                POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
  2489     if (!r) {
  2490       nsresult rv = mFile->CalculateSizeAndModifiedDate();
  2491       if (NS_FAILED(rv)) {
  2492         r = new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
  2496     if (!r) {
  2497       r = new PostResultEvent(mRequest.forget(), mFile);
  2499     return NS_DispatchToMainThread(r);
  2502 private:
  2503   nsRefPtr<DeviceStorageFile> mFile;
  2504   nsRefPtr<DOMRequest> mRequest;
  2505 };
  2507 class DeleteFileEvent : public nsRunnable
  2509 public:
  2510   DeleteFileEvent(DeviceStorageFile* aFile,
  2511                   already_AddRefed<DOMRequest> aRequest)
  2512     : mFile(aFile)
  2513     , mRequest(aRequest)
  2515     MOZ_ASSERT(mFile);
  2516     MOZ_ASSERT(mRequest);
  2519   ~DeleteFileEvent() {}
  2521   NS_IMETHOD Run()
  2523     MOZ_ASSERT(!NS_IsMainThread());
  2524     mFile->Remove();
  2526     nsCOMPtr<nsIRunnable> r;
  2527     bool check = false;
  2528     mFile->mFile->Exists(&check);
  2529     if (check) {
  2530       r = new PostErrorEvent(mRequest.forget(),
  2531                              POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
  2533     else {
  2534       nsString fullPath;
  2535       mFile->GetFullPath(fullPath);
  2536       r = new PostResultEvent(mRequest.forget(), fullPath);
  2538     return NS_DispatchToMainThread(r);
  2541 private:
  2542   nsRefPtr<DeviceStorageFile> mFile;
  2543   nsRefPtr<DOMRequest> mRequest;
  2544 };
  2546 class UsedSpaceFileEvent : public nsRunnable
  2548 public:
  2549   UsedSpaceFileEvent(DeviceStorageFile* aFile,
  2550                      already_AddRefed<DOMRequest> aRequest)
  2551     : mFile(aFile)
  2552     , mRequest(aRequest)
  2554     MOZ_ASSERT(mFile);
  2555     MOZ_ASSERT(mRequest);
  2558   ~UsedSpaceFileEvent() {}
  2560   NS_IMETHOD Run()
  2562     MOZ_ASSERT(!NS_IsMainThread());
  2564     uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0;
  2565     mFile->AccumDiskUsage(&picturesUsage, &videosUsage,
  2566                           &musicUsage, &totalUsage);
  2567     nsCOMPtr<nsIRunnable> r;
  2568     if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
  2569       r = new PostResultEvent(mRequest.forget(), picturesUsage);
  2571     else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
  2572       r = new PostResultEvent(mRequest.forget(), videosUsage);
  2574     else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
  2575       r = new PostResultEvent(mRequest.forget(), musicUsage);
  2576     } else {
  2577       r = new PostResultEvent(mRequest.forget(), totalUsage);
  2579     return NS_DispatchToMainThread(r);
  2582 private:
  2583   nsRefPtr<DeviceStorageFile> mFile;
  2584   nsRefPtr<DOMRequest> mRequest;
  2585 };
  2587 class FreeSpaceFileEvent : public nsRunnable
  2589 public:
  2590   FreeSpaceFileEvent(DeviceStorageFile* aFile,
  2591                      already_AddRefed<DOMRequest> aRequest)
  2592     : mFile(aFile)
  2593     , mRequest(aRequest)
  2595     MOZ_ASSERT(mFile);
  2596     MOZ_ASSERT(mRequest);
  2599   ~FreeSpaceFileEvent() {}
  2601   NS_IMETHOD Run()
  2603     MOZ_ASSERT(!NS_IsMainThread());
  2605     int64_t freeSpace = 0;
  2606     if (mFile) {
  2607       mFile->GetDiskFreeSpace(&freeSpace);
  2610     nsCOMPtr<nsIRunnable> r;
  2611     r = new PostResultEvent(mRequest.forget(),
  2612                             static_cast<uint64_t>(freeSpace));
  2613     return NS_DispatchToMainThread(r);
  2616 private:
  2617   nsRefPtr<DeviceStorageFile> mFile;
  2618   nsRefPtr<DOMRequest> mRequest;
  2619 };
  2621 class DeviceStorageRequest MOZ_FINAL
  2622   : public nsIContentPermissionRequest
  2623   , public nsIRunnable
  2624   , public PCOMContentPermissionRequestChild
  2626 public:
  2628   DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
  2629                        nsPIDOMWindow* aWindow,
  2630                        nsIPrincipal* aPrincipal,
  2631                        DeviceStorageFile* aFile,
  2632                        DOMRequest* aRequest,
  2633                        nsDOMDeviceStorage* aDeviceStorage)
  2634     : mRequestType(aRequestType)
  2635     , mWindow(aWindow)
  2636     , mPrincipal(aPrincipal)
  2637     , mFile(aFile)
  2638     , mRequest(aRequest)
  2639     , mDeviceStorage(aDeviceStorage)
  2641     MOZ_ASSERT(mWindow);
  2642     MOZ_ASSERT(mPrincipal);
  2643     MOZ_ASSERT(mFile);
  2644     MOZ_ASSERT(mRequest);
  2645     MOZ_ASSERT(mDeviceStorage);
  2648   DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
  2649                        nsPIDOMWindow* aWindow,
  2650                        nsIPrincipal* aPrincipal,
  2651                        DeviceStorageFile* aFile,
  2652                        DOMRequest* aRequest,
  2653                        nsIDOMBlob* aBlob = nullptr)
  2654     : mRequestType(aRequestType)
  2655     , mWindow(aWindow)
  2656     , mPrincipal(aPrincipal)
  2657     , mFile(aFile)
  2658     , mRequest(aRequest)
  2659     , mBlob(aBlob)
  2661     MOZ_ASSERT(mWindow);
  2662     MOZ_ASSERT(mPrincipal);
  2663     MOZ_ASSERT(mFile);
  2664     MOZ_ASSERT(mRequest);
  2667   DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
  2668                        nsPIDOMWindow* aWindow,
  2669                        nsIPrincipal* aPrincipal,
  2670                        DeviceStorageFile* aFile,
  2671                        DOMRequest* aRequest,
  2672                        DeviceStorageFileDescriptor* aDSFileDescriptor)
  2673     : mRequestType(aRequestType)
  2674     , mWindow(aWindow)
  2675     , mPrincipal(aPrincipal)
  2676     , mFile(aFile)
  2677     , mRequest(aRequest)
  2678     , mDSFileDescriptor(aDSFileDescriptor)
  2680     MOZ_ASSERT(mRequestType == DEVICE_STORAGE_REQUEST_CREATEFD);
  2681     MOZ_ASSERT(mWindow);
  2682     MOZ_ASSERT(mPrincipal);
  2683     MOZ_ASSERT(mFile);
  2684     MOZ_ASSERT(mRequest);
  2685     MOZ_ASSERT(mDSFileDescriptor);
  2688   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  2689   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageRequest,
  2690                                            nsIContentPermissionRequest)
  2692   NS_IMETHOD Run() {
  2693     MOZ_ASSERT(NS_IsMainThread());
  2695     if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
  2696       Allow(JS::UndefinedHandleValue);
  2697       return NS_OK;
  2700     if (XRE_GetProcessType() == GeckoProcessType_Content) {
  2702       // because owner implements nsITabChild, we can assume that it is
  2703       // the one and only TabChild.
  2704       TabChild* child = TabChild::GetFrom(mWindow->GetDocShell());
  2705       if (!child) {
  2706         return NS_OK;
  2709       // Retain a reference so the object isn't deleted without IPDL's
  2710       // knowledge. Corresponding release occurs in
  2711       // DeallocPContentPermissionRequest.
  2712       AddRef();
  2714       nsCString type;
  2715       nsresult rv = DeviceStorageTypeChecker::GetPermissionForType(
  2716         mFile->mStorageType, type);
  2717       if (NS_FAILED(rv)) {
  2718         return rv;
  2720       nsCString access;
  2721       rv = DeviceStorageTypeChecker::GetAccessForRequest(
  2722         DeviceStorageRequestType(mRequestType), access);
  2723       if (NS_FAILED(rv)) {
  2724         return rv;
  2726       nsTArray<PermissionRequest> permArray;
  2727       nsTArray<nsString> emptyOptions;
  2728       permArray.AppendElement(PermissionRequest(type, access, emptyOptions));
  2729       child->SendPContentPermissionRequestConstructor(
  2730         this, permArray, IPC::Principal(mPrincipal));
  2732       Sendprompt();
  2733       return NS_OK;
  2736     nsCOMPtr<nsIContentPermissionPrompt> prompt
  2737       = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
  2738     if (prompt) {
  2739       prompt->Prompt(this);
  2741     return NS_OK;
  2744   NS_IMETHODIMP GetTypes(nsIArray** aTypes)
  2746     nsCString type;
  2747     nsresult rv =
  2748       DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
  2749     if (NS_FAILED(rv)) {
  2750       return rv;
  2753     nsCString access;
  2754     rv = DeviceStorageTypeChecker::GetAccessForRequest(
  2755       DeviceStorageRequestType(mRequestType), access);
  2756     if (NS_FAILED(rv)) {
  2757       return rv;
  2760     nsTArray<nsString> emptyOptions;
  2761     return CreatePermissionArray(type, access, emptyOptions, aTypes);
  2764   NS_IMETHOD GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
  2766     NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
  2767     return NS_OK;
  2770   NS_IMETHOD GetWindow(nsIDOMWindow * *aRequestingWindow)
  2772     NS_IF_ADDREF(*aRequestingWindow = mWindow);
  2773     return NS_OK;
  2776   NS_IMETHOD GetElement(nsIDOMElement * *aRequestingElement)
  2778     *aRequestingElement = nullptr;
  2779     return NS_OK;
  2782   NS_IMETHOD Cancel()
  2784     nsCOMPtr<nsIRunnable> event
  2785       = new PostErrorEvent(mRequest.forget(),
  2786                            POST_ERROR_EVENT_PERMISSION_DENIED);
  2787     return NS_DispatchToMainThread(event);
  2790   NS_IMETHOD Allow(JS::HandleValue aChoices)
  2792     MOZ_ASSERT(NS_IsMainThread());
  2793     MOZ_ASSERT(aChoices.isUndefined());
  2795     if (!mRequest) {
  2796       return NS_ERROR_FAILURE;
  2799     nsCOMPtr<nsIRunnable> r;
  2801     switch(mRequestType) {
  2802       case DEVICE_STORAGE_REQUEST_CREATEFD:
  2804         if (!mFile->mFile) {
  2805           return NS_ERROR_FAILURE;
  2808         DeviceStorageTypeChecker* typeChecker
  2809           = DeviceStorageTypeChecker::CreateOrGet();
  2810         if (!typeChecker) {
  2811           return NS_OK;
  2814         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
  2815           r = new PostErrorEvent(mRequest.forget(),
  2816                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
  2817           return NS_DispatchToCurrentThread(r);
  2820         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  2822           DeviceStorageCreateFdParams params;
  2823           params.type() = mFile->mStorageType;
  2824           params.storageName() = mFile->mStorageName;
  2825           params.relpath() = mFile->mPath;
  2827           mFile->Dump("DeviceStorageCreateFdParams");
  2829           PDeviceStorageRequestChild* child
  2830             = new DeviceStorageRequestChild(mRequest, mFile,
  2831                                             mDSFileDescriptor.get());
  2832           ContentChild::GetSingleton()
  2833             ->SendPDeviceStorageRequestConstructor(child, params);
  2834           return NS_OK;
  2836         mDSFileDescriptor->mDSFile = mFile;
  2837         r = new CreateFdEvent(mDSFileDescriptor.get(), mRequest.forget());
  2838         break;
  2841       case DEVICE_STORAGE_REQUEST_CREATE:
  2843         if (!mBlob || !mFile->mFile) {
  2844           return NS_ERROR_FAILURE;
  2847         DeviceStorageTypeChecker* typeChecker
  2848           = DeviceStorageTypeChecker::CreateOrGet();
  2849         if (!typeChecker) {
  2850           return NS_OK;
  2853         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile) ||
  2854             !typeChecker->Check(mFile->mStorageType, mBlob)) {
  2855           r = new PostErrorEvent(mRequest.forget(),
  2856                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
  2857           return NS_DispatchToCurrentThread(r);
  2860         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  2861           BlobChild* actor
  2862             = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlob);
  2863           if (!actor) {
  2864             return NS_ERROR_FAILURE;
  2867           DeviceStorageAddParams params;
  2868           params.blobChild() = actor;
  2869           params.type() = mFile->mStorageType;
  2870           params.storageName() = mFile->mStorageName;
  2871           params.relpath() = mFile->mPath;
  2873           PDeviceStorageRequestChild* child
  2874             = new DeviceStorageRequestChild(mRequest, mFile);
  2875           ContentChild::GetSingleton()
  2876             ->SendPDeviceStorageRequestConstructor(child, params);
  2877           return NS_OK;
  2879         r = new WriteFileEvent(mBlob, mFile, mRequest.forget());
  2880         break;
  2883       case DEVICE_STORAGE_REQUEST_READ:
  2884       case DEVICE_STORAGE_REQUEST_WRITE:
  2886         if (!mFile->mFile) {
  2887           return NS_ERROR_FAILURE;
  2890         DeviceStorageTypeChecker* typeChecker
  2891           = DeviceStorageTypeChecker::CreateOrGet();
  2892         if (!typeChecker) {
  2893           return NS_OK;
  2896         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
  2897           r = new PostErrorEvent(mRequest.forget(),
  2898                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
  2899           return NS_DispatchToCurrentThread(r);
  2902         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  2903           PDeviceStorageRequestChild* child
  2904             = new DeviceStorageRequestChild(mRequest, mFile);
  2905           DeviceStorageGetParams params(mFile->mStorageType,
  2906                                         mFile->mStorageName,
  2907                                         mFile->mRootDir,
  2908                                         mFile->mPath);
  2909           ContentChild::GetSingleton()
  2910             ->SendPDeviceStorageRequestConstructor(child, params);
  2911           return NS_OK;
  2914         r = new ReadFileEvent(mFile, mRequest.forget());
  2915         break;
  2918       case DEVICE_STORAGE_REQUEST_DELETE:
  2920         if (!mFile->mFile) {
  2921           return NS_ERROR_FAILURE;
  2924         DeviceStorageTypeChecker* typeChecker
  2925           = DeviceStorageTypeChecker::CreateOrGet();
  2926         if (!typeChecker) {
  2927           return NS_OK;
  2930         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
  2931           r = new PostErrorEvent(mRequest.forget(),
  2932                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
  2933           return NS_DispatchToCurrentThread(r);
  2936         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  2937           PDeviceStorageRequestChild* child
  2938             = new DeviceStorageRequestChild(mRequest, mFile);
  2939           DeviceStorageDeleteParams params(mFile->mStorageType,
  2940                                            mFile->mStorageName,
  2941                                            mFile->mPath);
  2942           ContentChild::GetSingleton()
  2943             ->SendPDeviceStorageRequestConstructor(child, params);
  2944           return NS_OK;
  2946         r = new DeleteFileEvent(mFile, mRequest.forget());
  2947         break;
  2950       case DEVICE_STORAGE_REQUEST_FREE_SPACE:
  2952         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  2953           PDeviceStorageRequestChild* child
  2954             = new DeviceStorageRequestChild(mRequest, mFile);
  2955           DeviceStorageFreeSpaceParams params(mFile->mStorageType,
  2956                                               mFile->mStorageName);
  2957           ContentChild::GetSingleton()
  2958             ->SendPDeviceStorageRequestConstructor(child, params);
  2959           return NS_OK;
  2961         r = new FreeSpaceFileEvent(mFile, mRequest.forget());
  2962         break;
  2965       case DEVICE_STORAGE_REQUEST_USED_SPACE:
  2967         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  2968           PDeviceStorageRequestChild* child
  2969             = new DeviceStorageRequestChild(mRequest, mFile);
  2970           DeviceStorageUsedSpaceParams params(mFile->mStorageType,
  2971                                               mFile->mStorageName);
  2972           ContentChild::GetSingleton()
  2973             ->SendPDeviceStorageRequestConstructor(child, params);
  2974           return NS_OK;
  2976         // this needs to be dispatched to only one (1)
  2977         // thread or we will do more work than required.
  2978         DeviceStorageUsedSpaceCache* usedSpaceCache
  2979           = DeviceStorageUsedSpaceCache::CreateOrGet();
  2980         MOZ_ASSERT(usedSpaceCache);
  2981         r = new UsedSpaceFileEvent(mFile, mRequest.forget());
  2982         usedSpaceCache->Dispatch(r);
  2983         return NS_OK;
  2986       case DEVICE_STORAGE_REQUEST_AVAILABLE:
  2988         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  2989           PDeviceStorageRequestChild* child
  2990             = new DeviceStorageRequestChild(mRequest, mFile);
  2991           DeviceStorageAvailableParams params(mFile->mStorageType,
  2992                                               mFile->mStorageName);
  2993           ContentChild::GetSingleton()
  2994             ->SendPDeviceStorageRequestConstructor(child, params);
  2995           return NS_OK;
  2997         r = new PostAvailableResultEvent(mFile, mRequest);
  2998         return NS_DispatchToCurrentThread(r);
  3001       case DEVICE_STORAGE_REQUEST_STATUS:
  3003         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  3004           PDeviceStorageRequestChild* child
  3005             = new DeviceStorageRequestChild(mRequest, mFile);
  3006           DeviceStorageStatusParams params(mFile->mStorageType,
  3007                                               mFile->mStorageName);
  3008           ContentChild::GetSingleton()
  3009             ->SendPDeviceStorageRequestConstructor(child, params);
  3010           return NS_OK;
  3012         r = new PostStatusResultEvent(mFile, mRequest);
  3013         return NS_DispatchToCurrentThread(r);
  3016       case DEVICE_STORAGE_REQUEST_WATCH:
  3018         mDeviceStorage->mAllowedToWatchFile = true;
  3019         return NS_OK;
  3022       case DEVICE_STORAGE_REQUEST_FORMAT:
  3024         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  3025           PDeviceStorageRequestChild* child
  3026             = new DeviceStorageRequestChild(mRequest, mFile);
  3027           DeviceStorageFormatParams params(mFile->mStorageType,
  3028                                            mFile->mStorageName);
  3029           ContentChild::GetSingleton()
  3030             ->SendPDeviceStorageRequestConstructor(child, params);
  3031           return NS_OK;
  3033         r = new PostFormatResultEvent(mFile, mRequest);
  3034         return NS_DispatchToCurrentThread(r);
  3037       case DEVICE_STORAGE_REQUEST_MOUNT:
  3039         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  3040           PDeviceStorageRequestChild* child
  3041             = new DeviceStorageRequestChild(mRequest, mFile);
  3042           DeviceStorageMountParams params(mFile->mStorageType,
  3043                                            mFile->mStorageName);
  3044           ContentChild::GetSingleton()
  3045             ->SendPDeviceStorageRequestConstructor(child, params);
  3046           return NS_OK;
  3048         r = new PostMountResultEvent(mFile, mRequest);
  3049         return NS_DispatchToCurrentThread(r);
  3052       case DEVICE_STORAGE_REQUEST_UNMOUNT:
  3054         if (XRE_GetProcessType() != GeckoProcessType_Default) {
  3055           PDeviceStorageRequestChild* child
  3056             = new DeviceStorageRequestChild(mRequest, mFile);
  3057           DeviceStorageUnmountParams params(mFile->mStorageType,
  3058                                            mFile->mStorageName);
  3059           ContentChild::GetSingleton()
  3060             ->SendPDeviceStorageRequestConstructor(child, params);
  3061           return NS_OK;
  3063         r = new PostUnmountResultEvent(mFile, mRequest);
  3064         return NS_DispatchToCurrentThread(r);
  3068     if (r) {
  3069       nsCOMPtr<nsIEventTarget> target
  3070         = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  3071       MOZ_ASSERT(target);
  3072       target->Dispatch(r, NS_DISPATCH_NORMAL);
  3075     return NS_OK;
  3078   bool Recv__delete__(const bool& allow,
  3079                       const InfallibleTArray<PermissionChoice>& choices)
  3081     MOZ_ASSERT(choices.IsEmpty(), "DeviceStorage doesn't support permission choice");
  3083     if (allow) {
  3084       Allow(JS::UndefinedHandleValue);
  3086     else {
  3087       Cancel();
  3089     return true;
  3092   void IPDLRelease()
  3094     Release();
  3097 private:
  3098   int32_t mRequestType;
  3099   nsCOMPtr<nsPIDOMWindow> mWindow;
  3100   nsCOMPtr<nsIPrincipal> mPrincipal;
  3101   nsRefPtr<DeviceStorageFile> mFile;
  3103   nsRefPtr<DOMRequest> mRequest;
  3104   nsCOMPtr<nsIDOMBlob> mBlob;
  3105   nsRefPtr<nsDOMDeviceStorage> mDeviceStorage;
  3106   nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
  3107 };
  3109 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageRequest)
  3110   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
  3111   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
  3112   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
  3113 NS_INTERFACE_MAP_END
  3115 NS_IMPL_CYCLE_COLLECTING_ADDREF(DeviceStorageRequest)
  3116 NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceStorageRequest)
  3118 NS_IMPL_CYCLE_COLLECTION(DeviceStorageRequest,
  3119                          mRequest,
  3120                          mWindow,
  3121                          mBlob,
  3122                          mDeviceStorage)
  3125 NS_INTERFACE_MAP_BEGIN(nsDOMDeviceStorage)
  3126   NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorage)
  3127   NS_INTERFACE_MAP_ENTRY(nsIObserver)
  3128 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
  3130 NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
  3131 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
  3133 nsDOMDeviceStorage::nsDOMDeviceStorage(nsPIDOMWindow* aWindow)
  3134   : DOMEventTargetHelper(aWindow)
  3135   , mIsWatchingFile(false)
  3136   , mAllowedToWatchFile(false)
  3140 /* virtual */ JSObject*
  3141 nsDOMDeviceStorage::WrapObject(JSContext* aCx)
  3143   return DeviceStorageBinding::Wrap(aCx, this);
  3146 nsresult
  3147 nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType,
  3148                          const nsAString &aVolName)
  3150   DebugOnly<FileUpdateDispatcher*> observer
  3151     = FileUpdateDispatcher::GetSingleton();
  3152   MOZ_ASSERT(observer);
  3154   MOZ_ASSERT(aWindow);
  3156   SetRootDirectoryForType(aType, aVolName);
  3157   if (!mRootDirectory) {
  3158     return NS_ERROR_NOT_AVAILABLE;
  3160   if (!mStorageName.IsEmpty()) {
  3161     RegisterForSDCardChanges(this);
  3164   // Grab the principal of the document
  3165   nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
  3166   if (!doc) {
  3167     return NS_ERROR_FAILURE;
  3169   mPrincipal = doc->NodePrincipal();
  3171   // the 'apps' type is special.  We only want this exposed
  3172   // if the caller has the "webapps-manage" permission.
  3173   if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) {
  3174     nsCOMPtr<nsIPermissionManager> permissionManager
  3175       = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
  3176     NS_ENSURE_TRUE(permissionManager, NS_ERROR_FAILURE);
  3178     uint32_t permission;
  3179     nsresult rv
  3180       = permissionManager->TestPermissionFromPrincipal(mPrincipal,
  3181                                                        "webapps-manage",
  3182                                                        &permission);
  3184     if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
  3185       return NS_ERROR_NOT_AVAILABLE;
  3189   return NS_OK;
  3192 nsDOMDeviceStorage::~nsDOMDeviceStorage()
  3196 void
  3197 nsDOMDeviceStorage::Shutdown()
  3199   MOZ_ASSERT(NS_IsMainThread());
  3201   if (mFileSystem) {
  3202     mFileSystem->Shutdown();
  3203     mFileSystem = nullptr;
  3206   if (!mStorageName.IsEmpty()) {
  3207     UnregisterForSDCardChanges(this);
  3210   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  3211   obs->RemoveObserver(this, "file-watcher-update");
  3212   obs->RemoveObserver(this, "disk-space-watcher");
  3215 StaticAutoPtr<nsTArray<nsString>> nsDOMDeviceStorage::sVolumeNameCache;
  3217 // static
  3218 void
  3219 nsDOMDeviceStorage::GetOrderedVolumeNames(
  3220   nsDOMDeviceStorage::VolumeNameArray &aVolumeNames)
  3222   if (sVolumeNameCache && sVolumeNameCache->Length() > 0) {
  3223     aVolumeNames.AppendElements(*sVolumeNameCache);
  3224     return;
  3226 #ifdef MOZ_WIDGET_GONK
  3227   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
  3228   if (vs) {
  3229     vs->GetVolumeNames(aVolumeNames);
  3231     // If the volume sdcard exists, then we want it to be first.
  3233     VolumeNameArray::index_type sdcardIndex;
  3234     sdcardIndex = aVolumeNames.IndexOf(NS_LITERAL_STRING("sdcard"));
  3235     if (sdcardIndex != VolumeNameArray::NoIndex && sdcardIndex > 0) {
  3236       aVolumeNames.RemoveElementAt(sdcardIndex);
  3237       aVolumeNames.InsertElementAt(0, NS_LITERAL_STRING("sdcard"));
  3240 #endif
  3241   if (aVolumeNames.IsEmpty()) {
  3242     aVolumeNames.AppendElement(EmptyString());
  3244   sVolumeNameCache = new nsTArray<nsString>;
  3245   sVolumeNameCache->AppendElements(aVolumeNames);
  3248 // static
  3249 void
  3250 nsDOMDeviceStorage::CreateDeviceStorageFor(nsPIDOMWindow* aWin,
  3251                                            const nsAString &aType,
  3252                                            nsDOMDeviceStorage** aStore)
  3254   nsString storageName;
  3255   if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
  3256     // The storage name will be the empty string
  3257     storageName.Truncate();
  3258   } else {
  3259     GetDefaultStorageName(aType, storageName);
  3262   nsRefPtr<nsDOMDeviceStorage> ds = new nsDOMDeviceStorage(aWin);
  3263   if (NS_FAILED(ds->Init(aWin, aType, storageName))) {
  3264     *aStore = nullptr;
  3265     return;
  3267   NS_ADDREF(*aStore = ds.get());
  3270 // static
  3271 void
  3272 nsDOMDeviceStorage::CreateDeviceStoragesFor(
  3273   nsPIDOMWindow* aWin,
  3274   const nsAString &aType,
  3275   nsTArray<nsRefPtr<nsDOMDeviceStorage> > &aStores)
  3277   nsresult rv;
  3279   if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
  3280     nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
  3281     rv = storage->Init(aWin, aType, EmptyString());
  3282     if (NS_SUCCEEDED(rv)) {
  3283       aStores.AppendElement(storage);
  3285     return;
  3287   VolumeNameArray volNames;
  3288   GetOrderedVolumeNames(volNames);
  3290   VolumeNameArray::size_type numVolumeNames = volNames.Length();
  3291   for (VolumeNameArray::index_type i = 0; i < numVolumeNames; i++) {
  3292     nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
  3293     rv = storage->Init(aWin, aType, volNames[i]);
  3294     if (NS_FAILED(rv)) {
  3295       break;
  3297     aStores.AppendElement(storage);
  3301 // static
  3302 bool
  3303 nsDOMDeviceStorage::ParseFullPath(const nsAString& aFullPath,
  3304                                   nsAString& aOutStorageName,
  3305                                   nsAString& aOutStoragePath)
  3307   aOutStorageName.Truncate();
  3308   aOutStoragePath.Truncate();
  3310   NS_NAMED_LITERAL_STRING(slash, "/");
  3312   nsDependentSubstring storageName;
  3314   if (StringBeginsWith(aFullPath, slash)) {
  3315     int32_t slashIndex = aFullPath.FindChar('/', 1);
  3316     if (slashIndex == kNotFound) {
  3317       // names of the form /filename are illegal
  3318       return false;
  3320     storageName.Rebind(aFullPath, 1, slashIndex - 1);
  3321     aOutStoragePath = Substring(aFullPath, slashIndex + 1);
  3322   } else {
  3323     aOutStoragePath = aFullPath;
  3325   // If no volume name was specified in aFullPath, then aOutStorageName
  3326   // will wind up being the empty string. It's up to the caller to figure
  3327   // out which storage name to actually use.
  3328   aOutStorageName = storageName;
  3329   return true;
  3332 already_AddRefed<nsDOMDeviceStorage>
  3333 nsDOMDeviceStorage::GetStorage(const nsAString& aFullPath,
  3334                                nsAString& aOutStoragePath)
  3336   nsString storageName;
  3337   if (!ParseFullPath(aFullPath, storageName, aOutStoragePath)) {
  3338     return nullptr;
  3340   nsRefPtr<nsDOMDeviceStorage> ds;
  3341   if (storageName.IsEmpty()) {
  3342     ds = this;
  3343   } else {
  3344     ds = GetStorageByName(storageName);
  3346   return ds.forget();
  3349 already_AddRefed<nsDOMDeviceStorage>
  3350 nsDOMDeviceStorage::GetStorageByName(const nsAString& aStorageName)
  3352   MOZ_ASSERT(NS_IsMainThread());
  3354   nsRefPtr<nsDOMDeviceStorage> ds;
  3356   if (mStorageName.Equals(aStorageName)) {
  3357     ds = this;
  3358     return ds.forget();
  3360   VolumeNameArray volNames;
  3361   GetOrderedVolumeNames(volNames);
  3362   VolumeNameArray::size_type numVolumes = volNames.Length();
  3363   VolumeNameArray::index_type i;
  3364   for (i = 0; i < numVolumes; i++) {
  3365     if (volNames[i].Equals(aStorageName)) {
  3366       ds = new nsDOMDeviceStorage(GetOwner());
  3367       nsresult rv = ds->Init(GetOwner(), mStorageType, aStorageName);
  3368       if (NS_FAILED(rv)) {
  3369         return nullptr;
  3371       return ds.forget();
  3374   return nullptr;
  3377 // static
  3378 void
  3379 nsDOMDeviceStorage::GetDefaultStorageName(const nsAString& aStorageType,
  3380                                           nsAString& aStorageName)
  3382   // See if the preferred volume is available.
  3383   nsRefPtr<nsDOMDeviceStorage> ds;
  3384   nsAdoptingString prefStorageName =
  3385     mozilla::Preferences::GetString("device.storage.writable.name");
  3386   if (prefStorageName) {
  3387     aStorageName = prefStorageName;
  3388     return;
  3391   // No preferred storage, we'll use the first one (which should be sdcard).
  3393   VolumeNameArray volNames;
  3394   GetOrderedVolumeNames(volNames);
  3395   if (volNames.Length() > 0) {
  3396     aStorageName = volNames[0];
  3397     return;
  3400   // No volumes available, return the empty string. This is normal for
  3401   // b2g-desktop.
  3402   aStorageName.Truncate();
  3405 bool
  3406 nsDOMDeviceStorage::IsAvailable()
  3408   DeviceStorageFile dsf(mStorageType, mStorageName);
  3409   return dsf.IsAvailable();
  3412 NS_IMETHODIMP
  3413 nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval)
  3415   ErrorResult rv;
  3416   nsRefPtr<DOMRequest> request = Add(aBlob, rv);
  3417   request.forget(_retval);
  3418   return rv.ErrorCode();
  3421 already_AddRefed<DOMRequest>
  3422 nsDOMDeviceStorage::Add(nsIDOMBlob* aBlob, ErrorResult& aRv)
  3424   if (!aBlob) {
  3425     return nullptr;
  3428   nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
  3429   if (!mimeSvc) {
  3430     aRv.Throw(NS_ERROR_FAILURE);
  3431     return nullptr;
  3434   // if mimeType isn't set, we will not get a correct
  3435   // extension, and AddNamed() will fail.  This will post an
  3436   // onerror to the requestee.
  3437   nsString mimeType;
  3438   aBlob->GetType(mimeType);
  3440   nsCString extension;
  3441   mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
  3442                                EmptyCString(), extension);
  3443   // if extension is null here, we will ignore it for now.
  3444   // AddNamed() will check the file path and fail.  This
  3445   // will post an onerror to the requestee.
  3447   // possible race here w/ unique filename
  3448   char buffer[32];
  3449   NS_MakeRandomString(buffer, ArrayLength(buffer) - 1);
  3451   nsAutoCString path;
  3452   path.Assign(nsDependentCString(buffer));
  3453   path.Append(".");
  3454   path.Append(extension);
  3456   return AddNamed(aBlob, NS_ConvertASCIItoUTF16(path), aRv);
  3459 NS_IMETHODIMP
  3460 nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob,
  3461                              const nsAString & aPath,
  3462                              nsIDOMDOMRequest * *_retval)
  3464   ErrorResult rv;
  3465   nsRefPtr<DOMRequest> request = AddNamed(aBlob, aPath, rv);
  3466   request.forget(_retval);
  3467   return rv.ErrorCode();
  3470 already_AddRefed<DOMRequest>
  3471 nsDOMDeviceStorage::AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
  3472                              ErrorResult& aRv)
  3474   MOZ_ASSERT(NS_IsMainThread());
  3476   // if the blob is null here, bail
  3477   if (!aBlob) {
  3478     return nullptr;
  3481   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3482   if (!win) {
  3483     aRv.Throw(NS_ERROR_UNEXPECTED);
  3484     return nullptr;
  3487   DeviceStorageTypeChecker* typeChecker
  3488     = DeviceStorageTypeChecker::CreateOrGet();
  3489   if (!typeChecker) {
  3490     aRv.Throw(NS_ERROR_FAILURE);
  3491     return nullptr;
  3494   nsCOMPtr<nsIRunnable> r;
  3495   nsresult rv;
  3497   if (IsFullPath(aPath)) {
  3498     nsString storagePath;
  3499     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
  3500     if (!ds) {
  3501       nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3502       r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
  3503       rv = NS_DispatchToCurrentThread(r);
  3504       if (NS_FAILED(rv)) {
  3505         aRv.Throw(rv);
  3507       return request.forget();
  3509     return ds->AddNamed(aBlob, storagePath, aRv);
  3512   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3514   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3515                                                           mStorageName,
  3516                                                           aPath);
  3517   if (!dsf->IsSafePath()) {
  3518     r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
  3519   } else if (!typeChecker->Check(mStorageType, dsf->mFile) ||
  3520       !typeChecker->Check(mStorageType, aBlob)) {
  3521     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
  3522   } else {
  3523     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATE,
  3524                                  win, mPrincipal, dsf, request, aBlob);
  3527   rv = NS_DispatchToCurrentThread(r);
  3528   if (NS_FAILED(rv)) {
  3529     aRv.Throw(rv);
  3531   return request.forget();
  3534 NS_IMETHODIMP
  3535 nsDOMDeviceStorage::Get(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
  3537   ErrorResult rv;
  3538   nsRefPtr<DOMRequest> request = Get(aPath, rv);
  3539   request.forget(aRetval);
  3540   return rv.ErrorCode();
  3543 NS_IMETHODIMP
  3544 nsDOMDeviceStorage::GetEditable(const nsAString& aPath,
  3545                                 nsIDOMDOMRequest** aRetval)
  3547   ErrorResult rv;
  3548   nsRefPtr<DOMRequest> request = GetEditable(aPath, rv);
  3549   request.forget(aRetval);
  3550   return rv.ErrorCode();
  3553 already_AddRefed<DOMRequest>
  3554 nsDOMDeviceStorage::GetInternal(const nsAString& aPath, bool aEditable,
  3555                                 ErrorResult& aRv)
  3557   MOZ_ASSERT(NS_IsMainThread());
  3559   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3560   if (!win) {
  3561     aRv.Throw(NS_ERROR_UNEXPECTED);
  3562     return nullptr;
  3565   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3567   if (IsFullPath(aPath)) {
  3568     nsString storagePath;
  3569     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
  3570     if (!ds) {
  3571       nsCOMPtr<nsIRunnable> r =
  3572         new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
  3573       nsresult rv = NS_DispatchToCurrentThread(r);
  3574       if (NS_FAILED(rv)) {
  3575         aRv.Throw(rv);
  3577       return request.forget();
  3579     ds->GetInternal(win, storagePath, request, aEditable);
  3580     return request.forget();
  3582   GetInternal(win, aPath, request, aEditable);
  3583   return request.forget();
  3586 void
  3587 nsDOMDeviceStorage::GetInternal(nsPIDOMWindow *aWin,
  3588                                 const nsAString& aPath,
  3589                                 DOMRequest* aRequest,
  3590                                 bool aEditable)
  3592   MOZ_ASSERT(NS_IsMainThread());
  3594   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3595                                                           mStorageName,
  3596                                                           aPath);
  3597   dsf->SetEditable(aEditable);
  3599   nsCOMPtr<nsIRunnable> r;
  3600   if (!dsf->IsSafePath()) {
  3601     r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
  3602   } else {
  3603     r = new DeviceStorageRequest(aEditable ? DEVICE_STORAGE_REQUEST_WRITE
  3604                                            : DEVICE_STORAGE_REQUEST_READ,
  3605                                  aWin, mPrincipal, dsf, aRequest);
  3607   DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
  3608   MOZ_ASSERT(NS_SUCCEEDED(rv));
  3611 NS_IMETHODIMP
  3612 nsDOMDeviceStorage::Delete(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
  3614   ErrorResult rv;
  3615   nsRefPtr<DOMRequest> request = Delete(aPath, rv);
  3616   request.forget(aRetval);
  3617   return rv.ErrorCode();
  3620 already_AddRefed<DOMRequest>
  3621 nsDOMDeviceStorage::Delete(const nsAString& aPath, ErrorResult& aRv)
  3623   MOZ_ASSERT(NS_IsMainThread());
  3625   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3626   if (!win) {
  3627     aRv.Throw(NS_ERROR_UNEXPECTED);
  3628     return nullptr;
  3631   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3633   if (IsFullPath(aPath)) {
  3634     nsString storagePath;
  3635     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
  3636     if (!ds) {
  3637       nsCOMPtr<nsIRunnable> r =
  3638         new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
  3639       nsresult rv = NS_DispatchToCurrentThread(r);
  3640       if (NS_FAILED(rv)) {
  3641         aRv.Throw(rv);
  3643       return request.forget();
  3645     ds->DeleteInternal(win, storagePath, request);
  3646     return request.forget();
  3648   DeleteInternal(win, aPath, request);
  3649   return request.forget();
  3652 void
  3653 nsDOMDeviceStorage::DeleteInternal(nsPIDOMWindow *aWin,
  3654                                    const nsAString& aPath,
  3655                                    DOMRequest* aRequest)
  3657   MOZ_ASSERT(NS_IsMainThread());
  3659   nsCOMPtr<nsIRunnable> r;
  3660   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3661                                                           mStorageName,
  3662                                                           aPath);
  3663   if (!dsf->IsSafePath()) {
  3664     r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
  3665   } else {
  3666     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE,
  3667                                  aWin, mPrincipal, dsf, aRequest);
  3669   DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
  3670   MOZ_ASSERT(NS_SUCCEEDED(rv));
  3673 NS_IMETHODIMP
  3674 nsDOMDeviceStorage::FreeSpace(nsIDOMDOMRequest** aRetval)
  3676   ErrorResult rv;
  3677   nsRefPtr<DOMRequest> request = FreeSpace(rv);
  3678   request.forget(aRetval);
  3679   return rv.ErrorCode();
  3682 already_AddRefed<DOMRequest>
  3683 nsDOMDeviceStorage::FreeSpace(ErrorResult& aRv)
  3685   MOZ_ASSERT(NS_IsMainThread());
  3687   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3688   if (!win) {
  3689     aRv.Throw(NS_ERROR_UNEXPECTED);
  3690     return nullptr;
  3693   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3695   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3696                                                           mStorageName);
  3697   nsCOMPtr<nsIRunnable> r
  3698     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE,
  3699                                win, mPrincipal, dsf, request);
  3700   nsresult rv = NS_DispatchToCurrentThread(r);
  3701   if (NS_FAILED(rv)) {
  3702     aRv.Throw(rv);
  3704   return request.forget();
  3707 NS_IMETHODIMP
  3708 nsDOMDeviceStorage::UsedSpace(nsIDOMDOMRequest** aRetval)
  3710   ErrorResult rv;
  3711   nsRefPtr<DOMRequest> request = UsedSpace(rv);
  3712   request.forget(aRetval);
  3713   return rv.ErrorCode();
  3716 already_AddRefed<DOMRequest>
  3717 nsDOMDeviceStorage::UsedSpace(ErrorResult& aRv)
  3719   MOZ_ASSERT(NS_IsMainThread());
  3721   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3722   if (!win) {
  3723     aRv.Throw(NS_ERROR_UNEXPECTED);
  3724     return nullptr;
  3727   DebugOnly<DeviceStorageUsedSpaceCache*> usedSpaceCache
  3728     = DeviceStorageUsedSpaceCache::CreateOrGet();
  3729   MOZ_ASSERT(usedSpaceCache);
  3731   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3733   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3734                                                           mStorageName);
  3735   nsCOMPtr<nsIRunnable> r
  3736     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE,
  3737                                win, mPrincipal, dsf, request);
  3738   nsresult rv = NS_DispatchToCurrentThread(r);
  3739   if (NS_FAILED(rv)) {
  3740     aRv.Throw(rv);
  3742   return request.forget();
  3745 NS_IMETHODIMP
  3746 nsDOMDeviceStorage::Available(nsIDOMDOMRequest** aRetval)
  3748   ErrorResult rv;
  3749   nsRefPtr<DOMRequest> request = Available(rv);
  3750   request.forget(aRetval);
  3751   return rv.ErrorCode();
  3754 already_AddRefed<DOMRequest>
  3755 nsDOMDeviceStorage::Available(ErrorResult& aRv)
  3757   MOZ_ASSERT(NS_IsMainThread());
  3759   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3760   if (!win) {
  3761     aRv.Throw(NS_ERROR_UNEXPECTED);
  3762     return nullptr;
  3765   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3767   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3768                                                           mStorageName);
  3769   nsCOMPtr<nsIRunnable> r
  3770     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE,
  3771                                win, mPrincipal, dsf, request);
  3772   nsresult rv = NS_DispatchToCurrentThread(r);
  3773   if (NS_FAILED(rv)) {
  3774     aRv.Throw(rv);
  3776   return request.forget();
  3779 already_AddRefed<DOMRequest>
  3780 nsDOMDeviceStorage::StorageStatus(ErrorResult& aRv)
  3782   MOZ_ASSERT(NS_IsMainThread());
  3784   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3785   if (!win) {
  3786     aRv.Throw(NS_ERROR_UNEXPECTED);
  3787     return nullptr;
  3790   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3792   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3793                                                           mStorageName);
  3794   nsCOMPtr<nsIRunnable> r
  3795     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_STATUS,
  3796                                win, mPrincipal, dsf, request);
  3797   nsresult rv = NS_DispatchToCurrentThread(r);
  3798   if (NS_FAILED(rv)) {
  3799     aRv.Throw(rv);
  3801   return request.forget();
  3804 already_AddRefed<DOMRequest>
  3805 nsDOMDeviceStorage::Format(ErrorResult& aRv)
  3807   MOZ_ASSERT(NS_IsMainThread());
  3809   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3810   if (!win) {
  3811     aRv.Throw(NS_ERROR_UNEXPECTED);
  3812     return nullptr;
  3815   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3817   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3818                                                           mStorageName);
  3819   nsCOMPtr<nsIRunnable> r
  3820     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FORMAT,
  3821                                win, mPrincipal, dsf, request);
  3822   nsresult rv = NS_DispatchToCurrentThread(r);
  3823   if (NS_FAILED(rv)) {
  3824     aRv.Throw(rv);
  3826   return request.forget();
  3829 already_AddRefed<DOMRequest>
  3830 nsDOMDeviceStorage::Mount(ErrorResult& aRv)
  3832   MOZ_ASSERT(NS_IsMainThread());
  3834   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3835   if (!win) {
  3836     aRv.Throw(NS_ERROR_UNEXPECTED);
  3837     return nullptr;
  3840   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3842   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3843                                                           mStorageName);
  3844   nsCOMPtr<nsIRunnable> r
  3845     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_MOUNT,
  3846                                win, mPrincipal, dsf, request);
  3847   nsresult rv = NS_DispatchToCurrentThread(r);
  3848   if (NS_FAILED(rv)) {
  3849     aRv.Throw(rv);
  3851   return request.forget();
  3854 already_AddRefed<DOMRequest>
  3855 nsDOMDeviceStorage::Unmount(ErrorResult& aRv)
  3857   MOZ_ASSERT(NS_IsMainThread());
  3859   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3860   if (!win) {
  3861     aRv.Throw(NS_ERROR_UNEXPECTED);
  3862     return nullptr;
  3865   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3867   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3868                                                           mStorageName);
  3869   nsCOMPtr<nsIRunnable> r
  3870     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_UNMOUNT,
  3871                                win, mPrincipal, dsf, request);
  3872   nsresult rv = NS_DispatchToCurrentThread(r);
  3873   if (NS_FAILED(rv)) {
  3874     aRv.Throw(rv);
  3876   return request.forget();
  3879 NS_IMETHODIMP
  3880 nsDOMDeviceStorage::CreateFileDescriptor(const nsAString& aPath,
  3881                                          DeviceStorageFileDescriptor* aDSFileDescriptor,
  3882                                          nsIDOMDOMRequest** aRequest)
  3884   MOZ_ASSERT(NS_IsMainThread());
  3885   MOZ_ASSERT(aDSFileDescriptor);
  3887   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3888   if (!win) {
  3889     return NS_ERROR_UNEXPECTED;
  3892   DeviceStorageTypeChecker* typeChecker
  3893     = DeviceStorageTypeChecker::CreateOrGet();
  3894   if (!typeChecker) {
  3895     return NS_ERROR_FAILURE;
  3898   nsCOMPtr<nsIRunnable> r;
  3899   nsresult rv;
  3901   if (IsFullPath(aPath)) {
  3902     nsString storagePath;
  3903     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
  3904     if (!ds) {
  3905       nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3906       r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
  3907       rv = NS_DispatchToCurrentThread(r);
  3908       if (NS_FAILED(rv)) {
  3909         return rv;
  3911       request.forget(aRequest);
  3912       return NS_OK;
  3914     return ds->CreateFileDescriptor(storagePath, aDSFileDescriptor, aRequest);
  3917   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  3919   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  3920                                                           mStorageName,
  3921                                                           aPath);
  3922   if (!dsf->IsSafePath()) {
  3923     r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
  3924   } else if (!typeChecker->Check(mStorageType, dsf->mFile)) {
  3925     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
  3926   } else {
  3927     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATEFD,
  3928                                  win, mPrincipal, dsf, request,
  3929                                  aDSFileDescriptor);
  3932   rv = NS_DispatchToCurrentThread(r);
  3933   if (NS_FAILED(rv)) {
  3934     return rv;
  3936   request.forget(aRequest);
  3937   return NS_OK;
  3940 bool
  3941 nsDOMDeviceStorage::Default()
  3943   nsString defaultStorageName;
  3944   GetDefaultStorageName(mStorageType, defaultStorageName);
  3945   return mStorageName.Equals(defaultStorageName);
  3948 already_AddRefed<Promise>
  3949 nsDOMDeviceStorage::GetRoot()
  3951   if (!mFileSystem) {
  3952     mFileSystem = new DeviceStorageFileSystem(mStorageType, mStorageName);
  3953     mFileSystem->Init(this);
  3955   return mozilla::dom::Directory::GetRoot(mFileSystem);
  3958 NS_IMETHODIMP
  3959 nsDOMDeviceStorage::GetDefault(bool* aDefault)
  3961   *aDefault = Default();
  3962   return NS_OK;
  3965 NS_IMETHODIMP
  3966 nsDOMDeviceStorage::GetStorageName(nsAString& aStorageName)
  3968   aStorageName = mStorageName;
  3969   return NS_OK;
  3972 already_AddRefed<DOMCursor>
  3973 nsDOMDeviceStorage::Enumerate(const nsAString& aPath,
  3974                               const EnumerationParameters& aOptions,
  3975                               ErrorResult& aRv)
  3977   return EnumerateInternal(aPath, aOptions, false, aRv);
  3980 already_AddRefed<DOMCursor>
  3981 nsDOMDeviceStorage::EnumerateEditable(const nsAString& aPath,
  3982                                       const EnumerationParameters& aOptions,
  3983                                       ErrorResult& aRv)
  3985   return EnumerateInternal(aPath, aOptions, true, aRv);
  3989 already_AddRefed<DOMCursor>
  3990 nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
  3991                                       const EnumerationParameters& aOptions,
  3992                                       bool aEditable, ErrorResult& aRv)
  3994   MOZ_ASSERT(NS_IsMainThread());
  3996   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  3997   if (!win) {
  3998     aRv.Throw(NS_ERROR_UNEXPECTED);
  3999     return nullptr;
  4002   PRTime since = 0;
  4003   if (aOptions.mSince.WasPassed() && !aOptions.mSince.Value().IsUndefined()) {
  4004     since = PRTime(aOptions.mSince.Value().TimeStamp());
  4007   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  4008                                                           mStorageName,
  4009                                                           aPath,
  4010                                                           EmptyString());
  4011   dsf->SetEditable(aEditable);
  4013   nsRefPtr<nsDOMDeviceStorageCursor> cursor
  4014     = new nsDOMDeviceStorageCursor(win, mPrincipal, dsf, since);
  4015   nsRefPtr<DeviceStorageCursorRequest> r
  4016     = new DeviceStorageCursorRequest(cursor);
  4018   if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
  4019     r->Allow(JS::UndefinedHandleValue);
  4020     return cursor.forget();
  4023   if (XRE_GetProcessType() == GeckoProcessType_Content) {
  4024     // because owner implements nsITabChild, we can assume that it is
  4025     // the one and only TabChild.
  4026     TabChild* child = TabChild::GetFrom(win->GetDocShell());
  4027     if (!child) {
  4028       return cursor.forget();
  4031     // Retain a reference so the object isn't deleted without IPDL's knowledge.
  4032     // Corresponding release occurs in DeallocPContentPermissionRequest.
  4033     r->AddRef();
  4035     nsCString type;
  4036     aRv = DeviceStorageTypeChecker::GetPermissionForType(mStorageType, type);
  4037     if (aRv.Failed()) {
  4038       return nullptr;
  4040     nsTArray<PermissionRequest> permArray;
  4041     nsTArray<nsString> emptyOptions;
  4042     permArray.AppendElement(PermissionRequest(type,
  4043                                               NS_LITERAL_CSTRING("read"),
  4044                                               emptyOptions));
  4045     child->SendPContentPermissionRequestConstructor(r,
  4046                                                     permArray,
  4047                                                     IPC::Principal(mPrincipal));
  4049     r->Sendprompt();
  4051     return cursor.forget();
  4054   nsCOMPtr<nsIContentPermissionPrompt> prompt
  4055     = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
  4056   if (prompt) {
  4057     prompt->Prompt(r);
  4060   return cursor.forget();
  4063 #ifdef MOZ_WIDGET_GONK
  4064 void
  4065 nsDOMDeviceStorage::DispatchMountChangeEvent(nsAString& aVolumeStatus)
  4067   if (aVolumeStatus == mLastStatus) {
  4068     // We've already sent this status, don't bother sending it again.
  4069     return;
  4071   mLastStatus = aVolumeStatus;
  4073   nsCOMPtr<nsIDOMEvent> event;
  4074   NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this,
  4075                                     nullptr, nullptr);
  4077   nsCOMPtr<nsIDOMDeviceStorageChangeEvent> ce = do_QueryInterface(event);
  4078   nsresult rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"),
  4079                                                  true, false,
  4080                                                  mStorageName,
  4081                                                  aVolumeStatus);
  4082   if (NS_FAILED(rv)) {
  4083     return;
  4086   bool ignore;
  4087   DispatchEvent(ce, &ignore);
  4089 #endif
  4091 NS_IMETHODIMP
  4092 nsDOMDeviceStorage::Observe(nsISupports *aSubject,
  4093                             const char *aTopic,
  4094                             const char16_t *aData)
  4096   MOZ_ASSERT(NS_IsMainThread());
  4098   if (!strcmp(aTopic, "file-watcher-update")) {
  4100     DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
  4101     Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
  4102     return NS_OK;
  4104   if (!strcmp(aTopic, "disk-space-watcher")) {
  4105     // 'disk-space-watcher' notifications are sent when there is a modification
  4106     // of a file in a specific location while a low device storage situation
  4107     // exists or after recovery of a low storage situation. For Firefox OS,
  4108     // these notifications are specific for apps storage.
  4109     nsRefPtr<DeviceStorageFile> file =
  4110       new DeviceStorageFile(mStorageType, mStorageName);
  4111     if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "full")) {
  4112       Notify("low-disk-space", file);
  4113     } else if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "free")) {
  4114       Notify("available-disk-space", file);
  4116     return NS_OK;
  4119 #ifdef MOZ_WIDGET_GONK
  4120   else if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
  4121     // We invalidate the used space cache for the volume that actually changed
  4122     // state.
  4123     nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
  4124     if (!vol) {
  4125       return NS_OK;
  4127     nsString volName;
  4128     vol->GetName(volName);
  4130     DeviceStorageUsedSpaceCache* usedSpaceCache
  4131       = DeviceStorageUsedSpaceCache::CreateOrGet();
  4132     MOZ_ASSERT(usedSpaceCache);
  4133     usedSpaceCache->Invalidate(volName);
  4135     if (!volName.Equals(mStorageName)) {
  4136       // Not our volume - we can ignore.
  4137       return NS_OK;
  4140     DeviceStorageFile dsf(mStorageType, mStorageName);
  4141     nsString status;
  4142     dsf.GetStatus(status);
  4143     DispatchMountChangeEvent(status);
  4144     return NS_OK;
  4146 #endif
  4147   return NS_OK;
  4150 nsresult
  4151 nsDOMDeviceStorage::Notify(const char* aReason, DeviceStorageFile* aFile)
  4153   if (!mAllowedToWatchFile) {
  4154     return NS_OK;
  4157   if (!mStorageType.Equals(aFile->mStorageType) ||
  4158       !mStorageName.Equals(aFile->mStorageName)) {
  4159     // Ignore this
  4160     return NS_OK;
  4163   nsCOMPtr<nsIDOMEvent> event;
  4164   NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), this,
  4165                                     nullptr, nullptr);
  4167   nsCOMPtr<nsIDOMDeviceStorageChangeEvent> ce = do_QueryInterface(event);
  4169   nsString reason;
  4170   reason.AssignWithConversion(aReason);
  4172   nsString fullPath;
  4173   aFile->GetFullPath(fullPath);
  4174   nsresult rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"),
  4175                                                  true, false, fullPath,
  4176                                                  reason);
  4177   NS_ENSURE_SUCCESS(rv, rv);
  4179   bool ignore;
  4180   DispatchEvent(ce, &ignore);
  4181   return NS_OK;
  4184 NS_IMETHODIMP
  4185 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
  4186                                      nsIDOMEventListener *aListener,
  4187                                      bool aUseCapture,
  4188                                      bool aWantsUntrusted,
  4189                                      uint8_t aArgc)
  4191   MOZ_ASSERT(NS_IsMainThread());
  4193   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  4194   if (!win) {
  4195     return NS_ERROR_UNEXPECTED;
  4198   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  4199   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  4200                                                           mStorageName);
  4201   nsCOMPtr<nsIRunnable> r
  4202     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
  4203                                win, mPrincipal, dsf, request, this);
  4204   nsresult rv = NS_DispatchToCurrentThread(r);
  4205   if (NS_WARN_IF(NS_FAILED(rv))) {
  4206     return rv;
  4209   return DOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
  4210                                                 aWantsUntrusted, aArgc);
  4213 void
  4214 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
  4215                                      EventListener *aListener,
  4216                                      bool aUseCapture,
  4217                                      const Nullable<bool>& aWantsUntrusted,
  4218                                      ErrorResult& aRv)
  4220   MOZ_ASSERT(NS_IsMainThread());
  4222   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
  4223   if (!win) {
  4224     aRv.Throw(NS_ERROR_UNEXPECTED);
  4225     return;
  4228   nsRefPtr<DOMRequest> request = new DOMRequest(win);
  4229   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
  4230                                                           mStorageName);
  4231   nsCOMPtr<nsIRunnable> r
  4232     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
  4233                                win, mPrincipal, dsf, request, this);
  4234   nsresult rv = NS_DispatchToCurrentThread(r);
  4235   if (NS_WARN_IF(NS_FAILED(rv))) {
  4236     return;
  4238   DOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
  4239                                          aWantsUntrusted, aRv);
  4242 NS_IMETHODIMP
  4243 nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
  4244                                            nsIDOMEventListener *aListener,
  4245                                            bool aUseCapture,
  4246                                            bool aWantsUntrusted,
  4247                                            uint8_t aArgc)
  4249   if (!mIsWatchingFile) {
  4250     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  4251     obs->AddObserver(this, "file-watcher-update", false);
  4252     mIsWatchingFile = true;
  4255   return nsDOMDeviceStorage::AddEventListener(aType, aListener, aUseCapture,
  4256                                               aWantsUntrusted, aArgc);
  4259 NS_IMETHODIMP
  4260 nsDOMDeviceStorage::RemoveEventListener(const nsAString & aType,
  4261                                         nsIDOMEventListener *aListener,
  4262                                         bool aUseCapture)
  4264   DOMEventTargetHelper::RemoveEventListener(aType, aListener, false);
  4266   if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) {
  4267     mIsWatchingFile = false;
  4268     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  4269     obs->RemoveObserver(this, "file-watcher-update");
  4271   return NS_OK;
  4274 void
  4275 nsDOMDeviceStorage::RemoveEventListener(const nsAString& aType,
  4276                                         EventListener* aListener,
  4277                                         bool aCapture,
  4278                                         ErrorResult& aRv)
  4280   DOMEventTargetHelper::RemoveEventListener(aType, aListener, aCapture, aRv);
  4282   if (mIsWatchingFile && !HasListenersFor(nsGkAtoms::onchange)) {
  4283     mIsWatchingFile = false;
  4284     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  4285     obs->RemoveObserver(this, "file-watcher-update");
  4289 NS_IMETHODIMP
  4290 nsDOMDeviceStorage::RemoveSystemEventListener(const nsAString & aType,
  4291                                               nsIDOMEventListener *aListener,
  4292                                               bool aUseCapture)
  4294   return nsDOMDeviceStorage::RemoveEventListener(aType, aListener, aUseCapture);
  4297 NS_IMETHODIMP
  4298 nsDOMDeviceStorage::DispatchEvent(nsIDOMEvent *aEvt,
  4299                                   bool *aRetval)
  4301   return DOMEventTargetHelper::DispatchEvent(aEvt, aRetval);
  4304 EventTarget*
  4305 nsDOMDeviceStorage::GetTargetForDOMEvent()
  4307   return DOMEventTargetHelper::GetTargetForDOMEvent();
  4310 EventTarget *
  4311 nsDOMDeviceStorage::GetTargetForEventTargetChain()
  4313   return DOMEventTargetHelper::GetTargetForEventTargetChain();
  4316 nsresult
  4317 nsDOMDeviceStorage::PreHandleEvent(EventChainPreVisitor& aVisitor)
  4319   return DOMEventTargetHelper::PreHandleEvent(aVisitor);
  4322 nsresult
  4323 nsDOMDeviceStorage::WillHandleEvent(EventChainPostVisitor& aVisitor)
  4325   return DOMEventTargetHelper::WillHandleEvent(aVisitor);
  4328 nsresult
  4329 nsDOMDeviceStorage::PostHandleEvent(EventChainPostVisitor& aVisitor)
  4331   return DOMEventTargetHelper::PostHandleEvent(aVisitor);
  4334 nsresult
  4335 nsDOMDeviceStorage::DispatchDOMEvent(WidgetEvent* aEvent,
  4336                                      nsIDOMEvent* aDOMEvent,
  4337                                      nsPresContext* aPresContext,
  4338                                      nsEventStatus* aEventStatus)
  4340   return DOMEventTargetHelper::DispatchDOMEvent(aEvent,
  4341                                                 aDOMEvent,
  4342                                                 aPresContext,
  4343                                                 aEventStatus);
  4346 EventListenerManager*
  4347 nsDOMDeviceStorage::GetOrCreateListenerManager()
  4349   return DOMEventTargetHelper::GetOrCreateListenerManager();
  4352 EventListenerManager*
  4353 nsDOMDeviceStorage::GetExistingListenerManager() const
  4355   return DOMEventTargetHelper::GetExistingListenerManager();
  4358 nsIScriptContext *
  4359 nsDOMDeviceStorage::GetContextForEventHandlers(nsresult *aRv)
  4361   return DOMEventTargetHelper::GetContextForEventHandlers(aRv);
  4364 JSContext *
  4365 nsDOMDeviceStorage::GetJSContextForEventHandlers()
  4367   return DOMEventTargetHelper::GetJSContextForEventHandlers();
  4370 NS_IMPL_EVENT_HANDLER(nsDOMDeviceStorage, change)

mercurial