Bug 838038 - Pt 2 - Add support for multiple device storage objects. r=dougt, a=leo+
authorDave Hylands <dhylands@mozilla.com>
Tue, 02 Apr 2013 12:52:21 -0700
changeset 119030 317c9fa4879085f81c1a47d13e225fffc0e2a6ff
parent 119029 2764a587c074ad3444c6e6d47d54d416ec29e614
child 119031 b644771c03f5aa28d01e798b3d5d29b22c9f04d5
push id649
push userryanvm@gmail.com
push dateWed, 10 Apr 2013 12:34:26 +0000
reviewersdougt, leo
bugs838038
milestone18.0
Bug 838038 - Pt 2 - Add support for multiple device storage objects. r=dougt, a=leo+
dom/base/Navigator.cpp
dom/devicestorage/DeviceStorage.h
dom/devicestorage/DeviceStorageRequestParent.cpp
dom/devicestorage/DeviceStorageRequestParent.h
dom/devicestorage/nsDeviceStorage.cpp
dom/devicestorage/nsDeviceStorage.h
dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
dom/interfaces/devicestorage/nsIDOMNavigatorDeviceStorage.idl
dom/ipc/PContent.ipdl
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -960,35 +960,81 @@ Navigator::MozIsLocallyAvailable(const n
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorDeviceStorage
 //*****************************************************************************
 
 NS_IMETHODIMP Navigator::GetDeviceStorage(const nsAString &aType, nsIDOMDeviceStorage** _retval)
 {
+  // We're going to obsolete getDeviceStorage, but want to leave it in for
+  // compatability right now. So we do essentially the same thing as GetDeviceStorages
+  // but only take the first element of the array.
+
+  NS_WARNING("navigator.getDeviceStorage is deprecated. Returning navigator.getDeviceStorages[0]");
+
+  nsCOMPtr<nsIVariant> variantArray;
+
+  nsresult rv = GetDeviceStorages(aType, getter_AddRefs(variantArray));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint16_t dataType;
+  variantArray->GetDataType(&dataType);
+
+  if (dataType != nsIDataType::VTYPE_ARRAY) {
+    NS_ASSERTION(dataType == nsIDataType::VTYPE_EMPTY_ARRAY,
+                 "Expecting an empty array");
+    *_retval = nullptr;
+    return NS_OK;
+  }
+
+  uint16_t valueType;
+  nsIID iid;
+  uint32_t valueCount;
+  void* rawArray;
+  variantArray->GetAsArray(&valueType, &iid, &valueCount, &rawArray);
+  NS_ASSERTION(valueCount > 0, "Expecting non-zero array size");
+  nsIDOMDeviceStorage** values = static_cast<nsIDOMDeviceStorage**>(rawArray);
+  *_retval = values[0];
+  for (uint32_t i = 1; i < valueCount; i++) {
+    values[i]->Release();
+  }
+  nsMemory::Free(rawArray);
+  return NS_OK;
+}
+
+NS_IMETHODIMP Navigator::GetDeviceStorages(const nsAString &aType, nsIVariant** _retval)
+{
   if (!Preferences::GetBool("device.storage.enabled", false)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mWindow));
 
   if (!win || !win->GetOuterWindow() || !win->GetDocShell()) {
     return NS_ERROR_FAILURE;
   }
 
-  nsRefPtr<nsDOMDeviceStorage> storage;
-  nsDOMDeviceStorage::CreateDeviceStoragesFor(win, aType, getter_AddRefs(storage));
+  nsTArray<nsRefPtr<nsDOMDeviceStorage> > stores;
+  nsDOMDeviceStorage::CreateDeviceStoragesFor(win, aType, stores);
+
+  nsCOMPtr<nsIWritableVariant> result = do_CreateInstance("@mozilla.org/variant;1");
+  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
 
-  if (!storage) {
-    return NS_OK;
+  if (stores.Length() == 0) {
+    result->SetAsEmptyArray();
+  } else {
+    result->SetAsArray(nsIDataType::VTYPE_INTERFACE,
+                       &NS_GET_IID(nsIDOMDeviceStorage),
+                       stores.Length(),
+                       const_cast<void*>(static_cast<const void*>(stores.Elements())));
   }
+  result.forget(_retval);
 
-  NS_ADDREF(*_retval = storage.get());
-  mDeviceStorageStores.AppendElement(storage);
+  mDeviceStorageStores.AppendElements(stores);
   return NS_OK;
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorGeolocation
 //*****************************************************************************
 
 NS_IMETHODIMP Navigator::GetGeolocation(nsIDOMGeoGeolocation** _retval)
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -68,23 +68,26 @@ public:
   NS_DECL_NSIDOMDEVICESTORAGE
 
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIDOMEVENTTARGET
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
 
   nsDOMDeviceStorage();
 
-  nsresult Init(nsPIDOMWindow* aWindow, const nsAString &aType);
+  nsresult Init(nsPIDOMWindow* aWindow, const nsAString &aType, const nsAString &aVolName);
 
-  void SetRootDirectoryForType(const nsAString& aType);
+  void SetRootDirectoryForType(const nsAString& aType, const nsAString &aVolName);
+
+  static void GetOrderedVolumeNames(const nsAString &aType,
+                                    nsTArray<nsString> &aVolumeNames);
 
   static void CreateDeviceStoragesFor(nsPIDOMWindow* aWin,
                                       const nsAString &aType,
-                                      nsDOMDeviceStorage** aStore);
+                                      nsTArray<nsRefPtr<nsDOMDeviceStorage> > &aStores);
   void Shutdown();
 
 private:
   ~nsDOMDeviceStorage();
 
   nsresult GetInternal(const JS::Value & aName,
                        JSContext* aCx,
                        nsIDOMDOMRequest** aRetval,
@@ -94,16 +97,17 @@ private:
                              const JS::Value & aOptions,
                              JSContext* aCx,
                              uint8_t aArgc,
                              bool aEditable,
                              nsIDOMDeviceStorageCursor** aRetval);
 
   nsString mStorageType;
   nsCOMPtr<nsIFile> mRootDirectory;
+  nsString mVolumeName;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   bool mIsWatchingFile;
   bool mAllowedToWatchFile;
 
   nsresult Notify(const char* aReason, class DeviceStorageFile* aFile);
 
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -115,17 +115,18 @@ DeviceStorageRequestParent::Dispatch()
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageAvailableParams:
     {
-      nsRefPtr<PostAvailableResultEvent> r = new PostAvailableResultEvent(this);
+      DeviceStorageAvailableParams p = mParams;
+      nsRefPtr<PostAvailableResultEvent> r = new PostAvailableResultEvent(this, p.fullpath());
       NS_DispatchToMainThread(r);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageEnumerationParams:
     {
       DeviceStorageEnumerationParams p = mParams;
 
@@ -648,34 +649,36 @@ DeviceStorageRequestParent::PostPathResu
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   SuccessResponse response;
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
-DeviceStorageRequestParent::PostAvailableResultEvent::PostAvailableResultEvent(DeviceStorageRequestParent* aParent)
+DeviceStorageRequestParent::PostAvailableResultEvent::PostAvailableResultEvent(DeviceStorageRequestParent* aParent,
+                                                                               const nsAString &aPath)
   : CancelableRunnable(aParent)
+  , mPath(aPath)
 {
 }
 
 DeviceStorageRequestParent::PostAvailableResultEvent::~PostAvailableResultEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::PostAvailableResultEvent::CancelableRun()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsString state;
   state.Assign(NS_LITERAL_STRING("available"));
 #ifdef MOZ_WIDGET_GONK
-  nsresult rv = GetSDCardStatus(state);
+  nsresult rv = GetSDCardStatus(mPath, state);
   if (NS_FAILED(rv)) {
     state.Assign(NS_LITERAL_STRING("unavailable"));
   }
 #endif
 
   AvailableStorageResponse response(state);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
--- a/dom/devicestorage/DeviceStorageRequestParent.h
+++ b/dom/devicestorage/DeviceStorageRequestParent.h
@@ -206,19 +206,21 @@ private:
       virtual nsresult CancelableRun();
     private:
       int64_t mUsedSpace;
  };
 
  class PostAvailableResultEvent : public CancelableRunnable
  {
     public:
-      PostAvailableResultEvent(DeviceStorageRequestParent* aParent);
+      PostAvailableResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath);
       virtual ~PostAvailableResultEvent();
       virtual nsresult CancelableRun();
+    private:
+      nsString mPath;
  };
 
 protected:
   bool AddRunnable(CancelableRunnable* aRunnable) {
     MutexAutoLock lock(mMutex);
     if (mActorDestoryed)
       return false;
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -219,16 +219,25 @@ DeviceStorageTypeChecker::GetAccessForRe
       aAccessResult.AssignLiteral("create");
       break;
     default:
       aAccessResult.AssignLiteral("undefined");
   }
   return NS_OK;
 }
 
+bool
+DeviceStorageTypeChecker::IsVolumeBased(const nsAString& aType)
+{
+  // The apps aren't stored in the same place as the media, so
+  // we only ever return a single apps object, and not an array
+  // with one per volume (as is the case for the remaining
+  // storage types).
+  return !aType.EqualsLiteral(DEVICESTORAGE_APPS);
+}
 
 NS_IMPL_ISUPPORTS1(FileUpdateDispatcher, nsIObserver)
 
 mozilla::StaticRefPtr<FileUpdateDispatcher> FileUpdateDispatcher::sSingleton;
 
 FileUpdateDispatcher*
 FileUpdateDispatcher::GetSingleton()
 {
@@ -652,23 +661,23 @@ DeviceStorageFile::DirectoryDiskUsage(ns
     }
   }
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(DeviceStorageFile)
 
 #ifdef MOZ_WIDGET_GONK
 nsresult
-GetSDCardStatus(nsAString& aState) {
+GetSDCardStatus(nsAString& aPath, nsAString& aState) {
   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
   if (!vs) {
     return NS_ERROR_FAILURE;
   }
   nsCOMPtr<nsIVolume> vol;
-  vs->GetVolumeByName(NS_LITERAL_STRING("sdcard"), getter_AddRefs(vol));
+  vs->GetVolumeByPath(aPath, getter_AddRefs(vol));
   if (!vol) {
     return NS_ERROR_FAILURE;
   }
 
   int32_t state;
   nsresult rv = vol->GetState(&state);
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
@@ -695,52 +704,69 @@ static void
 UnregisterForSDCardChanges(nsIObserver* aObserver)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->RemoveObserver(aObserver, NS_VOLUME_STATE_CHANGED);
 }
 #endif
 
 void
-nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType)
+nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType, const nsAString& aVolName)
 {
   nsCOMPtr<nsIFile> f;
   nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   NS_ASSERTION(dirService, "Must have directory service");
 
+  mVolumeName = NS_LITERAL_STRING("");
+#ifdef MOZ_WIDGET_GONK
+  nsString volMountPoint(NS_LITERAL_STRING("/sdcard"));
+  if (!aVolName.EqualsLiteral("")) {
+    nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
+    if (vs) {
+      nsresult rv;
+      nsCOMPtr<nsIVolume> vol;
+      rv = vs->GetVolumeByName(aVolName, getter_AddRefs(vol));
+      if (NS_SUCCEEDED(rv)) {
+        vol->GetMountPoint(volMountPoint);
+        mVolumeName = aVolName;
+      }
+    }
+  }
+#endif
+
   // Picture directory
   if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
 #ifdef MOZ_WIDGET_GONK
-    NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
+    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
 #elif defined (MOZ_WIDGET_COCOA)
     dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_UNIX)
     dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_WIN)
     dirService->Get(NS_WIN_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #endif
   }
 
   // Video directory
   else if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
 #ifdef MOZ_WIDGET_GONK
-    NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
+    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
 #elif defined (MOZ_WIDGET_COCOA)
     dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_UNIX)
     dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_WIN)
     dirService->Get(NS_WIN_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #endif
   }
 
   // Music directory
   else if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
 #ifdef MOZ_WIDGET_GONK
-    NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
+    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
 #elif defined (MOZ_WIDGET_COCOA)
     dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_UNIX)
     dirService->Get(NS_UNIX_XDG_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #elif defined (XP_WIN)
     dirService->Get(NS_WIN_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
 #endif
   }
@@ -754,19 +780,19 @@ nsDOMDeviceStorage::SetRootDirectoryForT
     if (f) {
       f->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
     }
 #endif
   }
 
    // default SDCard
    else if (aType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
- #ifdef MOZ_WIDGET_GONK
-     NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
- #else
+#ifdef MOZ_WIDGET_GONK
+     NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
+#else
     // Eventually, on desktop, we want to do something smarter -- for example,
     // detect when an sdcard is inserted, and use that instead of this.
     dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
     if (f) {
       f->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
     }
 #endif
   }
@@ -1232,43 +1258,45 @@ nsDOMDeviceStorageCursor::RequestComplet
 {
   NS_ASSERTION(!mOkToCallContinue, "mOkToCallContinue must be false");  
   mOkToCallContinue = true;
 }
 
 class PostAvailableResultEvent : public nsRunnable
 {
 public:
-  PostAvailableResultEvent(DOMRequest* aRequest)
-    : mRequest(aRequest)
+  PostAvailableResultEvent(const nsAString& aPath, DOMRequest* aRequest)
+    : mPath(aPath)
+    , mRequest(aRequest)
   {
   }
 
   ~PostAvailableResultEvent() {}
 
   NS_IMETHOD Run()
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
     nsString state;
     state.Assign(NS_LITERAL_STRING("available"));
 #ifdef MOZ_WIDGET_GONK
-    nsresult rv = GetSDCardStatus(state);
+    nsresult rv = GetSDCardStatus(mPath, state);
     if (NS_FAILED(rv)) {
       state.Assign(NS_LITERAL_STRING("unavailable"));
     }
 #endif
 
     jsval result = StringToJsval(mRequest->GetOwner(), state);
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
+  nsString mPath;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class PostResultEvent : public nsRunnable
 {
 public:
   PostResultEvent(nsRefPtr<DOMRequest>& aRequest, DeviceStorageFile* aFile)
     : mFile(aFile)
@@ -1751,21 +1779,21 @@ public:
         r = new UsedSpaceFileEvent(mFile, mRequest);
         break;
       }
 
       case DEVICE_STORAGE_REQUEST_AVAILABLE:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageAvailableParams params(mFile->mStorageType);
+          DeviceStorageAvailableParams params(mFile->mStorageType, fullpath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
-        r = new PostAvailableResultEvent(mRequest);
+        r = new PostAvailableResultEvent(mFile->mPath, mRequest);
         NS_DispatchToMainThread(r);
         return NS_OK;
       }
 
       case DEVICE_STORAGE_REQUEST_WATCH:
       {
         mDeviceStorage->mAllowedToWatchFile = true;
         return NS_OK;
@@ -1854,24 +1882,24 @@ NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStor
 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
 
 nsDOMDeviceStorage::nsDOMDeviceStorage()
   : mIsWatchingFile(false)
   , mAllowedToWatchFile(false)
 { }
 
 nsresult
-nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType)
+nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType, const nsAString &aVolName)
 {
   DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
   NS_ASSERTION(observer, "FileUpdateDispatcher is null");
 
   NS_ASSERTION(aWindow, "Must have a content dom");
 
-  SetRootDirectoryForType(aType);
+  SetRootDirectoryForType(aType, aVolName);
   if (!mRootDirectory) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   BindToOwner(aWindow);
 
   // Grab the principal of the document
   nsCOMPtr<nsIDOMDocument> domdoc;
@@ -1914,23 +1942,59 @@ nsDOMDeviceStorage::Shutdown()
   UnregisterForSDCardChanges(this);
 #endif
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->RemoveObserver(this, "file-watcher-update");
 }
 
 void
+nsDOMDeviceStorage::GetOrderedVolumeNames(const nsAString &aType,
+                                          nsTArray<nsString> &aVolumeNames)
+{
+#ifdef MOZ_WIDGET_GONK
+  if (DeviceStorageTypeChecker::IsVolumeBased(aType)) {
+    nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
+    if (vs) {
+      vs->GetVolumeNames(aVolumeNames);
+
+      // If the volume sdcard exists, then we want it to be first.
+
+      nsTArray<nsString>::index_type sdcardIndex;
+      sdcardIndex = aVolumeNames.IndexOf(NS_LITERAL_STRING("sdcard"));
+      if ((sdcardIndex != nsTArray<nsString>::NoIndex)
+      &&  (sdcardIndex > 0)) {
+        aVolumeNames.RemoveElementAt(sdcardIndex);
+        aVolumeNames.InsertElementAt(0, NS_LITERAL_STRING("sdcard"));
+      }
+    }
+  }
+#endif
+  if (aVolumeNames.Length() == 0) {
+    aVolumeNames.AppendElement(NS_LITERAL_STRING(""));
+  }
+}
+
+void
 nsDOMDeviceStorage::CreateDeviceStoragesFor(nsPIDOMWindow* aWin,
                                             const nsAString &aType,
-                                            nsDOMDeviceStorage** aStore)
+                                            nsTArray<nsRefPtr<nsDOMDeviceStorage> > &aStores)
 {
-  nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage();
-  if (NS_SUCCEEDED(storage->Init(aWin, aType))) {
-    NS_ADDREF(*aStore = storage);
+  nsTArray<nsString>  volNames;
+  GetOrderedVolumeNames(aType, volNames);
+
+  nsTArray<nsString>::size_type numVolumeNames = volNames.Length();
+  for (nsTArray<nsString>::index_type i = 0; i < numVolumeNames; i++) {
+    nsresult rv;
+    nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage();
+    rv = storage->Init(aWin, aType, volNames[i]);
+    if (NS_FAILED(rv)) {
+      break;
+    }
+    aStores.AppendElement(storage);
   }
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Add(nsIDOMBlob *aBlob, nsIDOMDOMRequest * *_retval)
 {
   if (!aBlob) {
     return NS_OK;
@@ -2161,16 +2225,23 @@ nsDOMDeviceStorage::GetRootDirectory(nsI
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIFile> file;
   return mRootDirectory->Clone(aRootDirectory);
 }
 
 NS_IMETHODIMP
+nsDOMDeviceStorage::GetVolumeName(nsAString & aVolumeName)
+{
+  aVolumeName = mVolumeName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMDeviceStorage::Enumerate(const JS::Value & aName,
                              const JS::Value & aOptions,
                              JSContext* aCx,
                              uint8_t aArgc,
                              nsIDOMDeviceStorageCursor** aRetval)
 {
   return EnumerateInternal(aName, aOptions, aCx, aArgc, false, aRetval);
 }
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -63,16 +63,17 @@ public:
 
   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);
   static nsresult GetAccessForRequest(const DeviceStorageRequestType aRequestType, nsACString& aAccessResult);
+  static bool IsVolumeBased(const nsAString& aType);
 
 private:
   nsString mPicturesExtensions;
   nsString mVideosExtensions;
   nsString mMusicExtensions;
 
   static nsAutoPtr<DeviceStorageTypeChecker> sDeviceStorageTypeChecker;
 };
@@ -128,12 +129,12 @@ private:
 };
 
 //helpers
 jsval StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString);
 jsval nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile);
 jsval InterfaceToJsval(nsPIDOMWindow* aWindow, nsISupports* aObject, const nsIID* aIID);
 
 #ifdef MOZ_WIDGET_GONK
-nsresult GetSDCardStatus(nsAString& aState);
+nsresult GetSDCardStatus(nsAString& aPath, nsAString& aState);
 #endif
 
 #endif
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
@@ -11,17 +11,17 @@ interface nsIDOMDeviceStorageChangeEvent
 interface nsIDOMEventListener;
 interface nsIFile;
 
 dictionary DeviceStorageEnumerationParameters
 {
   jsval since;
 };
 
-[scriptable, uuid(c611b701-ddfc-456d-893a-3b3fcb30d9fd), builtinclass]
+[scriptable, uuid(c89dbddf-7854-4d92-9d9f-408b0e42af2d), builtinclass]
 interface nsIDOMDeviceStorage : nsIDOMEventTarget
 {
     [implicit_jscontext] attribute jsval onchange;
     nsIDOMDOMRequest add(in nsIDOMBlob aBlob);
     nsIDOMDOMRequest addNamed(in nsIDOMBlob aBlob, in DOMString aName);
 
     [implicit_jscontext]
     nsIDOMDOMRequest get(in jsval aName);
@@ -39,10 +39,14 @@ interface nsIDOMDeviceStorage : nsIDOMEv
     nsIDOMDeviceStorageCursor enumerateEditable([optional] in jsval aName, /* DeviceStorageEnumerationParameters */ [optional] in jsval options);
 
     nsIDOMDOMRequest freeSpace();
 
     nsIDOMDOMRequest usedSpace();
 
     nsIDOMDOMRequest available();
 
+    // Note that the volumeName is just a name (like sdcard), and doesn't
+    // include any path information.
+    readonly attribute DOMString volumeName;
+
     [noscript] readonly attribute nsIFile rootDirectory;
 };
--- a/dom/interfaces/devicestorage/nsIDOMNavigatorDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMNavigatorDeviceStorage.idl
@@ -1,15 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 interface nsIDOMDeviceStorage;
+interface nsIVariant;
 
 /**
  * Property that extends the navigator object.
  */
-[scriptable, uuid(da1fbf6e-259c-40bc-ba8c-4ae81748dca3)]
+[scriptable, uuid(1caeaf6d-6529-4e1f-b080-da17ab6aa266)]
 interface nsIDOMNavigatorDeviceStorage : nsISupports
 {
   nsIDOMDeviceStorage getDeviceStorage(in DOMString type);
+  nsIVariant getDeviceStorages(in DOMString type);
 };
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -86,16 +86,17 @@ struct DeviceStorageUsedSpaceParams
 {
   nsString type;
   nsString fullpath;
 };
 
 struct DeviceStorageAvailableParams
 {
   nsString type;
+  nsString fullpath;
 };
 
 struct DeviceStorageAddParams
 {
   nsString type;
   PBlob blob;
   nsString name;
   nsString fullpath;