Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 14 Sep 2012 16:04:41 -0400
changeset 107218 d3d5b29a2a0dc3b486d9243212b164a19245af4b
parent 107217 2a64d6b2f11049369920ec2ce4e2fa5830c9ae73 (current diff)
parent 107153 2a64ad9f584ed1f7b268366318d7fc5a6ceef9c9 (diff)
child 107219 7689bfe3675d8afcf690d93208364d08b5f11477
push id74
push usershu@rfrn.org
push dateTue, 18 Sep 2012 19:23:47 +0000
milestone18.0a1
Merge m-c to inbound.
dom/bluetooth/BluetoothService.h
dom/bluetooth/linux/BluetoothDBusService.cpp
dom/bluetooth/linux/BluetoothDBusService.h
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -197,25 +197,16 @@ public:
    *
    * @return True if path set correctly, false otherwise
    */
   virtual bool
   GetDevicePath(const nsAString& aAdapterPath,
                 const nsAString& aDeviceAddress,
                 nsAString& aDevicePath) = 0;
 
-  virtual bool
-  AddReservedServicesInternal(const nsAString& aAdapterPath,
-                              const nsTArray<uint32_t>& aServices,
-                              nsTArray<uint32_t>& aServiceHandlesContainer) = 0;
-
-  virtual bool
-  RemoveReservedServicesInternal(const nsAString& aAdapterPath,
-                                 const nsTArray<uint32_t>& aServiceHandles) = 0;
-
   virtual nsresult
   CreatePairedDeviceInternal(const nsAString& aAdapterPath,
                              const nsAString& aAddress,
                              int aTimeout,
                              BluetoothReplyRunnable* aRunnable) = 0;
 
   virtual nsresult
   RemoveDeviceInternal(const nsAString& aAdapterPath,
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
@@ -165,35 +165,16 @@ BluetoothServiceChildProcess::GetDeviceP
   path.Append(aDeviceAddress);
   path.ReplaceChar(':', '_');
 
   aDevicePath = path;
 
   return NS_OK;
 }
 
-bool
-BluetoothServiceChildProcess::AddReservedServicesInternal(
-                                   const nsAString& aAdapterPath,
-                                   const nsTArray<uint32_t>& aServices,
-                                   nsTArray<uint32_t>& aServiceHandlesContainer)
-{
-  MOZ_NOT_REACHED("This function isn't used!");
-  return false;
-}
-
-bool
-BluetoothServiceChildProcess::RemoveReservedServicesInternal(
-                                      const nsAString& aAdapterPath,
-                                      const nsTArray<uint32_t>& aServiceHandles)
-{
-  MOZ_NOT_REACHED("This function isn't used!");
-  return false;
-}
-
 nsresult
 BluetoothServiceChildProcess::CreatePairedDeviceInternal(
                                               const nsAString& aAdapterPath,
                                               const nsAString& aAddress,
                                               int aTimeout,
                                               BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable,
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
@@ -67,27 +67,16 @@ public:
               const BluetoothNamedValue& aValue,
               BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual bool
   GetDevicePath(const nsAString& aAdapterPath,
                 const nsAString& aDeviceAddress,
                 nsAString& aDevicePath) MOZ_OVERRIDE;
 
-  virtual bool
-  AddReservedServicesInternal(const nsAString& aAdapterPath,
-                              const nsTArray<uint32_t>& aServices,
-                              nsTArray<uint32_t>& aServiceHandlesContainer)
-                              MOZ_OVERRIDE;
-
-  virtual bool
-  RemoveReservedServicesInternal(const nsAString& aAdapterPath,
-                                 const nsTArray<uint32_t>& aServiceHandles)
-                                 MOZ_OVERRIDE;
-
   virtual nsresult
   CreatePairedDeviceInternal(const nsAString& aAdapterPath,
                              const nsAString& aAddress,
                              int aTimeout,
                              BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual nsresult
   RemoveDeviceInternal(const nsAString& aAdapterPath,
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -564,24 +564,19 @@ AddReservedServices(const nsAString& aAd
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   nsTArray<uint32_t> uuids;
 
   uuids.AppendElement((uint32_t)(BluetoothServiceUuid::HandsfreeAG >> 32));
   uuids.AppendElement((uint32_t)(BluetoothServiceUuid::HeadsetAG >> 32));
 
-  BluetoothService* bs = BluetoothService::Get();
-  if (!bs) {
-    NS_WARNING("BluetoothService not available!");
-    return ;
-  }
-
   sServiceHandles.Clear();
-  bs->AddReservedServicesInternal(aAdapterPath, uuids, sServiceHandles);
+  BluetoothDBusService::AddReservedServicesInternal(aAdapterPath, uuids,
+                                                    sServiceHandles);
 }
 
 void
 RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable,
                 UnpackFunc aFunc)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   nsRefPtr<BluetoothReplyRunnable> replyRunnable =
@@ -1498,16 +1493,17 @@ ExtractHandles(DBusMessage *aReply, nsTA
         aOutHandles.AppendElement(handles[i]);
       }
     }
   } else {
     LOG_AND_FREE_DBUS_ERROR(&err);
   }
 }
 
+// static
 bool
 BluetoothDBusService::AddReservedServicesInternal(const nsAString& aAdapterPath,
                                                   const nsTArray<uint32_t>& aServices, 
                                                   nsTArray<uint32_t>& aServiceHandlesContainer)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   int length = aServices.Length();
@@ -1525,16 +1521,17 @@ BluetoothDBusService::AddReservedService
     LOG("Null DBus message. Couldn't extract handles.");
     return false;
   }
 
   ExtractHandles(reply, aServiceHandlesContainer);
   return true;
 }
 
+// static
 bool
 BluetoothDBusService::RemoveReservedServicesInternal(const nsAString& aAdapterPath,
                                                      const nsTArray<uint32_t>& aServiceHandles)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   int length = aServiceHandles.Length();
   if (length == 0) return false;
--- a/dom/bluetooth/linux/BluetoothDBusService.h
+++ b/dom/bluetooth/linux/BluetoothDBusService.h
@@ -42,22 +42,22 @@ public:
               const nsAString& aPath,
               const BluetoothNamedValue& aValue,
               BluetoothReplyRunnable* aRunnable);
   virtual bool
   GetDevicePath(const nsAString& aAdapterPath,
                 const nsAString& aDeviceAddress,
                 nsAString& aDevicePath);
 
-  virtual bool
+  static bool
   AddReservedServicesInternal(const nsAString& aAdapterPath,
                               const nsTArray<uint32_t>& aServices,
                               nsTArray<uint32_t>& aServiceHandlesContainer);
 
-  virtual bool
+  static bool
   RemoveReservedServicesInternal(const nsAString& aAdapterPath,
                                  const nsTArray<uint32_t>& aServiceHandles);
 
   virtual nsresult
   GetSocketViaService(const nsAString& aObjectPath,
                       const nsAString& aService,
                       int aType,
                       bool aAuth,
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -45,19 +45,16 @@ private:
 
   nsresult EnumerateInternal(const JS::Value & aName,
                              const JS::Value & aOptions,
                              JSContext* aCx,
                              uint8_t aArgc, 
                              bool aEditable, 
                              nsIDOMDeviceStorageCursor** aRetval);
 
-  static bool IsMimeTypeCorrectForStorageType(nsAString& aType,
-					      nsIDOMBlob* aBlob);
-
   nsString mStorageType;
   nsCOMPtr<nsIFile> mRootDirectory;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   bool mIsWatchingFile;
   bool mAllowedToWatchFile;
 
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -308,23 +308,23 @@ DeviceStorageRequestParent::StatFileEven
 
 nsresult
 DeviceStorageRequestParent::StatFileEvent::CancelableRun()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIRunnable> r;
   uint64_t diskUsage = 0;
-  DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage);
+  DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage, mFile->mStorageType);
   int64_t freeSpace = 0;
   nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
   if (NS_FAILED(rv)) {
     freeSpace = 0;
   }
-  
+
   r = new PostStatResultEvent(mParent, freeSpace, diskUsage);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::ReadFileEvent::ReadFileEvent(DeviceStorageRequestParent* aParent,
                                                          DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -3,19 +3,21 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/PContentPermissionRequestChild.h"
+#include "mozilla/ClearOnShutdown.h"
 
 #include "nsDeviceStorage.h"
 
+#include "nsAutoPtr.h"
 #include "nsDOMEvent.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIFile.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIDOMFile.h"
 #include "nsDOMBlobBuilder.h"
@@ -45,22 +47,144 @@
 #undef CreateEvent
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
 #endif
 
 #define DEVICESTORAGE_PROPERTIES "chrome://global/content/devicestorage.properties"
+#define DEVICESTORAGE_PICTURES   "pictures"
+#define DEVICESTORAGE_VIDEOS     "videos"
+#define DEVICESTORAGE_MUSIC      "music"
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
 
 #include "nsDirectoryServiceDefs.h"
 
+nsAutoPtr<DeviceStorageTypeChecker> DeviceStorageTypeChecker::sDeviceStorageTypeChecker;
+
+DeviceStorageTypeChecker::DeviceStorageTypeChecker()
+{
+}
+
+DeviceStorageTypeChecker::~DeviceStorageTypeChecker()
+{
+}
+
+DeviceStorageTypeChecker*
+DeviceStorageTypeChecker::CreateOrGet()
+{
+  if (sDeviceStorageTypeChecker) {
+    return sDeviceStorageTypeChecker;
+  }
+
+  NS_ASSERTION(NS_IsMainThread(), "This can only be created on the main thread!");
+
+  nsCOMPtr<nsIStringBundleService> stringService = mozilla::services::GetStringBundleService();
+  if (!stringService) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIStringBundle> filterBundle;
+  if (NS_FAILED(stringService->CreateBundle(DEVICESTORAGE_PROPERTIES, getter_AddRefs(filterBundle)))) {
+    return nullptr;
+  }
+
+  DeviceStorageTypeChecker* result = new DeviceStorageTypeChecker();
+  result->InitFromBundle(filterBundle);
+
+  sDeviceStorageTypeChecker = result;
+  ClearOnShutdown(&sDeviceStorageTypeChecker);
+  return result;
+}
+
+void
+DeviceStorageTypeChecker::InitFromBundle(nsIStringBundle* aBundle)
+{
+  aBundle->GetStringFromName(NS_ConvertASCIItoUTF16(DEVICESTORAGE_PICTURES).get(), getter_Copies(mPicturesExtensions));
+  aBundle->GetStringFromName(NS_ConvertASCIItoUTF16(DEVICESTORAGE_MUSIC).get(), getter_Copies(mMusicExtensions));
+  aBundle->GetStringFromName(NS_ConvertASCIItoUTF16(DEVICESTORAGE_VIDEOS).get(), getter_Copies(mVideosExtensions));
+}
+
+
+bool
+DeviceStorageTypeChecker::Check(const nsAString& aType, nsIDOMBlob* aBlob)
+{
+  NS_ASSERTION(aBlob, "Calling Check without a blob");
+
+  nsString mimeType;
+  if (NS_FAILED(aBlob->GetType(mimeType))) {
+    return false;
+  }
+
+  if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
+    return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/"));
+  }
+
+  if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
+    return StringBeginsWith(mimeType, NS_LITERAL_STRING("video/"));
+  }
+
+  if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
+    return StringBeginsWith(mimeType, NS_LITERAL_STRING("audio/"));
+  }
+
+  return false;
+}
+
+bool
+DeviceStorageTypeChecker::Check(const nsAString& aType, nsIFile* aFile)
+{
+  NS_ASSERTION(aFile, "Calling Check without a file");
+
+  nsString path;
+  aFile->GetPath(path);
+
+  int32_t dotIdx = path.RFindChar(PRUnichar('.'));
+  if (dotIdx == kNotFound) {
+    return false;
+  }
+
+  nsAutoString extensionMatch;
+  extensionMatch.AssignLiteral("*");
+  extensionMatch.Append(Substring(path, dotIdx));
+  extensionMatch.AppendLiteral(";");
+
+  if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
+    return FindInReadable(extensionMatch, mPicturesExtensions);
+  }
+
+  if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
+    return FindInReadable(extensionMatch, mVideosExtensions);
+  }
+
+  if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
+    return FindInReadable(extensionMatch, mMusicExtensions);
+  }
+
+  return false;
+}
+
+nsresult
+DeviceStorageTypeChecker::GetPermissionForType(const nsAString& aType, nsACString& aPermissionResult)
+{
+  if (!aType.EqualsLiteral(DEVICESTORAGE_PICTURES) &&
+      !aType.EqualsLiteral(DEVICESTORAGE_VIDEOS) &&
+      !aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
+    // unknown type
+    return NS_ERROR_FAILURE;
+  }
+
+  aPermissionResult.AssignLiteral("device-storage:");
+  aPermissionResult.Append(NS_ConvertUTF16toUTF8(aType));
+  return NS_OK;
+}
+
 class IOEventComplete : public nsRunnable
 {
 public:
   IOEventComplete(DeviceStorageFile *aFile, const char *aType)
     : mFile(aFile)
     , mType(aType)
   {
   }
@@ -78,39 +202,45 @@ public:
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsCString mType;
 };
 
 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
-				     nsIFile* aFile,
-				     const nsAString& aPath)
+                                     nsIFile* aFile,
+                                     const nsAString& aPath)
   : mPath(aPath)
   , mStorageType(aStorageType)
   , mEditable(false)
 {
   NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
   // always take a clone
   nsCOMPtr<nsIFile> file;
   aFile->Clone(getter_AddRefs(mFile));
 
   AppendRelativePath();
   NormalizeFilePath();
+
+  DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet();
+  NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
 }
 
 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile)
   : mStorageType(aStorageType)
   , mEditable(false)
 {
   NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
   // always take a clone
   nsCOMPtr<nsIFile> file;
   aFile->Clone(getter_AddRefs(mFile));
+
+  DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet();
+  NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
 }
 
 void
 DeviceStorageFile::SetPath(const nsAString& aPath) {
   mPath.Assign(aPath);
   NormalizeFilePath();
 }
 
@@ -145,55 +275,16 @@ DeviceStorageFile::IsSafePath()
         PL_strcmp(token, ".") == 0 ||
         PL_strcmp(token, "..") == 0 ) {
       return false;
     }
   }
   return true;
 }
 
-bool
-DeviceStorageFile::IsType(nsAString& aType)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // String bundles are cached by the bundle service.
-  nsCOMPtr<nsIStringBundleService> stringService = mozilla::services::GetStringBundleService();
-  if (!stringService) {
-    return false;
-  }
-
-  nsCOMPtr<nsIStringBundle> filterBundle;
-  if (NS_FAILED(stringService->CreateBundle(DEVICESTORAGE_PROPERTIES,
-					    getter_AddRefs(filterBundle)))) {
-    return false;
-  }
-
-  nsString path;
-  mFile->GetPath(path);
-
-  int32_t dotIdx = path.RFindChar(PRUnichar('.'));
-  if (dotIdx == kNotFound) {
-    return false;
-  }
-
-  nsAutoString extensionMatch;
-  extensionMatch.AssignASCII("*");
-  extensionMatch.Append(Substring(path, dotIdx));
-  extensionMatch.AppendASCII(";");
-
-  nsString extensionListStr;
-  if (NS_FAILED(filterBundle->GetStringFromName(aType.BeginReading(),
-						getter_Copies(extensionListStr)))) {
-    return false;
-  }
-
-  return FindInReadable(extensionMatch, extensionListStr);
-}
-
 void
 DeviceStorageFile::NormalizeFilePath() {
 #if defined(XP_WIN)
   PRUnichar* cur = mPath.BeginWriting();
   PRUnichar* end = mPath.EndWriting();
   for (; cur < end; ++cur) {
     if (PRUnichar('\\') == *cur)
       *cur = PRUnichar('/');
@@ -255,18 +346,18 @@ DeviceStorageFile::Write(nsIInputStream*
   if (!bufferedOutputStream) {
     return NS_ERROR_FAILURE;
   }
 
   rv = NS_OK;
   while (bufSize) {
     uint32_t wrote;
     rv = bufferedOutputStream->WriteFrom(aInputStream,
-					 static_cast<uint32_t>(NS_MIN<uint64_t>(bufSize, PR_UINT32_MAX)),
-					 &wrote);
+                                         static_cast<uint32_t>(NS_MIN<uint64_t>(bufSize, PR_UINT32_MAX)),
+                                         &wrote);
     if (NS_FAILED(rv)) {
       break;
     }
     bufSize -= wrote;
   }
 
   iocomplete = new IOEventComplete(this, "modified");
   NS_DispatchToMainThread(iocomplete);
@@ -400,33 +491,38 @@ DeviceStorageFile::collectFilesInternal(
       nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, f);
       dsf->SetPath(newPath);
       aFiles.AppendElement(dsf);
     }
   }
 }
 
 void
-DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar)
+DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar, const nsAString& aStorageType)
 {
   if (!aFile) {
     return;
   }
 
   nsresult rv;
   nsCOMPtr<nsISimpleEnumerator> e;
   rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
 
   if (NS_FAILED(rv) || !e) {
     return;
   }
 
   nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
   NS_ASSERTION(files, "GetDirectoryEntries must return a nsIDirectoryEnumerator");
 
+  DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet();
+  if (!typeChecker) {
+    return;
+  }
+
   nsCOMPtr<nsIFile> f;
   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
     bool isDir;
     rv = f->IsDirectory(&isDir);
     if (NS_FAILED(rv)) {
       continue;
     }
 
@@ -436,26 +532,32 @@ DeviceStorageFile::DirectoryDiskUsage(ns
       continue;
     }
 
     bool isLink;
     rv = f->IsSymlink(&isLink);
     if (NS_FAILED(rv)) {
       continue;
     }
+
     if (isLink) {
       // for now, lets just totally ignore symlinks.
       NS_WARNING("DirectoryDiskUsage ignores symlinks");
     } else if (isDir) {
-      DirectoryDiskUsage(f, aSoFar);
+      DirectoryDiskUsage(f, aSoFar, aStorageType);
     } else if (isFile) {
+
+      if (!typeChecker->Check(aStorageType, f)) {
+        continue;
+      }
+
       int64_t size;
       rv = f->GetFileSize(&size);
       if (NS_SUCCEEDED(rv)) {
-	*aSoFar += size;
+        *aSoFar += size;
       }
     }
   }
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(DeviceStorageFile)
 
 #ifdef MOZ_WIDGET_GONK
@@ -473,21 +575,21 @@ GetSDCardStatus(nsAString& aState) {
 
   int32_t state;
   nsresult rv = vol->GetState(&state);
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
   }
 
   if (state == nsIVolume::STATE_MOUNTED) {
-    aState.AssignASCII("available");
+    aState.AssignLiteral("available");
   } else if (state == nsIVolume::STATE_SHARED || state == nsIVolume::STATE_SHAREDMNT) {
-    aState.AssignASCII("shared");
+    aState.AssignLiteral("shared");
   } else {
-    aState.AssignASCII("unavailable");
+    aState.AssignLiteral("unavailable");
   }
   return NS_OK;
 }
 
 static void
 RegisterForSDCardChanges(nsIObserver* aObserver)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
@@ -771,20 +873,25 @@ ContinueCursorEvent::Run() {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   jsval val = JSVAL_NULL;
 
   nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
   nsString cursorStorageType;
   cursor->GetStorageType(cursorStorageType);
 
+  DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet();
+  if (!typeChecker) {
+    return NS_ERROR_FAILURE;
+  }
+
   while (cursor->mFiles.Length() > 0) {
     nsRefPtr<DeviceStorageFile> file = cursor->mFiles[0];
     cursor->mFiles.RemoveElementAt(0);
-    if (!file->IsType(cursorStorageType)) {
+    if (!typeChecker->Check(cursorStorageType, file->mFile)) {
       continue;
     }
     val = nsIFileToJsval(cursor->GetOwner(), file);
     cursor->mOkToCallContinue = true;
     break;
   }
 
   mRequest->FireSuccess(val);
@@ -864,18 +971,17 @@ void
 nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType)
 {
   aType = mFile->mStorageType;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorageCursor::GetType(nsACString & aType)
 {
-  aType = "device-storage";
-  return NS_OK;
+  return DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, aType);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorageCursor::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
 {
   NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
   return NS_OK;
 }
@@ -1003,18 +1109,18 @@ public:
     if (NS_FAILED(rv)) {
       state.Assign(NS_LITERAL_STRING("unavailable"));
     }
 #endif
 
     nsRefPtr<nsIDOMDeviceStorageStat> domstat = new nsDOMDeviceStorageStat(mFreeBytes, mTotalBytes, state);
 
     jsval result = InterfaceToJsval(mRequest->GetOwner(),
-				    domstat,
-				    &NS_GET_IID(nsIDOMDeviceStorageStat));
+                                    domstat,
+                                    &NS_GET_IID(nsIDOMDeviceStorageStat));
 
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
   uint64_t mFreeBytes, mTotalBytes;
@@ -1188,17 +1294,17 @@ public:
 
   ~StatFileEvent() {}
 
   NS_IMETHOD Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
     nsCOMPtr<nsIRunnable> r;
     uint64_t diskUsage = 0;
-    DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage);
+    DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage, mFile->mStorageType);
     int64_t freeSpace = 0;
     nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
     if (NS_FAILED(rv)) {
       freeSpace = 0;
     }
 
     r = new PostStatResultEvent(mRequest, freeSpace, diskUsage);
     NS_DispatchToMainThread(r);
@@ -1271,33 +1377,41 @@ public:
       if (!child) {
         return NS_OK;
       }
 
       // Retain a reference so the object isn't deleted without IPDL's knowledge.
       // Corresponding release occurs in DeallocPContentPermissionRequest.
       AddRef();
 
-      nsCString type = NS_LITERAL_CSTRING("device-storage");
+      nsCString type;
+      nsresult rv = DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
       child->SendPContentPermissionRequestConstructor(this, type, IPC::Principal(mPrincipal));
 
       Sendprompt();
       return NS_OK;
     }
 
     nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
     if (prompt) {
       prompt->Prompt(this);
     }
     return NS_OK;
   }
 
   NS_IMETHOD GetType(nsACString & aType)
   {
-    aType = "device-storage";
+    nsCString type;
+    nsresult rv = DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, aType);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
     return NS_OK;
   }
 
   NS_IMETHOD GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
   {
     NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
     return NS_OK;
   }
@@ -1343,26 +1457,26 @@ public:
       case DEVICE_STORAGE_REQUEST_WRITE:
       {
         if (!mBlob) {
           return NS_ERROR_FAILURE;
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
 
-	  BlobChild* actor = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlob);
-	  if (!actor) {
-	    return NS_ERROR_FAILURE;
-	  }
+          BlobChild* actor = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlob);
+          if (!actor) {
+            return NS_ERROR_FAILURE;
+          }
 
           DeviceStorageAddParams params;
-	  params.blobChild() = actor;
-	  params.type() = mFile->mStorageType;
-	  params.name() = mFile->mPath;
-	  params.fullpath() = fullpath;
+          params.blobChild() = actor;
+          params.type() = mFile->mStorageType;
+          params.name() = mFile->mPath;
+          params.fullpath() = fullpath;
 
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new WriteFileEvent(mBlob, mFile, mRequest);
         break;
       }
@@ -1393,26 +1507,26 @@ public:
       }
 
       case DEVICE_STORAGE_REQUEST_STAT:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
           DeviceStorageStatParams params(mFile->mStorageType, fullpath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
-	  return NS_OK;
+          return NS_OK;
         }
         r = new StatFileEvent(mFile, mRequest);
         break;
       }
 
       case DEVICE_STORAGE_REQUEST_WATCH:
       {
-	mDeviceStorage->mAllowedToWatchFile = true;
-	return NS_OK;
+        mDeviceStorage->mAllowedToWatchFile = true;
+        return NS_OK;
       }
     }
 
     if (r) {
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
     }
@@ -1543,41 +1657,16 @@ nsDOMDeviceStorage::CreateDeviceStorages
                                             nsDOMDeviceStorage** aStore)
 {
   nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage();
   if (NS_SUCCEEDED(storage->Init(aWin, aType))) {
     NS_ADDREF(*aStore = storage);
   }
 }
 
-bool
-nsDOMDeviceStorage::IsMimeTypeCorrectForStorageType(nsAString& aType, nsIDOMBlob* aBlob)
-{
-  NS_ASSERTION(aBlob, "Calling IsMimeTypeCorrectForStorageType without a blob");
-
-  nsString mimeType;
-  if (NS_FAILED(aBlob->GetType(mimeType))) {
-    return false;
-  }
-
-  if (aType.Equals(NS_LITERAL_STRING("pictures"))) {
-    return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/"));
-  }
-
-  if (aType.Equals(NS_LITERAL_STRING("videos"))) {
-    return StringBeginsWith(mimeType, NS_LITERAL_STRING("video/"));
-  }
-
-  if (aType.Equals(NS_LITERAL_STRING("music"))) {
-    return StringBeginsWith(mimeType, NS_LITERAL_STRING("audio/"));
-  }
-
-  return false;
-}
-
 NS_IMETHODIMP
 nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval)
 {
   if (!aBlob) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
@@ -1618,31 +1707,37 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob 
   if (aBlob == nullptr)
     return NS_OK;
 
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet();
+  if (!typeChecker) {
+    return NS_ERROR_FAILURE;
+  }
+
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*_retval = request);
 
   nsCOMPtr<nsIRunnable> r;
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, aPath);
-  if (!dsf->IsType(mStorageType) || !IsMimeTypeCorrectForStorageType(mStorageType, aBlob)) {
+  if (!typeChecker->Check(mStorageType, dsf->mFile) ||
+      !typeChecker->Check(mStorageType, aBlob)) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE, dsf);
   }
   else if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
   }
   else {
     r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_WRITE,
-				 win, mPrincipal, dsf, request, aBlob);
+                                 win, mPrincipal, dsf, request, aBlob);
   }
 
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Get(const JS::Value & aPath,
@@ -1742,20 +1837,20 @@ nsDOMDeviceStorage::Stat(nsIDOMDOMReques
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*aRetval = request);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_STAT,
-						     win,
-						     mPrincipal,
-						     dsf,
-						     request);
+                                                     win,
+                                                     mPrincipal,
+                                                     dsf,
+                                                     request);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Enumerate(const JS::Value & aName,
                              const JS::Value & aOptions,
                              JSContext* aCx,
@@ -1849,17 +1944,21 @@ nsDOMDeviceStorage::EnumerateInternal(co
     TabChild* child = GetTabChildFrom(win->GetDocShell());
     if (!child)
       return NS_OK;
 
     // Retain a reference so the object isn't deleted without IPDL's knowledge.
     // Corresponding release occurs in DeallocPContentPermissionRequest.
     r->AddRef();
 
-    nsCString type = NS_LITERAL_CSTRING("device-storage");
+    nsCString type;
+    nsresult rv = DeviceStorageTypeChecker::GetPermissionForType(mStorageType, type);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
     child->SendPContentPermissionRequestConstructor(r, type, IPC::Principal(mPrincipal));
 
     r->Sendprompt();
 
     return NS_OK;
   }
 
   nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -20,32 +20,56 @@ class nsPIDOMWindow;
 #include "nsIURI.h"
 #include "nsInterfaceHashtable.h"
 #include "nsIPrincipal.h"
 #include "nsString.h"
 #include "nsWeakPtr.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIObserver.h"
+#include "nsIStringBundle.h"
 #include "mozilla/Mutex.h"
 #include "prtime.h"
 #include "DeviceStorage.h"
 
 
 #define POST_ERROR_EVENT_FILE_DOES_NOT_EXIST         "File location doesn't exists"
 #define POST_ERROR_EVENT_FILE_NOT_ENUMERABLE         "File location is not enumerable"
 #define POST_ERROR_EVENT_PERMISSION_DENIED           "Permission Denied"
 #define POST_ERROR_EVENT_ILLEGAL_FILE_NAME           "Illegal file name"
 #define POST_ERROR_EVENT_ILLEGAL_TYPE                "Illegal content type"
 #define POST_ERROR_EVENT_UNKNOWN                     "Unknown"
 #define POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED "Non-string type unsupported"
 #define POST_ERROR_EVENT_NOT_IMPLEMENTED             "Not implemented"
 
 using namespace mozilla::dom;
 
+class DeviceStorageTypeChecker MOZ_FINAL
+{
+public:
+  static DeviceStorageTypeChecker* CreateOrGet();
+
+  DeviceStorageTypeChecker();
+  ~DeviceStorageTypeChecker();
+
+  void InitFromBundle(nsIStringBundle* aBundle);
+
+  bool Check(const nsAString& aType, nsIDOMBlob* aBlob);
+  bool Check(const nsAString& aType, nsIFile* aFile);
+
+  static nsresult GetPermissionForType(const nsAString& aType, nsACString& aPermissionResult);
+
+private:
+  nsString mPicturesExtensions;
+  nsString mVideosExtensions;
+  nsString mMusicExtensions;
+
+  static nsAutoPtr<DeviceStorageTypeChecker> sDeviceStorageTypeChecker;
+};
+
 class DeviceStorageFile MOZ_FINAL
   : public nsISupports {
 public:
   nsCOMPtr<nsIFile> mFile;
   nsString mPath;
   nsString mStorageType;
   bool mEditable;
 
@@ -54,25 +78,24 @@ public:
   void SetPath(const nsAString& aPath);
   void SetEditable(bool aEditable);
 
   NS_DECL_ISUPPORTS
 
   // we want to make sure that the names of file can't reach
   // outside of the type of storage the user asked for.
   bool IsSafePath();
-  bool IsType(nsAString& aType);
 
   nsresult Remove();
   nsresult Write(nsIInputStream* aInputStream);
   nsresult Write(InfallibleTArray<uint8_t>& bits);
   void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRTime aSince = 0);
   void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRTime aSince, nsAString& aRootPath);
 
-  static void DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar);
+  static void DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar, const nsAString& aStorageType);
 
 private:
   void NormalizeFilePath();
   void AppendRelativePath();
 };
 
 class ContinueCursorEvent MOZ_FINAL: public nsRunnable
 {
--- a/dom/devicestorage/test/Makefile.in
+++ b/dom/devicestorage/test/Makefile.in
@@ -8,16 +8,20 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 #  man, our mime database sucks hard.  followup bug # 788273
 #		test_add.html \
 
+# Possible race between the time we write a file, and the
+# time it takes to be reflected by statfs().  Bug # 791287
+#                 test_diskSpace.html \
+
 MOCHITEST_FILES	= \
 		test_sanity.html \
 		test_addCorrectType.html \
 		test_basic.html \
 		test_enumerate.html \
 		test_enumerateMultipleContinue.html \
 		test_overwrite.html \
 		test_dotdot.html \
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_diskSpace.html
@@ -0,0 +1,101 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html> <!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717103
+-->
+<head>
+  <title>Test for the device storage API </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="devicestorage_common.js"></script>
+
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+devicestorage_setup();
+
+
+var freeBytes = -1;
+var stats = 0;
+
+function stat(s, file_list_length) {
+  if (freeBytes == -1) {
+    freeBytes = s.target.result.freeBytes;
+  }
+
+  ok(freeBytes == s.target.result.freeBytes, "Free bytes should be the same");
+  ok(file_list_length * 1024 == s.target.result.totalBytes, "space taken up by files should match")
+
+  stats = stats + 1;
+
+  if (stats == 2) {
+    devicestorage_cleanup();
+  }
+}
+
+function addSuccess(e) {
+  added = added - 1;
+
+  if (added == 0) {
+    request = pictures.stat();
+    request.onsuccess = function(s) {stat(s, picture_files.length)};
+
+    request = videos.stat();
+    request.onsuccess = function(s) {stat(s, video_files.length)};
+
+    request = music.stat();
+    request.onsuccess = function(s) {stat(s, music_files.length)};
+  }
+}
+
+function addError(e) {
+  ok(false, "addError was called : " + e.target.error.name);
+  devicestorage_cleanup();
+}
+
+ok(true, "hi");
+
+var pictures = navigator.getDeviceStorage("pictures");
+var picture_files = [ "a.png", "b.png", "c.png", "d.png", "e.png" ];
+
+var videos = navigator.getDeviceStorage("videos");
+var video_files = [ "a.ogv", "b.ogv" ];
+
+var music = navigator.getDeviceStorage("music");
+var music_files = [ "a.mp3", "b.mp3", "c.mp3" ];
+
+var added = picture_files.length + video_files.length + music_files.length;
+
+for (var i=0; i < picture_files.length; i++) {
+ request = pictures.addNamed(createRandomBlob('image/png'), picture_files[i]);
+ request.onsuccess = addSuccess;
+ request.onerror = addError;
+}
+
+for (var i=0; i < video_files.length; i++) {
+ request = videos.addNamed(createRandomBlob('video/ogv'), video_files[i]);
+ request.onsuccess = addSuccess;
+ request.onerror = addError;
+}
+
+for (var i=0; i < music_files.length; i++) {
+ request = music.addNamed(createRandomBlob('audio/mp3'), music_files[i]);
+ request.onsuccess = addSuccess;
+ request.onerror = addError;
+}
+
+</script>
+</pre>
+</body>
+</html>
+