Bug 910498 - Changes to device storage to support CreateFd. r=bent
☠☠ backed out by ecd969426d00 ☠ ☠
authorDave Hylands <dhylands@mozilla.com>
Thu, 16 Jan 2014 17:01:27 -0800
changeset 163932 7ffefbeff08c759687893105f7f205702206f48c
parent 163931 f363dae6a41cffa765e51a6d30c74263c6a727a1
child 163933 64647f71727f1a1d7493daf61c85d54ede5d2a86
push id38584
push usercbook@mozilla.com
push dateFri, 17 Jan 2014 10:04:30 +0000
treeherdermozilla-inbound@28a9d7e2416f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs910498
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 910498 - Changes to device storage to support CreateFd. r=bent
dom/devicestorage/DeviceStorage.h
dom/devicestorage/DeviceStorageRequestChild.cpp
dom/devicestorage/DeviceStorageRequestChild.h
dom/devicestorage/DeviceStorageRequestParent.cpp
dom/devicestorage/DeviceStorageRequestParent.h
dom/devicestorage/PDeviceStorageRequest.ipdl
dom/devicestorage/nsDeviceStorage.cpp
dom/devicestorage/nsDeviceStorage.h
dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
dom/ipc/PContent.ipdl
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -9,35 +9,45 @@
 
 #include "nsIDOMDeviceStorage.h"
 #include "nsIFile.h"
 #include "nsIPrincipal.h"
 #include "nsIObserver.h"
 #include "nsDOMEventTargetHelper.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
-#include "DOMRequest.h"
+#include "mozilla/dom/DOMRequest.h"
+#include "mozilla/ipc/FileDescriptor.h"
 
 #define DEVICESTORAGE_PICTURES   "pictures"
 #define DEVICESTORAGE_VIDEOS     "videos"
 #define DEVICESTORAGE_MUSIC      "music"
 #define DEVICESTORAGE_APPS       "apps"
 #define DEVICESTORAGE_SDCARD     "sdcard"
 #define DEVICESTORAGE_CRASHES    "crashes"
 
+class DeviceStorageFile;
 class nsIInputStream;
 
 namespace mozilla {
 namespace dom {
 class DeviceStorageEnumerationParameters;
 class DOMCursor;
 class DOMRequest;
 } // namespace dom
 } // namespace mozilla
 
+class DeviceStorageFileDescriptor MOZ_FINAL
+  : public mozilla::RefCounted<DeviceStorageFileDescriptor>
+{
+public:
+  nsRefPtr<DeviceStorageFile> mDSFile;
+  mozilla::ipc::FileDescriptor mFileDescriptor;
+};
+
 class DeviceStorageFile MOZ_FINAL
   : public nsISupports {
 public:
   nsCOMPtr<nsIFile> mFile;
   nsString mStorageType;
   nsString mStorageName;
   nsString mRootDir;
   nsString mPath;
@@ -97,16 +107,17 @@ public:
   void GetStatus(nsAString& aStatus);
   void DoFormat(nsAString& aStatus);
   static void GetRootDirectoryForType(const nsAString& aStorageType,
                                       const nsAString& aStorageName,
                                       nsIFile** aFile);
 
   nsresult CalculateSizeAndModifiedDate();
   nsresult CalculateMimeType();
+  nsresult CreateFileDescriptor(mozilla::ipc::FileDescriptor& aFileDescriptor);
 
 private:
   void Init();
   void NormalizeFilePath();
   void AppendRelativePath(const nsAString& aPath);
   void AccumDirectoryUsage(nsIFile* aFile,
                            uint64_t* aPicturesSoFar,
                            uint64_t* aVideosSoFar,
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -15,21 +15,37 @@ namespace devicestorage {
 
 DeviceStorageRequestChild::DeviceStorageRequestChild()
   : mCallback(nullptr)
 {
   MOZ_COUNT_CTOR(DeviceStorageRequestChild);
 }
 
 DeviceStorageRequestChild::DeviceStorageRequestChild(DOMRequest* aRequest,
-                                                     DeviceStorageFile* aFile)
+                                                     DeviceStorageFile* aDSFile)
   : mRequest(aRequest)
-  , mFile(aFile)
+  , mDSFile(aDSFile)
   , mCallback(nullptr)
 {
+  MOZ_ASSERT(aRequest);
+  MOZ_ASSERT(aDSFile);
+  MOZ_COUNT_CTOR(DeviceStorageRequestChild);
+}
+
+DeviceStorageRequestChild::DeviceStorageRequestChild(DOMRequest* aRequest,
+                                                     DeviceStorageFile* aDSFile,
+                                                     DeviceStorageFileDescriptor* aDSFileDescriptor)
+  : mRequest(aRequest)
+  , mDSFile(aDSFile)
+  , mDSFileDescriptor(aDSFileDescriptor)
+  , mCallback(nullptr)
+{
+  MOZ_ASSERT(aRequest);
+  MOZ_ASSERT(aDSFile);
+  MOZ_ASSERT(aDSFileDescriptor);
   MOZ_COUNT_CTOR(DeviceStorageRequestChild);
 }
 
 DeviceStorageRequestChild::~DeviceStorageRequestChild() {
   MOZ_COUNT_DTOR(DeviceStorageRequestChild);
 }
 
 bool
@@ -48,24 +64,40 @@ DeviceStorageRequestChild::
       ErrorResponse r = aValue;
       mRequest->FireError(r.error());
       break;
     }
 
     case DeviceStorageResponseValue::TSuccessResponse:
     {
       nsString fullPath;
-      mFile->GetFullPath(fullPath);
+      mDSFile->GetFullPath(fullPath);
       AutoJSContext cx;
       JS::Rooted<JS::Value> result(cx,
         StringToJsval(mRequest->GetOwner(), fullPath));
       mRequest->FireSuccess(result);
       break;
     }
 
+    case DeviceStorageResponseValue::TFileDescriptorResponse:
+    {
+      FileDescriptorResponse r = aValue;
+
+      nsString fullPath;
+      mDSFile->GetFullPath(fullPath);
+      AutoJSContext cx;
+      JS::Rooted<JS::Value> result(cx,
+        StringToJsval(mRequest->GetOwner(), fullPath));
+
+      mDSFileDescriptor->mDSFile = mDSFile;
+      mDSFileDescriptor->mFileDescriptor = r.fileDescriptor();
+      mRequest->FireSuccess(result);
+      break;
+    }
+
     case DeviceStorageResponseValue::TBlobResponse:
     {
       BlobResponse r = aValue;
       BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
       nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob();
 
       nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
       AutoJSContext cx;
--- a/dom/devicestorage/DeviceStorageRequestChild.h
+++ b/dom/devicestorage/DeviceStorageRequestChild.h
@@ -2,44 +2,50 @@
 /* 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_dom_devicestorage_DeviceStorageRequestChild_h
 #define mozilla_dom_devicestorage_DeviceStorageRequestChild_h
 
 #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
-#include "DOMRequest.h"
 
 class DeviceStorageFile;
+class DeviceStorageFileDescriptor;
 
 namespace mozilla {
 namespace dom {
+
+class DOMRequest;
+
 namespace devicestorage {
 
 class DeviceStorageRequestChildCallback
 {
   public:
     virtual void RequestComplete() = 0;
 };
 
 class DeviceStorageRequestChild : public PDeviceStorageRequestChild
 {
 public:
   DeviceStorageRequestChild();
   DeviceStorageRequestChild(DOMRequest* aRequest, DeviceStorageFile* aFile);
+  DeviceStorageRequestChild(DOMRequest* aRequest, DeviceStorageFile* aFile,
+                            DeviceStorageFileDescriptor* aFileDescrptor);
   ~DeviceStorageRequestChild();
 
   void SetCallback(class DeviceStorageRequestChildCallback *aCallback);
 
   virtual bool Recv__delete__(const DeviceStorageResponseValue& value);
 
 private:
   nsRefPtr<DOMRequest> mRequest;
-  nsRefPtr<DeviceStorageFile> mFile;
+  nsRefPtr<DeviceStorageFile> mDSFile;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
 
   DeviceStorageRequestChildCallback* mCallback;
 };
 
 } // namespace devicestorage
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -30,16 +30,18 @@ DeviceStorageRequestParent::DeviceStorag
   DebugOnly<DeviceStorageUsedSpaceCache*> usedSpaceCache
     = DeviceStorageUsedSpaceCache::CreateOrGet();
   NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
 }
 
 void
 DeviceStorageRequestParent::Dispatch()
 {
+  nsresult rv;
+
   switch (mParams.type()) {
     case DeviceStorageParams::TDeviceStorageAddParams:
     {
       DeviceStorageAddParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName(), p.relpath());
 
@@ -53,16 +55,32 @@ 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::TDeviceStorageCreateFdParams:
+    {
+      DeviceStorageCreateFdParams p = mParams;
+
+      nsRefPtr<DeviceStorageFile> dsf =
+        new DeviceStorageFile(p.type(), p.storageName(), p.relpath());
+
+      nsRefPtr<CancelableRunnable> r = new CreateFdEvent(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::TDeviceStorageGetParams:
     {
       DeviceStorageGetParams p = mParams;
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName(),
                               p.rootDir(), p.relpath());
       nsRefPtr<CancelableRunnable> r = new ReadFileEvent(this, dsf);
 
@@ -122,29 +140,31 @@ DeviceStorageRequestParent::Dispatch()
     case DeviceStorageParams::TDeviceStorageAvailableParams:
     {
       DeviceStorageAvailableParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName());
       nsRefPtr<PostAvailableResultEvent> r
         = new PostAvailableResultEvent(this, dsf);
-      NS_DispatchToMainThread(r);
+      rv = NS_DispatchToMainThread(r);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageFormatParams:
     {
       DeviceStorageFormatParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName());
       nsRefPtr<PostFormatResultEvent> r
         = new PostFormatResultEvent(this, dsf);
-      NS_DispatchToMainThread(r);
+      rv = NS_DispatchToMainThread(r);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageEnumerationParams:
     {
       DeviceStorageEnumerationParams p = mParams;
       nsRefPtr<DeviceStorageFile> dsf
         = new DeviceStorageFile(p.type(), p.storageName(),
@@ -182,16 +202,24 @@ DeviceStorageRequestParent::EnsureRequir
     case DeviceStorageParams::TDeviceStorageAddParams:
     {
       DeviceStorageAddParams p = mParams;
       type = p.type();
       requestType = DEVICE_STORAGE_REQUEST_CREATE;
       break;
     }
 
+    case DeviceStorageParams::TDeviceStorageCreateFdParams:
+    {
+      DeviceStorageCreateFdParams p = mParams;
+      type = p.type();
+      requestType = DEVICE_STORAGE_REQUEST_CREATEFD;
+      break;
+    }
+
     case DeviceStorageParams::TDeviceStorageGetParams:
     {
       DeviceStorageGetParams p = mParams;
       type = p.type();
       requestType = DEVICE_STORAGE_REQUEST_READ;
       break;
     }
 
@@ -439,16 +467,56 @@ nsresult
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   EnumerationResponse response(mStorageType, mRelPath, mPaths);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
+DeviceStorageRequestParent::CreateFdEvent::
+  CreateFdEvent(DeviceStorageRequestParent* aParent,
+                 DeviceStorageFile* aFile)
+  : CancelableRunnable(aParent)
+  , mFile(aFile)
+{
+}
+
+DeviceStorageRequestParent::CreateFdEvent::~CreateFdEvent()
+{
+}
+
+nsresult
+DeviceStorageRequestParent::CreateFdEvent::CancelableRun()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<nsRunnable> r;
+
+  bool check = false;
+  mFile->mFile->Exists(&check);
+  if (check) {
+    nsCOMPtr<PostErrorEvent> event
+      = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS);
+    return NS_DispatchToMainThread(event);
+  }
+
+  FileDescriptor fileDescriptor;
+  nsresult rv = mFile->CreateFileDescriptor(fileDescriptor);
+
+  if (NS_FAILED(rv)) {
+    r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
+  }
+  else {
+    r = new PostFileDescriptorResultEvent(mParent, fileDescriptor);
+  }
+
+  return NS_DispatchToMainThread(r);
+}
+
 DeviceStorageRequestParent::WriteFileEvent::
   WriteFileEvent(DeviceStorageRequestParent* aParent,
                  DeviceStorageFile* aFile,
                  nsIInputStream* aInputStream)
   : CancelableRunnable(aParent)
   , mFile(aFile)
   , mInputStream(aInputStream)
 {
@@ -462,43 +530,39 @@ nsresult
 DeviceStorageRequestParent::WriteFileEvent::CancelableRun()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   nsRefPtr<nsRunnable> r;
 
   if (!mInputStream) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   bool check = false;
   mFile->mFile->Exists(&check);
   if (check) {
     nsCOMPtr<PostErrorEvent> event
       = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS);
-    NS_DispatchToMainThread(event);
-    return NS_OK;
+    return NS_DispatchToMainThread(event);
   }
 
   nsresult rv = mFile->Write(mInputStream);
 
   if (NS_FAILED(rv)) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
   }
   else {
     r = new PostPathResultEvent(mParent, mFile->mPath);
   }
 
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
-
 DeviceStorageRequestParent::DeleteFileEvent::
   DeleteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
 }
 
 DeviceStorageRequestParent::DeleteFileEvent::~DeleteFileEvent()
@@ -518,18 +582,17 @@ DeviceStorageRequestParent::DeleteFileEv
   mFile->mFile->Exists(&check);
   if (check) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
   }
   else {
     r = new PostPathResultEvent(mParent, mFile->mPath);
   }
 
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 DeviceStorageRequestParent::FreeSpaceFileEvent::
   FreeSpaceFileEvent(DeviceStorageRequestParent* aParent,
                      DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
@@ -546,18 +609,17 @@ DeviceStorageRequestParent::FreeSpaceFil
 
   int64_t freeSpace = 0;
   if (mFile) {
     mFile->GetDiskFreeSpace(&freeSpace);
   }
 
   nsCOMPtr<nsIRunnable> r;
   r = new PostFreeSpaceResultEvent(mParent, static_cast<uint64_t>(freeSpace));
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 DeviceStorageRequestParent::UsedSpaceFileEvent::
   UsedSpaceFileEvent(DeviceStorageRequestParent* aParent,
                      DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
@@ -583,18 +645,17 @@ DeviceStorageRequestParent::UsedSpaceFil
   else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
     r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, videosUsage);
   }
   else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
     r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, musicUsage);
   } else {
     r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, totalUsage);
   }
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 DeviceStorageRequestParent::ReadFileEvent::
   ReadFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
   nsCOMPtr<nsIMIMEService> mimeService
@@ -617,40 +678,36 @@ DeviceStorageRequestParent::ReadFileEven
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIRunnable> r;
   bool check = false;
   mFile->mFile->Exists(&check);
 
   if (!check) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   int64_t fileSize;
   nsresult rv = mFile->mFile->GetFileSize(&fileSize);
   if (NS_FAILED(rv)) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   PRTime modDate;
   rv = mFile->mFile->GetLastModifiedTime(&modDate);
   if (NS_FAILED(rv)) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   r = new PostBlobSuccessEvent(mParent, mFile, static_cast<uint64_t>(fileSize),
                                mMimeType, modDate);
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 DeviceStorageRequestParent::EnumerateFileEvent::
   EnumerateFileEvent(DeviceStorageRequestParent* aParent,
                      DeviceStorageFile* aFile,
                      uint64_t aSince)
   : CancelableRunnable(aParent)
   , mFile(aFile)
@@ -668,36 +725,34 @@ DeviceStorageRequestParent::EnumerateFil
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIRunnable> r;
   if (mFile->mFile) {
     bool check = false;
     mFile->mFile->Exists(&check);
     if (!check) {
       r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
-      NS_DispatchToMainThread(r);
-      return NS_OK;
+      return NS_DispatchToMainThread(r);
     }
   }
 
   nsTArray<nsRefPtr<DeviceStorageFile> > files;
   mFile->CollectFiles(files, mSince);
 
   InfallibleTArray<DeviceStorageFileValue> values;
 
   uint32_t count = files.Length();
   for (uint32_t i = 0; i < count; i++) {
     DeviceStorageFileValue dsvf(files[i]->mStorageName, files[i]->mPath);
     values.AppendElement(dsvf);
   }
 
   r = new PostEnumerationSuccessEvent(mParent, mFile->mStorageType,
                                       mFile->mRootDir, values);
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 
 DeviceStorageRequestParent::PostPathResultEvent::
   PostPathResultEvent(DeviceStorageRequestParent* aParent,
                       const nsAString& aPath)
   : CancelableRunnable(aParent)
   , mPath(aPath)
@@ -713,16 +768,39 @@ DeviceStorageRequestParent::PostPathResu
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   SuccessResponse response;
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
+DeviceStorageRequestParent::PostFileDescriptorResultEvent::
+  PostFileDescriptorResultEvent(DeviceStorageRequestParent* aParent,
+                                const FileDescriptor& aFileDescriptor)
+  : CancelableRunnable(aParent)
+  , mFileDescriptor(aFileDescriptor)
+{
+}
+
+DeviceStorageRequestParent::PostFileDescriptorResultEvent::
+  ~PostFileDescriptorResultEvent()
+{
+}
+
+nsresult
+DeviceStorageRequestParent::PostFileDescriptorResultEvent::CancelableRun()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  FileDescriptorResponse response(mFileDescriptor);
+  unused << mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
 DeviceStorageRequestParent::PostAvailableResultEvent::
   PostAvailableResultEvent(DeviceStorageRequestParent* aParent,
                            DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
 }
 
--- a/dom/devicestorage/DeviceStorageRequestParent.h
+++ b/dom/devicestorage/DeviceStorageRequestParent.h
@@ -114,16 +114,26 @@ private:
       virtual ~PostEnumerationSuccessEvent();
       virtual nsresult CancelableRun();
     private:
       const nsString mStorageType;
       const nsString mRelPath;
       InfallibleTArray<DeviceStorageFileValue> mPaths;
   };
 
+  class CreateFdEvent : public CancelableRunnable
+  {
+    public:
+      CreateFdEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
+      virtual ~CreateFdEvent();
+      virtual nsresult CancelableRun();
+    private:
+      nsRefPtr<DeviceStorageFile> mFile;
+  };
+
   class WriteFileEvent : public CancelableRunnable
   {
     public:
       WriteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, nsIInputStream* aInputStream);
       virtual ~WriteFileEvent();
       virtual nsresult CancelableRun();
     private:
       nsRefPtr<DeviceStorageFile> mFile;
@@ -188,16 +198,28 @@ private:
       PostPathResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath);
       virtual ~PostPathResultEvent();
       virtual nsresult CancelableRun();
     private:
       nsRefPtr<DeviceStorageFile> mFile;
       nsString mPath;
   };
 
+  class PostFileDescriptorResultEvent : public CancelableRunnable
+  {
+    public:
+      PostFileDescriptorResultEvent(DeviceStorageRequestParent* aParent,
+                                    const FileDescriptor& aFileDescriptor);
+      virtual ~PostFileDescriptorResultEvent();
+      virtual nsresult CancelableRun();
+    private:
+      nsRefPtr<DeviceStorageFile> mFile;
+      FileDescriptor mFileDescriptor;
+  };
+
  class PostFreeSpaceResultEvent : public CancelableRunnable
  {
     public:
       PostFreeSpaceResultEvent(DeviceStorageRequestParent* aParent,
                                uint64_t aFreeSpace);
       virtual ~PostFreeSpaceResultEvent();
       virtual nsresult CancelableRun();
     private:
--- a/dom/devicestorage/PDeviceStorageRequest.ipdl
+++ b/dom/devicestorage/PDeviceStorageRequest.ipdl
@@ -15,16 +15,21 @@ struct ErrorResponse
 {
   nsString error;
 };
 
 struct SuccessResponse
 {
 };
 
+struct FileDescriptorResponse
+{
+  FileDescriptor fileDescriptor;
+};
+
 struct BlobResponse
 {
   PBlob blob;
 };
 
 struct DeviceStorageFileValue
 {
   nsString storageName;
@@ -57,16 +62,17 @@ struct FormatStorageResponse
 {
   nsString mountState;
 };
 
 union DeviceStorageResponseValue
 {
   ErrorResponse;
   SuccessResponse;
+  FileDescriptorResponse;
   BlobResponse;
   EnumerationResponse;
   FreeSpaceStorageResponse;
   UsedSpaceStorageResponse;
   AvailableStorageResponse;
   FormatStorageResponse;
 };
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -44,16 +44,17 @@
 #include "nsIObserverService.h"
 #include "GeneratedEvents.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIPermissionManager.h"
 #include "nsIStringBundle.h"
 #include "nsIDocument.h"
 #include <algorithm>
+#include "private/pprio.h"
 
 #include "mozilla/dom/DeviceStorageBinding.h"
 
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolume.h"
@@ -62,16 +63,17 @@
 
 #define DEVICESTORAGE_PROPERTIES \
   "chrome://global/content/devicestorage.properties"
 #define DEFAULT_THREAD_TIMEOUT_MS 30000
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
+using namespace mozilla::ipc;
 
 #include "nsDirectoryServiceDefs.h"
 
 StaticAutoPtr<DeviceStorageUsedSpaceCache>
   DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
 
 DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache()
 {
@@ -389,16 +391,17 @@ DeviceStorageTypeChecker::GetAccessForRe
       aAccessResult.AssignLiteral("read");
       break;
     case DEVICE_STORAGE_REQUEST_WRITE:
     case DEVICE_STORAGE_REQUEST_DELETE:
     case DEVICE_STORAGE_REQUEST_FORMAT:
       aAccessResult.AssignLiteral("write");
       break;
     case DEVICE_STORAGE_REQUEST_CREATE:
+    case DEVICE_STORAGE_REQUEST_CREATEFD:
       aAccessResult.AssignLiteral("create");
       break;
     default:
       aAccessResult.AssignLiteral("undefined");
   }
   return NS_OK;
 }
 
@@ -919,29 +922,46 @@ DeviceStorageFile::AppendRelativePath(co
   }
   mFile->AppendRelativePath(temp);
 #else
   mFile->AppendRelativePath(aPath);
 #endif
 }
 
 nsresult
+DeviceStorageFile::CreateFileDescriptor(FileDescriptor& aFileDescriptor)
+{
+  PRFileDesc* fd;
+  nsresult rv = mFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE,
+                                        0660, &fd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aFileDescriptor =
+    FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd));
+
+  return NS_OK;
+}
+
+nsresult
 DeviceStorageFile::Write(nsIInputStream* aInputStream)
 {
   if (!aInputStream || !mFile) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "created");
-  NS_DispatchToMainThread(iocomplete);
+  rv = NS_DispatchToMainThread(iocomplete);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   uint64_t bufSize = 0;
   aInputStream->Available(&bufSize);
 
   nsCOMPtr<nsIOutputStream> outputStream;
   NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
 
   if (!outputStream) {
@@ -962,54 +982,63 @@ DeviceStorageFile::Write(nsIInputStream*
       &wrote);
     if (NS_FAILED(rv)) {
       break;
     }
     bufSize -= wrote;
   }
 
   iocomplete = new IOEventComplete(this, "modified");
-  NS_DispatchToMainThread(iocomplete);
+  rv = NS_DispatchToMainThread(iocomplete);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   bufferedOutputStream->Close();
   outputStream->Close();
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 DeviceStorageFile::Write(InfallibleTArray<uint8_t>& aBits)
 {
   if (!mFile) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "created");
-  NS_DispatchToMainThread(iocomplete);
+  rv = NS_DispatchToMainThread(iocomplete);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   nsCOMPtr<nsIOutputStream> outputStream;
   NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
 
   if (!outputStream) {
     return NS_ERROR_FAILURE;
   }
 
   uint32_t wrote;
   outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote);
   outputStream->Close();
 
   iocomplete = new IOEventComplete(this, "modified");
-  NS_DispatchToMainThread(iocomplete);
+  rv = NS_DispatchToMainThread(iocomplete);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   if (aBits.Length() != wrote) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
@@ -1027,23 +1056,22 @@ DeviceStorageFile::Remove()
     return rv;
   }
 
   if (!check) {
     return NS_OK;
   }
 
   rv = mFile->Remove(true);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "deleted");
-  NS_DispatchToMainThread(iocomplete);
-  return NS_OK;
+  return NS_DispatchToMainThread(iocomplete);
 }
 
 nsresult
 DeviceStorageFile::CalculateMimeType()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoCString mimeType;
@@ -1111,17 +1139,17 @@ DeviceStorageFile::collectFilesInternal(
 
   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
 
     PRTime msecs;
     f->GetLastModifiedTime(&msecs);
 
     if (msecs < aSince) {
       continue;
-     }
+    }
 
     bool isDir;
     f->IsDirectory(&isDir);
 
     bool isFile;
     f->IsFile(&isFile);
 
     nsString fullpath;
@@ -1614,26 +1642,30 @@ ContinueCursorEvent::GetNextFile()
   return nullptr;
 }
 
 ContinueCursorEvent::~ContinueCursorEvent() {}
 
 void
 ContinueCursorEvent::Continue()
 {
+  nsresult rv;
+
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
-    NS_DispatchToMainThread(this);
+    rv = NS_DispatchToMainThread(this);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
     return;
   }
 
   nsRefPtr<DeviceStorageFile> file = GetNextFile();
 
   if (!file) {
     // done with enumeration.
-    NS_DispatchToMainThread(this);
+    rv = NS_DispatchToMainThread(this);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
     return;
   }
 
   nsDOMDeviceStorageCursor* cursor
     = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
   nsString cursorStorageType;
   cursor->GetStorageType(cursorStorageType);
 
@@ -1686,18 +1718,17 @@ public:
 
     if (mFile->mFile) {
       bool check;
       mFile->mFile->IsDirectory(&check);
       if (!check) {
         nsCOMPtr<PostErrorEvent> event =
           new PostErrorEvent(mRequest.forget(),
                              POST_ERROR_EVENT_FILE_NOT_ENUMERABLE);
-        NS_DispatchToMainThread(event);
-        return NS_OK;
+        return NS_DispatchToMainThread(event);
       }
     }
 
     nsDOMDeviceStorageCursor* cursor
       = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
     mFile->CollectFiles(cursor->mFiles, cursor->mSince);
 
     nsCOMPtr<ContinueCursorEvent> event
@@ -1777,28 +1808,26 @@ nsDOMDeviceStorageCursor::GetElement(nsI
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorageCursor::Cancel()
 {
   nsCOMPtr<PostErrorEvent> event
     = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
-  NS_DispatchToMainThread(event);
-  return NS_OK;
+  return NS_DispatchToMainThread(event);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorageCursor::Allow()
 {
   if (!mFile->IsSafePath()) {
     nsCOMPtr<nsIRunnable> r
       = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     PDeviceStorageRequestChild* child
       = new DeviceStorageRequestChild(this, mFile);
     DeviceStorageEnumerationParams params(mFile->mStorageType,
                                           mFile->mStorageName,
                                           mFile->mRootDir,
@@ -1865,16 +1894,17 @@ nsDOMDeviceStorageCursor::RequestComplet
 
 class PostAvailableResultEvent : public nsRunnable
 {
 public:
   PostAvailableResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
   {
+    MOZ_ASSERT(mRequest);
   }
 
   ~PostAvailableResultEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -1898,16 +1928,17 @@ private:
 
 class PostFormatResultEvent : public nsRunnable
 {
 public:
   PostFormatResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
   {
+    MOZ_ASSERT(mRequest);
   }
 
   ~PostFormatResultEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -1931,29 +1962,35 @@ private:
 
 class PostResultEvent : public nsRunnable
 {
 public:
   PostResultEvent(already_AddRefed<DOMRequest> aRequest,
                   DeviceStorageFile* aFile)
     : mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mRequest);
+  }
 
   PostResultEvent(already_AddRefed<DOMRequest> aRequest,
                   const nsAString & aPath)
     : mPath(aPath)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mRequest);
+  }
 
   PostResultEvent(already_AddRefed<DOMRequest> aRequest,
                   const uint64_t aValue)
     : mValue(aValue)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mRequest);
+  }
 
   ~PostResultEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     AutoJSContext cx;
@@ -1976,78 +2013,131 @@ public:
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsString mPath;
   uint64_t mValue;
   nsRefPtr<DOMRequest> mRequest;
 };
 
+class CreateFdEvent : public nsRunnable
+{
+public:
+  CreateFdEvent(DeviceStorageFileDescriptor* aDSFileDescriptor,
+                already_AddRefed<DOMRequest> aRequest)
+    : mDSFileDescriptor(aDSFileDescriptor)
+    , mRequest(aRequest)
+  {
+    MOZ_ASSERT(mDSFileDescriptor);
+    MOZ_ASSERT(mRequest);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    DeviceStorageFile* dsFile = mDSFileDescriptor->mDSFile;
+    MOZ_ASSERT(dsFile);
+
+    nsString fullPath;
+    dsFile->GetFullPath(fullPath);
+    MOZ_ASSERT(!fullPath.IsEmpty());
+
+    bool check = false;
+    dsFile->mFile->Exists(&check);
+    if (check) {
+      nsCOMPtr<PostErrorEvent> event =
+        new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
+      return NS_DispatchToMainThread(event);
+    }
+
+    nsresult rv = dsFile->CreateFileDescriptor(mDSFileDescriptor->mFileDescriptor);
+
+    if (NS_FAILED(rv)) {
+      dsFile->mFile->Remove(false);
+
+      nsCOMPtr<PostErrorEvent> event =
+        new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
+      return NS_DispatchToMainThread(event);
+    }
+
+    nsCOMPtr<PostResultEvent> event =
+      new PostResultEvent(mRequest.forget(), fullPath);
+    return NS_DispatchToMainThread(event);
+  }
+
+private:
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
+  nsRefPtr<DOMRequest> mRequest;
+};
+
 class WriteFileEvent : public nsRunnable
 {
 public:
   WriteFileEvent(nsIDOMBlob* aBlob,
                  DeviceStorageFile *aFile,
                  already_AddRefed<DOMRequest> aRequest)
     : mBlob(aBlob)
     , mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
 
   ~WriteFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsCOMPtr<nsIInputStream> stream;
     mBlob->GetInternalStream(getter_AddRefs(stream));
 
     bool check = false;
     mFile->mFile->Exists(&check);
     if (check) {
       nsCOMPtr<PostErrorEvent> event =
         new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
-      NS_DispatchToMainThread(event);
-      return NS_OK;
+      return NS_DispatchToMainThread(event);
     }
 
     nsresult rv = mFile->Write(stream);
 
     if (NS_FAILED(rv)) {
       mFile->mFile->Remove(false);
 
       nsCOMPtr<PostErrorEvent> event =
         new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
-      NS_DispatchToMainThread(event);
-      return NS_OK;
+      return NS_DispatchToMainThread(event);
     }
 
     nsString fullPath;
     mFile->GetFullPath(fullPath);
     nsCOMPtr<PostResultEvent> event =
       new PostResultEvent(mRequest.forget(), fullPath);
-    NS_DispatchToMainThread(event);
-    return NS_OK;
+    return NS_DispatchToMainThread(event);
   }
 
 private:
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class ReadFileEvent : public nsRunnable
 {
 public:
   ReadFileEvent(DeviceStorageFile* aFile,
                 already_AddRefed<DOMRequest> aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
   {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
     mFile->CalculateMimeType();
   }
 
   ~ReadFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
@@ -2067,33 +2157,35 @@ public:
       if (NS_FAILED(rv)) {
         r = new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
       }
     }
 
     if (!r) {
       r = new PostResultEvent(mRequest.forget(), mFile);
     }
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class DeleteFileEvent : public nsRunnable
 {
 public:
   DeleteFileEvent(DeviceStorageFile* aFile,
                   already_AddRefed<DOMRequest> aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
 
   ~DeleteFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
     mFile->Remove();
 
@@ -2104,33 +2196,35 @@ public:
       r = new PostErrorEvent(mRequest.forget(),
                              POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
     }
     else {
       nsString fullPath;
       mFile->GetFullPath(fullPath);
       r = new PostResultEvent(mRequest.forget(), fullPath);
     }
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class UsedSpaceFileEvent : public nsRunnable
 {
 public:
   UsedSpaceFileEvent(DeviceStorageFile* aFile,
                      already_AddRefed<DOMRequest> aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
 
   ~UsedSpaceFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0;
@@ -2143,90 +2237,124 @@ public:
     else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
       r = new PostResultEvent(mRequest.forget(), videosUsage);
     }
     else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
       r = new PostResultEvent(mRequest.forget(), musicUsage);
     } else {
       r = new PostResultEvent(mRequest.forget(), totalUsage);
     }
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class FreeSpaceFileEvent : public nsRunnable
 {
 public:
   FreeSpaceFileEvent(DeviceStorageFile* aFile,
                      already_AddRefed<DOMRequest> aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
 
   ~FreeSpaceFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     int64_t freeSpace = 0;
     if (mFile) {
       mFile->GetDiskFreeSpace(&freeSpace);
     }
 
     nsCOMPtr<nsIRunnable> r;
     r = new PostResultEvent(mRequest.forget(),
                             static_cast<uint64_t>(freeSpace));
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class DeviceStorageRequest MOZ_FINAL
   : public nsIContentPermissionRequest
   , public nsIRunnable
   , public PCOMContentPermissionRequestChild
 {
 public:
 
-    DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
-                         nsPIDOMWindow *aWindow,
-                         nsIPrincipal *aPrincipal,
-                         DeviceStorageFile *aFile,
-                         DOMRequest* aRequest,
-                         nsDOMDeviceStorage *aDeviceStorage)
-      : mRequestType(aRequestType)
-      , mWindow(aWindow)
-      , mPrincipal(aPrincipal)
-      , mFile(aFile)
-      , mRequest(aRequest)
-      , mDeviceStorage(aDeviceStorage)
-    {}
-
-    DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
-                         nsPIDOMWindow *aWindow,
-                         nsIPrincipal *aPrincipal,
-                         DeviceStorageFile *aFile,
-                         DOMRequest* aRequest,
-                         nsIDOMBlob *aBlob = nullptr)
-      : mRequestType(aRequestType)
-      , mWindow(aWindow)
-      , mPrincipal(aPrincipal)
-      , mFile(aFile)
-      , mRequest(aRequest)
-      , mBlob(aBlob) {}
+  DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
+                       nsPIDOMWindow* aWindow,
+                       nsIPrincipal* aPrincipal,
+                       DeviceStorageFile* aFile,
+                       DOMRequest* aRequest,
+                       nsDOMDeviceStorage* aDeviceStorage)
+    : mRequestType(aRequestType)
+    , mWindow(aWindow)
+    , mPrincipal(aPrincipal)
+    , mFile(aFile)
+    , mRequest(aRequest)
+    , mDeviceStorage(aDeviceStorage)
+  {
+    MOZ_ASSERT(mWindow);
+    MOZ_ASSERT(mPrincipal);
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+    MOZ_ASSERT(mDeviceStorage);
+  }
+
+  DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
+                       nsPIDOMWindow* aWindow,
+                       nsIPrincipal* aPrincipal,
+                       DeviceStorageFile* aFile,
+                       DOMRequest* aRequest,
+                       nsIDOMBlob* aBlob = nullptr)
+    : mRequestType(aRequestType)
+    , mWindow(aWindow)
+    , mPrincipal(aPrincipal)
+    , mFile(aFile)
+    , mRequest(aRequest)
+    , mBlob(aBlob)
+  {
+    MOZ_ASSERT(mWindow);
+    MOZ_ASSERT(mPrincipal);
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
+
+  DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
+                       nsPIDOMWindow* aWindow,
+                       nsIPrincipal* aPrincipal,
+                       DeviceStorageFile* aFile,
+                       DOMRequest* aRequest,
+                       DeviceStorageFileDescriptor* aDSFileDescriptor)
+    : mRequestType(aRequestType)
+    , mWindow(aWindow)
+    , mPrincipal(aPrincipal)
+    , mFile(aFile)
+    , mRequest(aRequest)
+    , mDSFileDescriptor(aDSFileDescriptor)
+  {
+    MOZ_ASSERT(mRequestType == DEVICE_STORAGE_REQUEST_CREATEFD);
+    MOZ_ASSERT(mWindow);
+    MOZ_ASSERT(mPrincipal);
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+    MOZ_ASSERT(mDSFileDescriptor);
+  }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageRequest,
                                            nsIContentPermissionRequest)
 
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -2316,47 +2444,86 @@ public:
     return NS_OK;
   }
 
   NS_IMETHOD Cancel()
   {
     nsCOMPtr<PostErrorEvent> event
       = new PostErrorEvent(mRequest.forget(),
                            POST_ERROR_EVENT_PERMISSION_DENIED);
-    NS_DispatchToMainThread(event);
-    return NS_OK;
+    return NS_DispatchToMainThread(event);
   }
 
   NS_IMETHOD Allow()
   {
-    nsCOMPtr<nsIRunnable> r;
+    MOZ_ASSERT(NS_IsMainThread());
 
     if (!mRequest) {
       return NS_ERROR_FAILURE;
     }
 
+    nsCOMPtr<nsIRunnable> r;
+
     switch(mRequestType) {
+      case DEVICE_STORAGE_REQUEST_CREATEFD:
+      {
+        if (!mFile->mFile) {
+          return NS_ERROR_FAILURE;
+        }
+
+        DeviceStorageTypeChecker* typeChecker
+          = DeviceStorageTypeChecker::CreateOrGet();
+        if (!typeChecker) {
+          return NS_OK;
+        }
+
+        if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
+          r = new PostErrorEvent(mRequest.forget(),
+                                 POST_ERROR_EVENT_ILLEGAL_TYPE);
+          return NS_DispatchToCurrentThread(r);
+        }
+
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+
+          DeviceStorageCreateFdParams params;
+          params.type() = mFile->mStorageType;
+          params.storageName() = mFile->mStorageName;
+          params.relpath() = mFile->mPath;
+
+          mFile->Dump("DeviceStorageCreateFdParams");
+
+          PDeviceStorageRequestChild* child
+            = new DeviceStorageRequestChild(mRequest, mFile,
+                                            mDSFileDescriptor.get());
+          ContentChild::GetSingleton()
+            ->SendPDeviceStorageRequestConstructor(child, params);
+          return NS_OK;
+        }
+        mDSFileDescriptor->mDSFile = mFile;
+        r = new CreateFdEvent(mDSFileDescriptor.get(), mRequest.forget());
+        break;
+      }
+
       case DEVICE_STORAGE_REQUEST_CREATE:
       {
         if (!mBlob || !mFile->mFile) {
           return NS_ERROR_FAILURE;
         }
 
         DeviceStorageTypeChecker* typeChecker
           = DeviceStorageTypeChecker::CreateOrGet();
         if (!typeChecker) {
           return NS_OK;
         }
 
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile) ||
             !typeChecker->Check(mFile->mStorageType, mBlob)) {
           r = new PostErrorEvent(mRequest.forget(),
                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
-          NS_DispatchToMainThread(r);
-          return NS_OK;
+          return NS_DispatchToCurrentThread(r);
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
 
           BlobChild* actor
             = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlob);
           if (!actor) {
             return NS_ERROR_FAILURE;
@@ -2389,18 +2556,17 @@ public:
           = DeviceStorageTypeChecker::CreateOrGet();
         if (!typeChecker) {
           return NS_OK;
         }
 
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
           r = new PostErrorEvent(mRequest.forget(),
                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
-          NS_DispatchToMainThread(r);
-          return NS_OK;
+          return NS_DispatchToCurrentThread(r);
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child
             = new DeviceStorageRequestChild(mRequest, mFile);
           DeviceStorageGetParams params(mFile->mStorageType,
                                         mFile->mStorageName,
                                         mFile->mRootDir,
@@ -2424,18 +2590,17 @@ public:
           = DeviceStorageTypeChecker::CreateOrGet();
         if (!typeChecker) {
           return NS_OK;
         }
 
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
           r = new PostErrorEvent(mRequest.forget(),
                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
-          NS_DispatchToMainThread(r);
-          return NS_OK;
+          return NS_DispatchToCurrentThread(r);
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child
             = new DeviceStorageRequestChild(mRequest, mFile);
           DeviceStorageDeleteParams params(mFile->mStorageType,
                                            mFile->mStorageName,
                                            mFile->mPath);
@@ -2490,18 +2655,17 @@ public:
             = new DeviceStorageRequestChild(mRequest, mFile);
           DeviceStorageAvailableParams params(mFile->mStorageType,
                                               mFile->mStorageName);
           ContentChild::GetSingleton()
             ->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new PostAvailableResultEvent(mFile, mRequest);
-        NS_DispatchToMainThread(r);
-        return NS_OK;
+        return NS_DispatchToCurrentThread(r);
       }
 
       case DEVICE_STORAGE_REQUEST_WATCH:
       {
         mDeviceStorage->mAllowedToWatchFile = true;
         return NS_OK;
       }
 
@@ -2512,18 +2676,17 @@ public:
             = new DeviceStorageRequestChild(mRequest, mFile);
           DeviceStorageFormatParams params(mFile->mStorageType,
                                            mFile->mStorageName);
           ContentChild::GetSingleton()
             ->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new PostFormatResultEvent(mFile, mRequest);
-        NS_DispatchToMainThread(r);
-        return NS_OK;
+        return NS_DispatchToCurrentThread(r);
       }
 
     }
 
     if (r) {
       nsCOMPtr<nsIEventTarget> target
         = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
@@ -2553,16 +2716,17 @@ private:
   int32_t mRequestType;
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsRefPtr<DeviceStorageFile> mFile;
 
   nsRefPtr<DOMRequest> mRequest;
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsRefPtr<nsDOMDeviceStorage> mDeviceStorage;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
 };
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageRequest)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
 NS_INTERFACE_MAP_END
 
@@ -2794,16 +2958,18 @@ nsDOMDeviceStorage::GetStorage(const nsA
     ds = GetStorageByName(storageName);
   }
   return ds.forget();
 }
 
 already_AddRefed<nsDOMDeviceStorage>
 nsDOMDeviceStorage::GetStorageByName(const nsAString& aStorageName)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsRefPtr<nsDOMDeviceStorage> ds;
 
   if (mStorageName.Equals(aStorageName)) {
     ds = this;
     return ds.forget();
   }
   VolumeNameArray volNames;
   GetOrderedVolumeNames(volNames);
@@ -2909,16 +3075,18 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob 
   request.forget(_retval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
                              ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   // if the blob is null here, bail
   if (!aBlob) {
     return nullptr;
   }
 
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
@@ -2928,24 +3096,28 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob*
   DeviceStorageTypeChecker* typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
   if (!typeChecker) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIRunnable> r;
+  nsresult rv;
 
   if (IsFullPath(aPath)) {
     nsString storagePath;
     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
     if (!ds) {
       nsRefPtr<DOMRequest> request = new DOMRequest(win);
       r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
-      NS_DispatchToMainThread(r);
+      rv = NS_DispatchToCurrentThread(r);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+      }
       return request.forget();
     }
     return ds->AddNamed(aBlob, storagePath, aRv);
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
@@ -2956,17 +3128,20 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob*
   } 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,
                                  win, mPrincipal, dsf, request, aBlob);
   }
 
-  NS_DispatchToMainThread(r);
+  rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Get(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = Get(aPath, rv);
@@ -2983,157 +3158,180 @@ nsDOMDeviceStorage::GetEditable(const ns
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::GetInternal(const nsAString& aPath, bool aEditable,
                                 ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   if (IsFullPath(aPath)) {
     nsString storagePath;
     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
     if (!ds) {
       nsCOMPtr<nsIRunnable> r =
         new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
-      NS_DispatchToMainThread(r);
+      nsresult rv = NS_DispatchToCurrentThread(r);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+      }
       return request.forget();
     }
     ds->GetInternal(win, storagePath, request, aEditable);
     return request.forget();
   }
   GetInternal(win, aPath, request, aEditable);
   return request.forget();
 }
 
 void
 nsDOMDeviceStorage::GetInternal(nsPIDOMWindow *aWin,
                                 const nsAString& aPath,
                                 DOMRequest* aRequest,
                                 bool aEditable)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName,
                                                           aPath);
   dsf->SetEditable(aEditable);
 
   nsCOMPtr<nsIRunnable> r;
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
   } else {
     r = new DeviceStorageRequest(aEditable ? DEVICE_STORAGE_REQUEST_WRITE
                                            : DEVICE_STORAGE_REQUEST_READ,
                                  aWin, mPrincipal, dsf, aRequest);
   }
-  NS_DispatchToMainThread(r);
+  DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Delete(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = Delete(aPath, rv);
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::Delete(const nsAString& aPath, ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   if (IsFullPath(aPath)) {
     nsString storagePath;
     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
     if (!ds) {
       nsCOMPtr<nsIRunnable> r =
         new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
-      NS_DispatchToMainThread(r);
+      nsresult rv = NS_DispatchToCurrentThread(r);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+      }
       return request.forget();
     }
     ds->DeleteInternal(win, storagePath, request);
     return request.forget();
   }
   DeleteInternal(win, aPath, request);
   return request.forget();
 }
 
 void
 nsDOMDeviceStorage::DeleteInternal(nsPIDOMWindow *aWin,
                                    const nsAString& aPath,
                                    DOMRequest* aRequest)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsIRunnable> r;
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName,
                                                           aPath);
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
   } else {
     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE,
                                  aWin, mPrincipal, dsf, aRequest);
   }
-  NS_DispatchToMainThread(r);
+  DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::FreeSpace(nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = FreeSpace(rv);
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::FreeSpace(ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE,
                                win, mPrincipal, dsf, request);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::UsedSpace(nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = UsedSpace(rv);
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::UsedSpace(ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   DebugOnly<DeviceStorageUsedSpaceCache*> usedSpaceCache
     = DeviceStorageUsedSpaceCache::CreateOrGet();
@@ -3141,85 +3339,135 @@ nsDOMDeviceStorage::UsedSpace(ErrorResul
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE,
                                win, mPrincipal, dsf, request);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Available(nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = Available(rv);
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::Available(ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE,
                                win, mPrincipal, dsf, request);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::Format(ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FORMAT,
                                win, mPrincipal, dsf, request);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 NS_IMETHODIMP
-nsDOMDeviceStorage::GetRootDirectoryForFile(const nsAString& aName,
-                                            nsIFile** aRootDirectory)
+nsDOMDeviceStorage::CreateFileDescriptor(const nsAString& aPath,
+                                         DeviceStorageFileDescriptor* aDSFileDescriptor,
+                                         nsIDOMDOMRequest** aRequest)
 {
-  nsRefPtr<nsDOMDeviceStorage> ds;
-
-  if (IsFullPath(aName)) {
-    nsString storagePath;
-    ds = GetStorage(aName, storagePath);
-  } else {
-    ds = this;
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aDSFileDescriptor);
+
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return NS_ERROR_UNEXPECTED;
   }
-  if (!ds || !ds->mRootDirectory) {
+
+  DeviceStorageTypeChecker* typeChecker
+    = DeviceStorageTypeChecker::CreateOrGet();
+  if (!typeChecker) {
     return NS_ERROR_FAILURE;
   }
-  return ds->mRootDirectory->Clone(aRootDirectory);
+
+  nsCOMPtr<nsIRunnable> r;
+  nsresult rv;
+
+  if (IsFullPath(aPath)) {
+    nsString storagePath;
+    nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
+    if (!ds) {
+      nsRefPtr<DOMRequest> request = new DOMRequest(win);
+      r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
+      rv = NS_DispatchToCurrentThread(r);
+      request.forget(aRequest);
+      return rv;
+    }
+    return ds->CreateFileDescriptor(storagePath, aDSFileDescriptor, aRequest);
+  }
+
+  nsRefPtr<DOMRequest> request = new DOMRequest(win);
+
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
+                                                          mStorageName,
+                                                          aPath);
+  if (!dsf->IsSafePath()) {
+    r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
+  } else if (!typeChecker->Check(mStorageType, dsf->mFile)) {
+    r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
+  } else {
+    r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATEFD,
+                                 win, mPrincipal, dsf, request,
+                                 aDSFileDescriptor);
+  }
+
+  rv = NS_DispatchToCurrentThread(r);
+  request.forget(aRequest);
+  return rv;
 }
 
 bool
 nsDOMDeviceStorage::Default()
 {
   nsString defaultStorageName;
   GetDefaultStorageName(mStorageType, defaultStorageName);
   return mStorageName.Equals(defaultStorageName);
@@ -3448,52 +3696,63 @@ nsDOMDeviceStorage::Notify(const char* a
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
                                      nsIDOMEventListener *aListener,
                                      bool aUseCapture,
                                      bool aWantsUntrusted,
                                      uint8_t aArgc)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
                                win, mPrincipal, dsf, request, this);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   return nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
                                                   aWantsUntrusted, aArgc);
 }
 
 void
 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
                                      EventListener *aListener,
                                      bool aUseCapture,
                                      const Nullable<bool>& aWantsUntrusted,
                                      ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
                                win, mPrincipal, dsf, request, this);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
   nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
                                            aWantsUntrusted, aRv);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
                                            nsIDOMEventListener *aListener,
                                            bool aUseCapture,
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -47,17 +47,18 @@ enum DeviceStorageRequestType {
     DEVICE_STORAGE_REQUEST_READ,
     DEVICE_STORAGE_REQUEST_WRITE,
     DEVICE_STORAGE_REQUEST_CREATE,
     DEVICE_STORAGE_REQUEST_DELETE,
     DEVICE_STORAGE_REQUEST_WATCH,
     DEVICE_STORAGE_REQUEST_FREE_SPACE,
     DEVICE_STORAGE_REQUEST_USED_SPACE,
     DEVICE_STORAGE_REQUEST_AVAILABLE,
-    DEVICE_STORAGE_REQUEST_FORMAT
+    DEVICE_STORAGE_REQUEST_FORMAT,
+    DEVICE_STORAGE_REQUEST_CREATEFD
 };
 
 class DeviceStorageUsedSpaceCache MOZ_FINAL
 {
 public:
   static DeviceStorageUsedSpaceCache* CreateOrGet();
 
   DeviceStorageUsedSpaceCache();
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
@@ -6,17 +6,22 @@
 #include "nsIDOMEventTarget.idl"
 interface nsIDOMBlob;
 interface nsIDOMDOMRequest;
 interface nsIDOMDOMCursor;
 interface nsIDOMDeviceStorageChangeEvent;
 interface nsIDOMEventListener;
 interface nsIFile;
 
-[scriptable, uuid(7c1b2305-0f14-4c07-8a8a-359eeb850068), builtinclass]
+%{C++
+class DeviceStorageFileDescriptor;
+%}
+[ptr] native DeviceStorageFdPtr(DeviceStorageFileDescriptor);
+
+[scriptable, uuid(8b724547-3c78-4244-969a-f00a1f4ae0c3), builtinclass]
 interface nsIDOMDeviceStorage : nsIDOMEventTarget
 {
     [implicit_jscontext] attribute jsval onchange;
     nsIDOMDOMRequest add(in nsIDOMBlob aBlob);
     nsIDOMDOMRequest addNamed(in nsIDOMBlob aBlob, in DOMString aName);
 
     nsIDOMDOMRequest get([Null(Stringify)] in DOMString aName);
     nsIDOMDOMRequest getEditable([Null(Stringify)] in DOMString aName);
@@ -29,10 +34,13 @@ interface nsIDOMDeviceStorage : nsIDOMEv
     // Note that the storageName is just a name (like sdcard), and doesn't
     // include any path information.
     readonly attribute DOMString storageName;
 
     // Determines if this storage area is the one which will be used by default
     // for storing new files.
     readonly attribute bool default;
 
-    [noscript] nsIFile getRootDirectoryForFile(in DOMString aName);
+    // Note: aFileDescriptor is reference counted, which is why we're using
+    //       a pointer rather than a reference.
+    [noscript] nsIDOMDOMRequest createFileDescriptor(in DOMString aName,
+                                                     in DeviceStorageFdPtr aFileDescriptor);
 };
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -89,16 +89,23 @@ struct DeviceStorageFormatParams
 struct DeviceStorageAddParams
 {
   nsString type;
   nsString storageName;
   nsString relpath;
   PBlob blob;
 };
 
+struct DeviceStorageCreateFdParams
+{
+  nsString type;
+  nsString storageName;
+  nsString relpath;
+};
+
 struct DeviceStorageGetParams
 {
   nsString type;
   nsString storageName;
   nsString rootDir;
   nsString relpath;
 };
 
@@ -115,16 +122,17 @@ struct DeviceStorageEnumerationParams
   nsString storageName;
   nsString rootdir;
   uint64_t since;
 };
 
 union DeviceStorageParams
 {
   DeviceStorageAddParams;
+  DeviceStorageCreateFdParams;
   DeviceStorageGetParams;
   DeviceStorageDeleteParams;
   DeviceStorageEnumerationParams;
   DeviceStorageFreeSpaceParams;
   DeviceStorageUsedSpaceParams;
   DeviceStorageAvailableParams;
   DeviceStorageFormatParams;
 };