Merge with mozilla-central
authorEd Morley <emorley@mozilla.com>
Tue, 23 Apr 2013 12:17:57 +0100
changeset 129573 c6c552b873a2764071c76ca120c38567e91de736
parent 129561 aa620f3fc2f7116e6fad91915cb273160a2f5f0a (current diff)
parent 129572 1071ca655e43ab8be7da42762200857f50e0cf1e (diff)
child 129574 5a10c10a4a09f6579e7c30e5986527ea99171c8a
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
milestone23.0a1
Merge with mozilla-central
--- a/b2g/components/DirectoryProvider.js
+++ b/b2g/components/DirectoryProvider.js
@@ -86,17 +86,17 @@ DirectoryProvider.prototype = {
   // The VolumeService only exists on the device, and not on desktop
   volumeHasFreeSpace: function dp_volumeHasFreeSpace(volumePath, requiredSpace) {
     if (!volumePath) {
       return false;
     }
     if (!Services.volumeService) {
       return false;
     }
-    let volume = Services.volumeService.getVolumeByPath(volumePath);
+    let volume = Services.volumeService.createOrGetVolumeByPath(volumePath);
     if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) {
       return false;
     }
     let stat = volume.getStats();
     if (!stat) {
       return false;
     }
     return requiredSpace <= stat.freeBytes;
@@ -196,17 +196,17 @@ DirectoryProvider.prototype = {
 
   getDefaultUpdateDir: function dp_getDefaultUpdateDir() {
     let path = gExtStorage;
     if (!path) {
       path = LOCAL_DIR;
     }
 
     if (Services.volumeService) {
-      let extVolume = Services.volumeService.getVolumeByPath(path);
+      let extVolume = Services.volumeService.createOrGetVolumeByPath(path);
       if (!extVolume) {
         path = LOCAL_DIR;
       }
     }
 
     let dir = Cc["@mozilla.org/file/local;1"]
                  .createInstance(Ci.nsILocalFile)
     dir.initWithPath(path);
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -24,17 +24,19 @@
 #include "nsIObserverService.h"
 #include "nsIDOMFile.h"
 #include "nsIFile.h"
 #include "nsIInputStream.h"
 #include "nsIMIMEService.h"
 #include "nsIOutputStream.h"
 #include "nsNetUtil.h"
 
-#define TARGET_FOLDER "/sdcard/downloads/bluetooth/"
+#define TARGET_ROOT   "/sdcard/"
+#define TARGET_SUBDIR "downloads/bluetooth/"
+#define TARGET_FOLDER TARGET_ROOT TARGET_SUBDIR
 
 USING_BLUETOOTH_NAMESPACE
 using namespace mozilla;
 using namespace mozilla::ipc;
 
 class BluetoothOppManagerObserver : public nsIObserver
 {
 public:
@@ -556,30 +558,35 @@ BluetoothOppManager::CreateFile()
 
   /*
    * The function CreateUnique() may create a file with a different file
    * name from the original sFileName. Therefore we have to retrieve
    * the file name again.
    */
   f->GetLeafName(sFileName);
 
+  nsString fullFileName;
+  f->GetPath(fullFileName);
+  MOZ_ASSERT(StringBeginsWith(fullFileName, NS_LITERAL_STRING(TARGET_ROOT)));
+  nsDependentSubstring storagePath = Substring(fullFileName, strlen(TARGET_ROOT));
+
   mDsFile = nullptr;
 
   nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
   if (mimeSvc) {
     nsCString mimeType;
     nsresult rv = mimeSvc->GetTypeFromFile(f, mimeType);
 
     if (NS_SUCCEEDED(rv)) {
       if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("image/"))) {
-        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("pictures"), f);
+        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("pictures"), storagePath);
       } else if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("video/"))) {
-        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("movies"), f);
+        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("movies"), storagePath);
       } else if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("audio/"))) {
-        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("music"), f);
+        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("music"), storagePath);
       } else {
         NS_WARNING("Couldn't recognize the mimetype of received file.");
       }
     }
   }
 
   NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
   if (!mOutputStream) {
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -900,17 +900,18 @@ nsGonkCameraControl::StartRecordingImpl(
    * filename to it.  The filename may include a relative subpath
    * (e.g.) "DCIM/IMG_0001.jpg".
    *
    * The camera app needs to provide the file extension '.3gp' for now.
    * See bug 795202.
    */
   nsCOMPtr<nsIFile> filename = aStartRecording->mFolder;
   filename->AppendRelativePath(aStartRecording->mFilename);
-  mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"), filename);
+  mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"),
+                                     aStartRecording->mFilename);
 
   nsAutoCString nativeFilename;
   filename->GetNativePath(nativeFilename);
   DOM_CAMERA_LOGI("Video filename is '%s'\n", nativeFilename.get());
 
   if (!mVideoFile->IsSafePath()) {
     DOM_CAMERA_LOGE("Invalid video file name\n");
     return NS_ERROR_INVALID_ARG;
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -15,40 +15,52 @@
 #include "mozilla/StaticPtr.h"
 
 class DeviceStorageFile MOZ_FINAL
   : public nsISupports {
 public:
   nsCOMPtr<nsIFile> mFile;
   nsString mPath;
   nsString mStorageType;
+  nsString mRootDir;
   bool mEditable;
 
-  DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile, const nsAString& aPath);
-  DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile);
+  // Used when the path will be set later via SetPath.
+  DeviceStorageFile(const nsAString& aStorageType);
+  // Used for non-enumeration purposes.
+  DeviceStorageFile(const nsAString& aStorageType, const nsAString& aPath);
+  // Used for enumerations. When you call Enumerate, you can pass in a directory to enumerate
+  // and the results that are returned are relative to that directory, files related to an
+  // enumeration need to know the "root of the enumeration" directory.
+  DeviceStorageFile(const nsAString& aStorageType, const nsAString& aRootDir, const nsAString& aPath);
+
   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 IsSafePath(const nsAString& aPath);
 
   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, const nsAString& aStorageType);
-
+  static void GetRootDirectoryForType(const nsAString& aType,
+                                      const nsAString& aVolName,
+                                      nsIFile** aFile);
 private:
+  void Init(const nsAString& aStorageType);
   void NormalizeFilePath();
-  void AppendRelativePath();
+  void AppendRelativePath(const nsAString& aPath);
 };
 
 /*
   The FileUpdateDispatcher converts file-watcher-notify
   observer events to file-watcher-update events.  This is
   used to be able to broadcast events from one child to
   another child in B2G.  (f.e., if one child decides to add
   a file, we want to be able to able to send a onchange
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -95,24 +95,19 @@ DeviceStorageRequestChild::Recv__delete_
 
     case DeviceStorageResponseValue::TEnumerationResponse:
     {
       EnumerationResponse r = aValue;
       nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
 
       uint32_t count = r.paths().Length();
       for (uint32_t i = 0; i < count; i++) {
-        nsCOMPtr<nsIFile> f;
-        nsresult rv = NS_NewLocalFile(r.paths()[i].fullpath(), false, getter_AddRefs(f));
-        if (NS_FAILED(rv)) {
-          continue;
-        }
-
-        nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(r.paths()[i].type(), f);
-        dsf->SetPath(r.paths()[i].name());
+        nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(r.type(),
+                                                                r.relpath(),
+                                                                r.paths()[i].name());
         cursor->mFiles.AppendElement(dsf);
       }
 
       nsCOMPtr<ContinueCursorEvent> event = new ContinueCursorEvent(cursor);
       event->Continue();
       break;
     }
 
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -29,20 +29,17 @@ DeviceStorageRequestParent::DeviceStorag
 void
 DeviceStorageRequestParent::Dispatch()
 {
   switch (mParams.type()) {
     case DeviceStorageParams::TDeviceStorageAddParams:
     {
       DeviceStorageAddParams p = mParams;
 
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
 
       BlobParent* bp = static_cast<BlobParent*>(p.blobParent());
       nsCOMPtr<nsIDOMBlob> blob = bp->GetBlob();
 
       nsCOMPtr<nsIInputStream> stream;
       blob->GetInternalStream(getter_AddRefs(stream));
 
       nsRefPtr<CancelableRunnable> r = new WriteFileEvent(this, dsf, stream);
@@ -51,94 +48,78 @@ DeviceStorageRequestParent::Dispatch()
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageGetParams:
     {
       DeviceStorageGetParams p = mParams;
-
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
-      dsf->SetPath(p.name());
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.rootDir(), p.relpath());
       nsRefPtr<CancelableRunnable> r = new ReadFileEvent(this, dsf);
 
       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::TDeviceStorageDeleteParams:
     {
       DeviceStorageDeleteParams p = mParams;
 
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
       nsRefPtr<CancelableRunnable> r = new DeleteFileEvent(this, dsf);
 
       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::TDeviceStorageFreeSpaceParams:
     {
       DeviceStorageFreeSpaceParams p = mParams;
 
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
       nsRefPtr<FreeSpaceFileEvent> r = new FreeSpaceFileEvent(this, dsf);
 
       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::TDeviceStorageUsedSpaceParams:
     {
       DeviceStorageUsedSpaceParams p = mParams;
 
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
       nsRefPtr<UsedSpaceFileEvent> r = new UsedSpaceFileEvent(this, dsf);
 
       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:
     {
       DeviceStorageAvailableParams p = mParams;
-      nsRefPtr<PostAvailableResultEvent> r = new PostAvailableResultEvent(this, p.fullpath());
+
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
+      nsRefPtr<PostAvailableResultEvent> r = new PostAvailableResultEvent(this, dsf);
       NS_DispatchToMainThread(r);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageEnumerationParams:
     {
       DeviceStorageEnumerationParams p = mParams;
-
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath(), NS_LITERAL_STRING(""));
       nsRefPtr<CancelableRunnable> r = new EnumerateFileEvent(this, dsf, p.since());
 
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
     default:
@@ -377,29 +358,33 @@ DeviceStorageRequestParent::PostBlobSucc
   BlobResponse response;
   response.blobParent() = actor;
 
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent,
+                                                                                     const nsAString& aStorageType,
+                                                                                     const nsAString& aRelPath,
                                                                                      InfallibleTArray<DeviceStorageFileValue>& aPaths)
   : CancelableRunnable(aParent)
+  , mStorageType(aStorageType)
+  , mRelPath(aRelPath)
   , mPaths(aPaths)
 {
 }
 
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::~PostEnumerationSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  EnumerationResponse response(mPaths);
+  EnumerationResponse response(mStorageType, mRelPath, mPaths);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::WriteFileEvent::WriteFileEvent(DeviceStorageRequestParent* aParent,
                                                            DeviceStorageFile* aFile,
                                                            nsIInputStream* aInputStream)
   : CancelableRunnable(aParent)
@@ -616,23 +601,21 @@ DeviceStorageRequestParent::EnumerateFil
 
   nsTArray<nsRefPtr<DeviceStorageFile> > files;
   mFile->CollectFiles(files, mSince);
 
   InfallibleTArray<DeviceStorageFileValue> values;
 
   uint32_t count = files.Length();
   for (uint32_t i = 0; i < count; i++) {
-    nsString fullpath;
-    files[i]->mFile->GetPath(fullpath);
-    DeviceStorageFileValue dsvf(mFile->mStorageType, files[i]->mPath, fullpath);
+    DeviceStorageFileValue dsvf(files[i]->mPath);
     values.AppendElement(dsvf);
   }
 
-  r = new PostEnumerationSuccessEvent(mParent, values);
+  r = new PostEnumerationSuccessEvent(mParent, mFile->mStorageType, mFile->mRootDir, values);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 
 DeviceStorageRequestParent::PostPathResultEvent::PostPathResultEvent(DeviceStorageRequestParent* aParent,
                                                                      const nsAString& aPath)
   : CancelableRunnable(aParent)
@@ -650,35 +633,35 @@ DeviceStorageRequestParent::PostPathResu
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   SuccessResponse response;
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostAvailableResultEvent::PostAvailableResultEvent(DeviceStorageRequestParent* aParent,
-                                                                               const nsAString &aPath)
+                                                                               DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
-  , mPath(aPath)
+  , mFile(aFile)
 {
 }
 
 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(mPath, state);
+  nsresult rv = GetSDCardStatus(mFile->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
@@ -100,20 +100,25 @@ private:
       uint64_t mLastModificationDate;
       nsRefPtr<DeviceStorageFile> mFile;
       nsCString mMimeType;
   };
 
   class PostEnumerationSuccessEvent : public CancelableRunnable
   {
     public:
-      PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent, InfallibleTArray<DeviceStorageFileValue>& aPaths);
+      PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent,
+                                  const nsAString& aStorageType,
+                                  const nsAString& aRelPath,
+                                  InfallibleTArray<DeviceStorageFileValue>& aPaths);
       virtual ~PostEnumerationSuccessEvent();
       virtual nsresult CancelableRun();
     private:
+      const nsString mStorageType;
+      const nsString mRelPath;
       InfallibleTArray<DeviceStorageFileValue> mPaths;
   };
 
   class WriteFileEvent : public CancelableRunnable
   {
     public:
       WriteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, nsIInputStream* aInputStream);
       virtual ~WriteFileEvent();
@@ -206,21 +211,21 @@ private:
       virtual nsresult CancelableRun();
     private:
       int64_t mUsedSpace;
  };
 
  class PostAvailableResultEvent : public CancelableRunnable
  {
     public:
-      PostAvailableResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath);
+      PostAvailableResultEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
       virtual ~PostAvailableResultEvent();
       virtual nsresult CancelableRun();
     private:
-      nsString mPath;
+      nsRefPtr<DeviceStorageFile> mFile;
  };
 
 protected:
   bool AddRunnable(CancelableRunnable* aRunnable) {
     MutexAutoLock lock(mMutex);
     if (mActorDestoryed)
       return false;
 
--- a/dom/devicestorage/PDeviceStorageRequest.ipdl
+++ b/dom/devicestorage/PDeviceStorageRequest.ipdl
@@ -22,23 +22,23 @@ struct SuccessResponse
 
 struct BlobResponse
 {
   PBlob blob;
 };
 
 struct DeviceStorageFileValue
 {
-  nsString type;
   nsString name;
-  nsString fullpath;
 };
 
 struct EnumerationResponse
 {
+  nsString type;
+  nsString relpath;
   DeviceStorageFileValue[] paths;
 };
 
 struct FreeSpaceStorageResponse
 {
   int64_t freeBytes;
 };
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -62,16 +62,31 @@
 #define DEVICESTORAGE_SDCARD     "sdcard"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
 
 #include "nsDirectoryServiceDefs.h"
 
+class GlobalDirs : public RefCounted<GlobalDirs>
+{
+public:
+#if !defined(MOZ_WIDGET_GONK)
+  nsCOMPtr<nsIFile> pictures;
+  nsCOMPtr<nsIFile> videos;
+  nsCOMPtr<nsIFile> music;
+  nsCOMPtr<nsIFile> apps;
+  nsCOMPtr<nsIFile> sdcard;
+#endif
+  nsCOMPtr<nsIFile> temp;
+};
+
+static StaticRefPtr<GlobalDirs> sDirs;
+
 nsAutoPtr<DeviceStorageTypeChecker> DeviceStorageTypeChecker::sDeviceStorageTypeChecker;
 
 DeviceStorageTypeChecker::DeviceStorageTypeChecker()
 {
 }
 
 DeviceStorageTypeChecker::~DeviceStorageTypeChecker()
 {
@@ -306,45 +321,192 @@ public:
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsCString mType;
 };
 
 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
-                                     nsIFile* aFile,
+                                     const nsAString& aRootDir,
+                                     const nsAString& aPath)
+  : mPath(aPath)
+  , mStorageType(aStorageType)
+  , mRootDir(aRootDir)
+  , mEditable(false)
+{
+  Init(aStorageType);
+  AppendRelativePath(mRootDir);
+  if (!mPath.EqualsLiteral("")) {
+    AppendRelativePath(mPath);
+  }
+  NormalizeFilePath();
+}
+
+DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
                                      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();
+  Init(aStorageType);
+  AppendRelativePath(aPath);
   NormalizeFilePath();
+}
+
+DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType)
+  : mStorageType(aStorageType)
+  , mEditable(false)
+{
+  Init(aStorageType);
+}
+
+void
+DeviceStorageFile::Init(const nsAString& aStorageType)
+{
+  // The hard-coded sdcard below will change as part of bug 858416
+  DeviceStorageFile::GetRootDirectoryForType(aStorageType,
+                                             NS_LITERAL_STRING("sdcard"),
+                                             getter_AddRefs(mFile));
 
   DebugOnly<DeviceStorageTypeChecker*> typeChecker = DeviceStorageTypeChecker::CreateOrGet();
   NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
 }
 
-DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile)
-  : mStorageType(aStorageType)
-  , mEditable(false)
+static void
+InitDirs()
+{
+  if (sDirs) {
+    return;
+  }
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  sDirs = new GlobalDirs;
+  ClearOnShutdown(&sDirs);
+
+  nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+  NS_ASSERTION(dirService, "Must have directory service");
+
+#if !defined(MOZ_WIDGET_GONK)
+
+#if defined (MOZ_WIDGET_COCOA)
+  dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures));
+  dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos));
+  dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music));
+#elif defined (XP_UNIX)
+  dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures));
+  dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos));
+  dirService->Get(NS_UNIX_XDG_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music));
+#elif defined (XP_WIN)
+  dirService->Get(NS_WIN_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures));
+  dirService->Get(NS_WIN_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos));
+  dirService->Get(NS_WIN_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music));
+#endif
+
+  dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->apps));
+  if (sDirs->apps) {
+    sDirs->apps->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
+  }
+
+  // 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(sDirs->sdcard));
+  if (sDirs->sdcard) {
+    sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
+  }
+#endif // !MOZ_WIDGET_GONK
+
+  if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
+    dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->temp));
+    if (sDirs->temp) {
+      sDirs->temp->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing"));
+      sDirs->temp->Create(nsIFile::DIRECTORY_TYPE, 0777);
+      sDirs->temp->Normalize();
+    }
+  }
+}
+
+void
+DeviceStorageFile::GetRootDirectoryForType(const nsAString &aType, const
+                                           nsAString &aVolName,
+                                           nsIFile** aFile)
 {
-  NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
-  // always take a clone
-  nsCOMPtr<nsIFile> file;
-  aFile->Clone(getter_AddRefs(mFile));
-
-  DebugOnly<DeviceStorageTypeChecker*> typeChecker = DeviceStorageTypeChecker::CreateOrGet();
-  NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
+  nsCOMPtr<nsIFile> f;
+
+  InitDirs();
+
+#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);
+      }
+    }
+  }
+#endif
+
+  // Picture directory
+  if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
+#ifdef MOZ_WIDGET_GONK
+    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
+#else
+    f = sDirs->pictures;
+#endif
+  }
+
+  // Video directory
+  else if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
+#ifdef MOZ_WIDGET_GONK
+    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
+#else
+    f = sDirs->videos;
+#endif
+  }
+
+  // Music directory
+  else if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
+#ifdef MOZ_WIDGET_GONK
+    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
+#else
+    f = sDirs->music;
+#endif
+  }
+
+  // Apps directory
+  else if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) {
+#ifdef MOZ_WIDGET_GONK
+    NS_NewLocalFile(NS_LITERAL_STRING("/data"), false, getter_AddRefs(f));
+#else
+    f = sDirs->apps;
+#endif
+  }
+
+   // default SDCard
+   else if (aType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
+#ifdef MOZ_WIDGET_GONK
+     NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
+#else
+     f = sDirs->sdcard;
+#endif
+  }
+
+  // in testing, we default all device storage types to a temp directory
+  if (f && mozilla::Preferences::GetBool("device.storage.testing", false)) {
+    f = sDirs->temp;
+  }
+
+  if (f) {
+    f->Clone(aFile);
+  } else {
+    *aFile = nullptr;
+  }
 }
 
 void
 DeviceStorageFile::SetPath(const nsAString& aPath) {
   mPath.Assign(aPath);
   NormalizeFilePath();
 }
 
@@ -353,30 +515,36 @@ DeviceStorageFile::SetEditable(bool aEdi
   mEditable = aEditable;
 }
 
 // we want to make sure that the names of file can't reach
 // outside of the type of storage the user asked for.
 bool
 DeviceStorageFile::IsSafePath()
 {
+  return IsSafePath(mRootDir) && IsSafePath(mPath);
+}
+
+bool
+DeviceStorageFile::IsSafePath(const nsAString& aPath)
+{
   nsAString::const_iterator start, end;
-  mPath.BeginReading(start);
-  mPath.EndReading(end);
+  aPath.BeginReading(start);
+  aPath.EndReading(end);
 
   // if the path is a '~' or starts with '~/', return false.
   NS_NAMED_LITERAL_STRING(tilde, "~");
   NS_NAMED_LITERAL_STRING(tildeSlash, "~/");
-  if (mPath.Equals(tilde) ||
-      StringBeginsWith(mPath, tildeSlash)) {
+  if (aPath.Equals(tilde) ||
+      StringBeginsWith(aPath, tildeSlash)) {
     NS_WARNING("Path name starts with tilde!");
     return false;
    }
   // split on /.  if any token is "", ., or .., return false.
-  NS_ConvertUTF16toUTF8 cname(mPath);
+  NS_ConvertUTF16toUTF8 cname(aPath);
   char* buffer = cname.BeginWriting();
   const char* token;
 
   while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
     if (PL_strcmp(token, "") == 0 ||
         PL_strcmp(token, ".") == 0 ||
         PL_strcmp(token, "..") == 0 ) {
       return false;
@@ -393,33 +561,33 @@ DeviceStorageFile::NormalizeFilePath() {
   for (; cur < end; ++cur) {
     if (PRUnichar('\\') == *cur)
       *cur = PRUnichar('/');
   }
 #endif
 }
 
 void
-DeviceStorageFile::AppendRelativePath() {
+DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
 #if defined(XP_WIN)
   // replace forward slashes with backslashes,
   // since nsLocalFileWin chokes on them
   nsString temp;
-  temp.Assign(mPath);
+  temp.Assign(aPath);
 
   PRUnichar* cur = temp.BeginWriting();
   PRUnichar* end = temp.EndWriting();
 
   for (; cur < end; ++cur) {
     if (PRUnichar('/') == *cur)
       *cur = PRUnichar('\\');
   }
   mFile->AppendRelativePath(temp);
 #else
-  mFile->AppendRelativePath(mPath);
+  mFile->AppendRelativePath(aPath);
 #endif
 }
 
 nsresult
 DeviceStorageFile::Write(nsIInputStream* aInputStream)
 {
   if (!aInputStream) {
     return NS_ERROR_FAILURE;
@@ -531,17 +699,16 @@ void
 DeviceStorageFile::CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
                                 PRTime aSince)
 {
   nsString rootPath;
   nsresult rv = mFile->GetPath(rootPath);
   if (NS_FAILED(rv)) {
     return;
   }
-
   return collectFilesInternal(aFiles, aSince, rootPath);
 }
 
 void
 DeviceStorageFile::collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
                                         PRTime aSince,
                                         nsAString& aRootPath)
 {
@@ -580,22 +747,20 @@ DeviceStorageFile::collectFilesInternal(
       NS_ERROR("collectFiles returned a path that does not belong!");
       continue;
     }
 
     nsAString::size_type len = aRootPath.Length() + 1; // +1 for the trailing /
     nsDependentSubstring newPath = Substring(fullpath, len);
 
     if (isDir) {
-      DeviceStorageFile dsf(mStorageType, f);
-      dsf.SetPath(newPath);
+      DeviceStorageFile dsf(mStorageType, mRootDir, newPath);
       dsf.collectFilesInternal(aFiles, aSince, aRootPath);
     } else if (isFile) {
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, f);
-      dsf->SetPath(newPath);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDir, newPath);
       aFiles.AppendElement(dsf);
     }
   }
 }
 
 void
 DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar, const nsAString& aStorageType)
 {
@@ -701,115 +866,40 @@ 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, const nsAString& aVolName)
+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");
+  DeviceStorageFile::GetRootDirectoryForType(aType,
+                                             aVolName,
+                                             getter_AddRefs(f));
 
   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(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(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(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
-  }
-
-  // Apps directory
-  else if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) {
-#ifdef MOZ_WIDGET_GONK
-    NS_NewLocalFile(NS_LITERAL_STRING("/data"), false, getter_AddRefs(f));
-#else
-    dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    if (f) {
-      f->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
-    }
-#endif
-  }
-
-   // default SDCard
-   else if (aType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
-#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
-  }
-
-  // in testing, we default all device storage types to a temp directory
-  if (f && mozilla::Preferences::GetBool("device.storage.testing", false)) {
-    dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    if (f) {
-      f->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing"));
-      f->Create(nsIFile::DIRECTORY_TYPE, 0777);
-      f->Normalize();
-    }
-  }
-
-#ifdef MOZ_WIDGET_GONK
+
   RegisterForSDCardChanges(this);
 #endif
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->AddObserver(this, "file-watcher-update", false);
   mRootDirectory = f;
   mStorageType = aType;
 }
@@ -1021,30 +1111,23 @@ ContinueCursorEvent::Continue()
   nsRefPtr<DeviceStorageFile> file = GetNextFile();
 
   if (!file) {
     // done with enumeration.
     NS_DispatchToMainThread(this);
     return;
   }
 
-  nsString fullpath;
-  nsresult rv = file->mFile->GetPath(fullpath);
-  if (NS_FAILED(rv)) {
-    NS_ASSERTION(false, "GetPath failed to return a valid path");
-    return;
-  }
-
   nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
   nsString cursorStorageType;
   cursor->GetStorageType(cursorStorageType);
 
   DeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, file);
   child->SetCallback(cursor);
-  DeviceStorageGetParams params(cursorStorageType, file->mPath, fullpath);
+  DeviceStorageGetParams params(cursorStorageType, file->mRootDir, file->mPath);
   ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
   mRequest = nullptr;
 }
 
 NS_IMETHODIMP
 ContinueCursorEvent::Run()
 {
   nsRefPtr<DeviceStorageFile> file = GetNextFile();
@@ -1175,27 +1258,18 @@ nsDOMDeviceStorageCursor::Allow()
 {
   if (!mFile->IsSafePath()) {
     nsCOMPtr<nsIRunnable> r = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
-
-    nsString fullpath;
-    nsresult rv = mFile->mFile->GetPath(fullpath);
-
-    if (NS_FAILED(rv)) {
-      // just do nothing
-      return NS_OK;
-    }
-
     PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(this, mFile);
-    DeviceStorageEnumerationParams params(mFile->mStorageType, fullpath, mSince);
+    DeviceStorageEnumerationParams params(mFile->mStorageType, mFile->mRootDir, mSince);
     ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
     return NS_OK;
   }
 
   nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   NS_ASSERTION(target, "Must have stream transport service");
 
   nsCOMPtr<InitCursorEvent> event = new InitCursorEvent(this, mFile);
@@ -1645,24 +1719,16 @@ public:
   NS_IMETHOD Allow()
   {
     nsCOMPtr<nsIRunnable> r;
 
     if (!mRequest) {
       return NS_ERROR_FAILURE;
     }
 
-    nsString fullpath;
-    nsresult rv = mFile->mFile->GetPath(fullpath);
-
-    if (NS_FAILED(rv)) {
-      // just do nothing
-      return NS_OK;
-    }
-
     switch(mRequestType) {
       case DEVICE_STORAGE_REQUEST_CREATE:
       {
         if (!mBlob) {
           return NS_ERROR_FAILURE;
         }
 
         DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet();
@@ -1682,18 +1748,17 @@ public:
           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.relpath() = mFile->mPath;
 
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new WriteFileEvent(mBlob, mFile, mRequest);
         break;
       }
@@ -1709,17 +1774,17 @@ public:
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
           r = new PostErrorEvent(mRequest, POST_ERROR_EVENT_ILLEGAL_TYPE);
           NS_DispatchToMainThread(r);
           return NS_OK;
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageGetParams params(mFile->mStorageType, mFile->mPath, fullpath);
+          DeviceStorageGetParams params(mFile->mStorageType, mFile->mRootDir, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
 
         r = new ReadFileEvent(mFile, mRequest);
         break;
       }
 
@@ -1733,53 +1798,53 @@ public:
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
           r = new PostErrorEvent(mRequest, POST_ERROR_EVENT_ILLEGAL_TYPE);
           NS_DispatchToMainThread(r);
           return NS_OK;
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageDeleteParams params(mFile->mStorageType, fullpath);
+          DeviceStorageDeleteParams params(mFile->mStorageType, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new DeleteFileEvent(mFile, mRequest);
         break;
       }
 
       case DEVICE_STORAGE_REQUEST_FREE_SPACE:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageFreeSpaceParams params(mFile->mStorageType, fullpath);
+          DeviceStorageFreeSpaceParams params(mFile->mStorageType, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new FreeSpaceFileEvent(mFile, mRequest);
         break;
       }
 
       case DEVICE_STORAGE_REQUEST_USED_SPACE:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageUsedSpaceParams params(mFile->mStorageType, fullpath);
+          DeviceStorageUsedSpaceParams params(mFile->mStorageType, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         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, fullpath);
+          DeviceStorageAvailableParams params(mFile->mStorageType, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new PostAvailableResultEvent(mFile->mPath, mRequest);
         NS_DispatchToMainThread(r);
         return NS_OK;
       }
 
@@ -2019,17 +2084,17 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob 
     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);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, aPath);
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
   } else if (!typeChecker->Check(mStorageType, dsf->mFile) ||
       !typeChecker->Check(mStorageType, aBlob)) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
   }
   else {
     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATE,
@@ -2075,17 +2140,17 @@ nsDOMDeviceStorage::GetInternal(const JS
   JSString* jsstr = JS_ValueToString(aCx, aPath);
   nsDependentJSString path;
   if (!path.init(aCx, jsstr)) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, path);
   dsf->SetEditable(aEditable);
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
   } else {
     r = new DeviceStorageRequest(aEditable ? DEVICE_STORAGE_REQUEST_WRITE : DEVICE_STORAGE_REQUEST_READ,
                                  win, mPrincipal, dsf, request);
   }
   NS_DispatchToMainThread(r);
@@ -2108,17 +2173,17 @@ nsDOMDeviceStorage::Delete(const JS::Val
   JSString* jsstr = JS_ValueToString(aCx, aPath);
   nsDependentJSString path;
   if (!path.init(aCx, jsstr)) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, path);
 
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
   }
   else {
     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE,
                                  win, mPrincipal, dsf, request);
   }
@@ -2132,17 +2197,17 @@ nsDOMDeviceStorage::FreeSpace(nsIDOMDOMR
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*aRetval = request);
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE,
                                                      win,
                                                      mPrincipal,
                                                      dsf,
                                                      request);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
@@ -2153,17 +2218,17 @@ nsDOMDeviceStorage::UsedSpace(nsIDOMDOMR
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*aRetval = request);
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE,
                                                      win,
                                                      mPrincipal,
                                                      dsf,
                                                      request);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
@@ -2174,17 +2239,17 @@ nsDOMDeviceStorage::Available(nsIDOMDOMR
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*aRetval = request);
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE,
                                                      win,
                                                      mPrincipal,
                                                      dsf,
                                                      request);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
@@ -2274,17 +2339,17 @@ nsDOMDeviceStorage::EnumerateInternal(co
     }
 
     if (aArgc == 2 && (JSVAL_IS_VOID(aOptions) || aOptions.isNull() || !aOptions.isObject())) {
       return NS_ERROR_FAILURE;
     }
     since = ExtractDateFromOptions(aCx, aOptions);
   }
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, path, NS_LITERAL_STRING(""));
   dsf->SetEditable(aEditable);
 
   nsRefPtr<nsDOMDeviceStorageCursor> cursor = new nsDOMDeviceStorageCursor(win, mPrincipal,
                                                                            dsf, since);
   nsRefPtr<DeviceStorageCursorRequest> r = new DeviceStorageCursorRequest(cursor);
 
   NS_ADDREF(*aRetval = cursor);
 
@@ -2448,17 +2513,17 @@ nsDOMDeviceStorage::AddEventListener(con
                                      uint8_t aArgc)
 {
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
                                                      win, mPrincipal, dsf, request, this);
   NS_DispatchToMainThread(r);
   return nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted, aArgc);
 }
 
 void
 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
@@ -2469,17 +2534,17 @@ nsDOMDeviceStorage::AddEventListener(con
 {
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
                                                      win, mPrincipal, dsf, request, this);
   NS_DispatchToMainThread(r);
   nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted, aRv);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1171,20 +1171,17 @@ ContentChild::RecvLastPrivateDocShellDes
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
     return true;
 }
 
 bool
 ContentChild::RecvFilePathUpdate(const nsString& type, const nsString& path, const nsCString& aReason)
 {
-    nsCOMPtr<nsIFile> file;
-    NS_NewLocalFile(path, false, getter_AddRefs(file));
-
-    nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(type, file);
+    nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(type, path);
 
     nsString reason;
     CopyASCIItoUTF16(aReason, reason);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->NotifyObservers(dsf, "file-watcher-update", reason.get());
     return true;
 }
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1526,20 +1526,17 @@ ContentParent::Observe(nsISupports* aSub
     else if (!strcmp(aTopic, "last-pb-context-exited")) {
         unused << SendLastPrivateDocShellDestroyed();
     }
     else if (!strcmp(aTopic, "file-watcher-update")) {
         nsCString creason;
         CopyUTF16toUTF8(aData, creason);
         DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
 
-        nsString path;
-        file->mFile->GetPath(path);
-
-        unused << SendFilePathUpdate(file->mStorageType, path, creason);
+        unused << SendFilePathUpdate(file->mStorageType, file->mPath, creason);
     }
 #ifdef MOZ_WIDGET_GONK
     else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
         nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
         if (!vol) {
             return NS_ERROR_NOT_AVAILABLE;
         }
 
@@ -2344,25 +2341,21 @@ ContentParent::RecvAsyncMessage(const ns
     StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData);
     ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
                         aMsg, false, &cloneData, nullptr, nullptr);
   }
   return true;
 }
 
 bool
-ContentParent::RecvFilePathUpdateNotify(const nsString& aType, const nsString& aFilePath, const nsCString& aReason)
+ContentParent::RecvFilePathUpdateNotify(const nsString& aType,
+                                        const nsString& aFilePath,
+                                        const nsCString& aReason)
 {
-    nsCOMPtr<nsIFile> file;
-    nsresult rv = NS_NewLocalFile(aFilePath, false, getter_AddRefs(file));
-    if (NS_FAILED(rv)) {
-        // ignore
-        return true;
-    }
-    nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(aType, file);
+    nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(aType, aFilePath);
 
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (!obs) {
         return false;
     }
     obs->NotifyObservers(dsf, "file-watcher-update", NS_ConvertASCIItoUTF16(aReason).get());
     return true;
 }
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -58,56 +58,55 @@ struct FontListEntry {
     int16_t   stretch;
     uint8_t   italic;
     uint8_t   index;
 };
 
 struct DeviceStorageFreeSpaceParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
 };
 
 struct DeviceStorageUsedSpaceParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
 };
 
 struct DeviceStorageAvailableParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
 };
 
 struct DeviceStorageAddParams
 {
   nsString type;
+  nsString relpath;
   PBlob blob;
-  nsString name;
-  nsString fullpath;
 };
 
 struct DeviceStorageGetParams
 {
   nsString type;
-  nsString name;
-  nsString fullpath;
+  nsString rootDir;
+  nsString relpath;
 };
 
 struct DeviceStorageDeleteParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
 };
 
 struct DeviceStorageEnumerationParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
   uint64_t since;
 };
 
 union DeviceStorageParams
 {
   DeviceStorageAddParams;
   DeviceStorageGetParams;
   DeviceStorageDeleteParams;
--- a/dom/system/gonk/nsIVolumeService.idl
+++ b/dom/system/gonk/nsIVolumeService.idl
@@ -7,26 +7,27 @@
 #include "nsIVolumeMountLock.idl"
 
 %{C++
 #include "nsTArray.h"
 #include "nsString.h"
 %}
 [ref] native nsStringTArrayRef(nsTArray<nsString>);
 
-[scriptable, uuid(fc4e6449-922e-463d-880b-bfbab4909114)]
+[scriptable, uuid(7c179fb7-67a0-43a3-9337-294e0360b858)]
 interface nsIVolumeService : nsISupports
 {
     nsIVolume getVolumeByName(in DOMString volName);
     nsIVolume getVolumeByPath(in DOMString path);
+    nsIVolume createOrGetVolumeByPath(in DOMString path);
 
     void BroadcastVolume(in DOMString volName);
 
     nsIVolumeMountLock createMountLock(in DOMString volName);
 
     [noscript] void getVolumeNames(in nsStringTArrayRef aVolNames);
 };
 
 %{C++
 #define NS_VOLUMESERVICE_CID \
-  {0x597403c6, 0x5ba4, 0x4e7b, {0xb3, 0xf4, 0xed, 0x3f, 0x05, 0xf7, 0x75, 0xd8}}
+  {0x7c179fb7, 0x67a0, 0x43a3, {0x93, 0x37, 0x29, 0x4e, 0x03, 0x60, 0xb8, 0x58}}
 #define NS_VOLUMESERVICE_CONTRACTID "@mozilla.org/telephony/volume-service;1"
 %}
--- a/dom/system/gonk/nsVolumeService.cpp
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -49,18 +49,18 @@ StaticRefPtr<nsVolumeService> nsVolumeSe
 already_AddRefed<nsVolumeService>
 nsVolumeService::GetSingleton()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!sSingleton) {
     sSingleton = new nsVolumeService();
   }
-  NS_ADDREF(sSingleton.get());
-  return sSingleton.get();
+  nsRefPtr<nsVolumeService> volumeService = sSingleton.get();
+  return volumeService.forget();
 }
 
 // static
 void
 nsVolumeService::Shutdown()
 {
   if (!sSingleton) {
     return;
@@ -81,16 +81,17 @@ nsVolumeService::Shutdown()
   XRE_GetIOMessageLoop()->PostTask(
       FROM_HERE,
       NewRunnableFunction(ShutdownVolumeServiceIOThread));
 
   sSingleton = nullptr;
 }
 
 nsVolumeService::nsVolumeService()
+  : mArrayMonitor("nsVolumeServiceArray")
 {
   sSingleton = this;
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     // For child processes, we keep a cache of the volume state.
     nsCOMPtr<nsIObserverService> obs = GetObserverService();
     obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false /*weak*/);
 
@@ -113,45 +114,59 @@ nsVolumeService::nsVolumeService()
   pmService->AddWakeLockListener(this);
 }
 
 nsVolumeService::~nsVolumeService()
 {
 }
 
 // Callback for nsIDOMMozWakeLockListener
-NS_IMETHODIMP nsVolumeService::Callback(const nsAString& aTopic, const nsAString& aState)
+NS_IMETHODIMP
+nsVolumeService::Callback(const nsAString& aTopic, const nsAString& aState)
 {
   CheckMountLock(aTopic, aState);
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolumeService::BroadcastVolume(const nsAString& aVolName)
+NS_IMETHODIMP
+nsVolumeService::BroadcastVolume(const nsAString& aVolName)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
   if (aVolName.EqualsLiteral("")) {
+    nsVolume::Array volumeArray;
+    {
+      // Copy the array since we don't want to call BroadcastVolume
+      // while we're holding the lock.
+      MonitorAutoLock autoLock(mArrayMonitor);
+      volumeArray = mVolumeArray;
+    }
+
     // We treat being passed the empty string as "broadcast all volumes"
-    nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
+    nsVolume::Array::size_type numVolumes = volumeArray.Length();
     nsVolume::Array::index_type volIndex;
     for (volIndex = 0; volIndex < numVolumes; volIndex++) {
-      const nsString &volName(mVolumeArray[volIndex]->Name());
+      const nsString& volName(volumeArray[volIndex]->Name());
       if (!volName.EqualsLiteral("")) {
         // Note: The volume service is the only entity that should be able to
         // modify the array of volumes. So we shouldn't have any issues with
         // the array being modified under our feet (Since we're the volume
         // service the array can't change until after we finish iterating the
         // the loop).
         nsresult rv = BroadcastVolume(volName);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
     return NS_OK;
   }
-  nsRefPtr<nsVolume> vol = FindVolumeByName(aVolName);
+  nsRefPtr<nsVolume> vol;
+  {
+    MonitorAutoLock autoLock(mArrayMonitor);
+    vol = FindVolumeByName(aVolName);
+  }
   if (!vol) {
     ERR("BroadcastVolume: Unable to locate volume '%s'",
         NS_LossyConvertUTF16toASCII(aVolName).get());
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsCOMPtr<nsIObserverService> obs = GetObserverService();
   NS_ENSURE_TRUE(obs, NS_NOINTERFACE);
@@ -159,26 +174,29 @@ NS_IMETHODIMP nsVolumeService::Broadcast
   DBG("nsVolumeService::BroadcastVolume for '%s'", vol->NameStr().get());
   NS_ConvertUTF8toUTF16 stateStr(vol->StateStr());
   obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP nsVolumeService::GetVolumeByName(const nsAString& aVolName, nsIVolume **aResult)
 {
+  MonitorAutoLock autoLock(mArrayMonitor);
+
   nsRefPtr<nsVolume> vol = FindVolumeByName(aVolName);
   if (!vol) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  NS_ADDREF(*aResult = vol);
+  vol.forget(aResult);
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolumeService::GetVolumeByPath(const nsAString& aPath, nsIVolume **aResult)
+NS_IMETHODIMP
+nsVolumeService::GetVolumeByPath(const nsAString& aPath, nsIVolume **aResult)
 {
   nsCString utf8Path = NS_ConvertUTF16toUTF8(aPath);
   char realPathBuf[PATH_MAX];
 
   if (!realpath(utf8Path.get(), realPathBuf)) {
     ERR("GetVolumeByPath: realpath on '%s' failed: %d", utf8Path.get(), errno);
     return NSRESULT_FOR_ERRNO();
   }
@@ -188,131 +206,163 @@ NS_IMETHODIMP nsVolumeService::GetVolume
   // what realpath does), we basically check if aPath starts with the mount
   // point, but we don't want to have /mnt/sdcard match /mnt/sdcardfoo but we
   // do want it to match /mnt/sdcard/foo
   // So we add a trailing slash to the mount point and the pathname passed in
   // prior to doing the comparison.
 
   strlcat(realPathBuf, "/", sizeof(realPathBuf));
 
+  MonitorAutoLock autoLock(mArrayMonitor);
+
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
     nsAutoCString volMountPointSlash = NS_ConvertUTF16toUTF8(vol->MountPoint());
     volMountPointSlash.Append(NS_LITERAL_CSTRING("/"));
     nsDependentCSubstring testStr(realPathBuf, volMountPointSlash.Length());
     if (volMountPointSlash.Equals(testStr)) {
-      NS_ADDREF(*aResult = vol);
+      vol.forget(aResult);
       return NS_OK;
     }
   }
+  return NS_ERROR_FILE_NOT_FOUND;
+}
 
-  // In order to support queries by DeviceStorage and the updater, we will fabricate
-  // a volume from the pathname, so that the caller can determine the volume size
-  nsRefPtr<nsVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
-                                        aPath, nsIVolume::STATE_MOUNTED,
-                                        -1 /*generation*/);
-  NS_ADDREF(*aResult = vol);
+NS_IMETHODIMP
+nsVolumeService::CreateOrGetVolumeByPath(const nsAString& aPath, nsIVolume** aResult)
+{
+  nsresult rv = GetVolumeByPath(aPath, aResult);
+  if (rv == NS_OK) {
+    return NS_OK;
+  }
+
+  // In order to support queries by the updater, we will fabricate a volume
+  // from the pathname, so that the caller can determine the volume size.
+  nsCOMPtr<nsIVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
+                                         aPath, nsIVolume::STATE_MOUNTED,
+                                         -1 /*generation*/);
+  vol.forget(aResult);
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolumeService::GetVolumeNames(nsTArray<nsString>& aVolNames)
+NS_IMETHODIMP
+nsVolumeService::GetVolumeNames(nsTArray<nsString>& aVolNames)
 {
+  MonitorAutoLock autoLock(mArrayMonitor);
+
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
     aVolNames.AppendElement(vol->Name());
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolumeService::CreateMountLock(const nsAString& aVolumeName, nsIVolumeMountLock **aResult)
+NS_IMETHODIMP
+nsVolumeService::CreateMountLock(const nsAString& aVolumeName, nsIVolumeMountLock **aResult)
 {
-  nsRefPtr<nsVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
+  nsCOMPtr<nsIVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
   if (!mountLock) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-  NS_ADDREF(*aResult = mountLock);
+  mountLock.forget(aResult);
   return NS_OK;
 }
 
-void nsVolumeService::CheckMountLock(const nsAString& aMountLockName,
-                                     const nsAString& aMountLockState)
+void
+nsVolumeService::CheckMountLock(const nsAString& aMountLockName,
+                                const nsAString& aMountLockState)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   MOZ_ASSERT(NS_IsMainThread());
 
+  nsRefPtr<nsVolume> vol = FindVolumeByMountLockName(aMountLockName);
+  if (vol) {
+    vol->UpdateMountLock(aMountLockState);
+  }
+}
+
+already_AddRefed<nsVolume>
+nsVolumeService::FindVolumeByMountLockName(const nsAString& aMountLockName)
+{
+  MonitorAutoLock autoLock(mArrayMonitor);
+
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
     nsString mountLockName;
     vol->GetMountLockName(mountLockName);
     if (mountLockName.Equals(aMountLockName)) {
-      vol->UpdateMountLock(aMountLockState);
-      return;
+      return vol.forget();
     }
   }
+  return nullptr;
 }
 
-already_AddRefed<nsVolume> nsVolumeService::FindVolumeByName(const nsAString& aName)
+already_AddRefed<nsVolume>
+nsVolumeService::FindVolumeByName(const nsAString& aName)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  mArrayMonitor.AssertCurrentThreadOwns();
 
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
     if (vol->Name().Equals(aName)) {
       return vol.forget();
     }
   }
-  return NULL;
+  return nullptr;
 }
 
 //static
-already_AddRefed<nsVolume> nsVolumeService::FindAddVolumeByName(const nsAString& aName)
+already_AddRefed<nsVolume>
+nsVolumeService::CreateOrFindVolumeByName(const nsAString& aName)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  MonitorAutoLock autoLock(mArrayMonitor);
 
   nsRefPtr<nsVolume> vol;
   vol = FindVolumeByName(aName);
   if (vol) {
     return vol.forget();
   }
   // Volume not found - add a new one
   vol = new nsVolume(aName);
   mVolumeArray.AppendElement(vol);
   return vol.forget();
 }
 
-NS_IMETHODIMP nsVolumeService::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
+NS_IMETHODIMP
+nsVolumeService::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
 {
   if (strcmp(aTopic, NS_VOLUME_STATE_CHANGED) != 0) {
     return NS_OK;
   }
   MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
   nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
   if (!vol) {
     return NS_OK;
   }
   UpdateVolume(vol);
   return NS_OK;
 }
 
-void nsVolumeService::UpdateVolume(nsIVolume* aVolume)
+void
+nsVolumeService::UpdateVolume(nsIVolume* aVolume)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsString volName;
   aVolume->GetName(volName);
-  nsRefPtr<nsVolume> vol = FindAddVolumeByName(volName);
+  nsRefPtr<nsVolume> vol = CreateOrFindVolumeByName(volName);
   if (vol->Equals(aVolume)) {
     // Nothing has really changed. Don't bother telling anybody.
     return;
   }
   vol->Set(aVolume);
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     // Only the parent broadcasts the state changes
     return;
@@ -342,27 +392,28 @@ public:
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
     DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d",
         mVolume->NameStr().get(), mVolume->StateStr(),
         mVolume->MountGeneration(), (int)mVolume->IsMountLocked());
 
     mVolumeService->UpdateVolume(mVolume);
-    mVolumeService = NULL;
-    mVolume = NULL;
+    mVolumeService = nullptr;
+    mVolume = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsVolumeService> mVolumeService;
   nsRefPtr<nsVolume>        mVolume;
 };
 
-void nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
+void
+nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
 {
   DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d",
       aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(),
       aVolume->MountGeneration(), (int)aVolume->IsMountLocked());
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume));
 }
 
--- a/dom/system/gonk/nsVolumeService.h
+++ b/dom/system/gonk/nsVolumeService.h
@@ -1,15 +1,16 @@
 /* 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/. */
 
 #ifndef mozilla_system_nsvolumeservice_h__
 #define mozilla_system_nsvolumeservice_h__
 
+#include "mozilla/Monitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIObserver.h"
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
 #include "nsVolume.h"
@@ -37,26 +38,29 @@ public:
   NS_DECL_NSIDOMMOZWAKELOCKLISTENER
 
   nsVolumeService();
 
   static already_AddRefed<nsVolumeService> GetSingleton();
   //static nsVolumeService* GetSingleton();
   static void Shutdown();
 
-  void CheckMountLock(const nsAString& aMountLockName,
-                      const nsAString& aMountLockState);
-  already_AddRefed<nsVolume> FindVolumeByName(const nsAString& aName);
-  already_AddRefed<nsVolume> FindAddVolumeByName(const nsAString& aName);
   void UpdateVolume(nsIVolume* aVolume);
   void UpdateVolumeIOThread(const Volume* aVolume);
 
 private:
   ~nsVolumeService();
 
+  void CheckMountLock(const nsAString& aMountLockName,
+                      const nsAString& aMountLockState);
+  already_AddRefed<nsVolume> FindVolumeByMountLockName(const nsAString& aMountLockName);
+  already_AddRefed<nsVolume> FindVolumeByName(const nsAString& aName);
+  already_AddRefed<nsVolume> CreateOrFindVolumeByName(const nsAString& aName);
+
+  Monitor mArrayMonitor;
   nsVolume::Array mVolumeArray;
 
   static StaticRefPtr<nsVolumeService> sSingleton;
 };
 
 } // system
 } // mozilla