Bug 1258221 - patch 2 - Port FileSystem API and DeviceStorage API to PBackground, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Sat, 09 Apr 2016 19:17:02 +0100
changeset 292522 985e569816b221daeade5d3ac2313deb5ed43125
parent 292521 1d079b46a6d0c02c9b7d2d09a9e9f413308048bd
child 292523 f9aed5af58b63e251092035cb0168f0b1f7ea561
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs1258221
milestone48.0a1
Bug 1258221 - patch 2 - Port FileSystem API and DeviceStorage API to PBackground, r=smaug
dom/filesystem/CreateDirectoryTask.cpp
dom/filesystem/CreateDirectoryTask.h
dom/filesystem/CreateFileTask.cpp
dom/filesystem/CreateFileTask.h
dom/filesystem/DeviceStorageFileSystem.cpp
dom/filesystem/DeviceStorageFileSystem.h
dom/filesystem/FileSystemBase.cpp
dom/filesystem/FileSystemBase.h
dom/filesystem/FileSystemPermissionRequest.cpp
dom/filesystem/FileSystemPermissionRequest.h
dom/filesystem/FileSystemRequestParent.cpp
dom/filesystem/FileSystemRequestParent.h
dom/filesystem/FileSystemTaskBase.cpp
dom/filesystem/FileSystemTaskBase.h
dom/filesystem/GetDirectoryListingTask.cpp
dom/filesystem/GetDirectoryListingTask.h
dom/filesystem/GetFileOrDirectoryTask.cpp
dom/filesystem/GetFileOrDirectoryTask.h
dom/filesystem/OSFileSystem.cpp
dom/filesystem/OSFileSystem.h
dom/filesystem/PFileSystemParams.ipdlh
dom/filesystem/PFileSystemRequest.ipdl
dom/filesystem/RemoveTask.cpp
dom/filesystem/RemoveTask.h
dom/filesystem/moz.build
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
--- a/dom/filesystem/CreateDirectoryTask.cpp
+++ b/dom/filesystem/CreateDirectoryTask.cpp
@@ -4,23 +4,32 @@
  * 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 "CreateDirectoryTask.h"
 
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/ipc/BackgroundParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
+
+using namespace ipc;
+
 namespace dom {
 
+/**
+ * CreateDirectoryTask
+ */
+
 /* static */ already_AddRefed<CreateDirectoryTask>
 CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
                             nsIFile* aTargetPath,
                             ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 
@@ -39,61 +48,28 @@ CreateDirectoryTask::Create(FileSystemBa
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<CreateDirectoryTask>
-CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
-                            const FileSystemCreateDirectoryParams& aParam,
-                            FileSystemRequestParent* aParent,
-                            ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<CreateDirectoryTask> task =
-    new CreateDirectoryTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  return task.forget();
-}
-
 CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
                                          nsIFile* aTargetPath)
   : FileSystemTaskBase(aFileSystem)
   , mTargetPath(aTargetPath)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
-CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
-                                         const FileSystemCreateDirectoryParams& aParam,
-                                         FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
 CreateDirectoryTask::~CreateDirectoryTask()
 {
-  MOZ_ASSERT(!mPromise || NS_IsMainThread(),
-             "mPromise should be released on main thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
 CreateDirectoryTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
@@ -108,44 +84,113 @@ CreateDirectoryTask::GetRequestParams(co
   aRv = mTargetPath->GetPath(path);
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemCreateDirectoryParams();
   }
 
   return FileSystemCreateDirectoryParams(aSerializedDOMPath, path);
 }
 
-FileSystemResponseValue
-CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
+void
+CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                             ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
+  const FileSystemDirectoryResponse& r =
+    aValue.get_FileSystemDirectoryResponse();
+
+  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(r.realPath()), true,
+                              getter_AddRefs(mTargetPath));
+  NS_WARN_IF(aRv.Failed());
+}
+
+void
+CreateDirectoryTask::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
+                                            mTargetPath,
+                                            Directory::eNotDOMRootDirectory,
+                                            mFileSystem);
+  MOZ_ASSERT(dir);
+
+  mPromise->MaybeResolve(dir);
+  mPromise = nullptr;
+}
+
+void
+CreateDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
+}
+
+/**
+ * CreateDirectoryTaskParent
+ */
+
+/* static */ already_AddRefed<CreateDirectoryTaskParent>
+CreateDirectoryTaskParent::Create(FileSystemBase* aFileSystem,
+                                  const FileSystemCreateDirectoryParams& aParam,
+                                  FileSystemRequestParent* aParent,
+                                  ErrorResult& aRv)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<CreateDirectoryTaskParent> task =
+    new CreateDirectoryTaskParent(aFileSystem, aParam, aParent);
+
+  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aParam.realPath()), true,
+                              getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return task.forget();
+}
+
+CreateDirectoryTaskParent::CreateDirectoryTaskParent(FileSystemBase* aFileSystem,
+                                                     const FileSystemCreateDirectoryParams& aParam,
+                                                     FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+CreateDirectoryTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
 
   nsAutoString path;
   aRv = mTargetPath->GetPath(path);
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemDirectoryResponse();
   }
 
   return FileSystemDirectoryResponse(path);
 }
 
-void
-CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                             ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  FileSystemDirectoryResponse r = aValue;
-
-  NS_ConvertUTF16toUTF8 path(r.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
-  NS_WARN_IF(aRv.Failed());
-}
-
 nsresult
-CreateDirectoryTask::Work()
+CreateDirectoryTaskParent::IOWork()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
@@ -164,39 +209,15 @@ CreateDirectoryTask::Work()
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 void
-CreateDirectoryTask::HandlerCallback()
+CreateDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    return;
-  }
-
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    return;
-  }
-  RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
-                                            mTargetPath,
-                                            Directory::eNotDOMRootDirectory,
-                                            mFileSystem);
-  MOZ_ASSERT(dir);
-
-  mPromise->MaybeResolve(dir);
-  mPromise = nullptr;
-}
-
-void
-CreateDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
-{
-  aAccess.AssignLiteral("create");
+  aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/CreateDirectoryTask.h
+++ b/dom/filesystem/CreateDirectoryTask.h
@@ -6,70 +6,85 @@
 
 #ifndef mozilla_dom_CreateDirectoryTask_h
 #define mozilla_dom_CreateDirectoryTask_h
 
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ErrorResult.h"
 
+#define CREATE_DIRECTORY_TASK_PERMISSION "create"
+
 namespace mozilla {
 namespace dom {
 
+class FileSystemCreateDirectoryParams;
 class Promise;
 
 class CreateDirectoryTask final : public FileSystemTaskBase
 {
 public:
   static already_AddRefed<CreateDirectoryTask>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aTargetPath,
          ErrorResult& aRv);
 
-  static already_AddRefed<CreateDirectoryTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemCreateDirectoryParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
   ~CreateDirectoryTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
+  virtual void
+  HandlerCallback() override;
+
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
-  virtual nsresult
-  Work() override;
-
-  virtual void
-  HandlerCallback() override;
 
 private:
   CreateDirectoryTask(FileSystemBase* aFileSystem,
                       nsIFile* aTargetPath);
 
-  CreateDirectoryTask(FileSystemBase* aFileSystem,
-                      const FileSystemCreateDirectoryParams& aParam,
-                      FileSystemRequestParent* aParent);
+  RefPtr<Promise> mPromise;
+  nsCOMPtr<nsIFile> mTargetPath;
+};
+
+class CreateDirectoryTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<CreateDirectoryTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemCreateDirectoryParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
 
-  RefPtr<Promise> mPromise;
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
+
+protected:
+  virtual nsresult
+  IOWork() override;
+
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+private:
+  CreateDirectoryTaskParent(FileSystemBase* aFileSystem,
+                            const FileSystemCreateDirectoryParams& aParam,
+                            FileSystemRequestParent* aParent);
+
   nsCOMPtr<nsIFile> mTargetPath;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CreateDirectoryTask_h
--- a/dom/filesystem/CreateFileTask.cpp
+++ b/dom/filesystem/CreateFileTask.cpp
@@ -1,34 +1,48 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
 #include "CreateFileTask.h"
+#include "CreateDirectoryTask.h"
+#include "RemoveTask.h"
 
 #include <algorithm>
 
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIFile.h"
 #include "nsNetUtil.h"
 #include "nsIOutputStream.h"
 #include "nsStringGlue.h"
 
+#define GET_PERMISSION_ACCESS_TYPE(aAccess)                \
+  if (mReplace) {                                          \
+    aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);         \
+    return;                                                \
+  }                                                        \
+  aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
+
 namespace mozilla {
 namespace dom {
 
-uint32_t CreateFileTask::sOutputBufferSize = 0;
+/**
+ *CreateFileTask
+ */
 
 /* static */ already_AddRefed<CreateFileTask>
 CreateFileTask::Create(FileSystemBase* aFileSystem,
                        nsIFile* aTargetPath,
                        Blob* aBlobData,
                        InfallibleTArray<uint8_t>& aArrayData,
                        bool aReplace,
                        ErrorResult& aRv)
@@ -36,27 +50,18 @@ CreateFileTask::Create(FileSystemBase* a
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 
   RefPtr<CreateFileTask> task =
     new CreateFileTask(aFileSystem, aTargetPath, aReplace);
 
   // aTargetPath can be null. In this case SetError will be called.
 
-  task->GetOutputBufferSize();
-
   if (aBlobData) {
-    if (XRE_IsParentProcess()) {
-      aBlobData->GetInternalStream(getter_AddRefs(task->mBlobStream), aRv);
-      if (NS_WARN_IF(aRv.Failed())) {
-        return nullptr;
-      }
-    } else {
-      task->mBlobData = aBlobData;
-    }
+    task->mBlobImpl = aBlobData->Impl();
   }
 
   task->mArrayData.SwapElements(aArrayData);
 
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
@@ -66,89 +71,30 @@ CreateFileTask::Create(FileSystemBase* a
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<CreateFileTask>
-CreateFileTask::Create(FileSystemBase* aFileSystem,
-                       const FileSystemCreateFileParams& aParam,
-                       FileSystemRequestParent* aParent,
-                       ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<CreateFileTask> task =
-    new CreateFileTask(aFileSystem, aParam, aParent);
-
-  task->GetOutputBufferSize();
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mReplace = aParam.replace();
-
-  auto& data = aParam.data();
-
-  if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
-    task->mArrayData = data;
-    return task.forget();
-  }
-
-  BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
-  RefPtr<BlobImpl> blobImpl = bp->GetBlobImpl();
-  MOZ_ASSERT(blobImpl, "blobData should not be null.");
-
-  ErrorResult rv;
-  blobImpl->GetInternalStream(getter_AddRefs(task->mBlobStream), rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    rv.SuppressException();
-  }
-
-  return task.forget();
-}
-
 CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
                                nsIFile* aTargetPath,
                                bool aReplace)
   : FileSystemTaskBase(aFileSystem)
   , mTargetPath(aTargetPath)
   , mReplace(aReplace)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
-CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
-                               const FileSystemCreateFileParams& aParam,
-                               FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-  , mReplace(false)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
 CreateFileTask::~CreateFileTask()
 {
-  MOZ_ASSERT((!mPromise && !mBlobData) || NS_IsMainThread(),
-             "mPromise and mBlobData should be released on main thread!");
-
-  if (mBlobStream) {
-    mBlobStream->Close();
-  }
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
 CreateFileTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
@@ -161,59 +107,151 @@ CreateFileTask::GetRequestParams(const n
   FileSystemCreateFileParams param;
   param.filesystem() = aSerializedDOMPath;
 
   aRv = mTargetPath->GetPath(param.realPath());
   if (NS_WARN_IF(aRv.Failed())) {
     return param;
   }
 
+  // If we are here, PBackground must be up and running: this method is called
+  // when the task has been already started by FileSystemPermissionRequest
+  // class and this happens only when PBackground actor has already been
+  // created.
+  PBackgroundChild* actor =
+    mozilla::ipc::BackgroundChild::GetForCurrentThread();
+  MOZ_ASSERT(actor);
+
   param.replace() = mReplace;
-  if (mBlobData) {
-    BlobChild* actor =
-      ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
-    if (actor) {
-      param.data() = actor;
+  if (mBlobImpl) {
+    PBlobChild* blobActor =
+      mozilla::ipc::BackgroundChild::GetOrCreateActorForBlobImpl(actor,
+                                                                 mBlobImpl);
+    if (blobActor) {
+      param.data() = blobActor;
     }
   } else {
     param.data() = mArrayData;
   }
   return param;
 }
 
-FileSystemResponseValue
-CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const
+void
+CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                        ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
+  const FileSystemFileResponse& r = aValue.get_FileSystemFileResponse();
+
+  NS_ConvertUTF16toUTF8 path(r.realPath());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+}
+
+void
+CreateFileTask::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
+                                           mTargetPath);
+  mPromise->MaybeResolve(file);
+  mPromise = nullptr;
+}
+
+void
+CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const
+{
+  GET_PERMISSION_ACCESS_TYPE(aAccess)
+}
+
+/**
+ * CreateFileTaskParent
+ */
+
+uint32_t CreateFileTaskParent::sOutputBufferSize = 0;
+
+/* static */ already_AddRefed<CreateFileTaskParent>
+CreateFileTaskParent::Create(FileSystemBase* aFileSystem,
+                             const FileSystemCreateFileParams& aParam,
+                             FileSystemRequestParent* aParent,
+                             ErrorResult& aRv)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<CreateFileTaskParent> task =
+    new CreateFileTaskParent(aFileSystem, aParam, aParent);
+
+  NS_ConvertUTF16toUTF8 path(aParam.realPath());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  task->mReplace = aParam.replace();
+
+  const FileSystemFileDataValue& data = aParam.data();
+
+  if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
+    task->mArrayData = data;
+    return task.forget();
+  }
+
+  MOZ_ASSERT(data.type() == FileSystemFileDataValue::TPBlobParent);
+
+  BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
+  task->mBlobImpl = bp->GetBlobImpl();
+  MOZ_ASSERT(task->mBlobImpl, "blobData should not be null.");
+
+  return task.forget();
+}
+
+CreateFileTaskParent::CreateFileTaskParent(FileSystemBase* aFileSystem,
+                                           const FileSystemCreateFileParams& aParam,
+                                           FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+  , mReplace(false)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+CreateFileTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
+
   nsAutoString path;
   aRv = mTargetPath->GetPath(path);
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemDirectoryResponse();
   }
 
   return FileSystemFileResponse(path);
 }
 
-void
-CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                        ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  FileSystemFileResponse r = aValue;
-
-  NS_ConvertUTF16toUTF8 path(r.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
-  }
-}
-
 nsresult
-CreateFileTask::Work()
+CreateFileTaskParent::IOWork()
 {
   class MOZ_RAII AutoClose final
   {
   public:
     explicit AutoClose(nsIOutputStream* aStream)
       : mStream(aStream)
     {
       MOZ_ASSERT(aStream);
@@ -275,48 +313,54 @@ CreateFileTask::Work()
 
   nsCOMPtr<nsIOutputStream> outputStream;
   rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   AutoClose acOutputStream(outputStream);
+  MOZ_ASSERT(sOutputBufferSize);
 
   nsCOMPtr<nsIOutputStream> bufferedOutputStream;
   rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
                                   outputStream,
                                   sOutputBufferSize);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   AutoClose acBufferedOutputStream(bufferedOutputStream);
 
-  if (mBlobStream) {
-    // Write the file content from blob data.
+  // Write the file content from blob data.
+  if (mBlobImpl) {
+    ErrorResult error;
+    nsCOMPtr<nsIInputStream> blobStream;
+    mBlobImpl->GetInternalStream(getter_AddRefs(blobStream), error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.StealNSResult();
+    }
 
     uint64_t bufSize = 0;
-    rv = mBlobStream->Available(&bufSize);
+    rv = blobStream->Available(&bufSize);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     while (bufSize && !mFileSystem->IsShutdown()) {
       uint32_t written = 0;
       uint32_t writeSize = bufSize < UINT32_MAX ? bufSize : UINT32_MAX;
-      rv = bufferedOutputStream->WriteFrom(mBlobStream, writeSize, &written);
+      rv = bufferedOutputStream->WriteFrom(blobStream, writeSize, &written);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       bufSize -= written;
     }
 
-    mBlobStream->Close();
-    mBlobStream = nullptr;
+    blobStream->Close();
 
     if (mFileSystem->IsShutdown()) {
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
@@ -333,55 +377,29 @@ CreateFileTask::Work()
 
   if (mArrayData.Length() != written) {
     return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
   }
 
   return NS_OK;
 }
 
-void
-CreateFileTask::HandlerCallback()
+nsresult
+CreateFileTaskParent::MainThreadWork()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    mBlobData = nullptr;
-    return;
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sOutputBufferSize) {
+    sOutputBufferSize =
+      mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4);
   }
 
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    mBlobData = nullptr;
-    return;
-  }
-
-  RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
-                                           mTargetPath);
-  mPromise->MaybeResolve(file);
-  mPromise = nullptr;
-  mBlobData = nullptr;
+  return FileSystemTaskParentBase::MainThreadWork();
 }
 
 void
-CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const
+CreateFileTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  if (mReplace) {
-    aAccess.AssignLiteral("write");
-    return;
-  }
-
-  aAccess.AssignLiteral("create");
-}
-
-void
-CreateFileTask::GetOutputBufferSize() const
-{
-  if (sOutputBufferSize || !XRE_IsParentProcess()) {
-    return;
-  }
-  sOutputBufferSize =
-    mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4);
+  GET_PERMISSION_ACCESS_TYPE(aAccess)
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/CreateFileTask.h
+++ b/dom/filesystem/CreateFileTask.h
@@ -26,69 +26,93 @@ public:
   static already_AddRefed<CreateFileTask>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aFile,
          Blob* aBlobData,
          InfallibleTArray<uint8_t>& aArrayData,
          bool replace,
          ErrorResult& aRv);
 
-  static already_AddRefed<CreateFileTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemCreateFileParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
   ~CreateFileTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
-  virtual nsresult
-  Work() override;
-
   virtual void
   HandlerCallback() override;
 
 private:
   CreateFileTask(FileSystemBase* aFileSystem,
                  nsIFile* aFile,
                  bool aReplace);
 
-  CreateFileTask(FileSystemBase* aFileSystem,
-                 const FileSystemCreateFileParams& aParam,
-                 FileSystemRequestParent* aParent);
-
-  void
-  GetOutputBufferSize() const;
-
-  static uint32_t sOutputBufferSize;
   RefPtr<Promise> mPromise;
   nsCOMPtr<nsIFile> mTargetPath;
 
-  // Not thread-safe and should be released on main thread.
-  RefPtr<Blob> mBlobData;
+  RefPtr<BlobImpl> mBlobImpl;
+
+  // This is going to be the content of the file, received by createFile()
+  // params.
+  InfallibleTArray<uint8_t> mArrayData;
+
+  bool mReplace;
+};
+
+class CreateFileTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<CreateFileTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemCreateFileParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
+
+  virtual bool
+  NeedToGoToMainThread() const override { return true; }
+
+  virtual nsresult
+  MainThreadWork() override;
 
-  nsCOMPtr<nsIInputStream> mBlobStream;
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
+
+protected:
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+  virtual nsresult
+  IOWork() override;
+
+private:
+  CreateFileTaskParent(FileSystemBase* aFileSystem,
+                       const FileSystemCreateFileParams& aParam,
+                       FileSystemRequestParent* aParent);
+
+  static uint32_t sOutputBufferSize;
+
+  nsCOMPtr<nsIFile> mTargetPath;
+
+  RefPtr<BlobImpl> mBlobImpl;
+
+  // This is going to be the content of the file, received by createFile()
+  // params.
   InfallibleTArray<uint8_t> mArrayData;
+
   bool mReplace;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CreateFileTask_h
--- a/dom/filesystem/DeviceStorageFileSystem.cpp
+++ b/dom/filesystem/DeviceStorageFileSystem.cpp
@@ -18,25 +18,31 @@
 #include "nsPIDOMWindow.h"
 #include "nsGlobalWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType,
                                                  const nsAString& aStorageName)
-  : mWindowId(0)
+  : mStorageType(aStorageType)
+  , mStorageName(aStorageName)
+  , mWindowId(0)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  mPermissionCheckType = ePermissionCheckByTestingPref;
 
-  mStorageType = aStorageType;
-  mStorageName = aStorageName;
-
-  mRequiresPermissionChecks =
-    !mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
+  if (NS_IsMainThread()) {
+    if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
+      mPermissionCheckType = ePermissionCheckNotRequired;
+    } else {
+      mPermissionCheckType = ePermissionCheckRequired;
+    }
+  } else {
+    AssertIsOnBackgroundThread();
+  }
 
   // Get the permission name required to access the file system.
   nsresult rv =
     DeviceStorageTypeChecker::GetPermissionForType(mStorageType, mPermission);
   NS_WARN_IF(NS_FAILED(rv));
 
   // Get the local path of the file system root.
   nsCOMPtr<nsIFile> rootFile;
@@ -48,73 +54,83 @@ DeviceStorageFileSystem::DeviceStorageFi
 
   if (!XRE_IsParentProcess()) {
     return;
   }
 
   // DeviceStorageTypeChecker is a singleton object and must be initialized on
   // the main thread. We initialize it here so that we can use it on the worker
   // thread.
-  DebugOnly<DeviceStorageTypeChecker*> typeChecker
-    = DeviceStorageTypeChecker::CreateOrGet();
-  MOZ_ASSERT(typeChecker);
+  if (NS_IsMainThread()) {
+    DebugOnly<DeviceStorageTypeChecker*> typeChecker =
+      DeviceStorageTypeChecker::CreateOrGet();
+    MOZ_ASSERT(typeChecker);
+  }
 }
 
 DeviceStorageFileSystem::~DeviceStorageFileSystem()
 {
+  AssertIsOnOwningThread();
 }
 
 already_AddRefed<FileSystemBase>
 DeviceStorageFileSystem::Clone()
 {
+  AssertIsOnOwningThread();
+
   RefPtr<DeviceStorageFileSystem> fs =
     new DeviceStorageFileSystem(mStorageType, mStorageName);
 
   fs->mWindowId = mWindowId;
 
   return fs.forget();
 }
 
 void
 DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aDeviceStorage);
+
   nsCOMPtr<nsPIDOMWindowInner> window = aDeviceStorage->GetOwner();
   MOZ_ASSERT(window->IsInnerWindow());
   mWindowId = window->WindowID();
 }
 
 void
 DeviceStorageFileSystem::Shutdown()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  AssertIsOnOwningThread();
   mShutdown = true;
 }
 
 nsISupports*
 DeviceStorageFileSystem::GetParentObject() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  AssertIsOnOwningThread();
+
   nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
   MOZ_ASSERT_IF(!mShutdown, window);
   return window ? window->AsInner() : nullptr;
 }
 
 void
 DeviceStorageFileSystem::GetRootName(nsAString& aRetval) const
 {
+  AssertIsOnOwningThread();
   aRetval = mStorageName;
 }
 
 bool
 DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
 {
-  MOZ_ASSERT(XRE_IsParentProcess(),
-             "Should be on parent process!");
+  MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!");
+  MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aFile);
 
   nsCOMPtr<nsIFile> rootPath;
   nsresult rv = NS_NewLocalFile(LocalOrDeviceStorageRootPath(), false,
                                 getter_AddRefs(rootPath));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
@@ -129,17 +145,17 @@ DeviceStorageFileSystem::IsSafeFile(nsIF
     = DeviceStorageTypeChecker::CreateOrGet();
   MOZ_ASSERT(typeChecker);
   return typeChecker->Check(mStorageType, aFile);
 }
 
 bool
 DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aDir);
 
   ErrorResult rv;
   RefPtr<FileSystemBase> fs = aDir->GetFileSystem(rv);
   if (NS_WARN_IF(rv.Failed())) {
     rv.SuppressException();
     return false;
   }
@@ -152,17 +168,30 @@ DeviceStorageFileSystem::IsSafeDirectory
 
   // Check if the given directory is from this storage.
   return fsSerialization == thisSerialization;
 }
 
 void
 DeviceStorageFileSystem::SerializeDOMPath(nsAString& aString) const
 {
+  AssertIsOnOwningThread();
+
   // Generate the string representation of the file system.
   aString.AssignLiteral("devicestorage-");
   aString.Append(mStorageType);
   aString.Append('-');
   aString.Append(mStorageName);
 }
 
+nsresult
+DeviceStorageFileSystem::MainThreadWork()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  DebugOnly<DeviceStorageTypeChecker*> typeChecker =
+    DeviceStorageTypeChecker::CreateOrGet();
+  MOZ_ASSERT(typeChecker);
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/DeviceStorageFileSystem.h
+++ b/dom/filesystem/DeviceStorageFileSystem.h
@@ -43,16 +43,22 @@ public:
   IsSafeFile(nsIFile* aFile) const override;
 
   virtual bool
   IsSafeDirectory(Directory* aDir) const override;
 
   virtual void
   SerializeDOMPath(nsAString& aSerializedString) const override;
 
+  virtual bool
+  NeedToGoToMainThread() const override { return true; }
+
+  virtual nsresult
+  MainThreadWork() override;
+
 private:
   virtual
   ~DeviceStorageFileSystem();
 
   nsString mStorageType;
   nsString mStorageName;
 
   uint64_t mWindowId;
--- a/dom/filesystem/FileSystemBase.cpp
+++ b/dom/filesystem/FileSystemBase.cpp
@@ -12,16 +12,19 @@
 
 namespace mozilla {
 namespace dom {
 
 // static
 already_AddRefed<FileSystemBase>
 FileSystemBase::DeserializeDOMPath(const nsAString& aString)
 {
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+
   if (StringBeginsWith(aString, NS_LITERAL_STRING("devicestorage-"))) {
     // The string representation of devicestorage file system is of the format:
     // devicestorage-StorageType-StorageName
 
     nsCharSeparatedTokenizer tokenizer(aString, char16_t('-'));
     tokenizer.nextToken();
 
     nsString storageType;
@@ -34,44 +37,51 @@ FileSystemBase::DeserializeDOMPath(const
       storageName = tokenizer.nextToken();
     }
 
     RefPtr<DeviceStorageFileSystem> f =
       new DeviceStorageFileSystem(storageType, storageName);
     return f.forget();
   }
 
-  return RefPtr<OSFileSystem>(new OSFileSystem(aString)).forget();
+  return RefPtr<OSFileSystemParent>(new OSFileSystemParent(aString)).forget();
 }
 
 FileSystemBase::FileSystemBase()
   : mShutdown(false)
-  , mRequiresPermissionChecks(true)
+  , mPermissionCheckType(eNotSet)
+#ifdef DEBUG
+  , mOwningThread(PR_GetCurrentThread())
+#endif
 {
 }
 
 FileSystemBase::~FileSystemBase()
 {
+  AssertIsOnOwningThread();
 }
 
 void
 FileSystemBase::Shutdown()
 {
+  AssertIsOnOwningThread();
   mShutdown = true;
 }
 
 nsISupports*
 FileSystemBase::GetParentObject() const
 {
+  AssertIsOnOwningThread();
   return nullptr;
 }
 
 bool
 FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const
 {
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aFile, "aFile Should not be null.");
   MOZ_ASSERT(aPath);
 
   nsAutoString filePath;
   ErrorResult rv;
   aFile->GetMozFullPathInternal(filePath, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return false;
@@ -84,31 +94,34 @@ FileSystemBase::GetRealPath(BlobImpl* aF
   }
 
   return true;
 }
 
 bool
 FileSystemBase::IsSafeFile(nsIFile* aFile) const
 {
+  AssertIsOnOwningThread();
   return false;
 }
 
 bool
 FileSystemBase::IsSafeDirectory(Directory* aDir) const
 {
+  AssertIsOnOwningThread();
   return false;
 }
 
 void
 FileSystemBase::GetDOMPath(nsIFile* aFile,
                            Directory::DirectoryType aType,
                            nsAString& aRetval,
                            ErrorResult& aRv) const
 {
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aFile);
 
   if (aType == Directory::eDOMRootDirectory) {
     aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
     return;
   }
 
   nsCOMPtr<nsIFile> fileSystemPath;
@@ -166,10 +179,17 @@ FileSystemBase::GetDOMPath(nsIFile* aFil
   aRetval.Truncate();
 
   for (int32_t i = parts.Length() - 1; i >= 0; --i) {
     aRetval.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
     aRetval.Append(parts[i]);
   }
 }
 
+void
+FileSystemBase::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+  MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemBase.h
+++ b/dom/filesystem/FileSystemBase.h
@@ -13,18 +13,18 @@
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 
 class FileSystemBase
 {
+public:
   NS_INLINE_DECL_REFCOUNTING(FileSystemBase)
-public:
 
   // Create file system object from its string representation.
   static already_AddRefed<FileSystemBase>
   DeserializeDOMPath(const nsAString& aString);
 
   FileSystemBase();
 
   virtual void
@@ -82,26 +82,63 @@ public:
    * Get the permission name required to access this file system.
    */
   const nsCString&
   GetPermission() const
   {
     return mPermission;
   }
 
-  bool
-  RequiresPermissionChecks() const
+  // The decision about doing or not doing the permission check cannot be done
+  // everywhere because, for some FileSystemBase implementation, this depends on
+  // a preference.
+  // This enum describes all the possible decisions. The implementation will do
+  // the check on the main-thread in the child and in the parent process when
+  // needed.
+  // Note: the permission check should not fail in PBackground because that
+  // means that the child has been compromised. If this happens the child
+  // process is killed.
+  enum ePermissionCheckType {
+    // When on the main-thread, we must check if we have
+    // device.storage.prompt.testing set to true.
+    ePermissionCheckByTestingPref,
+
+    // No permission check must be done.
+    ePermissionCheckNotRequired,
+
+    // Permission check is required.
+    ePermissionCheckRequired,
+
+    // This is the default value. We crash if this is let like this.
+    eNotSet
+  };
+
+  ePermissionCheckType
+  PermissionCheckType() const
   {
-    return mRequiresPermissionChecks;
+    MOZ_ASSERT(mPermissionCheckType != eNotSet);
+    return mPermissionCheckType;
   }
 
+  // IPC initialization
+  // See how these 2 methods are used in FileSystemTaskBase.
+
+  virtual bool
+  NeedToGoToMainThread() const { return false; }
+
+  virtual nsresult
+  MainThreadWork() { return NS_ERROR_FAILURE; }
+
   // CC methods
   virtual void Unlink() {}
   virtual void Traverse(nsCycleCollectionTraversalCallback &cb) {}
 
+  void
+  AssertIsOnOwningThread() const;
+
 protected:
   virtual ~FileSystemBase();
 
   // The local path of the root (i.e. the OS path, with OS path separators, of
   // the OS directory that acts as the root of this OSFileSystem).
   // This path must be set by the FileSystem implementation immediately
   // because it will be used for the validation of any FileSystemTaskBase.
   // The concept of this path is that, any task will never go out of it and this
@@ -114,15 +151,19 @@ protected:
   // the parent directory of the exposed root path.
   nsString mLocalOrDeviceStorageRootPath;
 
   bool mShutdown;
 
   // The permission name required to access the file system.
   nsCString mPermission;
 
-  bool mRequiresPermissionChecks;
+  ePermissionCheckType mPermissionCheckType;
+
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemBase_h
--- a/dom/filesystem/FileSystemPermissionRequest.cpp
+++ b/dom/filesystem/FileSystemPermissionRequest.cpp
@@ -3,32 +3,35 @@
 /* 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 "FileSystemPermissionRequest.h"
 
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIDocument.h"
 #include "nsPIDOMWindow.h"
 #include "nsContentPermissionHelper.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable,
-                  nsIContentPermissionRequest)
+                  nsIContentPermissionRequest,
+                  nsIIPCBackgroundChildCreateCallback)
 
-// static
-void
+/* static */ void
 FileSystemPermissionRequest::RequestForTask(FileSystemTaskBase* aTask)
 {
   MOZ_ASSERT(aTask, "aTask should not be null!");
   MOZ_ASSERT(NS_IsMainThread());
+
   RefPtr<FileSystemPermissionRequest> request =
     new FileSystemPermissionRequest(aTask);
   NS_DispatchToCurrentThread(request);
 }
 
 FileSystemPermissionRequest::FileSystemPermissionRequest(FileSystemTaskBase* aTask)
   : mTask(aTask)
 {
@@ -93,41 +96,47 @@ FileSystemPermissionRequest::GetElement(
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FileSystemPermissionRequest::Cancel()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mTask->SetError(NS_ERROR_DOM_SECURITY_ERR);
-  mTask->Start();
+  ScheduleTask();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FileSystemPermissionRequest::Allow(JS::HandleValue aChoices)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aChoices.isUndefined());
-  mTask->Start();
+  ScheduleTask();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FileSystemPermissionRequest::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
   if (!filesystem) {
     Cancel();
     return NS_OK;
   }
 
-  if (!filesystem->RequiresPermissionChecks()) {
+  if (filesystem->PermissionCheckType() == FileSystemBase::ePermissionCheckNotRequired) {
+    Allow(JS::UndefinedHandleValue);
+    return NS_OK;
+  }
+
+  if (filesystem->PermissionCheckType() == FileSystemBase::ePermissionCheckByTestingPref &&
+      mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
     Allow(JS::UndefinedHandleValue);
     return NS_OK;
   }
 
   if (!mWindow) {
     Cancel();
     return NS_OK;
   }
@@ -141,10 +150,37 @@ FileSystemPermissionRequest::GetRequeste
 {
   NS_ENSURE_ARG_POINTER(aRequester);
 
   nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
   requester.forget(aRequester);
   return NS_OK;
 }
 
+void
+FileSystemPermissionRequest::ActorFailed()
+{
+  MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+}
+
+void
+FileSystemPermissionRequest::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
+{
+  mTask->Start();
+}
+
+void
+FileSystemPermissionRequest::ScheduleTask()
+{
+  PBackgroundChild* actor =
+    mozilla::ipc::BackgroundChild::GetForCurrentThread();
+  if (actor) {
+    ActorCreated(actor);
+  } else {
+    if (NS_WARN_IF(
+        !mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
+      MOZ_CRASH();
+    }
+  }
+}
+
 } /* namespace dom */
 } /* namespace mozilla */
--- a/dom/filesystem/FileSystemPermissionRequest.h
+++ b/dom/filesystem/FileSystemPermissionRequest.h
@@ -5,43 +5,52 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FileSystemPermissionRequest_h
 #define mozilla_dom_FileSystemPermissionRequest_h
 
 #include "nsAutoPtr.h"
 #include "nsIRunnable.h"
 #include "nsIContentPermissionPrompt.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsString.h"
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
 class FileSystemTaskBase;
 
 class FileSystemPermissionRequest final
   : public nsIContentPermissionRequest
   , public nsIRunnable
+  , public nsIIPCBackgroundChildCreateCallback
 {
 public:
   // Request permission for the given task.
   static void
   RequestForTask(FileSystemTaskBase* aTask);
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSICONTENTPERMISSIONREQUEST
   NS_DECL_NSIRUNNABLE
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+
 private:
   explicit FileSystemPermissionRequest(FileSystemTaskBase* aTask);
 
-  virtual
   ~FileSystemPermissionRequest();
 
+  // Once the permission check has been done, we must run the task using IPC and
+  // PBackground. This method checks if the PBackground thread is ready to
+  // receive the task and in case waits for ActorCreated() to be called.
+  void
+  ScheduleTask();
+
   nsCString mPermissionType;
   nsCString mPermissionAccess;
   RefPtr<FileSystemTaskBase> mTask;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIContentPermissionRequester> mRequester;
 };
 
--- a/dom/filesystem/FileSystemRequestParent.cpp
+++ b/dom/filesystem/FileSystemRequestParent.cpp
@@ -13,85 +13,96 @@
 
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/dom/FileSystemBase.h"
 
 namespace mozilla {
 namespace dom {
 
 FileSystemRequestParent::FileSystemRequestParent()
+  : mDestroyed(false)
 {
+  AssertIsOnBackgroundThread();
 }
 
 FileSystemRequestParent::~FileSystemRequestParent()
 {
+  AssertIsOnBackgroundThread();
 }
 
 #define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name)                         \
     case FileSystemParams::TFileSystem##name##Params: {                        \
       const FileSystem##name##Params& p = aParams;                             \
       mFileSystem = FileSystemBase::DeserializeDOMPath(p.filesystem());        \
-      task = name##Task::Create(mFileSystem, p, this, rv);                     \
+      MOZ_ASSERT(mFileSystem);                                                 \
+      mTask = name##TaskParent::Create(mFileSystem, p, this, rv);              \
       if (NS_WARN_IF(rv.Failed())) {                                           \
         return false;                                                          \
       }                                                                        \
       break;                                                                   \
     }
 
 bool
-FileSystemRequestParent::Dispatch(ContentParent* aParent,
-                                  const FileSystemParams& aParams)
+FileSystemRequestParent::Initialize(const FileSystemParams& aParams)
 {
-  MOZ_ASSERT(aParent, "aParent should not be null.");
-  RefPtr<FileSystemTaskBase> task;
+  AssertIsOnBackgroundThread();
+
   ErrorResult rv;
 
   switch (aParams.type()) {
 
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateFile)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetDirectoryListing)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(Remove)
 
     default: {
       NS_RUNTIMEABORT("not reached");
       break;
     }
   }
 
-  if (NS_WARN_IF(!task || !mFileSystem)) {
+  if (NS_WARN_IF(!mTask || !mFileSystem)) {
     // Should never reach here.
     return false;
   }
 
-  if (mFileSystem->RequiresPermissionChecks()) {
-    // Check the content process permission.
-
-    nsCString access;
-    task->GetPermissionAccessType(access);
+  if (mFileSystem->PermissionCheckType() != FileSystemBase::ePermissionCheckNotRequired) {
+    nsAutoCString access;
+    mTask->GetPermissionAccessType(access);
 
-    nsAutoCString permissionName;
-    permissionName = mFileSystem->GetPermission();
-    permissionName.Append('-');
-    permissionName.Append(access);
-
-    if (!AssertAppProcessPermission(aParent, permissionName.get())) {
-      return false;
-    }
+    mPermissionName = mFileSystem->GetPermission();
+    mPermissionName.Append('-');
+    mPermissionName.Append(access);
   }
 
-  task->Start();
   return true;
 }
 
 void
-FileSystemRequestParent::ActorDestroy(ActorDestroyReason why)
+FileSystemRequestParent::Start()
 {
+  MOZ_ASSERT(!mDestroyed);
+  MOZ_ASSERT(mFileSystem);
+  MOZ_ASSERT(mTask);
+
+  mTask->Start();
+}
+
+void
+FileSystemRequestParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mDestroyed);
+
   if (!mFileSystem) {
     return;
   }
+
   mFileSystem->Shutdown();
   mFileSystem = nullptr;
+  mTask = nullptr;
+  mDestroyed = true;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemRequestParent.h
+++ b/dom/filesystem/FileSystemRequestParent.h
@@ -3,47 +3,65 @@
 /* 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_FileSystemRequestParent_h
 #define mozilla_dom_FileSystemRequestParent_h
 
 #include "mozilla/dom/PFileSystemRequestParent.h"
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/FileSystemBase.h"
 
 namespace mozilla {
 namespace dom {
 
-class FileSystemBase;
+class FileSystemParams;
+class FileSystemTaskParentBase;
 
-class FileSystemRequestParent final
-  : public PFileSystemRequestParent
+class FileSystemRequestParent final : public PFileSystemRequestParent
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemRequestParent)
+
 public:
   FileSystemRequestParent();
 
-  bool
-  IsRunning()
+  const nsCString&
+  PermissionName() const
   {
-    return state() == PFileSystemRequest::__Start;
+    return mPermissionName;
+  }
+
+  FileSystemBase::ePermissionCheckType
+  PermissionCheckType() const
+  {
+    return mFileSystem ? mFileSystem->PermissionCheckType()
+                       : FileSystemBase::eNotSet;
   }
 
   bool
-  Dispatch(ContentParent* aParent, const FileSystemParams& aParams);
+  Initialize(const FileSystemParams& aParams);
+
+  void
+  Start();
+
+  bool Destroyed() const
+  {
+    return mDestroyed;
+  }
 
   virtual void
   ActorDestroy(ActorDestroyReason why) override;
 
 private:
-  // Private destructor, to discourage deletion outside of Release():
-  virtual
   ~FileSystemRequestParent();
 
   RefPtr<FileSystemBase> mFileSystem;
+  RefPtr<FileSystemTaskParentBase> mTask;
+
+  nsCString mPermissionName;
+
+  bool mDestroyed;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemRequestParent_h
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -2,254 +2,328 @@
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
 #include "mozilla/dom/FileSystemTaskBase.h"
 
 #include "nsNetCID.h"
-#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/PContent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/unused.h"
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
-class FileSystemReleaseRunnable final : public nsRunnable
+nsresult
+FileSystemErrorFromNsError(const nsresult& aErrorValue)
 {
-public:
-  explicit FileSystemReleaseRunnable(RefPtr<FileSystemBase>& aDoomed)
-    : mDoomed(nullptr)
-  {
-    aDoomed.swap(mDoomed);
+  uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
+  if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
+      module == NS_ERROR_MODULE_DOM_FILE ||
+      module == NS_ERROR_MODULE_DOM) {
+    return aErrorValue;
   }
 
-  NS_IMETHOD Run()
-  {
-    mDoomed->Release();
-    return NS_OK;
+  switch (aErrorValue) {
+    case NS_OK:
+      return NS_OK;
+
+    case NS_ERROR_FILE_INVALID_PATH:
+    case NS_ERROR_FILE_UNRECOGNIZED_PATH:
+      return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+
+    case NS_ERROR_FILE_DESTINATION_NOT_DIR:
+      return NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR;
+
+    case NS_ERROR_FILE_ACCESS_DENIED:
+    case NS_ERROR_FILE_DIR_NOT_EMPTY:
+      return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
+
+    case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
+    case NS_ERROR_NOT_AVAILABLE:
+      return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
+
+    case NS_ERROR_FILE_ALREADY_EXISTS:
+      return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
+
+    case NS_ERROR_FILE_NOT_DIRECTORY:
+      return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
+
+    case NS_ERROR_UNEXPECTED:
+    default:
+      return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
   }
+}
 
-private:
-  FileSystemBase* MOZ_OWNING_REF mDoomed;
-};
+nsresult
+DispatchToIOThread(nsIRunnable* aRunnable)
+{
+  MOZ_ASSERT(aRunnable);
+
+  nsCOMPtr<nsIEventTarget> target
+    = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(target);
+
+  return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
+}
 
 } // anonymous namespace
 
+/**
+ * FileSystemTaskBase class
+ */
+
 FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem)
   : mErrorValue(NS_OK)
   , mFileSystem(aFileSystem)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
 }
 
-FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem,
-                                       const FileSystemParams& aParam,
-                                       FileSystemRequestParent* aParent)
-  : mErrorValue(NS_OK)
-  , mFileSystem(aFileSystem)
-  , mRequestParent(aParent)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(),
-             "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
-}
-
 FileSystemTaskBase::~FileSystemTaskBase()
 {
-  if (!NS_IsMainThread()) {
-    RefPtr<FileSystemReleaseRunnable> runnable =
-      new FileSystemReleaseRunnable(mFileSystem);
-    MOZ_ASSERT(!mFileSystem);
-    NS_DispatchToMainThread(runnable);
-  }
+  mFileSystem->AssertIsOnOwningThread();
 }
 
 FileSystemBase*
 FileSystemTaskBase::GetFileSystem() const
 {
+  mFileSystem->AssertIsOnOwningThread();
   return mFileSystem.get();
 }
 
 void
 FileSystemTaskBase::Start()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  mFileSystem->AssertIsOnOwningThread();
 
   if (HasError()) {
-    NS_DispatchToMainThread(this);
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewRunnableMethod(this, &FileSystemTaskBase::HandlerCallback);
+    NS_DispatchToMainThread(runnable);
     return;
   }
 
-  if (XRE_IsParentProcess()) {
-    // Run in parent process.
-    // Start worker thread.
-    nsCOMPtr<nsIEventTarget> target
-      = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-    NS_ASSERTION(target, "Must have stream transport service.");
-    target->Dispatch(this, NS_DISPATCH_NORMAL);
-    return;
-  }
-
-  // Run in child process.
   if (mFileSystem->IsShutdown()) {
     return;
   }
 
   nsAutoString serialization;
   mFileSystem->SerializeDOMPath(serialization);
 
   ErrorResult rv;
   FileSystemParams params = GetRequestParams(serialization, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return;
   }
 
   // Retain a reference so the task object isn't deleted without IPDL's
   // knowledge. The reference will be released by
-  // mozilla::dom::ContentChild::DeallocPFileSystemRequestChild.
+  // mozilla::ipc::BackgroundChildImpl::DeallocPFileSystemRequestChild.
   NS_ADDREF_THIS();
 
-  ContentChild::GetSingleton()->SendPFileSystemRequestConstructor(this,
-                                                                  params);
-}
-
-NS_IMETHODIMP
-FileSystemTaskBase::Run()
-{
-  if (!NS_IsMainThread()) {
-    // Run worker thread tasks
-    nsresult rv = Work();
-    if (NS_FAILED(rv)) {
-      SetError(rv);
-    }
-    // Dispatch itself to main thread
-    NS_DispatchToMainThread(this);
-    return NS_OK;
-  }
-
-  // Run main thread tasks
-  HandleResult();
-  return NS_OK;
-}
+  // If we are here, PBackground must be up and running, because Start() is
+  // called only by FileSystemPermissionRequest, and that class takes care of
+  // PBackground initialization.
+  PBackgroundChild* actor =
+    mozilla::ipc::BackgroundChild::GetForCurrentThread();
+  MOZ_ASSERT(actor);
 
-void
-FileSystemTaskBase::HandleResult()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    return;
-  }
-  if (mRequestParent && mRequestParent->IsRunning()) {
-    Unused << mRequestParent->Send__delete__(mRequestParent,
-      GetRequestResult());
-  } else {
-    HandlerCallback();
-  }
-}
-
-FileSystemResponseValue
-FileSystemTaskBase::GetRequestResult() const
-{
-  MOZ_ASSERT(XRE_IsParentProcess(),
-             "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (!HasError()) {
-    ErrorResult rv;
-    FileSystemResponseValue value = GetSuccessRequestResult(rv);
-    if (NS_WARN_IF(rv.Failed())) {
-      return FileSystemErrorResponse(rv.StealNSResult());
-    }
-
-    return value;
-  }
-
-  return FileSystemErrorResponse(mErrorValue);
+  actor->SendPFileSystemRequestConstructor(this, params);
 }
 
 void
 FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue)
 {
-  MOZ_ASSERT(!XRE_IsParentProcess(),
-             "Only call from child process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  mFileSystem->AssertIsOnOwningThread();
+
   if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) {
     FileSystemErrorResponse r = aValue;
     mErrorValue = r.error();
   } else {
     ErrorResult rv;
     SetSuccessRequestResult(aValue, rv);
     mErrorValue = rv.StealNSResult();
   }
 }
 
 bool
 FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue)
 {
+  mFileSystem->AssertIsOnOwningThread();
+
   SetRequestResult(aValue);
   HandlerCallback();
   return true;
 }
 
 void
 FileSystemTaskBase::SetError(const nsresult& aErrorValue)
 {
-  uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
-  if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
-      module == NS_ERROR_MODULE_DOM_FILE ||
-      module == NS_ERROR_MODULE_DOM) {
-    mErrorValue = aErrorValue;
+  mErrorValue = FileSystemErrorFromNsError(aErrorValue);
+}
+
+/**
+ * FileSystemTaskParentBase class
+ */
+
+FileSystemTaskParentBase::FileSystemTaskParentBase(FileSystemBase* aFileSystem,
+                                                  const FileSystemParams& aParam,
+                                                  FileSystemRequestParent* aParent)
+  : mErrorValue(NS_OK)
+  , mFileSystem(aFileSystem)
+  , mRequestParent(aParent)
+  , mBackgroundEventTarget(NS_GetCurrentThread())
+{
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Only call from parent process!");
+  MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(mBackgroundEventTarget);
+  AssertIsOnBackgroundThread();
+}
+
+FileSystemTaskParentBase::~FileSystemTaskParentBase()
+{
+  // This task can be released on different threads because we dispatch it (as
+  // runnable) to main-thread, I/O and then back to the PBackground thread.
+  NS_ProxyRelease(mBackgroundEventTarget, mFileSystem.forget());
+  NS_ProxyRelease(mBackgroundEventTarget, mRequestParent.forget());
+}
+
+void
+FileSystemTaskParentBase::Start()
+{
+  AssertIsOnBackgroundThread();
+  mFileSystem->AssertIsOnOwningThread();
+
+  if (NeedToGoToMainThread()) {
+    nsresult rv = NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
+    NS_WARN_IF(NS_FAILED(rv));
+    return;
+  }
+
+  nsresult rv = DispatchToIOThread(this);
+  NS_WARN_IF(NS_FAILED(rv));
+}
+
+void
+FileSystemTaskParentBase::HandleResult()
+{
+  AssertIsOnBackgroundThread();
+  mFileSystem->AssertIsOnOwningThread();
+
+  if (mFileSystem->IsShutdown()) {
     return;
   }
 
-  switch (aErrorValue) {
-    case NS_OK:
-      mErrorValue = NS_OK;
-      return;
+  MOZ_ASSERT(mRequestParent);
+  Unused << mRequestParent->Send__delete__(mRequestParent, GetRequestResult());
+}
+
+FileSystemResponseValue
+FileSystemTaskParentBase::GetRequestResult() const
+{
+  AssertIsOnBackgroundThread();
+  mFileSystem->AssertIsOnOwningThread();
+
+  if (HasError()) {
+    return FileSystemErrorResponse(mErrorValue);
+  }
+
+  ErrorResult rv;
+  FileSystemResponseValue value = GetSuccessRequestResult(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return FileSystemErrorResponse(rv.StealNSResult());
+  }
 
-    case NS_ERROR_FILE_INVALID_PATH:
-    case NS_ERROR_FILE_UNRECOGNIZED_PATH:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
-      return;
+  return value;
+}
+
+void
+FileSystemTaskParentBase::SetError(const nsresult& aErrorValue)
+{
+  mErrorValue = FileSystemErrorFromNsError(aErrorValue);
+}
 
-    case NS_ERROR_FILE_DESTINATION_NOT_DIR:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR;
-      return;
+bool
+FileSystemTaskParentBase::NeedToGoToMainThread() const
+{
+  return mFileSystem->NeedToGoToMainThread();
+}
 
-    case NS_ERROR_FILE_ACCESS_DENIED:
-    case NS_ERROR_FILE_DIR_NOT_EMPTY:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
-      return;
+nsresult
+FileSystemTaskParentBase::MainThreadWork()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return mFileSystem->MainThreadWork();
+}
 
-    case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
-    case NS_ERROR_NOT_AVAILABLE:
-      mErrorValue = NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
-      return;
+NS_IMETHODIMP
+FileSystemTaskParentBase::Run()
+{
+  // This method can run in 3 different threads. Here why:
+  // 1. if we are on the main-thread it's because the task must do something
+  //    here. If no errors are returned we go the step 2.
+  // 2. We can be here directly if the task doesn't have nothing to do on the
+  //    main-thread. We are are on the I/O thread and we call IOWork().
+  // 3. Both step 1 (in case of error) and step 2 end up here where return the
+  //    value back to the PBackground thread.
+  if (NS_IsMainThread()) {
+    MOZ_ASSERT(NeedToGoToMainThread());
 
-    case NS_ERROR_FILE_ALREADY_EXISTS:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
-      return;
+    nsresult rv = MainThreadWork();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      SetError(rv);
+
+      // Something when wrong. Let's go to the Background thread directly
+      // skipping the I/O thread step.
+      rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
 
-    case NS_ERROR_FILE_NOT_DIRECTORY:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
-      return;
+    // Next step must happen on the I/O thread.
+    rv = DispatchToIOThread(this);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
 
-    case NS_ERROR_UNEXPECTED:
-    default:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
-      return;
+  // Run I/O thread tasks
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = IOWork();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      SetError(rv);
+    }
+
+    // Let's go back to PBackground thread to finish the work.
+    rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
   }
+
+  // If we are here, it's because the I/O work has been done and we have to
+  // handle the result back via IPC.
+  AssertIsOnBackgroundThread();
+  HandleResult();
+  return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -5,121 +5,125 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FileSystemTaskBase_h
 #define mozilla_dom_FileSystemTaskBase_h
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
-class BlobParent;
 class FileSystemBase;
 class FileSystemParams;
+class PBlobParent;
 
 /*
  * The base class to implement a Task class.
- * The task is used to handle the OOP (out of process) operations.
- * The file system operations can only be performed in the parent process. When
- * performing such a parent-process-only operation, a task will delivered the
- * operation to the parent process if needed.
+ * The file system operations can only be performed in the parent process. In
+ * order to avoid duplicated code, we used PBackground for child-parent and
+ * parent-parent communications.
  *
  * The following diagram illustrates the how a API call from the content page
  * starts a task and gets call back results.
  *
- * The left block is the call sequence inside the child process, while the
- * right block is the call sequence inside the parent process.
- *
- * There are two types of API call. One is from the content page of the child
- * process and we mark the steps as (1) to (8). The other is from the content
- * page of the parent process and we mark the steps as (1') to (4').
+ * The left block is the call sequence inside any process loading content, while
+ * the right block is the call sequence only inside the parent process.
  *
- *       Page                                             Page
- *        |                                                |
- *        | (1)                                            |  (1')
- *  ______|________________     |     _____________________|_____________
- * |      |                |    |    |                     |             |
- * |      |  Task in       |    |    |  Task in            |             |
- * |      |  Child Process |    |    |  Parent Process     |             |
- * |      V                |   IPC   |                     V             |
- * [new FileSystemTaskBase()]   |    |     [new FileSystemTaskBase()]    |
- * |         |             |    |    |                         |         |
- * |         | (2)         |         |                         | (2')    |
- * |         V             |   (3)   |                         |         |
- * |    [GetRequestParams]------------->[new FileSystemTaskBase(...)]    |
- * |                       |         |          |              |         |
- * |                       |    |    |          | (4)          |         |
- * |                       |    |    |          |              V         |
- * |                       |    |    |          -----------> [Work]      |
- * |                       |   IPC   |                         |         |
- * |                       |    |    |                     (5) | (3')    |
- * |                       |    |    |                         V         |
- * |                       |    |    |          --------[HandleResult]   |
- * |                       |    |    |          |              |         |
- * |                       |         |          | (6)          |         |
- * |                       |   (7)   |          V              |         |
- * |   [SetRequestResult]<-------------[GetRequestResult]      |         |
- * |       |               |         |                         | (4')    |
- * |       | (8)           |    |    |                         |         |
- * |       V               |    |    |                         V         |
- * |[HandlerCallback]      |   IPC   |               [HandlerCallback]   |
- * |_______|_______________|    |    |_________________________|_________|
- *         |                    |                              |
- *         V                                                   V
- *        Page                                                Page
+ *       Page
+ *        |
+ *        | (1)
+ *  ______|_______________________     |     __________________________________
+ * |      |                       |    |    |                                  |
+ * |      V                       |   IPC   | PBackground thread on            |
+ * | [new FileSystemTaskBase()]   |    |    | the parent process               |
+ * |       |                      |    |    |                                  |
+ * |       | (2)                  |    |    |                                  |
+ * |       V                      |    |    |                                  |
+ * | [FileSystemPermissionRequest------------------\                           |
+ * |  ::RequestForTask()] <------------------------/                           |
+ * |         |                    |    |    |                                  |
+ * |         | (3)                |         |                                  |
+ * |         V                    |   (4)   |                                  |
+ * |    [GetRequestParams]------------------->[new FileSystemTaskParentBase()] |
+ * |                              |         |          |                       |
+ * |                              |    |    |          | (5)   _____________   |
+ * |                              |    |    |          |      |             |  |
+ * |                              |    |    |          |      | I/O Thread  |  |
+ * |                              |    |    |          |      |             |  |
+ * |                              |    |    |          ---------> [IOWork]  |  |
+ * |                              |   IPC   |                 |     |       |  |
+ * |                              |    |    |                 |     | (6)   |  |
+ * |                              |    |    |          --------------       |  |
+ * |                              |    |    |          |      |_____________|  |
+ * |                              |    |    |          |                       |
+ * |                              |    |    |          V                       |
+ * |                              |    |    |     [HandleResult]               |
+ * |                              |    |    |          |                       |
+ * |                              |         |          | (7)                   |
+ * |                              |   (8)   |          V                       |
+ * |   [SetRequestResult]<--------------------[GetRequestResult]               |
+ * |       |                      |         |                                  |
+ * |       | (9)                  |    |    |                                  |
+ * |       V                      |    |    |                                  |
+ * |[HandlerCallback]             |   IPC   |                                  |
+ * |_______|______________________|    |    |__________________________________|
+ *         |                           |
+ *         V
+ *        Page
  *
- * 1. From child process page
- * Child:
+ * 1. From the process that is handling the request
+ * Child/Parent (it can be in any process):
  *   (1) Call FileSystem API from content page with JS. Create a task and run.
  *   The base constructor [FileSystemTaskBase()] of the task should be called.
- *   (2) Forward the task to the parent process through the IPC and call
+ *   (2) The FileSystemTaskBase object is given to
+ *   [FileSystemPermissionRequest::RequestForTask()] that will perform a
+ *   permission check step if needed (See ePermissionCheckType enum). The real
+ *   operation is done on the parent process but it's hidden by
+ *   [nsContentPermissionUtils::AskPermission()]. If the permission check is not
+ *   needed or if the page has the right permission, the
+ *   FileSystemPermissionRequest will start the task (only once PBackground
+ *   actor is fully initialized).
+ *   (3) Forward the task to the parent process through the IPC and call
  *   [GetRequestParams] to prepare the parameters of the IPC.
  * Parent:
- *   (3) The parent process receives IPC and handle it in
- *   FileystemRequestParent.
- *   Get the IPC parameters and create a task to run the IPC task. The base
- *   constructor [FileSystemTaskBase(aParam, aParent)] of the task should be
- *   called to set the task as an IPC task.
- *   (4) The task operation will be performed in the member function of [Work].
- *   A worker thread will be created to run that function. If error occurs
+ *   (4) The parent process receives IPC and handle it in
+ *   FileystemRequestParent. Get the IPC parameters and create a task to run the
+ *   IPC task. The base constructor [FileSystemTaskParentBase(aParam, aParent)]
+ *   For security reasons, we do an additional permission check if needed. In
+ *   the check fails, the child process will be killed.
+ *   of the task should be called to set the task as an IPC task.
+ *   (5) The task operation will be performed in the member function of [IOWork].
+ *   A I/O  thread will be created to run that function. If error occurs
  *   during the operation, call [SetError] to record the error and then abort.
- *   (5) After finishing the task operation, call [HandleResult] to send the
+ *   (6) After finishing the task operation, call [HandleResult] to send the
  *   result back to the child process though the IPC.
- *   (6) Call [GetRequestResult] request result to prepare the parameters of the
+ *   (7) Call [GetRequestResult] request result to prepare the parameters of the
  *   IPC. Because the formats of the error result for different task are the
  *   same, FileSystemTaskBase can handle the error message without interfering.
  *   Each task only needs to implement its specific success result preparation
  *   function -[GetSuccessRequestResult].
- * Child:
- *   (7) The child process receives IPC and calls [SetRequestResult] to get the
+ * Child/Parent:
+ *   (8) The process receives IPC and calls [SetRequestResult] to get the
  *   task result. Each task needs to implement its specific success result
  *   parsing function [SetSuccessRequestResult] to get the success result.
- *   (8) Call [HandlerCallback] to send the task result to the content page.
- * 2. From parent process page
- * We don't need to send the task parameters and result to other process. So
- * there are less steps, but their functions are the same. The correspondence
- * between the two types of steps is:
- *   (1') = (1),
- *   (2') = (4),
- *   (3') = (5),
- *   (4') = (8).
+ *   (9) Call [HandlerCallback] to send the task result to the content page.
  */
-class FileSystemTaskBase
-  : public nsRunnable
-  , public PFileSystemRequestChild
+class FileSystemTaskBase : public PFileSystemRequestChild
 {
 public:
+  NS_INLINE_DECL_REFCOUNTING(FileSystemTaskBase)
+
   /*
-   * Start the task. If the task is running the child process, it will be
-   * forwarded to parent process by IPC, or else, creates a worker thread to
-   * do the task work.
+   * Start the task. It will dispatch all the information to the parent process,
+   * PBackground thread. This method must be called from the owning thread.
    */
   void
   Start();
 
   /*
    * The error codes are defined in xpcom/base/ErrorList.h and their
    * corresponding error name and message are defined in dom/base/domerr.msg.
    */
@@ -130,112 +134,164 @@ public:
   GetFileSystem() const;
 
   /*
    * Get the type of permission access required to perform this task.
    */
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const = 0;
 
-  NS_DECL_NSIRUNNABLE
+  /*
+   * After the task is completed, this function will be called to pass the task
+   * result to the content page. This method is called in the owning thread.
+   * Override this function to handle the call back to the content page.
+   */
+  virtual void
+  HandlerCallback() = 0;
+
+  bool
+  HasError() const { return NS_FAILED(mErrorValue); }
+
 protected:
   /*
    * To create a task to handle the page content request.
    */
   explicit FileSystemTaskBase(FileSystemBase* aFileSystem);
 
-  /*
-   * To create a parent process task delivered from the child process through
-   * IPC.
-   */
-  FileSystemTaskBase(FileSystemBase* aFileSystem,
-                     const FileSystemParams& aParam,
-                     FileSystemRequestParent* aParent);
-
   virtual
   ~FileSystemTaskBase();
 
   /*
-   * The function to perform task operation. It will be run on the worker
-   * thread of the parent process.
-   * Overrides this function to define the task operation for individual task.
-   */
-  virtual nsresult
-  Work() = 0;
-
-  /*
-   * After the task is completed, this function will be called to pass the task
-   * result to the content page.
-   * Override this function to handle the call back to the content page.
-   */
-  virtual void
-  HandlerCallback() = 0;
-
-  /*
    * Wrap the task parameter to FileSystemParams for sending it through IPC.
    * It will be called when we need to forward a task from the child process to
-   * the prarent process.
+   * the parent process. This method runs in the owning thread.
    * @param filesystem The string representation of the file system.
    */
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const = 0;
 
   /*
+   * Unwrap the IPC message to get the task success result.
+   * It will be called when the task is completed successfully and an IPC
+   * message is received in the child process and we want to get the task
+   * success result. This method runs in the owning thread.
+   */
+  virtual void
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                          ErrorResult& aRv) = 0;
+
+  // Overrides PFileSystemRequestChild
+  virtual bool
+  Recv__delete__(const FileSystemResponseValue& value) override;
+
+  nsresult mErrorValue;
+  RefPtr<FileSystemBase> mFileSystem;
+
+private:
+
+  /*
+   * Unwrap the IPC message to get the task result.
+   * It will be called when the task is completed and an IPC message is received
+   * in the content process and we want to get the task result. This runs on the
+   * owning thread.
+   */
+  void
+  SetRequestResult(const FileSystemResponseValue& aValue);
+};
+
+// This class is the 'alter ego' of FileSystemTaskBase in the PBackground world.
+class FileSystemTaskParentBase : public nsRunnable
+{
+public:
+  /*
+   * Start the task. This must be called from the PBackground thread only.
+   */
+  void
+  Start();
+
+  /*
+   * The error codes are defined in xpcom/base/ErrorList.h and their
+   * corresponding error name and message are defined in dom/base/domerr.msg.
+   */
+  void
+  SetError(const nsresult& aErrorCode);
+
+  /*
+   * The function to perform task operation. It will be run on the I/O
+   * thread of the parent process.
+   * Overrides this function to define the task operation for individual task.
+   */
+  virtual nsresult
+  IOWork() = 0;
+
+  /*
    * Wrap the task success result to FileSystemResponseValue for sending it
-   * through IPC.
+   * through IPC. This method runs in the PBackground thread.
    * It will be called when the task is completed successfully and we need to
    * send the task success result back to the child process.
    */
   virtual FileSystemResponseValue
   GetSuccessRequestResult(ErrorResult& aRv) const = 0;
 
   /*
-   * Unwrap the IPC message to get the task success result.
-   * It will be called when the task is completed successfully and an IPC
-   * message is received in the child process and we want to get the task
-   * success result.
-   */
-  virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                          ErrorResult& aRv) = 0;
-
-  bool
-  HasError() const { return mErrorValue != NS_OK; }
-
-  // Overrides PFileSystemRequestChild
-  virtual bool
-  Recv__delete__(const FileSystemResponseValue& value) override;
-
-  nsresult mErrorValue;
-
-  RefPtr<FileSystemBase> mFileSystem;
-  RefPtr<FileSystemRequestParent> mRequestParent;
-private:
-  /*
    * After finishing the task operation, handle the task result.
-   * If it is an IPC task, send back the IPC result. Or else, send the result
-   * to the content page.
+   * If it is an IPC task, send back the IPC result. It runs on the PBackground
+   * thread.
    */
   void
   HandleResult();
 
+  // If this task must do something on the main-thread before IOWork(), it must
+  // overwrite this method. Otherwise it returns true if the FileSystem must be
+  // initialized on the main-thread. It's called from the Background thread.
+  virtual bool
+  NeedToGoToMainThread() const;
+
+  // This method is called only if NeedToGoToMainThread() returns true.
+  // Of course, it runs on the main-thread.
+  virtual nsresult
+  MainThreadWork();
+
+  /*
+   * Get the type of permission access required to perform this task.
+   */
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const = 0;
+
+  bool
+  HasError() const { return NS_FAILED(mErrorValue); }
+
+  NS_IMETHOD
+  Run() override;
+
+private:
   /*
    * Wrap the task result to FileSystemResponseValue for sending it through IPC.
    * It will be called when the task is completed and we need to
-   * send the task result back to the child process.
+   * send the task result back to the content. This runs on the PBackground
+   * thread.
    */
   FileSystemResponseValue
   GetRequestResult() const;
 
+protected:
   /*
-   * Unwrap the IPC message to get the task result.
-   * It will be called when the task is completed and an IPC message is received
-   * in the child process and we want to get the task result.
+   * To create a parent process task delivered from the child process through
+   * IPC.
    */
-  void
-  SetRequestResult(const FileSystemResponseValue& aValue);
+  FileSystemTaskParentBase(FileSystemBase* aFileSystem,
+                           const FileSystemParams& aParam,
+                           FileSystemRequestParent* aParent);
+
+  virtual
+  ~FileSystemTaskParentBase();
+
+  nsresult mErrorValue;
+  RefPtr<FileSystemBase> mFileSystem;
+  RefPtr<FileSystemRequestParent> mRequestParent;
+  nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemTaskBase_h
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -6,25 +6,32 @@
 
 #include "GetDirectoryListingTask.h"
 
 #include "HTMLSplitOnSpacesTokenizer.h"
 #include "js/Value.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
+#define GET_DIRECTORY_LISTING_PERMISSION "read"
+
 namespace mozilla {
 namespace dom {
 
+/**
+ * GetDirectoryListingTask
+ */
+
 /* static */ already_AddRefed<GetDirectoryListingTask>
 GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
                                 nsIFile* aTargetPath,
                                 Directory::DirectoryType aType,
                                 const nsAString& aFilters,
                                 ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
@@ -45,68 +52,32 @@ GetDirectoryListingTask::Create(FileSyst
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<GetDirectoryListingTask>
-GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
-                                const FileSystemGetDirectoryListingParams& aParam,
-                                FileSystemRequestParent* aParent,
-                                ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<GetDirectoryListingTask> task =
-    new GetDirectoryListingTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mType = aParam.isRoot()
-                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
-  return task.forget();
-}
-
 GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
                                                  nsIFile* aTargetPath,
                                                  Directory::DirectoryType aType,
                                                  const nsAString& aFilters)
   : FileSystemTaskBase(aFileSystem)
   , mTargetPath(aTargetPath)
   , mFilters(aFilters)
   , mType(aType)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
-GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                                                 const FileSystemGetDirectoryListingParams& aParam,
-                                                 FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-  , mFilters(aParam.filters())
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
 GetDirectoryListingTask::~GetDirectoryListingTask()
 {
-  MOZ_ASSERT(!mPromise || NS_IsMainThread(),
-             "mPromise should be released on main thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
 GetDirectoryListingTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
@@ -123,43 +94,16 @@ GetDirectoryListingTask::GetRequestParam
     return FileSystemGetDirectoryListingParams();
   }
 
   return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path,
                                              mType == Directory::eDOMRootDirectory,
                                              mFilters);
 }
 
-FileSystemResponseValue
-GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-
-  InfallibleTArray<PBlobParent*> blobs;
-
-  nsTArray<FileSystemDirectoryListingResponseData> inputs;
-
-  for (unsigned i = 0; i < mTargetData.Length(); i++) {
-    if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath) {
-      FileSystemDirectoryListingResponseFile fileData;
-      fileData.fileRealPath() = mTargetData[i].mPath;
-      inputs.AppendElement(fileData);
-    } else {
-      MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath);
-      FileSystemDirectoryListingResponseDirectory directoryData;
-      directoryData.directoryRealPath() = mTargetData[i].mPath;
-      inputs.AppendElement(directoryData);
-    }
-  }
-
-  FileSystemDirectoryListingResponse response;
-  response.data().SwapElements(inputs);
-  return response;
-}
-
 void
 GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                                                  ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aValue.type() ==
                FileSystemResponseValue::TFileSystemDirectoryListingResponse);
 
@@ -181,18 +125,162 @@ GetDirectoryListingTask::SetSuccessReque
 
     if (!mTargetData.AppendElement(element, fallible)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
   }
 }
 
+void
+GetDirectoryListingTask::HandlerCallback()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  size_t count = mTargetData.Length();
+
+  Sequence<OwningFileOrDirectory> listing;
+
+  if (!listing.SetLength(count, mozilla::fallible_t())) {
+    mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+    mPromise = nullptr;
+    return;
+  }
+
+  for (unsigned i = 0; i < count; i++) {
+    nsCOMPtr<nsIFile> path;
+    NS_ConvertUTF16toUTF8 fullPath(mTargetData[i].mPath);
+    nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mPromise->MaybeReject(rv);
+      mPromise = nullptr;
+      return;
+    }
+
+#ifdef DEBUG
+    nsCOMPtr<nsIFile> rootPath;
+    rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false,
+                         getter_AddRefs(rootPath));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mPromise->MaybeReject(rv);
+      mPromise = nullptr;
+      return;
+    }
+
+    MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path));
+#endif
+
+    if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath) {
+      RefPtr<Directory> directory =
+        Directory::Create(mFileSystem->GetParentObject(), path,
+                          Directory::eNotDOMRootDirectory, mFileSystem);
+      MOZ_ASSERT(directory);
+
+      // Propogate mFilter onto sub-Directory object:
+      directory->SetContentFilters(mFilters);
+      listing[i].SetAsDirectory() = directory;
+    } else {
+      MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath);
+
+      RefPtr<File> file =
+        File::CreateFromFile(mFileSystem->GetParentObject(), path);
+      MOZ_ASSERT(file);
+
+      listing[i].SetAsFile() = file;
+    }
+  }
+
+  mPromise->MaybeResolve(listing);
+  mPromise = nullptr;
+}
+
+void
+GetDirectoryListingTask::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION);
+}
+
+/**
+ * GetDirectoryListingTaskParent
+ */
+
+/* static */ already_AddRefed<GetDirectoryListingTaskParent>
+GetDirectoryListingTaskParent::Create(FileSystemBase* aFileSystem,
+                                      const FileSystemGetDirectoryListingParams& aParam,
+                                      FileSystemRequestParent* aParent,
+                                      ErrorResult& aRv)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<GetDirectoryListingTaskParent> task =
+    new GetDirectoryListingTaskParent(aFileSystem, aParam, aParent);
+
+  NS_ConvertUTF16toUTF8 path(aParam.realPath());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  task->mType = aParam.isRoot()
+                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
+  return task.forget();
+}
+
+GetDirectoryListingTaskParent::GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
+                                                             const FileSystemGetDirectoryListingParams& aParam,
+                                                             FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+  , mFilters(aParam.filters())
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+GetDirectoryListingTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
+
+  InfallibleTArray<PBlobParent*> blobs;
+
+  nsTArray<FileSystemDirectoryListingResponseData> inputs;
+
+  for (unsigned i = 0; i < mTargetData.Length(); i++) {
+    if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath) {
+      FileSystemDirectoryListingResponseFile fileData;
+      fileData.fileRealPath() = mTargetData[i].mPath;
+      inputs.AppendElement(fileData);
+    } else {
+      MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath);
+      FileSystemDirectoryListingResponseDirectory directoryData;
+      directoryData.directoryRealPath() = mTargetData[i].mPath;
+      inputs.AppendElement(directoryData);
+    }
+  }
+
+  FileSystemDirectoryListingResponse response;
+  response.data().SwapElements(inputs);
+  return response;
+}
+
 nsresult
-GetDirectoryListingTask::Work()
+GetDirectoryListingTaskParent::IOWork()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
@@ -298,87 +386,15 @@ GetDirectoryListingTask::Work()
     if (!mTargetData.AppendElement(element, fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
   return NS_OK;
 }
 
 void
-GetDirectoryListingTask::HandlerCallback()
+GetDirectoryListingTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    return;
-  }
-
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    return;
-  }
-
-  size_t count = mTargetData.Length();
-
-  Sequence<OwningFileOrDirectory> listing;
-
-  if (!listing.SetLength(count, mozilla::fallible_t())) {
-    mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
-    mPromise = nullptr;
-    return;
-  }
-
-  for (unsigned i = 0; i < count; i++) {
-    nsCOMPtr<nsIFile> path;
-    NS_ConvertUTF16toUTF8 fullPath(mTargetData[i].mPath);
-    nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      mPromise->MaybeReject(rv);
-      mPromise = nullptr;
-      return;
-    }
-
-#ifdef DEBUG
-    nsCOMPtr<nsIFile> rootPath;
-    rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false,
-                         getter_AddRefs(rootPath));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      mPromise->MaybeReject(rv);
-      mPromise = nullptr;
-      return;
-    }
-
-    MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path));
-#endif
-
-    if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath) {
-      RefPtr<Directory> directory =
-        Directory::Create(mFileSystem->GetParentObject(), path,
-                          Directory::eNotDOMRootDirectory, mFileSystem);
-      MOZ_ASSERT(directory);
-
-      // Propogate mFilter onto sub-Directory object:
-      directory->SetContentFilters(mFilters);
-      listing[i].SetAsDirectory() = directory;
-    } else {
-      MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath);
-
-      RefPtr<File> file =
-        File::CreateFromFile(mFileSystem->GetParentObject(), path);
-      MOZ_ASSERT(file);
-
-      listing[i].SetAsFile() = file;
-    }
-  }
-
-  mPromise->MaybeResolve(listing);
-  mPromise = nullptr;
-}
-
-void
-GetDirectoryListingTask::GetPermissionAccessType(nsCString& aAccess) const
-{
-  aAccess.AssignLiteral("read");
+  aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetDirectoryListingTask.h
+++ b/dom/filesystem/GetDirectoryListingTask.h
@@ -22,65 +22,81 @@ class GetDirectoryListingTask final : pu
 public:
   static already_AddRefed<GetDirectoryListingTask>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aTargetPath,
          Directory::DirectoryType aType,
          const nsAString& aFilters,
          ErrorResult& aRv);
 
-  static already_AddRefed<GetDirectoryListingTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemGetDirectoryListingParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
   ~GetDirectoryListingTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
   GetDirectoryListingTask(FileSystemBase* aFileSystem,
                           nsIFile* aTargetPath,
                           Directory::DirectoryType aType,
                           const nsAString& aFilters);
 
-  GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                          const FileSystemGetDirectoryListingParams& aParam,
-                          FileSystemRequestParent* aParent);
-
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
-  virtual nsresult
-  Work() override;
-
   virtual void
   HandlerCallback() override;
 
   RefPtr<Promise> mPromise;
   nsCOMPtr<nsIFile> mTargetPath;
   nsString mFilters;
   Directory::DirectoryType mType;
 
   // We cannot store File or Directory objects bacause this object is created
   // on a different thread and File and Directory are not thread-safe.
   FallibleTArray<Directory::FileOrDirectoryPath> mTargetData;
 };
 
+class GetDirectoryListingTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<GetDirectoryListingTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemGetDirectoryListingParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
+
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
+
+private:
+  GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
+                                const FileSystemGetDirectoryListingParams& aParam,
+                                FileSystemRequestParent* aParent);
+
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+  virtual nsresult
+  IOWork() override;
+
+  nsCOMPtr<nsIFile> mTargetPath;
+  nsString mFilters;
+  Directory::DirectoryType mType;
+
+  // We cannot store File or Directory objects bacause this object is created
+  // on a different thread and File and Directory are not thread-safe.
+  FallibleTArray<Directory::FileOrDirectoryPath> mTargetData;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_GetDirectoryListing_h
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -5,25 +5,32 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GetFileOrDirectoryTask.h"
 
 #include "js/Value.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
+#define GET_FILE_OR_DIRECTORY_PERMISSION "read"
+
 namespace mozilla {
 namespace dom {
 
+/**
+ * GetFileOrDirectoryTask
+ */
+
 /* static */ already_AddRefed<GetFileOrDirectoryTask>
 GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
                                nsIFile* aTargetPath,
                                Directory::DirectoryType aType,
                                bool aDirectoryOnly,
                                ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
@@ -44,68 +51,32 @@ GetFileOrDirectoryTask::Create(FileSyste
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<GetFileOrDirectoryTask>
-GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
-                               const FileSystemGetFileOrDirectoryParams& aParam,
-                               FileSystemRequestParent* aParent,
-                               ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<GetFileOrDirectoryTask> task =
-    new GetFileOrDirectoryTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mType = aParam.isRoot()
-                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
-  return task.forget();
-}
-
 GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
                                                nsIFile* aTargetPath,
                                                Directory::DirectoryType aType,
                                                bool aDirectoryOnly)
   : FileSystemTaskBase(aFileSystem)
   , mTargetPath(aTargetPath)
   , mIsDirectory(aDirectoryOnly)
   , mType(aType)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
-GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                                               const FileSystemGetFileOrDirectoryParams& aParam,
-                                               FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-  , mIsDirectory(false)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
 GetFileOrDirectoryTask::~GetFileOrDirectoryTask()
 {
-  MOZ_ASSERT(!mPromise || NS_IsMainThread(),
-             "mPromise should be released on main thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
 GetFileOrDirectoryTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
@@ -121,33 +92,16 @@ GetFileOrDirectoryTask::GetRequestParams
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemGetFileOrDirectoryParams();
   }
 
   return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path,
                                             mType == Directory::eDOMRootDirectory);
 }
 
-FileSystemResponseValue
-GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  nsAutoString path;
-  aRv = mTargetPath->GetPath(path);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return FileSystemDirectoryResponse();
-  }
-
-  if (mIsDirectory) {
-    return FileSystemDirectoryResponse(path);
-  }
-
-  return FileSystemFileResponse(path);
-}
-
 void
 GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                                                 ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   switch (aValue.type()) {
     case FileSystemResponseValue::TFileSystemFileResponse: {
       FileSystemFileResponse r = aValue;
@@ -175,18 +129,114 @@ GetFileOrDirectoryTask::SetSuccessReques
     }
     default: {
       NS_RUNTIMEABORT("not reached");
       break;
     }
   }
 }
 
+void
+GetFileOrDirectoryTask::HandlerCallback()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  if (mIsDirectory) {
+    RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
+                                              mTargetPath,
+                                              mType,
+                                              mFileSystem);
+    MOZ_ASSERT(dir);
+
+    mPromise->MaybeResolve(dir);
+    mPromise = nullptr;
+    return;
+  }
+
+  RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
+                                           mTargetPath);
+  mPromise->MaybeResolve(file);
+  mPromise = nullptr;
+}
+
+void
+GetFileOrDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION);
+}
+
+/**
+ * GetFileOrDirectoryTaskParent
+ */
+
+/* static */ already_AddRefed<GetFileOrDirectoryTaskParent>
+GetFileOrDirectoryTaskParent::Create(FileSystemBase* aFileSystem,
+                                     const FileSystemGetFileOrDirectoryParams& aParam,
+                                     FileSystemRequestParent* aParent,
+                                     ErrorResult& aRv)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<GetFileOrDirectoryTaskParent> task =
+    new GetFileOrDirectoryTaskParent(aFileSystem, aParam, aParent);
+
+  NS_ConvertUTF16toUTF8 path(aParam.realPath());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  task->mType = aParam.isRoot()
+                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
+  return task.forget();
+}
+
+GetFileOrDirectoryTaskParent::GetFileOrDirectoryTaskParent(FileSystemBase* aFileSystem,
+                                                           const FileSystemGetFileOrDirectoryParams& aParam,
+                                                           FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+  , mIsDirectory(false)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+GetFileOrDirectoryTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
+
+  nsAutoString path;
+  aRv = mTargetPath->GetPath(path);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return FileSystemDirectoryResponse();
+  }
+
+  if (mIsDirectory) {
+    return FileSystemDirectoryResponse(path);
+  }
+
+  return FileSystemFileResponse(path);
+}
+
 nsresult
-GetFileOrDirectoryTask::Work()
+GetFileOrDirectoryTaskParent::IOWork()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
@@ -240,48 +290,15 @@ GetFileOrDirectoryTask::Work()
   if (!mFileSystem->IsSafeFile(mTargetPath)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   return NS_OK;
 }
 
 void
-GetFileOrDirectoryTask::HandlerCallback()
+GetFileOrDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    return;
-  }
-
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    return;
-  }
-
-  if (mIsDirectory) {
-    RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
-                                              mTargetPath,
-                                              mType,
-                                              mFileSystem);
-    MOZ_ASSERT(dir);
-
-    mPromise->MaybeResolve(dir);
-    mPromise = nullptr;
-    return;
-  }
-
-  RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
-                                           mTargetPath);
-  mPromise->MaybeResolve(file);
-  mPromise = nullptr;
-}
-
-void
-GetFileOrDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
-{
-  aAccess.AssignLiteral("read");
+  aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetFileOrDirectoryTask.h
+++ b/dom/filesystem/GetFileOrDirectoryTask.h
@@ -22,60 +22,75 @@ class GetFileOrDirectoryTask final : pub
 public:
   static already_AddRefed<GetFileOrDirectoryTask>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aTargetPath,
          Directory::DirectoryType aType,
          bool aDirectoryOnly,
          ErrorResult& aRv);
 
-  static already_AddRefed<GetFileOrDirectoryTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemGetFileOrDirectoryParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
   ~GetFileOrDirectoryTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
+
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
-
-  virtual nsresult
-  Work() override;
-
   virtual void
   HandlerCallback() override;
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
   GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
                          nsIFile* aTargetPath,
                          Directory::DirectoryType aType,
                          bool aDirectoryOnly);
 
-  GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                         const FileSystemGetFileOrDirectoryParams& aParam,
-                         FileSystemRequestParent* aParent);
+  RefPtr<Promise> mPromise;
+  nsCOMPtr<nsIFile> mTargetPath;
+
+  // Whether we get a directory.
+  bool mIsDirectory;
+  Directory::DirectoryType mType;
+};
 
-  RefPtr<Promise> mPromise;
+class GetFileOrDirectoryTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<GetFileOrDirectoryTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemGetFileOrDirectoryParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
+
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
+
+protected:
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+  virtual nsresult
+  IOWork() override;
+
+private:
+  GetFileOrDirectoryTaskParent(FileSystemBase* aFileSystem,
+                               const FileSystemGetFileOrDirectoryParams& aParam,
+                               FileSystemRequestParent* aParent);
+
   nsCOMPtr<nsIFile> mTargetPath;
 
   // Whether we get a directory.
   bool mIsDirectory;
   Directory::DirectoryType mType;
 };
 
 } // namespace dom
--- a/dom/filesystem/OSFileSystem.cpp
+++ b/dom/filesystem/OSFileSystem.cpp
@@ -15,61 +15,64 @@
 #include "nsIFile.h"
 
 namespace mozilla {
 namespace dom {
 
 OSFileSystem::OSFileSystem(const nsAString& aRootDir)
 {
   mLocalOrDeviceStorageRootPath = aRootDir;
-
-  // Non-mobile devices don't have the concept of separate permissions to
-  // access different parts of devices storage like Pictures, or Videos, etc.
-  mRequiresPermissionChecks = false;
+  mPermissionCheckType = ePermissionCheckNotRequired;
 
 #ifdef DEBUG
   mPermission.AssignLiteral("never-used");
 #endif
 }
 
 already_AddRefed<FileSystemBase>
 OSFileSystem::Clone()
 {
+  AssertIsOnOwningThread();
+
   RefPtr<OSFileSystem> fs = new OSFileSystem(mLocalOrDeviceStorageRootPath);
   if (mParent) {
     fs->Init(mParent);
   }
 
   return fs.forget();
 }
 
 void
 OSFileSystem::Init(nsISupports* aParent)
 {
+  AssertIsOnOwningThread();
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(!mParent, "No duple Init() calls");
   MOZ_ASSERT(aParent);
+
   mParent = aParent;
 
 #ifdef DEBUG
   nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(aParent);
   MOZ_ASSERT(obj);
 #endif
 }
 
 nsISupports*
 OSFileSystem::GetParentObject() const
 {
+  AssertIsOnOwningThread();
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return mParent;
 }
 
 void
 OSFileSystem::GetRootName(nsAString& aRetval) const
 {
+  AssertIsOnOwningThread();
   aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
 }
 
 bool
 OSFileSystem::IsSafeFile(nsIFile* aFile) const
 {
   // The concept of "safe files" is specific to the Device Storage API where
   // files are only "safe" if they're of a type that is appropriate for the
@@ -86,26 +89,47 @@ OSFileSystem::IsSafeDirectory(Directory*
   // storage that it is being used with.
   MOZ_CRASH("Don't use OSFileSystem with the Device Storage API");
   return true;
 }
 
 void
 OSFileSystem::Unlink()
 {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
   mParent = nullptr;
 }
 
 void
 OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
   OSFileSystem* tmp = this;
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent);
 }
 
 void
 OSFileSystem::SerializeDOMPath(nsAString& aOutput) const
 {
+  AssertIsOnOwningThread();
   aOutput = mLocalOrDeviceStorageRootPath;
 }
 
+/**
+ * OSFileSystemParent
+ */
+
+OSFileSystemParent::OSFileSystemParent(const nsAString& aRootDir)
+{
+  mLocalOrDeviceStorageRootPath = aRootDir;
+  mPermissionCheckType = ePermissionCheckNotRequired;
+
+#ifdef DEBUG
+  mPermission.AssignLiteral("never-used");
+#endif
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/OSFileSystem.h
+++ b/dom/filesystem/OSFileSystem.h
@@ -42,15 +42,79 @@ public:
 
   // CC methods
   virtual void Unlink() override;
   virtual void Traverse(nsCycleCollectionTraversalCallback &cb) override;
 
 private:
   virtual ~OSFileSystem() {}
 
-   nsCOMPtr<nsISupports> mParent;
+  nsCOMPtr<nsISupports> mParent;
+};
+
+class OSFileSystemParent final : public FileSystemBase
+{
+public:
+  explicit OSFileSystemParent(const nsAString& aRootDir);
+
+  // Overrides FileSystemBase
+
+  virtual already_AddRefed<FileSystemBase>
+  Clone() override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+    return nullptr;
+  }
+
+  virtual nsISupports*
+  GetParentObject() const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+    return nullptr;
+  }
+
+  virtual void
+  GetRootName(nsAString& aRetval) const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+  }
+
+  virtual bool
+  IsSafeFile(nsIFile* aFile) const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+    return true;
+  }
+
+  virtual bool
+  IsSafeDirectory(Directory* aDir) const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+    return true;
+  }
+
+  virtual void
+  SerializeDOMPath(nsAString& aOutput) const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+  }
+
+  // CC methods
+  virtual void
+  Unlink() override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+  }
+
+  virtual void
+  Traverse(nsCycleCollectionTraversalCallback &cb) override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+  }
+
+private:
+  virtual ~OSFileSystemParent() {}
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_OSFileSystem_h
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/PFileSystemParams.ipdlh
@@ -0,0 +1,72 @@
+/* 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 protocol PBlob;
+
+namespace mozilla {
+namespace dom {
+
+struct FileSystemCreateDirectoryParams
+{
+  nsString filesystem;
+  nsString realPath;
+};
+
+union FileSystemFileDataValue
+{
+  uint8_t[];
+  PBlob;
+};
+
+struct FileSystemCreateFileParams
+{
+  nsString filesystem;
+  nsString realPath;
+  FileSystemFileDataValue data;
+  bool replace;
+};
+
+struct FileSystemGetDirectoryListingParams
+{
+  nsString filesystem;
+  nsString realPath;
+  bool isRoot;
+  // 'filters' could be an array rather than a semicolon separated string
+  // (we'd then use InfallibleTArray<nsString> internally), but that is
+  // wasteful.  E10s requires us to pass the filters over as a string anyway,
+  // so avoiding using an array avoids serialization on the side passing the
+  // filters.  Since an nsString can share its buffer when copied,
+  // using that instead of InfallibleTArray<nsString> makes copying the filters
+  // around in any given process a bit more efficient too, since copying a
+  // single nsString is cheaper than copying InfallibleTArray member data and
+  // each nsString that it contains.
+  nsString filters;
+};
+
+struct FileSystemGetFileOrDirectoryParams
+{
+  nsString filesystem;
+  nsString realPath;
+  bool isRoot;
+};
+
+struct FileSystemRemoveParams
+{
+  nsString filesystem;
+  nsString directory;
+  nsString targetDirectory;
+  bool recursive;
+};
+
+union FileSystemParams
+{
+  FileSystemCreateDirectoryParams;
+  FileSystemCreateFileParams;
+  FileSystemGetDirectoryListingParams;
+  FileSystemGetFileOrDirectoryParams;
+  FileSystemRemoveParams;
+};
+
+} // dom namespace
+} // mozilla namespace
--- a/dom/filesystem/PFileSystemRequest.ipdl
+++ b/dom/filesystem/PFileSystemRequest.ipdl
@@ -1,16 +1,16 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 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/. */
 
+include protocol PBackground;
 include protocol PBlob;
-include protocol PContent;
 
 namespace mozilla {
 namespace dom {
 
 struct FileSystemFileResponse
 {
   nsString realPath;
 };
@@ -57,18 +57,18 @@ union FileSystemResponseValue
 {
   FileSystemBooleanResponse;
   FileSystemDirectoryResponse;
   FileSystemDirectoryListingResponse;
   FileSystemFileResponse;
   FileSystemErrorResponse;
 };
 
-sync protocol PFileSystemRequest
+protocol PFileSystemRequest
 {
-  manager PContent;
+  manager PBackground;
 
 child:
   async __delete__(FileSystemResponseValue response);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/RemoveTask.cpp
+++ b/dom/filesystem/RemoveTask.cpp
@@ -4,25 +4,30 @@
  * 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 "RemoveTask.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
+/**
+ * RemoveTask
+ */
+
 /* static */ already_AddRefed<RemoveTask>
 RemoveTask::Create(FileSystemBase* aFileSystem,
                    nsIFile* aDirPath,
                    nsIFile* aTargetPath,
                    bool aRecursive,
                    ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
@@ -45,84 +50,35 @@ RemoveTask::Create(FileSystemBase* aFile
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<RemoveTask>
-RemoveTask::Create(FileSystemBase* aFileSystem,
-                   const FileSystemRemoveParams& aParam,
-                   FileSystemRequestParent* aParent,
-                   ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<RemoveTask> task =
-    new RemoveTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 directoryPath(aParam.directory());
-  aRv = NS_NewNativeLocalFile(directoryPath, true,
-                              getter_AddRefs(task->mDirPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mRecursive = aParam.recursive();
-
-  NS_ConvertUTF16toUTF8 path(aParam.targetDirectory());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  if (!FileSystemUtils::IsDescendantPath(task->mDirPath, task->mTargetPath)) {
-    aRv.Throw(NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR);
-    return nullptr;
-  }
-
-  return task.forget();
-}
-
 RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
                        nsIFile* aDirPath,
                        nsIFile* aTargetPath,
                        bool aRecursive)
   : FileSystemTaskBase(aFileSystem)
   , mDirPath(aDirPath)
   , mTargetPath(aTargetPath)
   , mRecursive(aRecursive)
   , mReturnValue(false)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
   MOZ_ASSERT(aDirPath);
   MOZ_ASSERT(aTargetPath);
 }
 
-RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
-                       const FileSystemRemoveParams& aParam,
-                       FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-  , mRecursive(false)
-  , mReturnValue(false)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
 RemoveTask::~RemoveTask()
 {
-  MOZ_ASSERT(!mPromise || NS_IsMainThread(),
-             "mPromise should be released on main thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
 RemoveTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
@@ -148,34 +104,114 @@ RemoveTask::GetRequestParams(const nsStr
     return param;
   }
 
   param.targetDirectory() = path;
 
   return param;
 }
 
-FileSystemResponseValue
-RemoveTask::GetSuccessRequestResult(ErrorResult& aRv) const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  return FileSystemBooleanResponse(mReturnValue);
-}
-
 void
 RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                                     ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
   FileSystemBooleanResponse r = aValue;
   mReturnValue = r.success();
 }
 
+void
+RemoveTask::HandlerCallback()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  mPromise->MaybeResolve(mReturnValue);
+  mPromise = nullptr;
+}
+
+void
+RemoveTask::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);
+}
+
+/**
+ * RemoveTaskParent
+ */
+
+/* static */ already_AddRefed<RemoveTaskParent>
+RemoveTaskParent::Create(FileSystemBase* aFileSystem,
+                         const FileSystemRemoveParams& aParam,
+                         FileSystemRequestParent* aParent,
+                         ErrorResult& aRv)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<RemoveTaskParent> task =
+    new RemoveTaskParent(aFileSystem, aParam, aParent);
+
+  NS_ConvertUTF16toUTF8 directoryPath(aParam.directory());
+  aRv = NS_NewNativeLocalFile(directoryPath, true,
+                              getter_AddRefs(task->mDirPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  task->mRecursive = aParam.recursive();
+
+  NS_ConvertUTF16toUTF8 path(aParam.targetDirectory());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  if (!FileSystemUtils::IsDescendantPath(task->mDirPath, task->mTargetPath)) {
+    aRv.Throw(NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR);
+    return nullptr;
+  }
+
+  return task.forget();
+}
+
+RemoveTaskParent::RemoveTaskParent(FileSystemBase* aFileSystem,
+                                   const FileSystemRemoveParams& aParam,
+                                   FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+  , mRecursive(false)
+  , mReturnValue(false)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+RemoveTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
+
+  return FileSystemBooleanResponse(mReturnValue);
+}
+
 nsresult
-RemoveTask::Work()
+RemoveTaskParent::IOWork()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
@@ -209,34 +245,15 @@ RemoveTask::Work()
   }
 
   mReturnValue = true;
 
   return NS_OK;
 }
 
 void
-RemoveTask::HandlerCallback()
+RemoveTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    return;
-  }
-
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    return;
-  }
-
-  mPromise->MaybeResolve(mReturnValue);
-  mPromise = nullptr;
-}
-
-void
-RemoveTask::GetPermissionAccessType(nsCString& aAccess) const
-{
-  aAccess.AssignLiteral("write");
+  aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/RemoveTask.h
+++ b/dom/filesystem/RemoveTask.h
@@ -6,76 +6,96 @@
 
 #ifndef mozilla_dom_RemoveTask_h
 #define mozilla_dom_RemoveTask_h
 
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ErrorResult.h"
 
+#define REMOVE_TASK_PERMISSION "write"
+
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 class Promise;
 
 class RemoveTask final : public FileSystemTaskBase
 {
 public:
   static already_AddRefed<RemoveTask>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aDirPath,
          nsIFile* aTargetPath,
          bool aRecursive,
          ErrorResult& aRv);
 
-  static already_AddRefed<RemoveTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemRemoveParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
   ~RemoveTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
-  virtual nsresult
-  Work() override;
-
   virtual void
   HandlerCallback() override;
 
 private:
   RemoveTask(FileSystemBase* aFileSystem,
              nsIFile* aDirPath,
              nsIFile* aTargetPath,
              bool aRecursive);
 
-  RemoveTask(FileSystemBase* aFileSystem,
-             const FileSystemRemoveParams& aParam,
-             FileSystemRequestParent* aParent);
+  RefPtr<Promise> mPromise;
+
+  // This path is the Directory::mFile.
+  nsCOMPtr<nsIFile> mDirPath;
+
+  // This is what we want to remove. mTargetPath is discendant path of mDirPath.
+  nsCOMPtr<nsIFile> mTargetPath;
+
+  bool mRecursive;
+  bool mReturnValue;
+};
 
-  RefPtr<Promise> mPromise;
+class RemoveTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<RemoveTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemRemoveParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
+
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
+
+protected:
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+  virtual nsresult
+  IOWork() override;
+
+private:
+  RemoveTaskParent(FileSystemBase* aFileSystem,
+                   const FileSystemRemoveParams& aParam,
+                   FileSystemRequestParent* aParent);
 
   // This path is the Directory::mFile.
   nsCOMPtr<nsIFile> mDirPath;
 
   // This is what we want to remove. mTargetPath is discendant path of mDirPath.
   nsCOMPtr<nsIFile> mTargetPath;
 
   bool mRecursive;
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -30,16 +30,17 @@ UNIFIED_SOURCES += [
     'GetFileOrDirectoryTask.cpp',
     'OSFileSystem.cpp',
     'RemoveTask.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 IPDL_SOURCES += [
+    'PFileSystemParams.ipdlh',
     'PFileSystemRequest.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -171,18 +171,16 @@
 #endif
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/cellbroadcast/CellBroadcastIPCService.h"
 #include "mozilla/dom/icc/IccChild.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
 #include "mozilla/dom/mobilemessage/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
-#include "mozilla/dom/PFileSystemRequestChild.h"
-#include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/bluetooth/PBluetoothChild.h"
 #include "mozilla/dom/PFMRadioChild.h"
 #include "mozilla/dom/PPresentationChild.h"
 #include "mozilla/dom/PresentationIPCService.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/PSpeechSynthesisChild.h"
@@ -1823,34 +1821,16 @@ ContentChild::AllocPDeviceStorageRequest
 
 bool
 ContentChild::DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild* aDeviceStorage)
 {
   delete aDeviceStorage;
   return true;
 }
 
-PFileSystemRequestChild*
-ContentChild::AllocPFileSystemRequestChild(const FileSystemParams& aParams)
-{
-  MOZ_CRASH("Should never get here!");
-  return nullptr;
-}
-
-bool
-ContentChild::DeallocPFileSystemRequestChild(PFileSystemRequestChild* aFileSystem)
-{
-  mozilla::dom::FileSystemTaskBase* child =
-    static_cast<mozilla::dom::FileSystemTaskBase*>(aFileSystem);
-  // The reference is increased in FileSystemTaskBase::Start of
-  // FileSystemTaskBase.cpp. We should decrease it after IPC.
-  NS_RELEASE(child);
-  return true;
-}
-
 PMobileConnectionChild*
 ContentChild::SendPMobileConnectionConstructor(PMobileConnectionChild* aActor,
                                                const uint32_t& aClientId)
 {
 #ifdef MOZ_B2G_RIL
   // Add an extra ref for IPDL. Will be released in
   // ContentChild::DeallocPMobileConnectionChild().
   static_cast<MobileConnectionChild*>(aActor)->AddRef();
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -186,22 +186,16 @@ public:
   virtual bool DeallocPBrowserChild(PBrowserChild*) override;
 
   virtual PDeviceStorageRequestChild*
   AllocPDeviceStorageRequestChild(const DeviceStorageParams&) override;
 
   virtual bool
   DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild*) override;
 
-  virtual PFileSystemRequestChild*
-  AllocPFileSystemRequestChild(const FileSystemParams&) override;
-
-  virtual bool
-  DeallocPFileSystemRequestChild(PFileSystemRequestChild*) override;
-
   virtual PBlobChild*
   AllocPBlobChild(const BlobConstructorParams& aParams) override;
 
   virtual bool DeallocPBlobChild(PBlobChild* aActor) override;
 
   virtual PCrashReporterChild*
   AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id,
                            const uint32_t& processType) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -42,17 +42,16 @@
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
-#include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #ifdef MOZ_EME
 #include "mozilla/dom/MediaKeySystemAccess.h"
 #endif
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
@@ -3516,34 +3515,16 @@ ContentParent::AllocPDeviceStorageReques
 bool
 ContentParent::DeallocPDeviceStorageRequestParent(PDeviceStorageRequestParent* doomed)
 {
   DeviceStorageRequestParent *parent = static_cast<DeviceStorageRequestParent*>(doomed);
   NS_RELEASE(parent);
   return true;
 }
 
-PFileSystemRequestParent*
-ContentParent::AllocPFileSystemRequestParent(const FileSystemParams& aParams)
-{
-  RefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
-  if (!result->Dispatch(this, aParams)) {
-    return nullptr;
-  }
-  return result.forget().take();
-}
-
-bool
-ContentParent::DeallocPFileSystemRequestParent(PFileSystemRequestParent* doomed)
-{
-  FileSystemRequestParent* parent = static_cast<FileSystemRequestParent*>(doomed);
-  NS_RELEASE(parent);
-  return true;
-}
-
 PBlobParent*
 ContentParent::AllocPBlobParent(const BlobConstructorParams& aParams)
 {
   return nsIContentParent::AllocPBlobParent(aParams);
 }
 
 bool
 ContentParent::DeallocPBlobParent(PBlobParent* aActor)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -732,22 +732,16 @@ private:
   virtual bool DeallocPBrowserParent(PBrowserParent* frame) override;
 
   virtual PDeviceStorageRequestParent*
   AllocPDeviceStorageRequestParent(const DeviceStorageParams&) override;
 
   virtual bool
   DeallocPDeviceStorageRequestParent(PDeviceStorageRequestParent*) override;
 
-  virtual PFileSystemRequestParent*
-  AllocPFileSystemRequestParent(const FileSystemParams&) override;
-
-  virtual bool
-  DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
-
   virtual PBlobParent*
   AllocPBlobParent(const BlobConstructorParams& aParams) override;
 
   virtual bool DeallocPBlobParent(PBlobParent* aActor) override;
 
   virtual bool
   RecvPBlobConstructor(PBlobParent* aActor,
                        const BlobConstructorParams& params) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -16,17 +16,16 @@ include protocol PContentPermissionReque
 include protocol PCycleCollectWithLogs;
 include protocol PCrashReporter;
 include protocol PPSMContentDownloader;
 include protocol PExternalHelperApp;
 include protocol PHandlerService;
 include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
-include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PHeapSnapshotTempFileHelper;
 include protocol PIcc;
 include protocol PProcessHangMonitor;
 include protocol PImageBridge;
 include protocol PMedia;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
@@ -262,77 +261,16 @@ union FMRadioRequestParams
 {
   FMRadioRequestEnableParams;
   FMRadioRequestDisableParams;
   FMRadioRequestSetFrequencyParams;
   FMRadioRequestSeekParams;
   FMRadioRequestCancelSeekParams;
 };
 
-struct FileSystemCreateDirectoryParams
-{
-  nsString filesystem;
-  nsString realPath;
-};
-
-union FileSystemFileDataValue
-{
-  uint8_t[];
-  PBlob;
-};
-
-struct FileSystemCreateFileParams
-{
-  nsString filesystem;
-  nsString realPath;
-  FileSystemFileDataValue data;
-  bool replace;
-};
-
-struct FileSystemGetDirectoryListingParams
-{
-  nsString filesystem;
-  nsString realPath;
-  bool isRoot;
-  // 'filters' could be an array rather than a semicolon separated string
-  // (we'd then use InfallibleTArray<nsString> internally), but that is
-  // wasteful.  E10s requires us to pass the filters over as a string anyway,
-  // so avoiding using an array avoids serialization on the side passing the
-  // filters.  Since an nsString can share its buffer when copied,
-  // using that instead of InfallibleTArray<nsString> makes copying the filters
-  // around in any given process a bit more efficient too, since copying a
-  // single nsString is cheaper than copying InfallibleTArray member data and
-  // each nsString that it contains.
-  nsString filters;
-};
-
-struct FileSystemGetFileOrDirectoryParams
-{
-  nsString filesystem;
-  nsString realPath;
-  bool isRoot;
-};
-
-struct FileSystemRemoveParams
-{
-  nsString filesystem;
-  nsString directory;
-  nsString targetDirectory;
-  bool recursive;
-};
-
-union FileSystemParams
-{
-  FileSystemCreateDirectoryParams;
-  FileSystemCreateFileParams;
-  FileSystemGetDirectoryListingParams;
-  FileSystemGetFileOrDirectoryParams;
-  FileSystemRemoveParams;
-};
-
 union PrefValue {
   nsCString;
   int32_t;
   bool;
 };
 
 union MaybePrefValue {
   PrefValue;
@@ -469,17 +407,16 @@ prio(normal upto urgent) sync protocol P
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCellBroadcast;
     manages PContentPermissionRequest;
     manages PCrashReporter;
     manages PCycleCollectWithLogs;
     manages PDeviceStorageRequest;
-    manages PFileSystemRequest;
     manages PPSMContentDownloader;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PFMRadio;
     manages PHal;
     manages PHandlerService;
     manages PHeapSnapshotTempFileHelper;
     manages PIcc;
@@ -820,18 +757,16 @@ parent:
      */
     sync FindPlugins(uint32_t pluginEpoch) returns (nsresult aResult, PluginTag[] plugins, uint32_t newPluginEpoch);
 
     async PJavaScript();
 
     async PRemoteSpellcheckEngine();
     async PDeviceStorageRequest(DeviceStorageParams params);
 
-    async PFileSystemRequest(FileSystemParams params);
-
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
 
     async GetSystemMemory(uint64_t getterId);
 
     sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
         returns (bool isSecureURI);
 
     async AccumulateMixedContentHSTS(URIParams uri, bool active);
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -9,16 +9,18 @@
 #include "ServiceWorkerManagerChild.h"
 #include "FileDescriptorSetChild.h"
 #ifdef MOZ_WEBRTC
 #include "CamerasChild.h"
 #endif
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
+#include "mozilla/dom/PFileSystemRequestChild.h"
+#include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/quota/PQuotaChild.h"
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/dom/NuwaChild.h"
@@ -444,16 +446,33 @@ bool
 BackgroundChildImpl::DeallocPQuotaChild(PQuotaChild* aActor)
 {
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
+dom::PFileSystemRequestChild*
+BackgroundChildImpl::AllocPFileSystemRequestChild(const FileSystemParams& aParams)
+{
+  MOZ_CRASH("Should never get here!");
+  return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPFileSystemRequestChild(PFileSystemRequestChild* aActor)
+{
+  // The reference is increased in FileSystemTaskBase::Start of
+  // FileSystemTaskBase.cpp. We should decrease it after IPC.
+  RefPtr<dom::FileSystemTaskBase> child =
+    dont_AddRef(static_cast<dom::FileSystemTaskBase*>(aActor));
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 bool
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -153,16 +153,23 @@ protected:
   virtual bool
   DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor) override;
 
   virtual PQuotaChild*
   AllocPQuotaChild() override;
 
   virtual bool
   DeallocPQuotaChild(PQuotaChild* aActor) override;
+
+  virtual PFileSystemRequestChild*
+  AllocPFileSystemRequestChild(const FileSystemParams&) override;
+
+  virtual bool
+  DeallocPFileSystemRequestChild(PFileSystemRequestChild*) override;
+
 };
 
 class BackgroundChildImpl::ThreadLocal final
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -9,34 +9,38 @@
 #ifdef MOZ_WEBRTC
 #include "CamerasParent.h"
 #endif
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMTypes.h"
+#include "mozilla/dom/FileSystemBase.h"
+#include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PBlobParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/quota/ActorsParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/layout/VsyncParent.h"
 #include "mozilla/dom/network/UDPSocketParent.h"
+#include "mozilla/Preferences.h"
 #include "nsIAppsService.h"
 #include "nsNetUtil.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsProxyRelease.h"
 #include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
 #include "nsTraceRefcnt.h"
 #include "nsXULAppAPI.h"
 #include "ServiceWorkerManagerParent.h"
 
 #ifdef DISABLE_ASSERTS_FOR_FUZZING
 #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
@@ -44,20 +48,22 @@
 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false)
 #endif
 
 using mozilla::ipc::AssertIsOnBackgroundThread;
 using mozilla::dom::asmjscache::PAsmJSCacheEntryParent;
 using mozilla::dom::cache::PCacheParent;
 using mozilla::dom::cache::PCacheStorageParent;
 using mozilla::dom::cache::PCacheStreamControlParent;
+using mozilla::dom::FileSystemBase;
+using mozilla::dom::FileSystemRequestParent;
 using mozilla::dom::MessagePortParent;
+using mozilla::dom::NuwaParent;
 using mozilla::dom::PMessagePortParent;
 using mozilla::dom::PNuwaParent;
-using mozilla::dom::NuwaParent;
 using mozilla::dom::UDPSocketParent;
 
 namespace {
 
 void
 AssertIsOnMainThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -450,16 +456,30 @@ BackgroundParentImpl::AllocPBroadcastCha
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   return new BroadcastChannelParent(aOrigin, aChannel, aPrivateBrowsing);
 }
 
 namespace {
 
+struct MOZ_STACK_CLASS NullifyContentParentRAII
+{
+  explicit NullifyContentParentRAII(RefPtr<ContentParent>& aContentParent)
+    : mContentParent(aContentParent)
+  {}
+
+  ~NullifyContentParentRAII()
+  {
+    mContentParent = nullptr;
+  }
+
+  RefPtr<ContentParent>& mContentParent;
+};
+
 class CheckPrincipalRunnable final : public nsRunnable
 {
 public:
   CheckPrincipalRunnable(already_AddRefed<ContentParent> aParent,
                          const PrincipalInfo& aPrincipalInfo,
                          const nsCString& aOrigin)
     : mContentParent(aParent)
     , mPrincipalInfo(aPrincipalInfo)
@@ -470,31 +490,17 @@ public:
 
     MOZ_ASSERT(mContentParent);
   }
 
   NS_IMETHODIMP Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    struct MOZ_STACK_CLASS RunRAII
-    {
-      explicit RunRAII(RefPtr<ContentParent>& aContentParent)
-        : mContentParent(aContentParent)
-      {}
-
-      ~RunRAII()
-      {
-        mContentParent = nullptr;
-      }
-
-      RefPtr<ContentParent>& mContentParent;
-    };
-
-    RunRAII raii(mContentParent);
+    NullifyContentParentRAII raii(mContentParent);
 
     nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo);
     AssertAppPrincipal(mContentParent, principal);
 
     bool isNullPrincipal;
     nsresult rv = principal->GetIsNullPrincipal(&isNullPrincipal);
     if (NS_WARN_IF(NS_FAILED(rv)) || isNullPrincipal) {
       mContentParent->KillHard("BroadcastChannel killed: no null principal.");
@@ -517,16 +523,97 @@ public:
   }
 
 private:
   RefPtr<ContentParent> mContentParent;
   PrincipalInfo mPrincipalInfo;
   nsCString mOrigin;
 };
 
+class CheckPermissionRunnable final : public nsRunnable
+{
+public:
+  CheckPermissionRunnable(already_AddRefed<ContentParent> aParent,
+                          FileSystemRequestParent* aActor,
+                          FileSystemBase::ePermissionCheckType aPermissionCheckType,
+                          const nsCString& aPermissionName)
+    : mContentParent(aParent)
+    , mActor(aActor)
+    , mPermissionCheckType(aPermissionCheckType)
+    , mPermissionName(aPermissionName)
+    , mBackgroundEventTarget(NS_GetCurrentThread())
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    MOZ_ASSERT(mContentParent);
+    MOZ_ASSERT(mBackgroundEventTarget);
+    MOZ_ASSERT(mPermissionCheckType == FileSystemBase::ePermissionCheckRequired ||
+               mPermissionCheckType == FileSystemBase::ePermissionCheckByTestingPref);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    if (NS_IsMainThread()) {
+      NullifyContentParentRAII raii(mContentParent);
+
+      // If the permission is granted, we go back to the background thread to
+      // dispatch this task.
+      if (CheckPermission()) {
+        return mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+      }
+
+      return NS_OK;
+    }
+
+    AssertIsOnBackgroundThread();
+
+    // It can happen that this actor has been destroyed in the meantime we were
+    // on the main-thread.
+    if (!mActor->Destroyed()) {
+      mActor->Start();
+    }
+
+    return NS_OK;
+  }
+
+private:
+  ~CheckPermissionRunnable()
+  {
+     NS_ProxyRelease(mBackgroundEventTarget, mActor.forget());
+  }
+
+  bool
+  CheckPermission()
+  {
+    if (mPermissionCheckType == FileSystemBase::ePermissionCheckByTestingPref &&
+        mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
+      return true;
+    }
+
+    if (!AssertAppProcessPermission(mContentParent.get(),
+                                    mPermissionName.get())) {
+      mContentParent->KillHard("PBackground actor killed: permission denied.");
+      return false;
+    }
+
+    return true;
+  }
+
+  RefPtr<ContentParent> mContentParent;
+
+  RefPtr<FileSystemRequestParent> mActor;
+
+  FileSystemBase::ePermissionCheckType mPermissionCheckType;
+  nsCString mPermissionName;
+
+  nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
+};
+
 } // namespace
 
 bool
 BackgroundParentImpl::RecvPBroadcastChannelConstructor(
                                             PBroadcastChannelParent* actor,
                                             const PrincipalInfo& aPrincipalInfo,
                                             const nsCString& aOrigin,
                                             const nsString& aChannel,
@@ -731,16 +818,83 @@ BackgroundParentImpl::DeallocPQuotaParen
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::quota::DeallocPQuotaParent(aActor);
 }
 
+dom::PFileSystemRequestParent*
+BackgroundParentImpl::AllocPFileSystemRequestParent(
+                                                const FileSystemParams& aParams)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  RefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
+
+  if (NS_WARN_IF(!result->Initialize(aParams))) {
+    return nullptr;
+  }
+
+  return result.forget().take();
+}
+
+bool
+BackgroundParentImpl::RecvPFileSystemRequestConstructor(
+                                               PFileSystemRequestParent* aActor,
+                                               const FileSystemParams& aParams)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  RefPtr<FileSystemRequestParent> actor = static_cast<FileSystemRequestParent*>(aActor);
+
+  if (actor->PermissionCheckType() == FileSystemBase::ePermissionCheckNotRequired) {
+    actor->Start();
+    return true;
+  }
+
+  RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
+
+  // If the ContentParent is null we are dealing with a same-process actor.
+  if (!parent) {
+    actor->Start();
+    return true;
+  }
+
+  const nsCString& permissionName = actor->PermissionName();
+  MOZ_ASSERT(!permissionName.IsEmpty());
+
+  // At this point we should have the right permission but we do the last check
+  // with this runnable. If the app doesn't have the permission, we kill the
+  // child process.
+  RefPtr<CheckPermissionRunnable> runnable =
+    new CheckPermissionRunnable(parent.forget(), actor,
+                                actor->PermissionCheckType(), permissionName);
+
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+
+  return true;
+}
+
+bool
+BackgroundParentImpl::DeallocPFileSystemRequestParent(
+                                              PFileSystemRequestParent* aDoomed)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  RefPtr<FileSystemRequestParent> parent =
+    dont_AddRef(static_cast<FileSystemRequestParent*>(aDoomed));
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mozilla::ipc::AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -181,14 +181,25 @@ protected:
   virtual bool
   DeallocPAsmJSCacheEntryParent(PAsmJSCacheEntryParent* aActor) override;
 
   virtual PQuotaParent*
   AllocPQuotaParent() override;
 
   virtual bool
   DeallocPQuotaParent(PQuotaParent* aActor) override;
+
+  virtual PFileSystemRequestParent*
+  AllocPFileSystemRequestParent(const FileSystemParams&) override;
+
+  virtual bool
+  RecvPFileSystemRequestConstructor(PFileSystemRequestParent* aActor,
+                                    const FileSystemParams& aParams) override;
+
+  virtual bool
+  DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
+
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -7,27 +7,29 @@ include protocol PBackgroundIDBFactory;
 include protocol PBackgroundIndexedDBUtils;
 include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
+include protocol PFileSystemRequest;
 include protocol PMessagePort;
 include protocol PCameras;
 include protocol PNuwa;
 include protocol PQuota;
 include protocol PServiceWorkerManager;
 include protocol PUDPSocket;
 include protocol PVsync;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
+include PFileSystemParams;
 
 include "mozilla/dom/cache/IPCUtils.h";
 
 using mozilla::dom::cache::Namespace
   from "mozilla/dom/cache/Types.h";
 
 using mozilla::dom::asmjscache::OpenMode
   from "mozilla/dom/asmjscache/AsmJSCache.h";
@@ -45,16 +47,17 @@ sync protocol PBackground
   manages PBackgroundIndexedDBUtils;
   manages PBackgroundTest;
   manages PBlob;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
+  manages PFileSystemRequest;
   manages PMessagePort;
   manages PCameras;
   manages PNuwa;
   manages PQuota;
   manages PServiceWorkerManager;
   manages PUDPSocket;
   manages PVsync;
 
@@ -90,16 +93,18 @@ parent:
   async MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
 
   async PAsmJSCacheEntry(OpenMode openMode,
                          WriteParams write,
                          PrincipalInfo principalInfo);
 
   async PQuota();
 
+  async PFileSystemRequest(FileSystemParams params);
+
 child:
   async PCache();
   async PCacheStreamControl();
 
 both:
   async PBlob(BlobConstructorParams params);
 
   async PFileDescriptorSet(FileDescriptor fd);