Bug 910498 - Changes to device storage to support CreateFd. r=bent
authorDave Hylands <dhylands@mozilla.com>
Thu, 16 Jan 2014 17:01:27 -0800
changeset 181524 4a1217f771c1646969eea35f7aa346972d9d37e4
parent 181523 3a2d7fa2a862c1bdbf2de949d9075bf754544fae
child 181525 2b74b0de04a4c7244ea1b53afe92bbb269a3b59c
push id462
push userraliiev@mozilla.com
push dateTue, 22 Apr 2014 00:22:30 +0000
treeherdermozilla-release@ac5db8c74ac0 [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 * * * Bug 910498 - Camera changes to use CreateFd. r=mikeh * * * Fixes for windows build
dom/devicestorage/DeviceStorage.h
dom/devicestorage/DeviceStorageFileDescriptor.h
dom/devicestorage/DeviceStorageRequestChild.cpp
dom/devicestorage/DeviceStorageRequestChild.h
dom/devicestorage/DeviceStorageRequestParent.cpp
dom/devicestorage/DeviceStorageRequestParent.h
dom/devicestorage/PDeviceStorageRequest.ipdl
dom/devicestorage/moz.build
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,33 +9,37 @@
 
 #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"
 
 #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 ipc {
+class FileDescriptor;
+}
 } // namespace mozilla
 
 class DeviceStorageFile MOZ_FINAL
   : public nsISupports {
 public:
   nsCOMPtr<nsIFile> mFile;
   nsString mStorageType;
   nsString mStorageName;
@@ -97,16 +101,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,
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageFileDescriptor.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
+/* 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 DeviceStorageFileDescriptor_h
+#define DeviceStorageFileDescriptor_h
+
+#include "mozilla/ipc/FileDescriptor.h"
+
+class DeviceStorageFileDescriptor MOZ_FINAL
+  : public mozilla::RefCounted<DeviceStorageFileDescriptor>
+{
+public:
+  nsRefPtr<DeviceStorageFile> mDSFile;
+  mozilla::ipc::FileDescriptor mFileDescriptor;
+};
+
+#endif
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -1,35 +1,52 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "DeviceStorageRequestChild.h"
+#include "DeviceStorageFileDescriptor.h"
 #include "nsDeviceStorage.h"
 #include "nsDOMFile.h"
 #include "mozilla/dom/ipc/Blob.h"
 
 namespace mozilla {
 namespace dom {
 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 +65,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
@@ -24,17 +24,17 @@ DeviceStorageRequestParent::DeviceStorag
   : mParams(aParams)
   , mMutex("DeviceStorageRequestParent::mMutex")
   , mActorDestoryed(false)
 {
   MOZ_COUNT_CTOR(DeviceStorageRequestParent);
 
   DebugOnly<DeviceStorageUsedSpaceCache*> usedSpaceCache
     = DeviceStorageUsedSpaceCache::CreateOrGet();
-  NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+  MOZ_ASSERT(usedSpaceCache);
 }
 
 void
 DeviceStorageRequestParent::Dispatch()
 {
   switch (mParams.type()) {
     case DeviceStorageParams::TDeviceStorageAddParams:
     {
@@ -48,71 +48,87 @@ DeviceStorageRequestParent::Dispatch()
 
       nsCOMPtr<nsIInputStream> stream;
       blob->GetInternalStream(getter_AddRefs(stream));
 
       nsRefPtr<CancelableRunnable> r = new WriteFileEvent(this, dsf, stream);
 
       nsCOMPtr<nsIEventTarget> target
         = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-      NS_ASSERTION(target, "Must have stream transport service");
+      MOZ_ASSERT(target);
+      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);
+      MOZ_ASSERT(target);
       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);
 
       nsCOMPtr<nsIEventTarget> target
         = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-      NS_ASSERTION(target, "Must have stream transport service");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageDeleteParams:
     {
       DeviceStorageDeleteParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName(), 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");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageFreeSpaceParams:
     {
       DeviceStorageFreeSpaceParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName());
       nsRefPtr<FreeSpaceFileEvent> r = new FreeSpaceFileEvent(this, dsf);
 
       nsCOMPtr<nsIEventTarget> target
         = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-      NS_ASSERTION(target, "Must have stream transport service");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageUsedSpaceParams:
     {
       DeviceStorageUsedSpaceCache* usedSpaceCache
         = DeviceStorageUsedSpaceCache::CreateOrGet();
-      NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+      MOZ_ASSERT(usedSpaceCache);
 
       DeviceStorageUsedSpaceParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName());
       nsRefPtr<UsedSpaceFileEvent> r = new UsedSpaceFileEvent(this, dsf);
 
       usedSpaceCache->Dispatch(r);
@@ -122,44 +138,46 @@ 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);
+      DebugOnly<nsresult> 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);
+      DebugOnly<nsresult> 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(),
                                 p.rootdir(), 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");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
     default:
     {
       NS_RUNTIMEABORT("not reached");
       break;
     }
@@ -182,16 +200,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;
     }
 
@@ -307,17 +333,17 @@ DeviceStorageRequestParent::PostFreeSpac
 {
 }
 
 DeviceStorageRequestParent::PostFreeSpaceResultEvent::
   ~PostFreeSpaceResultEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostFreeSpaceResultEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   FreeSpaceStorageResponse response(mFreeSpace);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostUsedSpaceResultEvent::
   PostUsedSpaceResultEvent(DeviceStorageRequestParent* aParent,
@@ -329,17 +355,17 @@ DeviceStorageRequestParent::PostUsedSpac
 {
 }
 
 DeviceStorageRequestParent::PostUsedSpaceResultEvent::
   ~PostUsedSpaceResultEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostUsedSpaceResultEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   UsedSpaceStorageResponse response(mUsedSpace);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostErrorEvent::
   PostErrorEvent(DeviceStorageRequestParent* aParent, const char* aError)
@@ -347,34 +373,34 @@ DeviceStorageRequestParent::PostErrorEve
 {
   CopyASCIItoUTF16(aError, mError);
 }
 
 DeviceStorageRequestParent::PostErrorEvent::~PostErrorEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostErrorEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   ErrorResponse response(mError);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostSuccessEvent::
   PostSuccessEvent(DeviceStorageRequestParent* aParent)
   : CancelableRunnable(aParent)
 {
 }
 
 DeviceStorageRequestParent::PostSuccessEvent::~PostSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostSuccessEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   SuccessResponse response;
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostBlobSuccessEvent::
   PostBlobSuccessEvent(DeviceStorageRequestParent* aParent,
@@ -389,17 +415,17 @@ DeviceStorageRequestParent::PostBlobSucc
   , mMimeType(aMimeType)
 {
 }
 
 DeviceStorageRequestParent::PostBlobSuccessEvent::~PostBlobSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostBlobSuccessEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   nsString mime;
   CopyASCIItoUTF16(mMimeType, mime);
 
   nsString fullPath;
   mFile->GetFullPath(fullPath);
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(fullPath, mime, mLength, 
                                                 mFile->mFile,
@@ -432,23 +458,64 @@ DeviceStorageRequestParent::PostEnumerat
 {
 }
 
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::
   ~PostEnumerationSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   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()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  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)) {
+    NS_WARNING("CreateFileDescriptor failed");
+    mFile->Dump("CreateFileDescriptor failed");
+    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)
 {
@@ -456,80 +523,75 @@ DeviceStorageRequestParent::WriteFileEve
 
 DeviceStorageRequestParent::WriteFileEvent::~WriteFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::WriteFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   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()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::DeleteFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   mFile->Remove();
 
   nsRefPtr<nsRunnable> r;
 
   bool check = false;
   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)
 {
@@ -537,27 +599,26 @@ DeviceStorageRequestParent::FreeSpaceFil
 
 DeviceStorageRequestParent::FreeSpaceFileEvent::~FreeSpaceFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::FreeSpaceFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   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)
 {
@@ -565,17 +626,17 @@ DeviceStorageRequestParent::UsedSpaceFil
 
 DeviceStorageRequestParent::UsedSpaceFileEvent::~UsedSpaceFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::UsedSpaceFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0;
   mFile->AccumDiskUsage(&picturesUsage, &videosUsage,
                         &musicUsage, &totalUsage);
   nsCOMPtr<nsIRunnable> r;
   if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
     r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType,
                                      picturesUsage);
@@ -583,18 +644,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
@@ -609,48 +669,44 @@ DeviceStorageRequestParent::ReadFileEven
 
 DeviceStorageRequestParent::ReadFileEvent::~ReadFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::ReadFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   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)
@@ -660,44 +716,42 @@ DeviceStorageRequestParent::EnumerateFil
 
 DeviceStorageRequestParent::EnumerateFileEvent::~EnumerateFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   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)
@@ -706,40 +760,63 @@ DeviceStorageRequestParent::PostPathResu
 
 DeviceStorageRequestParent::PostPathResultEvent::~PostPathResultEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::PostPathResultEvent::CancelableRun()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   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()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  FileDescriptorResponse response(mFileDescriptor);
+  unused << mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
 DeviceStorageRequestParent::PostAvailableResultEvent::
   PostAvailableResultEvent(DeviceStorageRequestParent* aParent,
                            DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
 }
 
 DeviceStorageRequestParent::PostAvailableResultEvent::
   ~PostAvailableResultEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::PostAvailableResultEvent::CancelableRun()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   nsString state = NS_LITERAL_STRING("unavailable");
   if (mFile) {
     mFile->GetStatus(state);
   }
 
   AvailableStorageResponse response(state);
   unused << mParent->Send__delete__(mParent, response);
@@ -757,17 +834,17 @@ DeviceStorageRequestParent::PostFormatRe
 DeviceStorageRequestParent::PostFormatResultEvent::
   ~PostFormatResultEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::PostFormatResultEvent::CancelableRun()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   nsString state = NS_LITERAL_STRING("unavailable");
   if (mFile) {
     mFile->DoFormat(state);
   }
 
   FormatStorageResponse response(state);
   unused << mParent->Send__delete__(mParent, response);
--- 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/moz.build
+++ b/dom/devicestorage/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += ['test', 'ipc']
 
 EXPORTS += [
     'DeviceStorage.h',
+    'DeviceStorageFileDescriptor.h',
     'nsDeviceStorage.h',
 ]
 
 EXPORTS.mozilla.dom.devicestorage += [
     'DeviceStorageRequestChild.h',
     'DeviceStorageRequestParent.h',
 ]
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -33,27 +33,29 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsIPrincipal.h"
 #include "nsJSUtils.h"
 #include "DictionaryHelpers.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsXULAppAPI.h"
 #include "TabChild.h"
+#include "DeviceStorageFileDescriptor.h"
 #include "DeviceStorageRequestChild.h"
 #include "nsIDOMDeviceStorageChangeEvent.h"
 #include "nsCRT.h"
 #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 +64,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()
 {
@@ -244,17 +247,17 @@ DeviceStorageTypeChecker::InitFromBundle
     NS_ConvertASCIItoUTF16(DEVICESTORAGE_VIDEOS).get(),
     getter_Copies(mVideosExtensions));
 }
 
 
 bool
 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIDOMBlob* aBlob)
 {
-  NS_ASSERTION(aBlob, "Calling Check without a blob");
+  MOZ_ASSERT(aBlob);
 
   nsString mimeType;
   if (NS_FAILED(aBlob->GetType(mimeType))) {
     return false;
   }
 
   if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
     return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/"));
@@ -276,17 +279,17 @@ DeviceStorageTypeChecker::Check(const ns
   }
 
   return false;
 }
 
 bool
 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIFile* aFile)
 {
-  NS_ASSERTION(aFile, "Calling Check without a file");
+  MOZ_ASSERT(aFile);
 
   if (aType.EqualsLiteral(DEVICESTORAGE_APPS) ||
       aType.EqualsLiteral(DEVICESTORAGE_SDCARD) ||
       aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
     // Apps, crashes and sdcard have no restrictions on what file extensions used.
     return true;
   }
 
@@ -316,17 +319,17 @@ DeviceStorageTypeChecker::Check(const ns
   }
 
   return false;
 }
 
 void
 DeviceStorageTypeChecker::GetTypeFromFile(nsIFile* aFile, nsAString& aType)
 {
-  NS_ASSERTION(aFile, "Calling Check without a file");
+  MOZ_ASSERT(aFile);
 
   nsString path;
   aFile->GetPath(path);
 
   GetTypeFromFileName(path, aType);
 }
 
 void
@@ -389,16 +392,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;
 }
 
@@ -478,17 +482,17 @@ public:
     nsString data;
     CopyASCIItoUTF16(mType, data);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 
     obs->NotifyObservers(mFile, "file-watcher-notify", data.get());
 
     DeviceStorageUsedSpaceCache* usedSpaceCache
       = DeviceStorageUsedSpaceCache::CreateOrGet();
-    NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+    MOZ_ASSERT(usedSpaceCache);
     usedSpaceCache->Invalidate(mFile->mStorageName);
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsCString mType;
 };
@@ -569,17 +573,17 @@ void
 DeviceStorageFile::Init()
 {
   DeviceStorageFile::GetRootDirectoryForType(mStorageType,
                                              mStorageName,
                                              getter_AddRefs(mFile));
 
   DebugOnly<DeviceStorageTypeChecker*> typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
-  NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
+  MOZ_ASSERT(typeChecker);
 }
 
 // Directories which don't depend on a volume should be calculated once
 // here. Directories which depend on the root directory of a volume
 // should be calculated in DeviceStorageFile::GetRootDirectoryForType.
 static void
 InitDirs()
 {
@@ -587,17 +591,17 @@ InitDirs()
     return;
   }
   MOZ_ASSERT(NS_IsMainThread());
   sDirs = new GlobalDirs;
   ClearOnShutdown(&sDirs);
 
   nsCOMPtr<nsIProperties> dirService
     = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
-  NS_ASSERTION(dirService, "Must have directory service");
+  MOZ_ASSERT(dirService);
 
 #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,
@@ -784,17 +788,17 @@ DeviceStorageFile::GetRootDirectoryForTy
 //static
 already_AddRefed<DeviceStorageFile>
 DeviceStorageFile::CreateUnique(nsAString& aFileName,
                                 uint32_t aFileType,
                                 uint32_t aFileAttributes)
 {
   DeviceStorageTypeChecker* typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
-  NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
+  MOZ_ASSERT(typeChecker);
 
   nsString storageType;
   typeChecker->GetTypeFromFileName(aFileName, storageType);
 
   nsString storageName;
   nsString storagePath;
   if (!nsDOMDeviceStorage::ParseFullPath(aFileName, storageName, storagePath)) {
     return nullptr;
@@ -919,29 +923,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 +983,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 +1057,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 +1140,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;
@@ -1160,17 +1189,17 @@ DeviceStorageFile::AccumDiskUsage(uint64
     return;
   }
 
   uint64_t pictureUsage = 0, videoUsage = 0, musicUsage = 0, totalUsage = 0;
 
   if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) {
     DeviceStorageUsedSpaceCache* usedSpaceCache =
       DeviceStorageUsedSpaceCache::CreateOrGet();
-    NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+    MOZ_ASSERT(usedSpaceCache);
     nsresult rv = usedSpaceCache->AccumUsedSizes(mStorageName,
                                                  aPicturesSoFar, aVideosSoFar,
                                                  aMusicSoFar, aTotalSoFar);
     if (NS_SUCCEEDED(rv)) {
       return;
     }
     AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage,
                         &musicUsage, &totalUsage);
@@ -1202,18 +1231,17 @@ DeviceStorageFile::AccumDirectoryUsage(n
   nsCOMPtr<nsISimpleEnumerator> e;
   rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
 
   if (NS_FAILED(rv) || !e) {
     return;
   }
 
   nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
-  NS_ASSERTION(files,
-               "GetDirectoryEntries must return a nsIDirectoryEnumerator");
+  MOZ_ASSERT(files);
 
   nsCOMPtr<nsIFile> f;
   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
     bool isDir;
     rv = f->IsDirectory(&isDir);
     if (NS_FAILED(rv)) {
       continue;
     }
@@ -1240,17 +1268,17 @@ DeviceStorageFile::AccumDirectoryUsage(n
 
       int64_t size;
       rv = f->GetFileSize(&size);
       if (NS_FAILED(rv)) {
         continue;
       }
       DeviceStorageTypeChecker* typeChecker
         = DeviceStorageTypeChecker::CreateOrGet();
-      NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
+      MOZ_ASSERT(typeChecker);
       nsString type;
       typeChecker->GetTypeFromFile(f, type);
 
       if (type.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
         *aPicturesSoFar += size;
       }
       else if (type.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
         *aVideosSoFar += size;
@@ -1436,47 +1464,46 @@ InterfaceToJsval(nsPIDOMWindow* aWindow,
 
   return someJsVal;
 }
 
 JS::Value
 nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ASSERTION(aWindow, "Null Window");
+  MOZ_ASSERT(aWindow);
 
   if (!aFile) {
     return JSVAL_NULL;
   }
 
   if (aFile->mEditable) {
     // TODO - needs janv's file handle support.
     return JSVAL_NULL;
   }
 
   nsString fullPath;
   aFile->GetFullPath(fullPath);
 
   // This check is useful to know if somewhere the DeviceStorageFile
   // has not been properly set. Mimetype is not checked because it can be
   // empty.
-  NS_ASSERTION(aFile->mLength != UINT64_MAX, "Size not set");
-  NS_ASSERTION(aFile->mLastModifiedDate != UINT64_MAX,
-               "LastModifiedDate not set");
+  MOZ_ASSERT(aFile->mLength != UINT64_MAX);
+  MOZ_ASSERT(aFile->mLastModifiedDate != UINT64_MAX);
 
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(fullPath, aFile->mMimeType,
                                                 aFile->mLength, aFile->mFile,
                                                 aFile->mLastModifiedDate);
   return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
 }
 
 JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ASSERTION(aWindow, "Null Window");
+  MOZ_ASSERT(aWindow);
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
   if (!sgo) {
     return JSVAL_NULL;
   }
 
   nsIScriptContext *scriptContext = sgo->GetScriptContext();
   if (!scriptContext) {
@@ -1615,25 +1642,27 @@ ContinueCursorEvent::GetNextFile()
 }
 
 ContinueCursorEvent::~ContinueCursorEvent() {}
 
 void
 ContinueCursorEvent::Continue()
 {
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
-    NS_DispatchToMainThread(this);
+    DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
     return;
   }
 
   nsRefPtr<DeviceStorageFile> file = GetNextFile();
 
   if (!file) {
     // done with enumeration.
-    NS_DispatchToMainThread(this);
+    DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
     return;
   }
 
   nsDOMDeviceStorageCursor* cursor
     = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
   nsString cursorStorageType;
   cursor->GetStorageType(cursorStorageType);
 
@@ -1686,18 +1715,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,45 +1805,43 @@ 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,
                                           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");
+  MOZ_ASSERT(target);
 
   nsCOMPtr<InitCursorEvent> event = new InitCursorEvent(this, mFile);
   target->Dispatch(event, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 void
 nsDOMDeviceStorageCursor::Continue(ErrorResult& aRv)
@@ -1854,27 +1880,28 @@ void
 nsDOMDeviceStorageCursor::IPDLRelease()
 {
   Release();
 }
 
 void
 nsDOMDeviceStorageCursor::RequestComplete()
 {
-  NS_ASSERTION(!mOkToCallContinue, "mOkToCallContinue must be false");
+  MOZ_ASSERT(!mOkToCallContinue);
   mOkToCallContinue = true;
 }
 
 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 +1925,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 +1959,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 +2010,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 +2154,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 +2193,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 +2234,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,51 +2441,89 @@ 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;
           }
 
           DeviceStorageAddParams params;
           params.blobChild() = actor;
@@ -2389,18 +2552,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 +2586,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);
@@ -2472,17 +2633,17 @@ public:
           ContentChild::GetSingleton()
             ->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         // this needs to be dispatched to only one (1)
         // thread or we will do more work than required.
         DeviceStorageUsedSpaceCache* usedSpaceCache
           = DeviceStorageUsedSpaceCache::CreateOrGet();
-        NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+        MOZ_ASSERT(usedSpaceCache);
         r = new UsedSpaceFileEvent(mFile, mRequest.forget());
         usedSpaceCache->Dispatch(r);
         return NS_OK;
       }
 
       case DEVICE_STORAGE_REQUEST_AVAILABLE:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
@@ -2490,18 +2651,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,26 +2672,25 @@ 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");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
     }
 
     return NS_OK;
   }
 
   bool Recv__delete__(const bool& allow)
   {
@@ -2553,16 +2712,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
 
@@ -2598,19 +2758,19 @@ nsDOMDeviceStorage::WrapObject(JSContext
 }
 
 nsresult
 nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType,
                          const nsAString &aVolName)
 {
   DebugOnly<FileUpdateDispatcher*> observer
     = FileUpdateDispatcher::GetSingleton();
-  NS_ASSERTION(observer, "FileUpdateDispatcher is null");
-
-  NS_ASSERTION(aWindow, "Must have a content dom");
+  MOZ_ASSERT(observer);
+
+  MOZ_ASSERT(aWindow);
 
   SetRootDirectoryForType(aType, aVolName);
   if (!mRootDirectory) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   if (!mStorageName.IsEmpty()) {
     RegisterForSDCardChanges(this);
   }
@@ -2794,16 +2954,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 +3071,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 +3092,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 +3124,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,243 +3154,322 @@ 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();
-  NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+  MOZ_ASSERT(usedSpaceCache);
 
   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);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+      request.forget(aRequest);
+      return NS_OK;
+    }
+    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);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  request.forget(aRequest);
+  return NS_OK;
 }
 
 bool
 nsDOMDeviceStorage::Default()
 {
   nsString defaultStorageName;
   GetDefaultStorageName(mStorageType, defaultStorageName);
   return mStorageName.Equals(defaultStorageName);
@@ -3389,17 +3639,17 @@ nsDOMDeviceStorage::Observe(nsISupports 
     if (!vol) {
       return NS_OK;
     }
     nsString volName;
     vol->GetName(volName);
 
     DeviceStorageUsedSpaceCache* usedSpaceCache
       = DeviceStorageUsedSpaceCache::CreateOrGet();
-    NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+    MOZ_ASSERT(usedSpaceCache);
     usedSpaceCache->Invalidate(volName);
 
     if (!volName.Equals(mStorageName)) {
       // Not our volume - we can ignore.
       return NS_OK;
     }
 
     DeviceStorageFile dsf(mStorageType, mStorageName);
@@ -3448,52 +3698,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();
@@ -85,27 +86,27 @@ public:
       }
     private:
       DeviceStorageUsedSpaceCache* mCache;
       nsString mStorageName;
   };
 
   void Invalidate(const nsAString& aStorageName)
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-    NS_ASSERTION(mIOThread, "Null mIOThread!");
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mIOThread);
 
     nsRefPtr<InvalidateRunnable> r = new InvalidateRunnable(this, aStorageName);
     mIOThread->Dispatch(r, NS_DISPATCH_NORMAL);
   }
 
   void Dispatch(nsIRunnable* aRunnable)
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-    NS_ASSERTION(mIOThread, "Null mIOThread!");
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mIOThread);
 
     mIOThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
   }
 
   nsresult AccumUsedSizes(const nsAString& aStorageName,
                           uint64_t* aPictureSize, uint64_t* aVideosSize,
                           uint64_t* aMusicSize, uint64_t* aTotalSize);
 
--- 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;
 };