Merge inbound to central, a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 09 Sep 2015 13:47:08 -0700
changeset 295896 dd2a1d737a64d9a3f23714ec5cc623ec8933b51f
parent 295825 7c8e40e00bfe521953b69051e5d59551a684c346 (current diff)
parent 295895 8480dd03b9c19cd3be93ac0b274bb83f29b1610a (diff)
child 295897 18f5b185fb971492bcb820ec4881141939ea497b
child 295898 c638ef3e2f2077dde2c1d9a174bf4bddc2e20714
child 295922 89103605d401c6f6f0b2a702b289b40e2f135e69
child 295961 b451aaf236d09fa4221544c30b7058782267ff95
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.0a1
first release with
nightly linux32
dd2a1d737a64 / 43.0a1 / 20150910030225 / files
nightly linux64
dd2a1d737a64 / 43.0a1 / 20150910030225 / files
nightly mac
dd2a1d737a64 / 43.0a1 / 20150910030225 / files
nightly win32
dd2a1d737a64 / 43.0a1 / 20150910030225 / files
nightly win64
dd2a1d737a64 / 43.0a1 / 20150910030225 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge CLOSED TREE
dom/filehandle/AsyncHelper.cpp
dom/filehandle/AsyncHelper.h
dom/filehandle/FileHandle.cpp
dom/filehandle/FileHandle.h
dom/filehandle/FileHelper.cpp
dom/filehandle/FileHelper.h
dom/filehandle/FileRequest.cpp
dom/filehandle/FileRequest.h
dom/filehandle/FileService.cpp
dom/filehandle/FileService.h
dom/filehandle/FileStreamWrappers.cpp
dom/filehandle/FileStreamWrappers.h
dom/filehandle/MemoryStreams.cpp
dom/filehandle/MemoryStreams.h
dom/filehandle/MetadataHelper.cpp
dom/filehandle/MetadataHelper.h
dom/filehandle/MutableFile.cpp
dom/filehandle/MutableFile.h
dom/indexedDB/test/test_get_filehandle.html
dom/ipc/TabChild.cpp
modules/libmar/tool/Makefile.in
mozglue/linker/Makefile.in
other-licenses/bsdiff/Makefile.in
testing/web-platform/meta/XMLHttpRequest/setrequestheader-allow-empty-value.htm.ini
toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1293,16 +1293,17 @@ pref("services.sync.prefs.sync.privacy.c
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.formdata", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.history", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.offlineApps", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true);
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true);
 pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
 pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true);
 pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true);
 pref("services.sync.prefs.sync.security.OCSP.enabled", true);
 pref("services.sync.prefs.sync.security.OCSP.require", true);
 pref("services.sync.prefs.sync.security.default_personal_cert", true);
 pref("services.sync.prefs.sync.security.tls.version.min", true);
 pref("services.sync.prefs.sync.security.tls.version.max", true);
 pref("services.sync.prefs.sync.signon.rememberSignons", true);
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
--- a/build/gen_test_packages_manifest.py
+++ b/build/gen_test_packages_manifest.py
@@ -13,24 +13,26 @@ ALL_HARNESSES = [
     'mochitest',
     'reftest',
     'webapprt',
     'xpcshell',
     'cppunittest',
     'jittest',
     'mozbase',
     'web-platform',
+    'talos',
 ]
 
 PACKAGE_SPECIFIED_HARNESSES = [
     'cppunittest',
     'mochitest',
     'reftest',
     'xpcshell',
     'web-platform',
+    'talos',
 ]
 
 
 def parse_args():
     parser = ArgumentParser(description='Generate a test_packages.json file to tell automation which harnesses require which test packages.')
     parser.add_argument("--common", required=True,
                         action="store", dest="tests_common",
                         help="Name of the \"common\" archive, a package to be used by all harnesses.")
--- a/build/unix/stdc++compat/Makefile.in
+++ b/build/unix/stdc++compat/Makefile.in
@@ -1,10 +1,7 @@
 # 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/.
 
 ENABLE_CLANG_PLUGIN :=
 
 include $(topsrcdir)/config/rules.mk
-
-CXXFLAGS += -DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_TARGET_VERSION)
-HOST_CXXFLAGS += -DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_HOST_VERSION)
--- a/build/unix/stdc++compat/moz.build
+++ b/build/unix/stdc++compat/moz.build
@@ -14,8 +14,11 @@ if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
         'stdc++compat.cpp',
     ]
 
 FORCE_STATIC_LIB = True
 
 NO_PGO = True
 
 DISABLE_STL_WRAPPING = True
+
+DEFINES['MOZ_LIBSTDCXX_VERSION'] = CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION']
+HOST_DEFINES['MOZ_LIBSTDCXX_VERSION'] = CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -36,18 +36,16 @@ HEADERS_FILES = \
 	$(NULL)
 HEADERS_DEST := $(DIST)/include
 HEADERS_TARGET := export
 INSTALL_TARGETS += HEADERS
 endif
 
 include $(topsrcdir)/config/rules.mk
 
-HOST_CFLAGS += -DUNICODE -D_UNICODE
-
 ifndef JS_STANDALONE
 ifndef MOZ_PROFILE_USE
 # Generate a new buildid every time we "export" in config... that's only
 # supposed to be once per-build!
 export::
 ifdef MOZ_BUILD_DATE
 	printf '%s' $(MOZ_BUILD_DATE) > buildid
 else
--- a/config/config.mk
+++ b/config/config.mk
@@ -423,16 +423,19 @@ COMPILE_CXXFLAGS = $(if $(DISABLE_STL_WR
 COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS)
 COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS)
 ASFLAGS += $(MOZBUILD_ASFLAGS)
 
 ifndef CROSS_COMPILE
 HOST_CFLAGS += $(RTL_FLAGS)
 endif
 
+HOST_CFLAGS += $(HOST_DEFINES) $(MOZBUILD_HOST_CFLAGS)
+HOST_CXXFLAGS += $(HOST_DEFINES) $(MOZBUILD_HOST_CXXFLAGS)
+
 #
 # Name of the binary code directories
 #
 # Override defaults
 
 # Default location of include files
 ifndef LIBXUL_SDK
 IDL_PARSER_DIR = $(topsrcdir)/xpcom/idl-parser
--- a/config/moz.build
+++ b/config/moz.build
@@ -40,8 +40,13 @@ PYTHON_UNIT_TESTS += [
     'tests/unit-printprereleasesuffix.py',
 ]
 
 if CONFIG['GNU_CC'] and CONFIG['MOZ_OPTIMIZE']:
     CFLAGS += ['-O3']
 
 # XXX: We should fix these warnings.
 ALLOW_COMPILER_WARNINGS = True
+
+HOST_DEFINES = {
+    'UNICODE': True,
+    '_UNICODE': True,
+}
--- a/configure.in
+++ b/configure.in
@@ -1478,17 +1478,16 @@ if test "$GNU_CC"; then
     fi
 
     # Turn on gcc/clang warnings:
     # https://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Warning-Options.html
     #
     # -Wall - turn on a lot of warnings
     # -Wchar-subscripts - catches array index using signed char
     # -Wcomment - catches nested comments
-    # -Wdeclaration-after-statement - MSVC doesn't like these
     # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
     # -Wendif-labels - catches `#else FOO` and `#endif FOO` not in comment
     # -Wenum-compare - catches comparison of different enum types
     # -Wignored-qualifiers - catches returns types with qualifiers like const
     # -Wint-to-pointer-cast - catches cast to pointer from integer of different size
     # -Wmultichar - catches multicharacter integer constants like 'THIS'
     # -Wnon-literal-null-conversion - catches expressions used as a null pointer constant
     # -Wnonnull - catches NULL used with functions arguments marked as non-null
@@ -1498,17 +1497,16 @@ if test "$GNU_CC"; then
     # -Wreturn-type - catches missing returns, zero false positives
     # -Wsequence-point - catches undefined order behavior like `a = a++`
     # -Wsign-compare - catches comparison of signed and unsigned types
     # -Wtrigraphs - catches unlikely use of trigraphs
     # -Wtype-limits - catches overflow bugs, few false positives
     # -Wunknown-pragmas - catches unexpected #pragma directives
     #
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall"
-    _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wdeclaration-after-statement"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wempty-body"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wpointer-to-int-cast"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wsign-compare"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wtype-limits"
 
     # Treat some warnings as errors if --enable-warnings-as-errors:
     if test "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=char-subscripts"
@@ -1834,17 +1832,17 @@ case "$host" in
         HOST_AR=lib
         HOST_AR_FLAGS='-NOLOGO -OUT:$@'
         HOST_CFLAGS="$HOST_CFLAGS -TC -nologo"
         HOST_RANLIB='echo ranlib'
     else
         HOST_CFLAGS="$HOST_CFLAGS -mwindows"
     fi
     HOST_CFLAGS="$HOST_CFLAGS -DXP_WIN32 -DXP_WIN -DWIN32 -D_WIN32 -DNO_X11 -D_CRT_SECURE_NO_WARNINGS"
-    HOST_NSPR_MDCPUCFG='\"md/_winnt.cfg\"'
+    HOST_NSPR_MDCPUCFG='"md/_winnt.cfg"'
     HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}"
     HOST_BIN_SUFFIX=.exe
     case "$host" in
     *mingw*)
         PERL="/bin/sh ${_topsrcdir}/build/msys-perl-wrapper"
         ;;
     esac
 
@@ -1860,23 +1858,23 @@ case "$host" in
         fi
         HOST_CFLAGS="$HOST_CFLAGS -D_AMD64_"
         ;;
     esac
     ;;
 
 *-darwin*)
     HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX -DXP_MACOSX -DNO_X11"
-    HOST_NSPR_MDCPUCFG='\"md/_darwin.cfg\"'
+    HOST_NSPR_MDCPUCFG='"md/_darwin.cfg"'
     HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O3}"
     ;;
 
 *-linux*|*-kfreebsd*-gnu|*-gnu*)
     HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX"
-    HOST_NSPR_MDCPUCFG='\"md/_linux.cfg\"'
+    HOST_NSPR_MDCPUCFG='"md/_linux.cfg"'
     HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O3}"
     ;;
 
 *)
     HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX"
     HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}"
     ;;
 esac
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -375,28 +375,16 @@ Blob::Constructor(
 }
 
 int64_t
 Blob::GetFileId()
 {
   return mImpl->GetFileId();
 }
 
-void
-Blob::AddFileInfo(indexedDB::FileInfo* aFileInfo)
-{
-  mImpl->AddFileInfo(aFileInfo);
-}
-
-indexedDB::FileInfo*
-Blob::GetFileInfo(indexedDB::FileManager* aFileManager)
-{
-  return mImpl->GetFileInfo(aFileManager);
-}
-
 bool
 Blob::IsMemoryFile() const
 {
   return mImpl->IsMemoryFile();
 }
 
 void
 Blob::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
@@ -457,46 +445,16 @@ File::CreateMemoryFile(nsISupports* aPar
 /* static */ already_AddRefed<File>
 File::CreateFromFile(nsISupports* aParent, nsIFile* aFile, bool aTemporary)
 {
   nsRefPtr<File> file = new File(aParent, new BlobImplFile(aFile, aTemporary));
   return file.forget();
 }
 
 /* static */ already_AddRefed<File>
-File::CreateFromFile(nsISupports* aParent, const nsAString& aContentType,
-                     uint64_t aLength, nsIFile* aFile,
-                     indexedDB::FileInfo* aFileInfo)
-{
-  nsRefPtr<File> file = new File(aParent,
-    new BlobImplFile(aContentType, aLength, aFile, aFileInfo));
-  return file.forget();
-}
-
-/* static */ already_AddRefed<File>
-File::CreateFromFile(nsISupports* aParent, const nsAString& aName,
-                     const nsAString& aContentType,
-                     uint64_t aLength, nsIFile* aFile,
-                     indexedDB::FileInfo* aFileInfo)
-{
-  nsRefPtr<File> file = new File(aParent,
-    new BlobImplFile(aName, aContentType, aLength, aFile, aFileInfo));
-  return file.forget();
-}
-
-/* static */ already_AddRefed<File>
-File::CreateFromFile(nsISupports* aParent, nsIFile* aFile,
-                     indexedDB::FileInfo* aFileInfo)
-{
-  nsRefPtr<File> file = new File(aParent,
-    new BlobImplFile(aFile, aFileInfo));
-  return file.forget();
-}
-
-/* static */ already_AddRefed<File>
 File::CreateFromFile(nsISupports* aParent, nsIFile* aFile,
                      const nsAString& aName, const nsAString& aContentType)
 {
   nsRefPtr<File> file = new File(aParent,
     new BlobImplFile(aFile, aName, aContentType));
   return file.forget();
 }
 
@@ -795,88 +753,17 @@ void
 BlobImplBase::SetLastModified(int64_t aLastModified)
 {
   mLastModificationDate = aLastModified * PR_USEC_PER_MSEC;
 }
 
 int64_t
 BlobImplBase::GetFileId()
 {
-  int64_t id = -1;
-
-  if (IsStoredFile() && IsWholeFile() && !IsSnapshot()) {
-    if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
-      indexedDB::IndexedDatabaseManager::FileMutex().Lock();
-    }
-
-    NS_ASSERTION(!mFileInfos.IsEmpty(),
-                 "A stored file must have at least one file info!");
-
-    nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(0);
-    if (fileInfo) {
-      id =  fileInfo->Id();
-    }
-
-    if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
-      indexedDB::IndexedDatabaseManager::FileMutex().Unlock();
-    }
-  }
-
-  return id;
-}
-
-void
-BlobImplBase::AddFileInfo(indexedDB::FileInfo* aFileInfo)
-{
-  if (indexedDB::IndexedDatabaseManager::IsClosed()) {
-    NS_ERROR("Shouldn't be called after shutdown!");
-    return;
-  }
-
-  nsRefPtr<indexedDB::FileInfo> fileInfo = aFileInfo;
-
-  MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex());
-
-  NS_ASSERTION(!mFileInfos.Contains(aFileInfo),
-               "Adding the same file info agan?!");
-
-  nsRefPtr<indexedDB::FileInfo>* element = mFileInfos.AppendElement();
-  element->swap(fileInfo);
-}
-
-indexedDB::FileInfo*
-BlobImplBase::GetFileInfo(indexedDB::FileManager* aFileManager)
-{
-  if (indexedDB::IndexedDatabaseManager::IsClosed()) {
-    NS_ERROR("Shouldn't be called after shutdown!");
-    return nullptr;
-  }
-
-  // A slice created from a stored file must keep the file info alive.
-  // However, we don't support sharing of slices yet, so the slice must be
-  // copied again. That's why we have to ignore the first file info.
-  // Snapshots are handled in a similar way (they have to be copied).
-  uint32_t startIndex;
-  if (IsStoredFile() && (!IsWholeFile() || IsSnapshot())) {
-    startIndex = 1;
-  }
-  else {
-    startIndex = 0;
-  }
-
-  MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex());
-
-  for (uint32_t i = startIndex; i < mFileInfos.Length(); i++) {
-    nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(i);
-    if (fileInfo->Manager() == aFileManager) {
-      return fileInfo;
-    }
-  }
-
-  return nullptr;
+  return -1;
 }
 
 nsresult
 BlobImplBase::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
                           nsACString& aContentType, nsACString& aCharset)
 {
   MOZ_ASSERT(aContentLength);
 
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -3,25 +3,23 @@
 /* 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_File_h
 #define mozilla_dom_File_h
 
 #include "mozilla/Attributes.h"
-
+#include "mozilla/ErrorResult.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Date.h"
-#include "mozilla/dom/indexedDB/FileInfo.h"
-#include "mozilla/dom/indexedDB/FileManager.h"
-#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMBlob.h"
 #include "nsIFile.h"
 #include "nsIMutable.h"
 #include "nsIXMLHttpRequest.h"
 #include "nsString.h"
@@ -34,20 +32,16 @@ class nsIInputStream;
 
 #define BLOBIMPL_IID \
   { 0xbccb3275, 0x6778, 0x4ac5, \
     { 0xaf, 0x03, 0x90, 0xed, 0x37, 0xad, 0xdf, 0x5d } }
 
 namespace mozilla {
 namespace dom {
 
-namespace indexedDB {
-class FileInfo;
-} // namespace indexedDB
-
 struct BlobPropertyBag;
 struct ChromeFilePropertyBag;
 struct FilePropertyBag;
 class BlobImpl;
 class File;
 class OwningArrayBufferOrArrayBufferViewOrBlobOrString;
 
 /**
@@ -131,22 +125,16 @@ public:
               ErrorResult& aRv);
 
   void
   GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv);
 
   int64_t
   GetFileId();
 
-  indexedDB::FileInfo*
-  GetFileInfo(indexedDB::FileManager* aFileManager);
-
-  void
-  AddFileInfo(indexedDB::FileInfo* aFileInfo);
-
   // WebIDL methods
   nsISupports* GetParentObject() const
   {
     return mParent;
   }
 
   bool
   IsMemoryFile() const;
@@ -217,30 +205,16 @@ public:
   CreateMemoryFile(nsISupports* aParent, void* aMemoryBuffer, uint64_t aLength,
                    const nsAString& aName, const nsAString& aContentType,
                    int64_t aLastModifiedDate);
 
   static already_AddRefed<File>
   CreateFromFile(nsISupports* aParent, nsIFile* aFile, bool aTemporary = false);
 
   static already_AddRefed<File>
-  CreateFromFile(nsISupports* aParent, const nsAString& aContentType,
-                 uint64_t aLength, nsIFile* aFile,
-                 indexedDB::FileInfo* aFileInfo);
-
-  static already_AddRefed<File>
-  CreateFromFile(nsISupports* aParent, const nsAString& aName,
-                 const nsAString& aContentType, uint64_t aLength,
-                 nsIFile* aFile, indexedDB::FileInfo* aFileInfo);
-
-  static already_AddRefed<File>
-  CreateFromFile(nsISupports* aParent, nsIFile* aFile,
-                 indexedDB::FileInfo* aFileInfo);
-
-  static already_AddRefed<File>
   CreateFromFile(nsISupports* aParent, nsIFile* aFile, const nsAString& aName,
                  const nsAString& aContentType);
 
   // WebIDL methods
 
   virtual JSObject* WrapObject(JSContext *cx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
@@ -340,21 +314,16 @@ public:
   virtual const nsTArray<nsRefPtr<BlobImpl>>*
   GetSubBlobImpls() const = 0;
 
   virtual void GetInternalStream(nsIInputStream** aStream,
                                  ErrorResult& aRv) = 0;
 
   virtual int64_t GetFileId() = 0;
 
-  virtual void AddFileInfo(indexedDB::FileInfo* aFileInfo) = 0;
-
-  virtual indexedDB::FileInfo*
-  GetFileInfo(indexedDB::FileManager* aFileManager) = 0;
-
   virtual nsresult GetSendInfo(nsIInputStream** aBody,
                                uint64_t* aContentLength,
                                nsACString& aContentType,
                                nsACString& aCharset) = 0;
 
   virtual nsresult GetMutable(bool* aMutable) const = 0;
 
   virtual nsresult SetMutable(bool aMutable) = 0;
@@ -512,37 +481,30 @@ public:
   virtual void GetInternalStream(nsIInputStream** aStream,
                                  ErrorResult& aRv) override
   {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   }
 
   virtual int64_t GetFileId() override;
 
-  virtual void AddFileInfo(indexedDB::FileInfo* aFileInfo) override;
-
-  virtual indexedDB::FileInfo*
-  GetFileInfo(indexedDB::FileManager* aFileManager) override;
-
   virtual nsresult GetSendInfo(nsIInputStream** aBody,
                                uint64_t* aContentLength,
                                nsACString& aContentType,
                                nsACString& aCharset) override;
 
   virtual nsresult GetMutable(bool* aMutable) const override;
 
   virtual nsresult SetMutable(bool aMutable) override;
 
   virtual void
   SetLazyData(const nsAString& aName, const nsAString& aContentType,
               uint64_t aLength, int64_t aLastModifiedDate,
               BlobDirState aDirState) override
   {
-    NS_ASSERTION(aLength, "must have length");
-
     mName = aName;
     mContentType = aContentType;
     mLength = aLength;
     mLastModificationDate = aLastModifiedDate;
     mIsFile = !aName.IsVoid();
   }
 
   virtual bool IsMemoryFile() const override
@@ -585,72 +547,45 @@ public:
     return mDirState == BlobDirState::eIsDir;
   }
 
   virtual BlobDirState GetDirState() const override
   {
     return mDirState;
   }
 
-  virtual bool IsStoredFile() const
-  {
-    return false;
-  }
-
-  virtual bool IsWholeFile() const
-  {
-    NS_NOTREACHED("Should only be called on dom blobs backed by files!");
-    return false;
-  }
-
-  virtual bool IsSnapshot() const
-  {
-    return false;
-  }
-
   virtual bool IsSizeUnknown() const override
   {
     return mLength == UINT64_MAX;
   }
 
 protected:
   virtual ~BlobImplBase() {}
 
   /**
    * Returns a new, effectively-unique serial number. This should be used
    * by implementations to obtain a serial number for GetSerialNumber().
    * The implementation is thread safe.
    */
   static uint64_t NextSerialNumber();
 
-  indexedDB::FileInfo* GetFileInfo() const
-  {
-    NS_ASSERTION(IsStoredFile(), "Should only be called on stored files!");
-    NS_ASSERTION(!mFileInfos.IsEmpty(), "Must have at least one file info!");
-
-    return mFileInfos.ElementAt(0);
-  }
-
   bool mIsFile;
   bool mImmutable;
   BlobDirState mDirState;
 
   nsString mContentType;
   nsString mName;
   nsString mPath; // The path relative to a directory chosen by the user
 
   uint64_t mStart;
   uint64_t mLength;
 
   int64_t mLastModificationDate;
 
   const uint64_t mSerialNumber;
-
-  // Protected by IndexedDatabaseManager::FileMutex()
-  nsTArray<nsRefPtr<indexedDB::FileInfo>> mFileInfos;
 };
 
 /**
  * This class may be used off the main thread, and in particular, its
  * constructor and destructor may not run on the same thread.  Be careful!
  */
 class BlobImplMemory final : public BlobImplBase
 {
@@ -788,113 +723,65 @@ class BlobImplFile : public BlobImplBase
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // Create as a file
   explicit BlobImplFile(nsIFile* aFile, bool aTemporary = false)
     : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX)
     , mFile(aFile)
     , mWholeFile(true)
-    , mStoredFile(false)
     , mIsTemporary(aTemporary)
   {
     NS_ASSERTION(mFile, "must have file");
     // Lazily get the content type and size
     mContentType.SetIsVoid(true);
     mFile->GetLeafName(mName);
   }
 
-  BlobImplFile(nsIFile* aFile, indexedDB::FileInfo* aFileInfo)
-    : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX)
-    , mFile(aFile)
-    , mWholeFile(true)
-    , mStoredFile(true)
-    , mIsTemporary(false)
-  {
-    NS_ASSERTION(mFile, "must have file");
-    NS_ASSERTION(aFileInfo, "must have file info");
-    // Lazily get the content type and size
-    mContentType.SetIsVoid(true);
-    mFile->GetLeafName(mName);
-
-    mFileInfos.AppendElement(aFileInfo);
-  }
-
   // Create as a file
   BlobImplFile(const nsAString& aName, const nsAString& aContentType,
                uint64_t aLength, nsIFile* aFile)
     : BlobImplBase(aName, aContentType, aLength, UINT64_MAX)
     , mFile(aFile)
     , mWholeFile(true)
-    , mStoredFile(false)
     , mIsTemporary(false)
   {
     NS_ASSERTION(mFile, "must have file");
   }
 
   BlobImplFile(const nsAString& aName, const nsAString& aContentType,
                uint64_t aLength, nsIFile* aFile,
                int64_t aLastModificationDate)
     : BlobImplBase(aName, aContentType, aLength, aLastModificationDate)
     , mFile(aFile)
     , mWholeFile(true)
-    , mStoredFile(false)
     , mIsTemporary(false)
   {
     NS_ASSERTION(mFile, "must have file");
   }
 
   // Create as a file with custom name
   BlobImplFile(nsIFile* aFile, const nsAString& aName,
                const nsAString& aContentType)
     : BlobImplBase(aName, aContentType, UINT64_MAX, INT64_MAX)
     , mFile(aFile)
     , mWholeFile(true)
-    , mStoredFile(false)
     , mIsTemporary(false)
   {
     NS_ASSERTION(mFile, "must have file");
     if (aContentType.IsEmpty()) {
       // Lazily get the content type and size
       mContentType.SetIsVoid(true);
     }
   }
 
-  // Create as a stored file
-  BlobImplFile(const nsAString& aName, const nsAString& aContentType,
-               uint64_t aLength, nsIFile* aFile,
-               indexedDB::FileInfo* aFileInfo)
-    : BlobImplBase(aName, aContentType, aLength, UINT64_MAX)
-    , mFile(aFile)
-    , mWholeFile(true)
-    , mStoredFile(true)
-    , mIsTemporary(false)
-  {
-    NS_ASSERTION(mFile, "must have file");
-    mFileInfos.AppendElement(aFileInfo);
-  }
-
-  // Create as a stored blob
-  BlobImplFile(const nsAString& aContentType, uint64_t aLength,
-               nsIFile* aFile, indexedDB::FileInfo* aFileInfo)
-    : BlobImplBase(aContentType, aLength)
-    , mFile(aFile)
-    , mWholeFile(true)
-    , mStoredFile(true)
-    , mIsTemporary(false)
-  {
-    NS_ASSERTION(mFile, "must have file");
-    mFileInfos.AppendElement(aFileInfo);
-  }
-
   // Create as a file to be later initialized
   BlobImplFile()
     : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX)
     , mWholeFile(true)
-    , mStoredFile(false)
     , mIsTemporary(false)
   {
     // Lazily get the content type and size
     mContentType.SetIsVoid(true);
     mName.SetIsVoid(true);
   }
 
   // Overrides
@@ -927,55 +814,27 @@ protected:
 
 private:
   // Create slice
   BlobImplFile(const BlobImplFile* aOther, uint64_t aStart,
                uint64_t aLength, const nsAString& aContentType)
     : BlobImplBase(aContentType, aOther->mStart + aStart, aLength)
     , mFile(aOther->mFile)
     , mWholeFile(false)
-    , mStoredFile(aOther->mStoredFile)
     , mIsTemporary(false)
   {
     NS_ASSERTION(mFile, "must have file");
     mImmutable = aOther->mImmutable;
-
-    if (mStoredFile) {
-      indexedDB::FileInfo* fileInfo;
-
-      using indexedDB::IndexedDatabaseManager;
-
-      if (IndexedDatabaseManager::IsClosed()) {
-        fileInfo = aOther->GetFileInfo();
-      }
-      else {
-        mozilla::MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
-        fileInfo = aOther->GetFileInfo();
-      }
-
-      mFileInfos.AppendElement(fileInfo);
-    }
   }
 
   virtual already_AddRefed<BlobImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType, ErrorResult& aRv) override;
 
-  virtual bool IsStoredFile() const override
-  {
-    return mStoredFile;
-  }
-
-  virtual bool IsWholeFile() const override
-  {
-    return mWholeFile;
-  }
-
   nsCOMPtr<nsIFile> mFile;
   bool mWholeFile;
-  bool mStoredFile;
   bool mIsTemporary;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_File_h
--- a/dom/base/StructuredCloneHelper.h
+++ b/dom/base/StructuredCloneHelper.h
@@ -6,16 +6,20 @@
 #ifndef mozilla_dom_StructuredCloneHelper_h
 #define mozilla_dom_StructuredCloneHelper_h
 
 #include "js/StructuredClone.h"
 #include "nsAutoPtr.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 
+#ifdef DEBUG
+#include "nsIThread.h"
+#endif
+
 namespace mozilla {
 class ErrorResult;
 namespace layers {
 class Image;
 }
 
 namespace dom {
 
@@ -162,38 +166,16 @@ public:
             ErrorResult &aRv);
 
   // Sometimes, when IPC is involved, you must send a buffer after a Write().
   // This method 'steals' the internal data from this helper class.
   // You should free this buffer with FreeBuffer().
   void MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
                              ErrorResult& aRv);
 
-  // If you receive a buffer from IPC, you can use this method to retrieve a
-  // JS::Value. It can happen that you want to pre-populate the array of Blobs
-  // and/or the PortIdentifiers.
-  void ReadFromBuffer(nsISupports* aParent,
-                      JSContext* aCx,
-                      uint64_t* aBuffer,
-                      size_t aBufferLength,
-                      JS::MutableHandle<JS::Value> aValue,
-                      ErrorResult &aRv);
-
-  void ReadFromBuffer(nsISupports* aParent,
-                      JSContext* aCx,
-                      uint64_t* aBuffer,
-                      size_t aBufferLength,
-                      uint32_t aAlgorithmVersion,
-                      JS::MutableHandle<JS::Value> aValue,
-                      ErrorResult &aRv);
-
-  // Use this method to free a buffer generated by MoveToBuffer().
-  void FreeBuffer(uint64_t* aBuffer,
-                  size_t aBufferLength);
-
   bool HasClonedDOMObjects() const
   {
     return !mBlobImplArray.IsEmpty() ||
            !mClonedImages.IsEmpty();
   }
 
   nsTArray<nsRefPtr<BlobImpl>>& BlobImpls()
   {
@@ -252,16 +234,38 @@ public:
                                      void** aContent,
                                      uint64_t* aExtraData) override;
 
   virtual void FreeTransferCallback(uint32_t aTag,
                                     JS::TransferableOwnership aOwnership,
                                     void* aContent,
                                     uint64_t aExtraData) override;
 protected:
+  // If you receive a buffer from IPC, you can use this method to retrieve a
+  // JS::Value. It can happen that you want to pre-populate the array of Blobs
+  // and/or the PortIdentifiers.
+  void ReadFromBuffer(nsISupports* aParent,
+                      JSContext* aCx,
+                      uint64_t* aBuffer,
+                      size_t aBufferLength,
+                      JS::MutableHandle<JS::Value> aValue,
+                      ErrorResult &aRv);
+
+  void ReadFromBuffer(nsISupports* aParent,
+                      JSContext* aCx,
+                      uint64_t* aBuffer,
+                      size_t aBufferLength,
+                      uint32_t aAlgorithmVersion,
+                      JS::MutableHandle<JS::Value> aValue,
+                      ErrorResult &aRv);
+
+  // Use this method to free a buffer generated by MoveToBuffer().
+  void FreeBuffer(uint64_t* aBuffer,
+                  size_t aBufferLength);
+
   bool mSupportsCloning;
   bool mSupportsTransferring;
   ContextSupport mContext;
 
   // Useful for the structured clone algorithm:
 
   nsTArray<nsRefPtr<BlobImpl>> mBlobImplArray;
 
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -758,17 +758,17 @@ nsFrameMessageManager::SendMessage(const
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   JS::Rooted<JSObject*> objects(aCx);
   if (aArgc >= 3 && aObjects.isObject()) {
     objects = &aObjects.toObject();
   }
 
-  nsTArray<OwningSerializedStructuredCloneBuffer> retval;
+  nsTArray<StructuredCloneIPCHelper> retval;
 
   sSendingSyncMessage |= aIsSync;
   bool ok = mCallback->DoSendBlockingMessage(aCx, aMessageName, helper, objects,
                                              aPrincipal, &retval, aIsSync);
   if (aIsSync) {
     sSendingSyncMessage = false;
   }
 
@@ -777,18 +777,19 @@ nsFrameMessageManager::SendMessage(const
   }
 
   uint32_t len = retval.Length();
   JS::Rooted<JSObject*> dataArray(aCx, JS_NewArrayObject(aCx, len));
   NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
 
   for (uint32_t i = 0; i < len; ++i) {
     JS::Rooted<JS::Value> ret(aCx);
-    if (!JS_ReadStructuredClone(aCx, retval[i].data, retval[i].dataLength,
-                                JS_STRUCTURED_CLONE_VERSION, &ret, nullptr, nullptr)) {
+    ErrorResult rv;
+    retval[i].Read(aCx, &ret, rv);
+    if (rv.Failed()) {
       MOZ_ASSERT(false, "Unable to read structured clone in SendMessage");
       return NS_ERROR_UNEXPECTED;
     }
 
     NS_ENSURE_TRUE(JS_DefineElement(aCx, dataArray, i, ret, JSPROP_ENUMERATE),
                    NS_ERROR_OUT_OF_MEMORY);
   }
 
@@ -1067,32 +1068,32 @@ public:
 nsresult
 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
                                       nsIFrameLoader* aTargetFrameLoader,
                                       const nsAString& aMessage,
                                       bool aIsSync,
                                       StructuredCloneIPCHelper* aCloneHelper,
                                       mozilla::jsipc::CpowHolder* aCpows,
                                       nsIPrincipal* aPrincipal,
-                                      nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal)
+                                      nsTArray<StructuredCloneIPCHelper>* aRetVal)
 {
   return ReceiveMessage(aTarget, aTargetFrameLoader, mClosed, aMessage, aIsSync,
                         aCloneHelper, aCpows, aPrincipal, aRetVal);
 }
 
 nsresult
 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
                                       nsIFrameLoader* aTargetFrameLoader,
                                       bool aTargetClosed,
                                       const nsAString& aMessage,
                                       bool aIsSync,
                                       StructuredCloneIPCHelper* aCloneHelper,
                                       mozilla::jsipc::CpowHolder* aCpows,
                                       nsIPrincipal* aPrincipal,
-                                      nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal)
+                                      nsTArray<StructuredCloneIPCHelper>* aRetVal)
 {
   nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
     mListeners.Get(aMessage);
   if (listeners) {
 
     MMListenerRemover lr(this);
 
     nsAutoTObserverArray<nsMessageListenerInfo, 1>::EndLimitedIterator
@@ -1261,34 +1262,34 @@ nsFrameMessageManager::ReceiveMessage(ns
           return NS_ERROR_UNEXPECTED;
         }
 
         if (!JS_CallFunctionValue(cx, thisObject, funval,
                                   JS::HandleValueArray(argv), &rval)) {
           continue;
         }
         if (aRetVal) {
-          JSAutoStructuredCloneBuffer buffer;
-          if (!buffer.write(cx, rval)) {
+          ErrorResult rv;
+          StructuredCloneIPCHelper* helper = aRetVal->AppendElement();
+          helper->Write(cx, rval, rv);
+          if (NS_WARN_IF(rv.Failed())) {
+            aRetVal->RemoveElementAt(aRetVal->Length() - 1);
             nsString msg = aMessage + NS_LITERAL_STRING(": message reply cannot be cloned. Are you trying to send an XPCOM object?");
 
             nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
             if (console) {
               nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
               error->Init(msg, EmptyString(), EmptyString(),
                           0, 0, nsIScriptError::warningFlag, "chrome javascript");
               console->LogMessage(error);
             }
 
             JS_ClearPendingException(cx);
             continue;
           }
-
-          OwningSerializedStructuredCloneBuffer* data = aRetVal->AppendElement();
-          buffer.steal(&data->data, &data->dataLength);
         }
       }
     }
   }
   nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
   return mParentManager ? mParentManager->ReceiveMessage(aTarget, aTargetFrameLoader,
                                                          aTargetClosed, aMessage,
                                                          aIsSync, aCloneHelper,
@@ -1989,17 +1990,17 @@ public:
     MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback);
   }
 
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                      const nsAString& aMessage,
                                      StructuredCloneIPCHelper& aHelper,
                                      JS::Handle<JSObject *> aCpows,
                                      nsIPrincipal* aPrincipal,
-                                     nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal,
+                                     nsTArray<StructuredCloneIPCHelper>* aRetVal,
                                      bool aIsSync) override
   {
     mozilla::dom::ContentChild* cc =
       mozilla::dom::ContentChild::GetSingleton();
     if (!cc) {
       return true;
     }
     ClonedMessageData data;
@@ -2080,17 +2081,17 @@ public:
     MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback);
   }
 
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                      const nsAString& aMessage,
                                      StructuredCloneIPCHelper& aHelper,
                                      JS::Handle<JSObject *> aCpows,
                                      nsIPrincipal* aPrincipal,
-                                     nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal,
+                                     nsTArray<StructuredCloneIPCHelper>* aRetVal,
                                      bool aIsSync) override
   {
     SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
     queue->Flush();
 
     if (nsFrameMessageManager::sSameProcessParentManager) {
       SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows);
       nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -29,19 +29,16 @@
 #include "nsTObserverArray.h"
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/StructuredCloneIPCHelper.h"
 #include "mozilla/jsipc/CpowHolder.h"
 
 class nsIFrameLoader;
 
 namespace mozilla {
-
-struct OwningSerializedStructuredCloneBuffer;
-
 namespace dom {
 
 class nsIContentParent;
 class nsIContentChild;
 class ClonedMessageData;
 class MessageManagerReporter;
 
 namespace ipc {
@@ -52,33 +49,30 @@ enum MessageManagerFlags {
   MM_GLOBAL = 2,
   MM_PROCESSMANAGER = 4,
   MM_BROADCASTER = 8,
   MM_OWNSCALLBACK = 16
 };
 
 class MessageManagerCallback
 {
-protected:
-  typedef mozilla::OwningSerializedStructuredCloneBuffer OwningSerializedStructuredCloneBuffer;
-
 public:
   virtual ~MessageManagerCallback() {}
 
   virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope)
   {
     return true;
   }
 
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                      const nsAString& aMessage,
                                      StructuredCloneIPCHelper& aHelper,
                                      JS::Handle<JSObject*> aCpows,
                                      nsIPrincipal* aPrincipal,
-                                     nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal,
+                                     nsTArray<StructuredCloneIPCHelper>* aRetVal,
                                      bool aIsSync)
   {
     return true;
   }
 
   virtual bool DoSendAsyncMessage(JSContext* aCx,
                                   const nsAString& aMessage,
                                   StructuredCloneIPCHelper& aHelper,
@@ -165,17 +159,16 @@ private:
 class nsFrameMessageManager final : public nsIContentFrameMessageManager,
                                     public nsIMessageBroadcaster,
                                     public nsIFrameScriptLoader,
                                     public nsIGlobalProcessScriptLoader,
                                     public nsIProcessChecker
 {
   friend class mozilla::dom::MessageManagerReporter;
   typedef mozilla::dom::StructuredCloneIPCHelper StructuredCloneIPCHelper;
-  typedef mozilla::OwningSerializedStructuredCloneBuffer OwningSerializedStructuredCloneBuffer;
 public:
   nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
                         nsFrameMessageManager* aParentManager,
                         /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags);
 
 private:
   ~nsFrameMessageManager();
 
@@ -196,17 +189,17 @@ public:
 
   static nsFrameMessageManager*
   NewProcessMessageManager(bool aIsRemote);
 
   nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader,
                           const nsAString& aMessage,
                           bool aIsSync, StructuredCloneIPCHelper* aCloneHelper,
                           mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
-                          nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal);
+                          nsTArray<StructuredCloneIPCHelper>* aRetVal);
 
   void AddChildManager(nsFrameMessageManager* aManager);
   void RemoveChildManager(nsFrameMessageManager* aManager)
   {
     mChildManagers.RemoveObject(aManager);
   }
   void Disconnect(bool aRemoveFromParent = true);
   void Close();
@@ -264,17 +257,17 @@ private:
                        uint8_t aArgc,
                        JS::MutableHandle<JS::Value> aRetval,
                        bool aIsSync);
 
   nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader,
                           bool aTargetClosed, const nsAString& aMessage,
                           bool aIsSync, StructuredCloneIPCHelper* aCloneHelper,
                           mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
-                          nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal);
+                          nsTArray<StructuredCloneIPCHelper>* aRetVal);
 
   NS_IMETHOD LoadScript(const nsAString& aURL,
                         bool aAllowDelayedLoad,
                         bool aRunInGlobalScope);
   NS_IMETHOD RemoveDelayedScript(const nsAString& aURL);
   NS_IMETHOD GetDelayedScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList);
 
 protected:
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -23,17 +23,17 @@ using namespace mozilla;
 using namespace mozilla::dom;
 
 bool
 nsInProcessTabChildGlobal::DoSendBlockingMessage(JSContext* aCx,
                                                  const nsAString& aMessage,
                                                  StructuredCloneIPCHelper& aHelper,
                                                  JS::Handle<JSObject *> aCpows,
                                                  nsIPrincipal* aPrincipal,
-                                                 nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal,
+                                                 nsTArray<StructuredCloneIPCHelper>* aRetVal,
                                                  bool aIsSync)
 {
   SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
   queue->Flush();
 
   if (mChromeMessageManager) {
     SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows);
     nsRefPtr<nsFrameMessageManager> mm = mChromeMessageManager;
--- a/dom/base/nsInProcessTabChildGlobal.h
+++ b/dom/base/nsInProcessTabChildGlobal.h
@@ -30,17 +30,17 @@ class EventChainPreVisitor;
 class nsInProcessTabChildGlobal : public mozilla::DOMEventTargetHelper,
                                   public nsMessageManagerScriptExecutor,
                                   public nsIInProcessContentFrameMessageManager,
                                   public nsIGlobalObject,
                                   public nsIScriptObjectPrincipal,
                                   public nsSupportsWeakReference,
                                   public mozilla::dom::ipc::MessageManagerCallback
 {
-  typedef mozilla::OwningSerializedStructuredCloneBuffer OwningSerializedStructuredCloneBuffer;
+  typedef mozilla::dom::StructuredCloneIPCHelper StructuredCloneIPCHelper;
 
 public:
   nsInProcessTabChildGlobal(nsIDocShell* aShell, nsIContent* aOwner,
                             nsFrameMessageManager* aChrome);
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsInProcessTabChildGlobal,
                                                          mozilla::DOMEventTargetHelper)
 
@@ -78,24 +78,24 @@ public:
 
   NS_DECL_NSIINPROCESSCONTENTFRAMEMESSAGEMANAGER
 
   /**
    * MessageManagerCallback methods that we override.
    */
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                       const nsAString& aMessage,
-                                      mozilla::dom::StructuredCloneIPCHelper& aHelper,
+                                      StructuredCloneIPCHelper& aHelper,
                                       JS::Handle<JSObject *> aCpows,
                                       nsIPrincipal* aPrincipal,
-                                      nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal,
+                                      nsTArray<StructuredCloneIPCHelper>* aRetVal,
                                       bool aIsSync) override;
   virtual bool DoSendAsyncMessage(JSContext* aCx,
                                   const nsAString& aMessage,
-                                  mozilla::dom::StructuredCloneIPCHelper& aHelper,
+                                  StructuredCloneIPCHelper& aHelper,
                                   JS::Handle<JSObject *> aCpows,
                                   nsIPrincipal* aPrincipal) override;
 
   virtual nsresult PreHandleEvent(
                      mozilla::EventChainPreVisitor& aVisitor) override;
   NS_IMETHOD AddEventListener(const nsAString& aType,
                               nsIDOMEventListener* aListener,
                               bool aUseCapture)
--- a/dom/base/nsStructuredCloneContainer.cpp
+++ b/dom/base/nsStructuredCloneContainer.cpp
@@ -11,125 +11,104 @@
 #include "nsIVariant.h"
 #include "nsIXPConnect.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
 #include "jsapi.h"
 #include "xpcpublic.h"
 
 #include "mozilla/Base64.h"
-#include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ADDREF(nsStructuredCloneContainer)
 NS_IMPL_RELEASE(nsStructuredCloneContainer)
 
 NS_INTERFACE_MAP_BEGIN(nsStructuredCloneContainer)
   NS_INTERFACE_MAP_ENTRY(nsIStructuredCloneContainer)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 nsStructuredCloneContainer::nsStructuredCloneContainer()
-  : StructuredCloneHelper(CloningSupported, TransferringNotSupported,
-                          DifferentProcess)
-  , mState(eNotInitialized) , mData(nullptr), mSize(0), mVersion(0)
+  : mVersion(0)
 {
 }
 
 nsStructuredCloneContainer::~nsStructuredCloneContainer()
 {
-  if (mData) {
-    free(mData);
-  }
 }
 
 NS_IMETHODIMP
 nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData,
                                           JSContext* aCx)
 {
-  if (mState != eNotInitialized) {
+  if (DataLength()) {
     return NS_ERROR_FAILURE;
   }
 
   ErrorResult rv;
   Write(aCx, aData, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
-  mState = eInitializedFromJSVal;
+  mVersion = JS_STRUCTURED_CLONE_VERSION;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStructuredCloneContainer::InitFromBase64(const nsAString &aData,
                                            uint32_t aFormatVersion,
                                            JSContext* aCx)
 {
-  if (mState != eNotInitialized) {
+  if (DataLength()) {
     return NS_ERROR_FAILURE;
   }
 
   NS_ConvertUTF16toUTF8 data(aData);
 
   nsAutoCString binaryData;
   nsresult rv = Base64Decode(data, binaryData);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Copy the string's data into our own buffer.
-  mData = (uint64_t*) malloc(binaryData.Length());
-  NS_ENSURE_STATE(mData);
-  memcpy(mData, binaryData.get(), binaryData.Length());
+  if (!CopyExternalData(binaryData.get(), binaryData.Length())) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
-  mSize = binaryData.Length();
   mVersion = aFormatVersion;
-
-  mState = eInitializedFromBase64;
   return NS_OK;
 }
 
 nsresult
 nsStructuredCloneContainer::DeserializeToJsval(JSContext* aCx,
                                                JS::MutableHandle<JS::Value> aValue)
 {
   aValue.setNull();
   JS::Rooted<JS::Value> jsStateObj(aCx);
 
-  if (mState == eInitializedFromJSVal) {
-    ErrorResult rv;
-    Read(nullptr, aCx, &jsStateObj, rv);
-    if (NS_WARN_IF(rv.Failed())) {
-      return rv.StealNSResult();
-    }
-  } else {
-    MOZ_ASSERT(mState == eInitializedFromBase64);
-    MOZ_ASSERT(mData);
-
-    ErrorResult rv;
-    ReadFromBuffer(nullptr, aCx, mData, mSize, mVersion, &jsStateObj, rv);
-    if (NS_WARN_IF(rv.Failed())) {
-      return rv.StealNSResult();
-    }
+  ErrorResult rv;
+  Read(aCx, &jsStateObj, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
   }
 
   aValue.set(jsStateObj);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStructuredCloneContainer::DeserializeToVariant(JSContext* aCx,
                                                  nsIVariant** aData)
 {
   NS_ENSURE_ARG_POINTER(aData);
   *aData = nullptr;
 
-  if (mState == eNotInitialized) {
+  if (!DataLength()) {
     return NS_ERROR_FAILURE;
   }
 
   // Deserialize to a JS::Value.
   JS::Rooted<JS::Value> jsStateObj(aCx);
   nsresult rv = DeserializeToJsval(aCx, &jsStateObj);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -146,83 +125,52 @@ nsStructuredCloneContainer::DeserializeT
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut)
 {
   aOut.Truncate();
 
-  if (mState == eNotInitialized) {
+  if (!DataLength()) {
     return NS_ERROR_FAILURE;
   }
 
-  uint64_t* data;
-  size_t size;
-
-  if (mState == eInitializedFromJSVal) {
-    if (HasClonedDOMObjects()) {
-      return NS_ERROR_FAILURE;
-    }
-
-    data = BufferData();
-    size = BufferSize();
-  } else {
-    MOZ_ASSERT(mState == eInitializedFromBase64);
-    MOZ_ASSERT(mData);
-
-    data = mData;
-    size = mSize;
+  if (HasClonedDOMObjects()) {
+    return NS_ERROR_FAILURE;
   }
 
-  nsAutoCString binaryData(reinterpret_cast<char*>(data), size);
+  nsAutoCString binaryData(reinterpret_cast<char*>(Data()), DataLength());
   nsAutoCString base64Data;
   nsresult rv = Base64Encode(binaryData, base64Data);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   CopyASCIItoUTF16(base64Data, aOut);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStructuredCloneContainer::GetSerializedNBytes(uint64_t* aSize)
 {
   NS_ENSURE_ARG_POINTER(aSize);
 
-  if (mState == eNotInitialized) {
+  if (!DataLength()) {
     return NS_ERROR_FAILURE;
   }
 
-  if (mState == eInitializedFromJSVal) {
-    *aSize = BufferSize();
-    return NS_OK;
-  }
-
-  MOZ_ASSERT(mState == eInitializedFromBase64);
-
-  // mSize is a size_t, while aSize is a uint64_t.  We rely on an implicit cast
-  // here so that we'll get a compile error if a size_t-to-uint64_t cast is
-  // narrowing.
-  *aSize = mSize;
-
+  *aSize = DataLength();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStructuredCloneContainer::GetFormatVersion(uint32_t* aFormatVersion)
 {
   NS_ENSURE_ARG_POINTER(aFormatVersion);
 
-  if (mState == eNotInitialized) {
+  if (!DataLength()) {
     return NS_ERROR_FAILURE;
   }
 
-  if (mState == eInitializedFromJSVal) {
-    *aFormatVersion = JS_STRUCTURED_CLONE_VERSION;
-    return NS_OK;
-  }
-
-  MOZ_ASSERT(mState == eInitializedFromBase64);
   *aFormatVersion = mVersion;
   return NS_OK;
 }
--- a/dom/base/nsStructuredCloneContainer.h
+++ b/dom/base/nsStructuredCloneContainer.h
@@ -4,47 +4,37 @@
  * 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 nsStructuredCloneContainer_h__
 #define nsStructuredCloneContainer_h__
 
 #include "nsIStructuredCloneContainer.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/dom/StructuredCloneHelper.h"
+#include "mozilla/dom/StructuredCloneIPCHelper.h"
 
 #define NS_STRUCTUREDCLONECONTAINER_CONTRACTID \
   "@mozilla.org/docshell/structured-clone-container;1"
 #define NS_STRUCTUREDCLONECONTAINER_CID \
 { /* 38bd0634-0fd4-46f0-b85f-13ced889eeec */       \
   0x38bd0634,                                      \
   0x0fd4,                                          \
   0x46f0,                                          \
   {0xb8, 0x5f, 0x13, 0xce, 0xd8, 0x89, 0xee, 0xec} \
 }
 
 class nsStructuredCloneContainer final
   : public nsIStructuredCloneContainer
-  , public mozilla::dom::StructuredCloneHelper
+  , public mozilla::dom::StructuredCloneIPCHelper
 {
   public:
     nsStructuredCloneContainer();
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSISTRUCTUREDCLONECONTAINER
 
   private:
     ~nsStructuredCloneContainer();
 
-    enum {
-      eNotInitialized = 0,
-      eInitializedFromJSVal,
-      eInitializedFromBase64,
-    } mState;
-
-    uint64_t* mData;
-
-    // This needs to be size_t rather than a PR-type so it matches the JS API.
-    size_t mSize;
     uint32_t mVersion;
 };
 
 #endif
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -3099,18 +3099,23 @@ nsXMLHttpRequest::SetRequestHeader(const
     }
   }
 
   if (!mAlreadySetHeaders.Contains(header)) {
     // Case 2 above
     mergeHeaders = false;
   }
 
-  // Merge headers depending on what we decided above.
-  nsresult rv = httpChannel->SetRequestHeader(header, value, mergeHeaders);
+  nsresult rv;
+  if (value.IsEmpty()) {
+    rv = httpChannel->SetEmptyRequestHeader(header);
+  } else {
+    // Merge headers depending on what we decided above.
+    rv = httpChannel->SetRequestHeader(header, value, mergeHeaders);
+  }
   if (rv == NS_ERROR_INVALID_ARG) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
   if (NS_SUCCEEDED(rv)) {
     // Remember that we've set this header, so subsequent set operations will merge values.
     mAlreadySetHeaders.PutEntry(nsCString(header));
 
     // We'll want to duplicate this header for any replacement channels (eg. on redirect)
@@ -3441,19 +3446,23 @@ nsXMLHttpRequest::OnRedirectVerifyCallba
   if (NS_SUCCEEDED(result)) {
     mChannel = mNewRedirectChannel;
 
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
     if (httpChannel) {
       // Ensure all original headers are duplicated for the new channel (bug #553888)
       for (uint32_t i = mModifiedRequestHeaders.Length(); i > 0; ) {
         --i;
-        httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header,
-                                      mModifiedRequestHeaders[i].value,
-                                      false);
+        if (mModifiedRequestHeaders[i].value.IsEmpty()) {
+          httpChannel->SetEmptyRequestHeader(mModifiedRequestHeaders[i].header);
+        } else {
+          httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header,
+                                        mModifiedRequestHeaders[i].value,
+                                        false);
+        }
       }
     }
   } else {
     mErrorLoad = true;
   }
 
   mNewRedirectChannel = nullptr;
 
--- a/dom/broadcastchannel/BroadcastChannelChild.cpp
+++ b/dom/broadcastchannel/BroadcastChannelChild.cpp
@@ -6,17 +6,17 @@
 
 #include "BroadcastChannelChild.h"
 #include "BroadcastChannel.h"
 #include "jsapi.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
-#include "mozilla/dom/StructuredCloneHelper.h"
+#include "mozilla/dom/StructuredCloneIPCHelper.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "WorkerPrivate.h"
 
 namespace mozilla {
 
@@ -80,29 +80,27 @@ BroadcastChannelChild::RecvNotify(const 
     globalObject = workerPrivate->GlobalScope();
   }
 
   if (!globalObject || !jsapi.Init(globalObject)) {
     NS_WARNING("Failed to initialize AutoJSAPI object.");
     return true;
   }
 
-  JSContext* cx = jsapi.cx();
-  const SerializedStructuredCloneBuffer& buffer = aData.data();
-  StructuredCloneHelper cloneHelper(StructuredCloneHelper::CloningSupported,
-                                    StructuredCloneHelper::TransferringNotSupported,
-                                    StructuredCloneHelper::DifferentProcess);
-
+  StructuredCloneIPCHelper cloneHelper;
   cloneHelper.BlobImpls().AppendElements(blobs);
 
+  const SerializedStructuredCloneBuffer& buffer = aData.data();
+  cloneHelper.UseExternalData(buffer.data, buffer.dataLength);
+
+  JSContext* cx = jsapi.cx();
   JS::Rooted<JS::Value> value(cx, JS::NullValue());
   if (buffer.dataLength) {
     ErrorResult rv;
-    cloneHelper.ReadFromBuffer(mBC->GetParentObject(), cx,
-                               buffer.data, buffer.dataLength, &value, rv);
+    cloneHelper.Read(cx, &value, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return true;
     }
   }
 
   RootedDictionary<MessageEventInit> init(cx);
   init.mBubbles = false;
   init.mCancelable = false;
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -472,17 +472,21 @@ FetchDriver::HttpFetch(bool aCORSFlag, b
       FailWithNetworkError();
       return rv;
     }
 
     // Set the same headers.
     nsAutoTArray<InternalHeaders::Entry, 5> headers;
     mRequest->Headers()->GetEntries(headers);
     for (uint32_t i = 0; i < headers.Length(); ++i) {
-      httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */);
+      if (headers[i].mValue.IsEmpty()) {
+        httpChan->SetEmptyRequestHeader(headers[i].mName);
+      } else {
+        httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */);
+      }
     }
 
     // Step 2. Set the referrer.
     nsAutoString referrer;
     mRequest->GetReferrer(referrer);
     if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
       rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
                                                          mDocument,
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/ActorsChild.cpp
@@ -0,0 +1,743 @@
+/* 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 "ActorsChild.h"
+
+#include "BackgroundChildImpl.h"
+#include "FileHandleBase.h"
+#include "FileRequestBase.h"
+#include "js/Date.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "MutableFileBase.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsString.h"
+#include "xpcpublic.h"
+
+namespace mozilla {
+namespace dom {
+
+/*******************************************************************************
+ * Helpers
+ ******************************************************************************/
+
+namespace {
+
+class MOZ_STACK_CLASS AutoSetCurrentFileHandle final
+{
+  typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl;
+
+  FileHandleBase* const mFileHandle;
+  FileHandleBase* mPreviousFileHandle;
+  FileHandleBase** mThreadLocalSlot;
+
+public:
+  explicit AutoSetCurrentFileHandle(FileHandleBase* aFileHandle)
+    : mFileHandle(aFileHandle)
+    , mPreviousFileHandle(nullptr)
+    , mThreadLocalSlot(nullptr)
+  {
+    if (aFileHandle) {
+      BackgroundChildImpl::ThreadLocal* threadLocal =
+        BackgroundChildImpl::GetThreadLocalForCurrentThread();
+      MOZ_ASSERT(threadLocal);
+
+      // Hang onto this location for resetting later.
+      mThreadLocalSlot = &threadLocal->mCurrentFileHandle;
+
+      // Save the current value.
+      mPreviousFileHandle = *mThreadLocalSlot;
+
+      // Set the new value.
+      *mThreadLocalSlot = aFileHandle;
+    }
+  }
+
+  ~AutoSetCurrentFileHandle()
+  {
+    MOZ_ASSERT_IF(mThreadLocalSlot, mFileHandle);
+    MOZ_ASSERT_IF(mThreadLocalSlot, *mThreadLocalSlot == mFileHandle);
+
+    if (mThreadLocalSlot) {
+      // Reset old value.
+      *mThreadLocalSlot = mPreviousFileHandle;
+    }
+  }
+
+  FileHandleBase*
+  FileHandle() const
+  {
+    return mFileHandle;
+  }
+};
+
+class MOZ_STACK_CLASS ResultHelper final
+  : public FileRequestBase::ResultCallback
+{
+  FileRequestBase* mFileRequest;
+  AutoSetCurrentFileHandle mAutoFileHandle;
+
+  union
+  {
+    File* mFile;
+    const nsCString* mString;
+    const FileRequestMetadata* mMetadata;
+    const JS::Handle<JS::Value>* mJSValHandle;
+  } mResult;
+
+  enum
+  {
+    ResultTypeFile,
+    ResultTypeString,
+    ResultTypeMetadata,
+    ResultTypeJSValHandle,
+  } mResultType;
+
+public:
+  ResultHelper(FileRequestBase* aFileRequest,
+               FileHandleBase* aFileHandle,
+               File* aResult)
+    : mFileRequest(aFileRequest)
+    , mAutoFileHandle(aFileHandle)
+    , mResultType(ResultTypeFile)
+  {
+    MOZ_ASSERT(aFileRequest);
+    MOZ_ASSERT(aFileHandle);
+    MOZ_ASSERT(aResult);
+
+    mResult.mFile = aResult;
+  }
+
+  ResultHelper(FileRequestBase* aFileRequest,
+               FileHandleBase* aFileHandle,
+               const nsCString* aResult)
+    : mFileRequest(aFileRequest)
+    , mAutoFileHandle(aFileHandle)
+    , mResultType(ResultTypeString)
+  {
+    MOZ_ASSERT(aFileRequest);
+    MOZ_ASSERT(aFileHandle);
+    MOZ_ASSERT(aResult);
+
+    mResult.mString = aResult;
+  }
+
+  ResultHelper(FileRequestBase* aFileRequest,
+               FileHandleBase* aFileHandle,
+               const FileRequestMetadata* aResult)
+    : mFileRequest(aFileRequest)
+    , mAutoFileHandle(aFileHandle)
+    , mResultType(ResultTypeMetadata)
+  {
+    MOZ_ASSERT(aFileRequest);
+    MOZ_ASSERT(aFileHandle);
+    MOZ_ASSERT(aResult);
+
+    mResult.mMetadata = aResult;
+  }
+
+
+  ResultHelper(FileRequestBase* aFileRequest,
+               FileHandleBase* aFileHandle,
+               const JS::Handle<JS::Value>* aResult)
+    : mFileRequest(aFileRequest)
+    , mAutoFileHandle(aFileHandle)
+    , mResultType(ResultTypeJSValHandle)
+  {
+    MOZ_ASSERT(aFileRequest);
+    MOZ_ASSERT(aFileHandle);
+    MOZ_ASSERT(aResult);
+
+    mResult.mJSValHandle = aResult;
+  }
+
+  FileRequestBase*
+  FileRequest() const
+  {
+    return mFileRequest;
+  }
+
+  FileHandleBase*
+  FileHandle() const
+  {
+    return mAutoFileHandle.FileHandle();
+  }
+
+  virtual nsresult
+  GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) override
+  {
+    MOZ_ASSERT(aCx);
+    MOZ_ASSERT(mFileRequest);
+
+    switch (mResultType) {
+      case ResultTypeFile:
+        return GetResult(aCx, mResult.mFile, aResult);
+
+      case ResultTypeString:
+        return GetResult(aCx, mResult.mString, aResult);
+
+      case ResultTypeMetadata:
+        return GetResult(aCx, mResult.mMetadata, aResult);
+
+      case ResultTypeJSValHandle:
+        aResult.set(*mResult.mJSValHandle);
+        return NS_OK;
+
+      default:
+        MOZ_CRASH("Unknown result type!");
+    }
+
+    MOZ_CRASH("Should never get here!");
+  }
+
+private:
+  nsresult
+  GetResult(JSContext* aCx,
+            File* aFile,
+            JS::MutableHandle<JS::Value> aResult)
+  {
+    bool ok = GetOrCreateDOMReflector(aCx, aFile, aResult);
+    if (NS_WARN_IF(!ok)) {
+      return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+    }
+
+    return NS_OK;
+  }
+
+  nsresult
+  GetResult(JSContext* aCx,
+            const nsCString* aString,
+            JS::MutableHandle<JS::Value> aResult)
+  {
+    const nsCString& data = *aString;
+
+    nsresult rv;
+
+    if (!mFileRequest->HasEncoding()) {
+      JS::Rooted<JSObject*> arrayBuffer(aCx);
+      rv = nsContentUtils::CreateArrayBuffer(aCx, data, arrayBuffer.address());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+      }
+
+      aResult.setObject(*arrayBuffer);
+      return NS_OK;
+    }
+
+    nsAutoCString encoding;
+    // The BOM sniffing is baked into the "decode" part of the Encoding
+    // Standard, which the File API references.
+    if (!nsContentUtils::CheckForBOM(
+          reinterpret_cast<const unsigned char *>(data.get()),
+          data.Length(),
+          encoding)) {
+      // BOM sniffing failed. Try the API argument.
+      if (!EncodingUtils::FindEncodingForLabel(mFileRequest->GetEncoding(),
+                                               encoding)) {
+        // API argument failed. Since we are dealing with a file system file,
+        // we don't have a meaningful type attribute for the blob available,
+        // so proceeding to the next step, which is defaulting to UTF-8.
+        encoding.AssignLiteral("UTF-8");
+      }
+    }
+
+    nsString tmpString;
+    rv = nsContentUtils::ConvertStringFromEncoding(encoding, data, tmpString);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+    }
+
+    if (NS_WARN_IF(!xpc::StringToJsval(aCx, tmpString, aResult))) {
+      return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+    }
+
+    return NS_OK;
+  }
+
+  nsresult
+  GetResult(JSContext* aCx,
+            const FileRequestMetadata* aMetadata,
+            JS::MutableHandle<JS::Value> aResult)
+  {
+    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+    if (NS_WARN_IF(!obj)) {
+      return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+    }
+
+    const FileRequestSize& size = aMetadata->size();
+    if (size.type() != FileRequestSize::Tvoid_t) {
+      MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t);
+
+      JS::Rooted<JS::Value> number(aCx, JS_NumberValue(size.get_uint64_t()));
+
+      if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "size", number, 0))) {
+        return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+      }
+    }
+
+    const FileRequestLastModified& lastModified = aMetadata->lastModified();
+    if (lastModified.type() != FileRequestLastModified::Tvoid_t) {
+      MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t);
+
+      JS::Rooted<JSObject*> date(aCx,
+        JS::NewDateObject(aCx, JS::TimeClip(lastModified.get_int64_t())));
+      if (NS_WARN_IF(!date)) {
+        return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+      }
+
+      if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModified", date, 0))) {
+        return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+      }
+    }
+
+    aResult.setObject(*obj);
+    return NS_OK;
+  }
+};
+
+already_AddRefed<File>
+ConvertActorToFile(FileHandleBase* aFileHandle,
+                   const FileRequestGetFileResponse& aResponse)
+{
+  auto* actor = static_cast<BlobChild*>(aResponse.fileChild());
+
+  MutableFileBase* mutableFile = aFileHandle->MutableFile();
+  MOZ_ASSERT(mutableFile);
+
+  const FileRequestMetadata& metadata = aResponse.metadata();
+
+  const FileRequestSize& size = metadata.size();
+  MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t);
+
+  const FileRequestLastModified& lastModified = metadata.lastModified();
+  MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t);
+
+  actor->SetMysteryBlobInfo(mutableFile->Name(),
+                            mutableFile->Type(),
+                            size.get_uint64_t(),
+                            lastModified.get_int64_t(),
+                            BlobDirState::eUnknownIfDir);
+
+  nsRefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
+  MOZ_ASSERT(blobImpl);
+
+  nsRefPtr<File> file = mutableFile->CreateFileFor(blobImpl, aFileHandle);
+  return file.forget();
+}
+
+void
+HandleSuccess(ResultHelper* aResultHelper)
+{
+  MOZ_ASSERT(aResultHelper);
+
+  nsRefPtr<FileRequestBase> fileRequest = aResultHelper->FileRequest();
+  MOZ_ASSERT(fileRequest);
+  fileRequest->AssertIsOnOwningThread();
+
+  nsRefPtr<FileHandleBase> fileHandle = aResultHelper->FileHandle();
+  MOZ_ASSERT(fileHandle);
+
+  if (fileHandle->IsAborted()) {
+    fileRequest->SetError(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR);
+    return;
+  }
+
+  MOZ_ASSERT(fileHandle->IsOpen());
+
+  fileRequest->SetResultCallback(aResultHelper);
+
+  MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted());
+}
+
+void
+HandleError(FileRequestBase* aFileRequest,
+            nsresult aErrorCode,
+            FileHandleBase* aFileHandle)
+{
+  MOZ_ASSERT(aFileRequest);
+  aFileRequest->AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_FAILED(aErrorCode));
+  MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_FILEHANDLE);
+  MOZ_ASSERT(aFileHandle);
+
+  nsRefPtr<FileRequestBase> fileRequest = aFileRequest;
+  nsRefPtr<FileHandleBase> fileHandle = aFileHandle;
+
+  AutoSetCurrentFileHandle ascfh(aFileHandle);
+
+  fileRequest->SetError(aErrorCode);
+
+  MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted());
+}
+
+} // anonymous namespace
+
+/*******************************************************************************
+ * BackgroundMutableFileChildBase
+ ******************************************************************************/
+
+BackgroundMutableFileChildBase::BackgroundMutableFileChildBase(
+                                             DEBUGONLY(PRThread* aOwningThread))
+  : ThreadObject(DEBUGONLY(aOwningThread))
+  , mMutableFile(nullptr)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(BackgroundMutableFileChildBase);
+}
+
+BackgroundMutableFileChildBase::~BackgroundMutableFileChildBase()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(BackgroundMutableFileChildBase);
+}
+
+void
+BackgroundMutableFileChildBase::EnsureDOMObject()
+{
+  AssertIsOnOwningThread();
+
+  if (mTemporaryStrongMutableFile) {
+    return;
+  }
+
+  mTemporaryStrongMutableFile = CreateMutableFile();
+
+  MOZ_ASSERT(mTemporaryStrongMutableFile);
+  mTemporaryStrongMutableFile->AssertIsOnOwningThread();
+
+  mMutableFile = mTemporaryStrongMutableFile;
+}
+
+void
+BackgroundMutableFileChildBase::ReleaseDOMObject()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mTemporaryStrongMutableFile);
+  mTemporaryStrongMutableFile->AssertIsOnOwningThread();
+  MOZ_ASSERT(mMutableFile == mTemporaryStrongMutableFile);
+
+  mTemporaryStrongMutableFile = nullptr;
+}
+
+void
+BackgroundMutableFileChildBase::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mTemporaryStrongMutableFile);
+
+  if (mMutableFile) {
+    mMutableFile->ClearBackgroundActor();
+    mMutableFile = nullptr;
+
+    MOZ_ALWAYS_TRUE(PBackgroundMutableFileChild::SendDeleteMe());
+  }
+}
+
+void
+BackgroundMutableFileChildBase::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  if (mMutableFile) {
+    mMutableFile->ClearBackgroundActor();
+    DEBUGONLY(mMutableFile = nullptr;)
+  }
+}
+
+PBackgroundFileHandleChild*
+BackgroundMutableFileChildBase::AllocPBackgroundFileHandleChild(
+                                                          const FileMode& aMode)
+{
+  MOZ_CRASH("PBackgroundFileHandleChild actors should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundMutableFileChildBase::DeallocPBackgroundFileHandleChild(
+                                             PBackgroundFileHandleChild* aActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundFileHandleChild*>(aActor);
+  return true;
+}
+
+/*******************************************************************************
+ * BackgroundFileHandleChild
+ ******************************************************************************/
+
+BackgroundFileHandleChild::BackgroundFileHandleChild(
+                                             DEBUGONLY(PRThread* aOwningThread,)
+                                             FileHandleBase* aFileHandle)
+  : ThreadObject(DEBUGONLY(aOwningThread))
+  , mTemporaryStrongFileHandle(aFileHandle)
+  , mFileHandle(aFileHandle)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileHandle);
+  aFileHandle->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(BackgroundFileHandleChild);
+}
+
+BackgroundFileHandleChild::~BackgroundFileHandleChild()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(BackgroundFileHandleChild);
+}
+
+void
+BackgroundFileHandleChild::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+
+  if (mFileHandle) {
+    NoteActorDestroyed();
+
+    MOZ_ALWAYS_TRUE(PBackgroundFileHandleChild::SendDeleteMe());
+  }
+}
+
+void
+BackgroundFileHandleChild::NoteActorDestroyed()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT_IF(mTemporaryStrongFileHandle, mFileHandle);
+
+  if (mFileHandle) {
+    mFileHandle->ClearBackgroundActor();
+
+    // Normally this would be DEBUG-only but NoteActorDestroyed is also called
+    // from SendDeleteMeInternal. In that case we're going to receive an actual
+    // ActorDestroy call later and we don't want to touch a dead object.
+    mTemporaryStrongFileHandle = nullptr;
+    mFileHandle = nullptr;
+  }
+}
+
+void
+BackgroundFileHandleChild::NoteComplete()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT_IF(mFileHandle, mTemporaryStrongFileHandle);
+
+  mTemporaryStrongFileHandle = nullptr;
+}
+
+void
+BackgroundFileHandleChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  NoteActorDestroyed();
+}
+
+bool
+BackgroundFileHandleChild::RecvComplete(const bool& aAborted)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileHandle);
+
+  mFileHandle->HandleCompleteOrAbort(aAborted);
+
+  NoteComplete();
+  return true;
+}
+
+PBackgroundFileRequestChild*
+BackgroundFileHandleChild::AllocPBackgroundFileRequestChild(
+                                               const FileRequestParams& aParams)
+{
+  MOZ_CRASH("PBackgroundFileRequestChild actors should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundFileHandleChild::DeallocPBackgroundFileRequestChild(
+                                            PBackgroundFileRequestChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundFileRequestChild*>(aActor);
+  return true;
+}
+
+/*******************************************************************************
+ * BackgroundFileRequestChild
+ ******************************************************************************/
+
+BackgroundFileRequestChild::BackgroundFileRequestChild(
+                                             DEBUGONLY(PRThread* aOwningThread,)
+                                             FileRequestBase* aFileRequest)
+  : ThreadObject(DEBUGONLY(aOwningThread))
+  , mFileRequest(aFileRequest)
+  , mFileHandle(aFileRequest->FileHandle())
+  , mActorDestroyed(false)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileRequest);
+  aFileRequest->AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileHandle);
+  mFileHandle->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(BackgroundFileRequestChild);
+}
+
+BackgroundFileRequestChild::~BackgroundFileRequestChild()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mFileHandle);
+
+  MOZ_COUNT_DTOR(BackgroundFileRequestChild);
+}
+
+void
+BackgroundFileRequestChild::HandleResponse(nsresult aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_FAILED(aResponse));
+  MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_FILEHANDLE);
+  MOZ_ASSERT(mFileHandle);
+
+  HandleError(mFileRequest, aResponse, mFileHandle);
+}
+
+void
+BackgroundFileRequestChild::HandleResponse(
+                                    const FileRequestGetFileResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  nsRefPtr<File> file = ConvertActorToFile(mFileHandle, aResponse);
+
+  ResultHelper helper(mFileRequest, mFileHandle, file);
+
+  HandleSuccess(&helper);
+}
+
+void
+BackgroundFileRequestChild::HandleResponse(const nsCString& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  ResultHelper helper(mFileRequest, mFileHandle, &aResponse);
+
+  HandleSuccess(&helper);
+}
+
+void
+BackgroundFileRequestChild::HandleResponse(const FileRequestMetadata& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  ResultHelper helper(mFileRequest, mFileHandle, &aResponse);
+
+  HandleSuccess(&helper);
+}
+
+void
+BackgroundFileRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse)
+{
+  AssertIsOnOwningThread();
+
+  ResultHelper helper(mFileRequest, mFileHandle, &aResponse);
+
+  HandleSuccess(&helper);
+}
+
+void
+BackgroundFileRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_ASSERT(!mActorDestroyed);
+
+  mActorDestroyed = true;
+
+  if (mFileHandle) {
+    mFileHandle->AssertIsOnOwningThread();
+
+    mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */
+                                   aWhy == Deletion);
+
+    DEBUGONLY(mFileHandle = nullptr;)
+  }
+}
+
+bool
+BackgroundFileRequestChild::Recv__delete__(const FileRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileRequest);
+  MOZ_ASSERT(mFileHandle);
+
+  if (mFileHandle->IsAborted()) {
+    // Always handle an "error" with ABORT_ERR if the file handle was aborted,
+    // even if the request succeeded or failed with another error.
+    HandleResponse(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR);
+  } else {
+    switch (aResponse.type()) {
+      case FileRequestResponse::Tnsresult:
+        HandleResponse(aResponse.get_nsresult());
+        break;
+
+      case FileRequestResponse::TFileRequestGetFileResponse:
+        HandleResponse(aResponse.get_FileRequestGetFileResponse());
+        break;
+
+      case FileRequestResponse::TFileRequestReadResponse:
+        HandleResponse(aResponse.get_FileRequestReadResponse().data());
+        break;
+
+      case FileRequestResponse::TFileRequestWriteResponse:
+        HandleResponse(JS::UndefinedHandleValue);
+        break;
+
+      case FileRequestResponse::TFileRequestTruncateResponse:
+        HandleResponse(JS::UndefinedHandleValue);
+        break;
+
+      case FileRequestResponse::TFileRequestFlushResponse:
+        HandleResponse(JS::UndefinedHandleValue);
+        break;
+
+      case FileRequestResponse::TFileRequestGetMetadataResponse:
+        HandleResponse(aResponse.get_FileRequestGetMetadataResponse()
+                                .metadata());
+        break;
+
+      default:
+        MOZ_CRASH("Unknown response type!");
+    }
+  }
+
+  mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true);
+
+  // Null this out so that we don't try to call OnRequestFinished() again in
+  // ActorDestroy.
+  mFileHandle = nullptr;
+
+  return true;
+}
+
+bool
+BackgroundFileRequestChild::RecvProgress(const uint64_t& aProgress,
+                                         const uint64_t& aProgressMax)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileRequest);
+
+  mFileRequest->OnProgress(aProgress, aProgressMax);
+
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/ActorsChild.h
@@ -0,0 +1,175 @@
+/* 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_filehandle_ActorsChild_h
+#define mozilla_dom_filehandle_ActorsChild_h
+
+#include "js/RootingAPI.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/FileHandleCommon.h"
+#include "mozilla/dom/PBackgroundFileHandleChild.h"
+#include "mozilla/dom/PBackgroundFileRequestChild.h"
+#include "mozilla/dom/PBackgroundMutableFileChild.h"
+#include "nsAutoPtr.h"
+
+class nsCString;
+
+namespace mozilla {
+namespace dom {
+
+class FileHandleBase;
+class FileRequestBase;
+class MutableFileBase;
+
+class BackgroundMutableFileChildBase
+  : public ThreadObject
+  , public PBackgroundMutableFileChild
+{
+protected:
+  friend class MutableFileBase;
+
+  nsRefPtr<MutableFileBase> mTemporaryStrongMutableFile;
+  MutableFileBase* mMutableFile;
+
+public:
+  void
+  EnsureDOMObject();
+
+  MutableFileBase*
+  GetDOMObject() const
+  {
+    AssertIsOnOwningThread();
+    return mMutableFile;
+  }
+
+  void
+  ReleaseDOMObject();
+
+protected:
+  BackgroundMutableFileChildBase(DEBUGONLY(PRThread* aOwningThread));
+
+  ~BackgroundMutableFileChildBase();
+
+  void
+  SendDeleteMeInternal();
+
+  virtual already_AddRefed<MutableFileBase>
+  CreateMutableFile() = 0;
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual PBackgroundFileHandleChild*
+  AllocPBackgroundFileHandleChild(const FileMode& aMode) override;
+
+  virtual bool
+  DeallocPBackgroundFileHandleChild(PBackgroundFileHandleChild* aActor)
+                                    override;
+
+  bool
+  SendDeleteMe() = delete;
+};
+
+class BackgroundFileHandleChild
+  : public ThreadObject
+  , public PBackgroundFileHandleChild
+{
+  friend class BackgroundMutableFileChildBase;
+  friend class MutableFileBase;
+
+  // mTemporaryStrongFileHandle is strong and is only valid until the end of
+  // NoteComplete() member function or until the NoteActorDestroyed() member
+  // function is called.
+  nsRefPtr<FileHandleBase> mTemporaryStrongFileHandle;
+
+  // mFileHandle is weak and is valid until the NoteActorDestroyed() member
+  // function is called.
+  FileHandleBase* mFileHandle;
+
+public:
+  BackgroundFileHandleChild(DEBUGONLY(PRThread* aOwningThread,)
+                            FileHandleBase* aFileHandle);
+
+  void
+  SendDeleteMeInternal();
+
+private:
+  ~BackgroundFileHandleChild();
+
+  void
+  NoteActorDestroyed();
+
+  void
+  NoteComplete();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  bool
+  RecvComplete(const bool& aAborted) override;
+
+  virtual PBackgroundFileRequestChild*
+  AllocPBackgroundFileRequestChild(const FileRequestParams& aParams)
+                                   override;
+
+  virtual bool
+  DeallocPBackgroundFileRequestChild(PBackgroundFileRequestChild* aActor)
+                                     override;
+
+  bool
+  SendDeleteMe() = delete;
+};
+
+class BackgroundFileRequestChild final
+  : public ThreadObject
+  , public PBackgroundFileRequestChild
+{
+  friend class BackgroundFileHandleChild;
+  friend class FileHandleBase;
+
+  nsRefPtr<FileRequestBase> mFileRequest;
+  nsRefPtr<FileHandleBase> mFileHandle;
+  bool mActorDestroyed;
+
+private:
+  // Only created by FileHandleBase.
+  BackgroundFileRequestChild(DEBUGONLY(PRThread* aOwningThread,)
+                             FileRequestBase* aFileRequest);
+
+  // Only destroyed by BackgroundFileHandleChild.
+  ~BackgroundFileRequestChild();
+
+  void
+  HandleResponse(nsresult aResponse);
+
+  void
+  HandleResponse(const FileRequestGetFileResponse& aResponse);
+
+  void
+  HandleResponse(const nsCString& aResponse);
+
+  void
+  HandleResponse(const FileRequestMetadata& aResponse);
+
+  void
+  HandleResponse(JS::Handle<JS::Value> aResponse);
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual bool
+  Recv__delete__(const FileRequestResponse& aResponse) override;
+
+  virtual bool
+  RecvProgress(const uint64_t& aProgress,
+               const uint64_t& aProgressMax) override;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_filehandle_ActorsChild_h
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/ActorsParent.cpp
@@ -0,0 +1,2664 @@
+/* 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 "ActorsParent.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/unused.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/FileHandleCommon.h"
+#include "mozilla/dom/PBackgroundFileHandleParent.h"
+#include "mozilla/dom/PBackgroundFileRequestParent.h"
+#include "mozilla/dom/indexedDB/ActorsParent.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsIEventTarget.h"
+#include "nsIFileStreams.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIRunnable.h"
+#include "nsISeekableStream.h"
+#include "nsIThread.h"
+#include "nsIThreadPool.h"
+#include "nsNetUtil.h"
+#include "nsStreamUtils.h"
+#include "nsStringStream.h"
+#include "nsTArray.h"
+#include "nsThreadPool.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOMCIDInternal.h"
+
+#define DISABLE_ASSERTS_FOR_FUZZING 0
+
+#if DISABLE_ASSERTS_FOR_FUZZING
+#define ASSERT_UNLESS_FUZZING(...) do { } while (0)
+#else
+#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
+#endif
+
+namespace mozilla {
+namespace dom {
+
+using namespace mozilla::ipc;
+
+namespace {
+
+/******************************************************************************
+ * Constants
+ ******************************************************************************/
+
+const uint32_t kThreadLimit = 5;
+const uint32_t kIdleThreadLimit = 1;
+const uint32_t kIdleThreadTimeoutMs = 30000;
+
+const uint32_t kStreamCopyBlockSize = 32768;
+
+} // namespace
+
+class FileHandleThreadPool::FileHandleQueue final
+  : public nsRunnable
+{
+  friend class FileHandleThreadPool;
+
+  nsRefPtr<FileHandleThreadPool> mOwningFileHandleThreadPool;
+  nsRefPtr<FileHandle> mFileHandle;
+  nsTArray<nsRefPtr<FileHandleOp>> mQueue;
+  nsRefPtr<FileHandleOp> mCurrentOp;
+  bool mShouldFinish;
+
+public:
+  explicit
+  FileHandleQueue(FileHandleThreadPool* aFileHandleThreadPool,
+                  FileHandle* aFileHandle);
+
+  void
+  Enqueue(FileHandleOp* aFileHandleOp);
+
+  void
+  Finish();
+
+  void
+  ProcessQueue();
+
+private:
+  ~FileHandleQueue() {}
+
+  NS_DECL_NSIRUNNABLE
+};
+
+struct FileHandleThreadPool::DelayedEnqueueInfo
+{
+  nsRefPtr<FileHandle> mFileHandle;
+  nsRefPtr<FileHandleOp> mFileHandleOp;
+  bool mFinish;
+};
+
+class FileHandleThreadPool::DirectoryInfo
+{
+  friend class FileHandleThreadPool;
+
+  nsRefPtr<FileHandleThreadPool> mOwningFileHandleThreadPool;
+  nsTArray<nsRefPtr<FileHandleQueue>> mFileHandleQueues;
+  nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos;
+  nsTHashtable<nsStringHashKey> mFilesReading;
+  nsTHashtable<nsStringHashKey> mFilesWriting;
+
+public:
+  FileHandleQueue*
+  CreateFileHandleQueue(FileHandle* aFileHandle);
+
+  FileHandleQueue*
+  GetFileHandleQueue(FileHandle* aFileHandle);
+
+  void
+  RemoveFileHandleQueue(FileHandle* aFileHandle);
+
+  bool
+  HasRunningFileHandles()
+  {
+    return !mFileHandleQueues.IsEmpty();
+  }
+
+  DelayedEnqueueInfo*
+  CreateDelayedEnqueueInfo(FileHandle* aFileHandle,
+                           FileHandleOp* aFileHandleOp,
+                           bool aFinish);
+
+  void
+  LockFileForReading(const nsAString& aFileName)
+  {
+    mFilesReading.PutEntry(aFileName);
+  }
+
+  void
+  LockFileForWriting(const nsAString& aFileName)
+  {
+    mFilesWriting.PutEntry(aFileName);
+  }
+
+  bool
+  IsFileLockedForReading(const nsAString& aFileName)
+  {
+    return mFilesReading.Contains(aFileName);
+  }
+
+  bool
+  IsFileLockedForWriting(const nsAString& aFileName)
+  {
+    return mFilesWriting.Contains(aFileName);
+  }
+
+private:
+  explicit DirectoryInfo(FileHandleThreadPool* aFileHandleThreadPool)
+    : mOwningFileHandleThreadPool(aFileHandleThreadPool)
+  { }
+};
+
+struct FileHandleThreadPool::StoragesCompleteCallback final
+{
+  friend class nsAutoPtr<StoragesCompleteCallback>;
+
+  nsTArray<nsCString> mDirectoryIds;
+  nsCOMPtr<nsIRunnable> mCallback;
+
+  StoragesCompleteCallback(nsTArray<nsCString>&& aDatabaseIds,
+                           nsIRunnable* aCallback);
+
+private:
+  ~StoragesCompleteCallback();
+};
+
+/******************************************************************************
+ * Actor class declarations
+ ******************************************************************************/
+
+class FileHandle
+  : public PBackgroundFileHandleParent
+{
+  friend class BackgroundMutableFileParentBase;
+
+  class FinishOp;
+
+  nsRefPtr<BackgroundMutableFileParentBase> mMutableFile;
+  nsCOMPtr<nsISupports> mStream;
+  uint64_t mActiveRequestCount;
+  FileHandleStorage mStorage;
+  Atomic<bool> mInvalidatedOnAnyThread;
+  FileMode mMode;
+  bool mHasBeenActive;
+  bool mActorDestroyed;
+  bool mInvalidated;
+  bool mAborted;
+  bool mFinishOrAbortReceived;
+  bool mFinishedOrAborted;
+  bool mForceAborted;
+
+  DEBUGONLY(nsCOMPtr<nsIEventTarget> mThreadPoolEventTarget;)
+
+public:
+  void
+  AssertIsOnThreadPool() const;
+
+  bool
+  IsActorDestroyed() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mActorDestroyed;
+  }
+
+  // Must be called on the background thread.
+  bool
+  IsInvalidated() const
+  {
+    MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
+    MOZ_ASSERT_IF(mInvalidated, mAborted);
+
+    return mInvalidated;
+  }
+
+  // May be called on any thread, but is more expensive than IsInvalidated().
+  bool
+  IsInvalidatedOnAnyThread() const
+  {
+    return mInvalidatedOnAnyThread;
+  }
+
+  void
+  SetActive()
+  {
+    AssertIsOnBackgroundThread();
+
+    mHasBeenActive = true;
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::FileHandle)
+
+  nsresult
+  GetOrCreateStream(nsISupports** aStream);
+
+  void
+  Abort(bool aForce);
+
+  FileHandleStorage
+  Storage() const
+  {
+    return mStorage;
+  }
+
+  FileMode
+  Mode() const
+  {
+    return mMode;
+  }
+
+  BackgroundMutableFileParentBase*
+  GetMutableFile() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mMutableFile);
+
+    return mMutableFile;
+  }
+
+  bool
+  IsAborted() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mAborted;
+  }
+
+  PBackgroundParent*
+  GetBackgroundParent() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(!IsActorDestroyed());
+
+    return GetMutableFile()->GetBackgroundParent();
+  }
+
+  void
+  NoteActiveRequest();
+
+  void
+  NoteFinishedRequest();
+
+  void
+  Invalidate();
+
+private:
+  // This constructor is only called by BackgroundMutableFileParentBase.
+  FileHandle(BackgroundMutableFileParentBase* aMutableFile,
+             FileMode aMode);
+
+  // Reference counted.
+  ~FileHandle();
+
+  void
+  MaybeFinishOrAbort()
+  {
+    AssertIsOnBackgroundThread();
+
+    // If we've already finished or aborted then there's nothing else to do.
+    if (mFinishedOrAborted) {
+      return;
+    }
+
+    // If there are active requests then we have to wait for those requests to
+    // complete (see NoteFinishedRequest).
+    if (mActiveRequestCount) {
+      return;
+    }
+
+    // If we haven't yet received a finish or abort message then there could be
+    // additional requests coming so we should wait unless we're being forced to
+    // abort.
+    if (!mFinishOrAbortReceived && !mForceAborted) {
+      return;
+    }
+
+    FinishOrAbort();
+  }
+
+  void
+  SendCompleteNotification(bool aAborted);
+
+  bool
+  VerifyRequestParams(const FileRequestParams& aParams) const;
+
+  bool
+  VerifyRequestData(const FileRequestData& aData) const;
+
+  void
+  FinishOrAbort();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual bool
+  RecvDeleteMe() override;
+
+  virtual bool
+  RecvFinish() override;
+
+  virtual bool
+  RecvAbort() override;
+
+  virtual PBackgroundFileRequestParent*
+  AllocPBackgroundFileRequestParent(const FileRequestParams& aParams) override;
+
+  virtual bool
+  RecvPBackgroundFileRequestConstructor(PBackgroundFileRequestParent* aActor,
+                                        const FileRequestParams& aParams)
+                                        override;
+
+  virtual bool
+  DeallocPBackgroundFileRequestParent(PBackgroundFileRequestParent* aActor)
+                                      override;
+};
+
+class FileHandleOp
+{
+protected:
+  nsCOMPtr<nsIEventTarget> mOwningThread;
+  nsRefPtr<FileHandle> mFileHandle;
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileHandleOp)
+
+  void
+  AssertIsOnOwningThread() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mOwningThread);
+    DebugOnly<bool> current;
+    MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)));
+    MOZ_ASSERT(current);
+  }
+
+  nsIEventTarget*
+  OwningThread() const
+  {
+    return mOwningThread;
+  }
+
+  void
+  AssertIsOnThreadPool() const
+  {
+    MOZ_ASSERT(mFileHandle);
+    mFileHandle->AssertIsOnThreadPool();
+  }
+
+  void
+  Enqueue();
+
+  virtual void
+  RunOnThreadPool() = 0;
+
+  virtual void
+  RunOnOwningThread() = 0;
+
+protected:
+  FileHandleOp(FileHandle* aFileHandle)
+    : mOwningThread(NS_GetCurrentThread())
+    , mFileHandle(aFileHandle)
+  {
+    AssertIsOnOwningThread();
+    MOZ_ASSERT(aFileHandle);
+  }
+
+  virtual
+  ~FileHandleOp()
+  { }
+};
+
+class FileHandle::FinishOp
+  : public FileHandleOp
+{
+  friend class FileHandle;
+
+  bool mAborted;
+
+private:
+  FinishOp(FileHandle* aFileHandle,
+           bool aAborted)
+    : FileHandleOp(aFileHandle)
+    , mAborted(aAborted)
+  {
+    MOZ_ASSERT(aFileHandle);
+  }
+
+  ~FinishOp()
+  { }
+
+  virtual void
+  RunOnThreadPool() override;
+
+  virtual void
+  RunOnOwningThread() override;
+};
+
+class NormalFileHandleOp
+  : public FileHandleOp
+  , public PBackgroundFileRequestParent
+{
+  nsresult mResultCode;
+  Atomic<bool> mOperationMayProceed;
+  bool mActorDestroyed;
+  const bool mFileHandleIsAborted;
+
+  DEBUGONLY(bool mResponseSent;)
+
+protected:
+  nsCOMPtr<nsISupports> mFileStream;
+
+public:
+  void
+  NoteActorDestroyed()
+  {
+    AssertIsOnOwningThread();
+
+    mActorDestroyed = true;
+    mOperationMayProceed = false;
+  }
+
+  bool
+  IsActorDestroyed() const
+  {
+    AssertIsOnOwningThread();
+
+    return mActorDestroyed;
+  }
+
+  // May be called on any thread, but you should call IsActorDestroyed() if
+  // you know you're on the background thread because it is slightly faster.
+  bool
+  OperationMayProceed() const
+  {
+    return mOperationMayProceed;
+  }
+
+  // May be overridden by subclasses if they need to perform work on the
+  // background thread before being enqueued. Returning false will kill the
+  // child actors and prevent enqueue.
+  virtual bool
+  Init(FileHandle* aFileHandle);
+
+  // This callback will be called on the background thread before releasing the
+  // final reference to this request object. Subclasses may perform any
+  // additional cleanup here but must always call the base class implementation.
+  virtual void
+  Cleanup();
+
+protected:
+  NormalFileHandleOp(FileHandle* aFileHandle)
+    : FileHandleOp(aFileHandle)
+    , mResultCode(NS_OK)
+    , mOperationMayProceed(true)
+    , mActorDestroyed(false)
+    , mFileHandleIsAborted(aFileHandle->IsAborted())
+    DEBUGONLY(, mResponseSent(false))
+  {
+    MOZ_ASSERT(aFileHandle);
+  }
+
+  virtual
+  ~NormalFileHandleOp();
+
+  // Must be overridden in subclasses. Called on the target thread to allow the
+  // subclass to perform necessary file operations. A successful return value
+  // will trigger a SendSuccessResult callback on the background thread while
+  // a failure value will trigger a SendFailureResult callback.
+  virtual nsresult
+  DoFileWork(FileHandle* aFileHandle) = 0;
+
+  // Subclasses use this override to set the IPDL response value.
+  virtual void
+  GetResponse(FileRequestResponse& aResponse) = 0;
+
+private:
+  nsresult
+  SendSuccessResult();
+
+  bool
+  SendFailureResult(nsresult aResultCode);
+
+  virtual void
+  RunOnThreadPool() override;
+
+  virtual void
+  RunOnOwningThread() override;
+
+  // IPDL methods.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+};
+
+class CopyFileHandleOp
+  : public NormalFileHandleOp
+{
+  class ProgressRunnable;
+
+protected:
+  nsCOMPtr<nsISupports> mBufferStream;
+
+  uint64_t mOffset;
+  uint64_t mSize;
+
+  bool mRead;
+
+protected:
+  CopyFileHandleOp(FileHandle* aFileHandle)
+    : NormalFileHandleOp(aFileHandle)
+    , mOffset(0)
+    , mSize(0)
+    , mRead(true)
+  { }
+
+  virtual nsresult
+  DoFileWork(FileHandle* aFileHandle) override;
+
+  virtual void
+  Cleanup() override;
+};
+
+class CopyFileHandleOp::ProgressRunnable final
+  : public nsRunnable
+{
+  nsRefPtr<CopyFileHandleOp> mCopyFileHandleOp;
+  uint64_t mProgress;
+  uint64_t mProgressMax;
+
+public:
+  ProgressRunnable(CopyFileHandleOp* aCopyFileHandleOp,
+                   uint64_t aProgress,
+                   uint64_t aProgressMax)
+    : mCopyFileHandleOp(aCopyFileHandleOp)
+    , mProgress(aProgress)
+    , mProgressMax(aProgressMax)
+  { }
+
+private:
+  ~ProgressRunnable() {}
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class GetMetadataOp
+  : public NormalFileHandleOp
+{
+  friend class FileHandle;
+
+  const FileRequestGetMetadataParams mParams;
+
+protected:
+  FileRequestMetadata mMetadata;
+
+protected:
+  // Only created by FileHandle.
+  GetMetadataOp(FileHandle* aFileHandle,
+                const FileRequestParams& aParams);
+
+  ~GetMetadataOp()
+  { }
+
+  virtual nsresult
+  DoFileWork(FileHandle* aFileHandle) override;
+
+  virtual void
+  GetResponse(FileRequestResponse& aResponse) override;
+};
+
+class ReadOp final
+  : public CopyFileHandleOp
+{
+  friend class FileHandle;
+
+  class MemoryOutputStream;
+
+  const FileRequestReadParams mParams;
+
+private:
+  // Only created by FileHandle.
+  ReadOp(FileHandle* aFileHandle,
+         const FileRequestParams& aParams);
+
+  ~ReadOp()
+  { }
+
+  virtual bool
+  Init(FileHandle* aFileHandle) override;
+
+  virtual void
+  GetResponse(FileRequestResponse& aResponse) override;
+};
+
+class ReadOp::MemoryOutputStream final
+  : public nsIOutputStream
+{
+  nsCString mData;
+  uint64_t mOffset;
+
+public:
+  static already_AddRefed<MemoryOutputStream>
+  Create(uint64_t aSize);
+
+  const nsCString&
+  Data() const
+  {
+    return mData;
+  }
+
+private:
+  MemoryOutputStream()
+  : mOffset(0)
+  { }
+
+  virtual ~MemoryOutputStream()
+  { }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIOUTPUTSTREAM
+};
+
+class WriteOp final
+  : public CopyFileHandleOp
+{
+  friend class FileHandle;
+
+  const FileRequestWriteParams mParams;
+
+private:
+  // Only created by FileHandle.
+  WriteOp(FileHandle* aFileHandle,
+          const FileRequestParams& aParams);
+
+  ~WriteOp()
+  { }
+
+  virtual bool
+  Init(FileHandle* aFileHandle) override;
+
+  virtual void
+  GetResponse(FileRequestResponse& aResponse) override;
+};
+
+class TruncateOp final
+  : public NormalFileHandleOp
+{
+  friend class FileHandle;
+
+  const FileRequestTruncateParams mParams;
+
+private:
+  // Only created by FileHandle.
+  TruncateOp(FileHandle* aFileHandle,
+                        const FileRequestParams& aParams);
+
+  ~TruncateOp()
+  { }
+
+  virtual nsresult
+  DoFileWork(FileHandle* aFileHandle) override;
+
+  virtual void
+  GetResponse(FileRequestResponse& aResponse) override;
+};
+
+class FlushOp final
+  : public NormalFileHandleOp
+{
+  friend class FileHandle;
+
+  const FileRequestFlushParams mParams;
+
+private:
+  // Only created by FileHandle.
+  FlushOp(FileHandle* aFileHandle,
+          const FileRequestParams& aParams);
+
+  ~FlushOp()
+  { }
+
+  virtual nsresult
+  DoFileWork(FileHandle* aFileHandle) override;
+
+  virtual void
+  GetResponse(FileRequestResponse& aResponse) override;
+};
+
+class GetFileOp final
+  : public GetMetadataOp
+{
+  friend class FileHandle;
+
+  PBackgroundParent* mBackgroundParent;
+
+private:
+  // Only created by FileHandle.
+  GetFileOp(FileHandle* aFileHandle,
+            const FileRequestParams& aParams);
+
+  ~GetFileOp()
+  { }
+
+  virtual void
+  GetResponse(FileRequestResponse& aResponse) override;
+};
+
+namespace {
+
+/*******************************************************************************
+ * Helper Functions
+ ******************************************************************************/
+
+FileHandleThreadPool*
+GetFileHandleThreadPoolFor(FileHandleStorage aStorage)
+{
+  switch (aStorage) {
+    case FILE_HANDLE_STORAGE_IDB:
+      return mozilla::dom::indexedDB::GetFileHandleThreadPool();
+
+    default:
+      MOZ_CRASH("Bad file handle storage value!");
+  }
+}
+
+} // namespace
+
+/*******************************************************************************
+ * FileHandleThreadPool implementation
+ ******************************************************************************/
+
+FileHandleThreadPool::FileHandleThreadPool()
+  : mOwningThread(NS_GetCurrentThread())
+  , mShutdownRequested(false)
+  , mShutdownComplete(false)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mOwningThread);
+  AssertIsOnOwningThread();
+}
+
+FileHandleThreadPool::~FileHandleThreadPool()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mDirectoryInfos.Count());
+  MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
+  MOZ_ASSERT(mShutdownRequested);
+  MOZ_ASSERT(mShutdownComplete);
+}
+
+// static
+already_AddRefed<FileHandleThreadPool>
+FileHandleThreadPool::Create()
+{
+  AssertIsOnBackgroundThread();
+
+  nsRefPtr<FileHandleThreadPool> fileHandleThreadPool =
+    new FileHandleThreadPool();
+  fileHandleThreadPool->AssertIsOnOwningThread();
+
+  if (NS_WARN_IF(NS_FAILED(fileHandleThreadPool->Init()))) {
+    return nullptr;
+  }
+
+  return fileHandleThreadPool.forget();
+}
+
+#ifdef DEBUG
+
+void
+FileHandleThreadPool::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+
+  bool current;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)));
+  MOZ_ASSERT(current);
+}
+
+nsIEventTarget*
+FileHandleThreadPool::GetThreadPoolEventTarget() const
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mThreadPool);
+
+  return mThreadPool;
+}
+
+#endif // DEBUG
+
+void
+FileHandleThreadPool::Enqueue(FileHandle* aFileHandle,
+                              FileHandleOp* aFileHandleOp,
+                              bool aFinish)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileHandle);
+  MOZ_ASSERT(!mShutdownRequested);
+
+  BackgroundMutableFileParentBase* mutableFile = aFileHandle->GetMutableFile();
+
+  const nsACString& directoryId = mutableFile->DirectoryId();
+  const nsAString& fileName = mutableFile->FileName();
+  bool modeIsWrite = aFileHandle->Mode() == FileMode::Readwrite;
+
+  DirectoryInfo* directoryInfo;
+  if (!mDirectoryInfos.Get(directoryId, &directoryInfo)) {
+    nsAutoPtr<DirectoryInfo> newDirectoryInfo(new DirectoryInfo(this));
+
+    mDirectoryInfos.Put(directoryId, newDirectoryInfo);
+
+    directoryInfo = newDirectoryInfo.forget();
+  }
+
+  FileHandleQueue* existingFileHandleQueue =
+    directoryInfo->GetFileHandleQueue(aFileHandle);
+
+  if (existingFileHandleQueue) {
+    existingFileHandleQueue->Enqueue(aFileHandleOp);
+    if (aFinish) {
+      existingFileHandleQueue->Finish();
+    }
+    return;
+  }
+
+  bool lockedForReading = directoryInfo->IsFileLockedForReading(fileName);
+  bool lockedForWriting = directoryInfo->IsFileLockedForWriting(fileName);
+
+  if (modeIsWrite) {
+    if (!lockedForWriting) {
+      directoryInfo->LockFileForWriting(fileName);
+    }
+  }
+  else {
+    if (!lockedForReading) {
+      directoryInfo->LockFileForReading(fileName);
+    }
+  }
+
+  if (lockedForWriting || (lockedForReading && modeIsWrite)) {
+    directoryInfo->CreateDelayedEnqueueInfo(aFileHandle,
+                                            aFileHandleOp,
+                                            aFinish);
+  }
+  else {
+    FileHandleQueue* fileHandleQueue =
+      directoryInfo->CreateFileHandleQueue(aFileHandle);
+
+    if (aFileHandleOp) {
+      fileHandleQueue->Enqueue(aFileHandleOp);
+      if (aFinish) {
+        existingFileHandleQueue->Finish();
+      }
+    }
+  }
+}
+
+void
+FileHandleThreadPool::WaitForDirectoriesToComplete(
+                                             nsTArray<nsCString>&& aDirectoryIds,
+                                             nsIRunnable* aCallback)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!aDirectoryIds.IsEmpty());
+  MOZ_ASSERT(aCallback);
+
+  nsAutoPtr<StoragesCompleteCallback> callback(
+    new StoragesCompleteCallback(Move(aDirectoryIds), aCallback));
+
+  if (!MaybeFireCallback(callback)) {
+    mCompleteCallbacks.AppendElement(callback.forget());
+  }
+}
+
+void
+FileHandleThreadPool::Shutdown()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mShutdownRequested);
+  MOZ_ASSERT(!mShutdownComplete);
+
+  mShutdownRequested = true;
+
+  if (!mThreadPool) {
+    MOZ_ASSERT(!mDirectoryInfos.Count());
+    MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
+
+    mShutdownComplete = true;
+    return;
+  }
+
+  if (!mDirectoryInfos.Count()) {
+    Cleanup();
+
+    MOZ_ASSERT(mShutdownComplete);
+    return;
+  }
+
+  nsIThread* currentThread = NS_GetCurrentThread();
+  MOZ_ASSERT(currentThread);
+
+  while (!mShutdownComplete) {
+    MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
+  }
+}
+
+nsresult
+FileHandleThreadPool::Init()
+{
+  AssertIsOnOwningThread();
+
+  mThreadPool = new nsThreadPool();
+
+  nsresult rv = mThreadPool->SetName(NS_LITERAL_CSTRING("FileHandles"));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mThreadPool->SetThreadLimit(kThreadLimit);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+FileHandleThreadPool::Cleanup()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mThreadPool);
+  MOZ_ASSERT(mShutdownRequested);
+  MOZ_ASSERT(!mShutdownComplete);
+  MOZ_ASSERT(!mDirectoryInfos.Count());
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mThreadPool->Shutdown()));
+
+  if (!mCompleteCallbacks.IsEmpty()) {
+    // Run all callbacks manually now.
+    for (uint32_t count = mCompleteCallbacks.Length(), index = 0;
+         index < count;
+         index++) {
+      nsAutoPtr<StoragesCompleteCallback> completeCallback(
+        mCompleteCallbacks[index].forget());
+      MOZ_ASSERT(completeCallback);
+      MOZ_ASSERT(completeCallback->mCallback);
+
+      unused << completeCallback->mCallback->Run();
+    }
+
+    mCompleteCallbacks.Clear();
+
+    // And make sure they get processed.
+    nsIThread* currentThread = NS_GetCurrentThread();
+    MOZ_ASSERT(currentThread);
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProcessPendingEvents(currentThread)));
+  }
+
+  mShutdownComplete = true;
+}
+
+void
+FileHandleThreadPool::FinishFileHandle(FileHandle* aFileHandle)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileHandle);
+
+  BackgroundMutableFileParentBase* mutableFile = aFileHandle->GetMutableFile();
+  const nsACString& directoryId = mutableFile->DirectoryId();
+
+  DirectoryInfo* directoryInfo;
+  if (!mDirectoryInfos.Get(directoryId, &directoryInfo)) {
+    NS_ERROR("We don't know anyting about this directory?!");
+    return;
+  }
+
+  directoryInfo->RemoveFileHandleQueue(aFileHandle);
+
+  if (!directoryInfo->HasRunningFileHandles()) {
+    mDirectoryInfos.Remove(directoryId);
+
+    // See if we need to fire any complete callbacks.
+    uint32_t index = 0;
+    while (index < mCompleteCallbacks.Length()) {
+      if (MaybeFireCallback(mCompleteCallbacks[index])) {
+        mCompleteCallbacks.RemoveElementAt(index);
+      }
+      else {
+        index++;
+      }
+    }
+
+    if (mShutdownRequested && !mDirectoryInfos.Count()) {
+      Cleanup();
+    }
+  }
+}
+
+bool
+FileHandleThreadPool::MaybeFireCallback(StoragesCompleteCallback* aCallback)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aCallback);
+  MOZ_ASSERT(!aCallback->mDirectoryIds.IsEmpty());
+  MOZ_ASSERT(aCallback->mCallback);
+
+  for (uint32_t count = aCallback->mDirectoryIds.Length(), index = 0;
+       index < count;
+       index++) {
+    const nsCString& directoryId = aCallback->mDirectoryIds[index];
+    MOZ_ASSERT(!directoryId.IsEmpty());
+
+    if (mDirectoryInfos.Get(directoryId, nullptr)) {
+      return false;
+    }
+  }
+
+  aCallback->mCallback->Run();
+  return true;
+}
+
+FileHandleThreadPool::
+FileHandleQueue::FileHandleQueue(FileHandleThreadPool* aFileHandleThreadPool,
+                                 FileHandle* aFileHandle)
+  : mOwningFileHandleThreadPool(aFileHandleThreadPool)
+  , mFileHandle(aFileHandle)
+  , mShouldFinish(false)
+{
+  MOZ_ASSERT(aFileHandleThreadPool);
+  aFileHandleThreadPool->AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileHandle);
+}
+
+void
+FileHandleThreadPool::
+FileHandleQueue::Enqueue(FileHandleOp* aFileHandleOp)
+{
+  MOZ_ASSERT(!mShouldFinish, "Enqueue called after Finish!");
+
+  mQueue.AppendElement(aFileHandleOp);
+
+  ProcessQueue();
+}
+
+void
+FileHandleThreadPool::
+FileHandleQueue::Finish()
+{
+  MOZ_ASSERT(!mShouldFinish, "Finish called more than once!");
+
+  mShouldFinish = true;
+}
+
+void
+FileHandleThreadPool::
+FileHandleQueue::ProcessQueue()
+{
+  if (mCurrentOp) {
+    return;
+  }
+
+  if (mQueue.IsEmpty()) {
+    if (mShouldFinish) {
+      mOwningFileHandleThreadPool->FinishFileHandle(mFileHandle);
+
+      // Make sure this is released on this thread.
+      mOwningFileHandleThreadPool = nullptr;
+    }
+
+    return;
+  }
+
+  mCurrentOp = mQueue[0];
+  mQueue.RemoveElementAt(0);
+
+  nsCOMPtr<nsIThreadPool> threadPool = mOwningFileHandleThreadPool->mThreadPool;
+  MOZ_ASSERT(threadPool);
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(threadPool->Dispatch(this, NS_DISPATCH_NORMAL)));
+}
+
+NS_IMETHODIMP
+FileHandleThreadPool::
+FileHandleQueue::Run()
+{
+  MOZ_ASSERT(mCurrentOp);
+
+  if (IsOnBackgroundThread()) {
+    nsRefPtr<FileHandleOp> currentOp;
+
+    mCurrentOp.swap(currentOp);
+    ProcessQueue();
+
+    currentOp->RunOnOwningThread();
+  } else {
+    mCurrentOp->RunOnThreadPool();
+
+    nsCOMPtr<nsIEventTarget> backgroundThread = mCurrentOp->OwningThread();
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      backgroundThread->Dispatch(this, NS_DISPATCH_NORMAL)));
+  }
+
+  return NS_OK;
+}
+
+auto
+FileHandleThreadPool::
+DirectoryInfo::CreateFileHandleQueue(FileHandle* aFileHandle)
+  -> FileHandleQueue*
+{
+  nsRefPtr<FileHandleQueue>* fileHandleQueue =
+    mFileHandleQueues.AppendElement();
+  *fileHandleQueue = new FileHandleQueue(mOwningFileHandleThreadPool,
+                                         aFileHandle);
+  return fileHandleQueue->get();
+}
+
+auto
+FileHandleThreadPool::
+DirectoryInfo::GetFileHandleQueue(FileHandle* aFileHandle) -> FileHandleQueue*
+{
+  uint32_t count = mFileHandleQueues.Length();
+  for (uint32_t index = 0; index < count; index++) {
+    nsRefPtr<FileHandleQueue>& fileHandleQueue = mFileHandleQueues[index];
+    if (fileHandleQueue->mFileHandle == aFileHandle) {
+      return fileHandleQueue;
+    }
+  }
+  return nullptr;
+}
+
+void
+FileHandleThreadPool::
+DirectoryInfo::RemoveFileHandleQueue(FileHandle* aFileHandle)
+{
+  for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
+    if (mDelayedEnqueueInfos[index].mFileHandle == aFileHandle) {
+      MOZ_ASSERT(!mDelayedEnqueueInfos[index].mFileHandleOp, "Should be null!");
+      mDelayedEnqueueInfos.RemoveElementAt(index);
+      return;
+    }
+  }
+
+  uint32_t fileHandleCount = mFileHandleQueues.Length();
+
+  // We can't just remove entries from lock hash tables, we have to rebuild
+  // them instead. Multiple FileHandle objects may lock the same file
+  // (one entry can represent multiple locks).
+
+  mFilesReading.Clear();
+  mFilesWriting.Clear();
+
+  for (uint32_t index = 0, count = fileHandleCount; index < count; index++) {
+    FileHandle* fileHandle = mFileHandleQueues[index]->mFileHandle;
+    if (fileHandle == aFileHandle) {
+      MOZ_ASSERT(count == fileHandleCount, "More than one match?!");
+
+      mFileHandleQueues.RemoveElementAt(index);
+      index--;
+      count--;
+
+      continue;
+    }
+
+    const nsAString& fileName = fileHandle->GetMutableFile()->FileName();
+
+    if (fileHandle->Mode() == FileMode::Readwrite) {
+      if (!IsFileLockedForWriting(fileName)) {
+        LockFileForWriting(fileName);
+      }
+    }
+    else {
+      if (!IsFileLockedForReading(fileName)) {
+        LockFileForReading(fileName);
+      }
+    }
+  }
+
+  MOZ_ASSERT(mFileHandleQueues.Length() == fileHandleCount - 1,
+             "Didn't find the file handle we were looking for!");
+
+  nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos;
+  delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos);
+
+  for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) {
+    DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
+    mOwningFileHandleThreadPool->Enqueue(delayedEnqueueInfo.mFileHandle,
+                                         delayedEnqueueInfo.mFileHandleOp,
+                                         delayedEnqueueInfo.mFinish);
+  }
+}
+
+auto
+FileHandleThreadPool::
+DirectoryInfo::CreateDelayedEnqueueInfo(FileHandle* aFileHandle,
+                                        FileHandleOp* aFileHandleOp,
+                                        bool aFinish) -> DelayedEnqueueInfo*
+{
+  DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
+  info->mFileHandle = aFileHandle;
+  info->mFileHandleOp = aFileHandleOp;
+  info->mFinish = aFinish;
+  return info;
+}
+
+FileHandleThreadPool::
+StoragesCompleteCallback::StoragesCompleteCallback(
+                                             nsTArray<nsCString>&& aDirectoryIds,
+                                             nsIRunnable* aCallback)
+  : mDirectoryIds(Move(aDirectoryIds))
+  , mCallback(aCallback)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mDirectoryIds.IsEmpty());
+  MOZ_ASSERT(aCallback);
+
+  MOZ_COUNT_CTOR(FileHandleThreadPool::StoragesCompleteCallback);
+}
+
+FileHandleThreadPool::
+StoragesCompleteCallback::~StoragesCompleteCallback()
+{
+  AssertIsOnBackgroundThread();
+
+  MOZ_COUNT_DTOR(FileHandleThreadPool::StoragesCompleteCallback);
+}
+
+/*******************************************************************************
+ * BackgroundMutableFileParentBase
+ ******************************************************************************/
+
+BackgroundMutableFileParentBase::BackgroundMutableFileParentBase(
+                                                 FileHandleStorage aStorage,
+                                                 const nsACString& aDirectoryId,
+                                                 const nsAString& aFileName,
+                                                 nsIFile* aFile)
+  : mDirectoryId(aDirectoryId)
+  , mFileName(aFileName)
+  , mStorage(aStorage)
+  , mInvalidated(false)
+  , mActorWasAlive(false)
+  , mActorDestroyed(false)
+  , mFile(aFile)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aStorage != FILE_HANDLE_STORAGE_MAX);
+  MOZ_ASSERT(!aDirectoryId.IsEmpty());
+  MOZ_ASSERT(!aFileName.IsEmpty());
+  MOZ_ASSERT(aFile);
+}
+
+BackgroundMutableFileParentBase::~BackgroundMutableFileParentBase()
+{
+  MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
+}
+
+void
+BackgroundMutableFileParentBase::Invalidate()
+{
+  AssertIsOnBackgroundThread();
+
+  class MOZ_STACK_CLASS Helper final
+  {
+  public:
+    static bool
+    InvalidateFileHandles(nsTHashtable<nsPtrHashKey<FileHandle>>& aTable)
+    {
+      AssertIsOnBackgroundThread();
+
+      const uint32_t count = aTable.Count();
+      if (!count) {
+        return true;
+      }
+
+      FallibleTArray<nsRefPtr<FileHandle>> fileHandles;
+      if (NS_WARN_IF(!fileHandles.SetCapacity(count, fallible))) {
+        return false;
+      }
+
+      for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
+        if (NS_WARN_IF(!fileHandles.AppendElement(iter.Get()->GetKey(),
+                                                  fallible))) {
+          return false;
+        }
+      }
+
+      if (count) {
+        for (uint32_t index = 0; index < count; index++) {
+          nsRefPtr<FileHandle> fileHandle = fileHandles[index].forget();
+          MOZ_ASSERT(fileHandle);
+
+          fileHandle->Invalidate();
+        }
+      }
+
+      return true;
+    }
+  };
+
+  if (mInvalidated) {
+    return;
+  }
+
+  mInvalidated = true;
+
+  if (!Helper::InvalidateFileHandles(mFileHandles)) {
+    NS_WARNING("Failed to abort all file handles!");
+  }
+}
+
+bool
+BackgroundMutableFileParentBase::RegisterFileHandle(FileHandle* aFileHandle)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileHandle);
+  MOZ_ASSERT(!mFileHandles.GetEntry(aFileHandle));
+  MOZ_ASSERT(!mInvalidated);
+
+  if (NS_WARN_IF(!mFileHandles.PutEntry(aFileHandle, fallible))) {
+    return false;
+  }
+
+  if (mFileHandles.Count() == 1) {
+    NoteActiveState();
+  }
+
+  return true;
+}
+
+void
+BackgroundMutableFileParentBase::UnregisterFileHandle(FileHandle* aFileHandle)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileHandle);
+  MOZ_ASSERT(mFileHandles.GetEntry(aFileHandle));
+
+  mFileHandles.RemoveEntry(aFileHandle);
+
+  if (!mFileHandles.Count()) {
+    NoteInactiveState();
+  }
+}
+
+void
+BackgroundMutableFileParentBase::SetActorAlive()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mActorWasAlive);
+  MOZ_ASSERT(!mActorDestroyed);
+
+  mActorWasAlive = true;
+
+  // This reference will be absorbed by IPDL and released when the actor is
+  // destroyed.
+  AddRef();
+}
+
+already_AddRefed<nsISupports>
+BackgroundMutableFileParentBase::CreateStream(bool aReadOnly)
+{
+  AssertIsOnBackgroundThread();
+
+  nsresult rv;
+
+  if (aReadOnly) {
+    nsCOMPtr<nsIInputStream> stream;
+    rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1,
+                                    nsIFileInputStream::DEFER_OPEN);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+    return stream.forget();
+  }
+
+  nsCOMPtr<nsIFileStream> stream;
+  rv = NS_NewLocalFileStream(getter_AddRefs(stream), mFile, -1, -1,
+                             nsIFileStream::DEFER_OPEN);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+  return stream.forget();
+}
+
+void
+BackgroundMutableFileParentBase::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mActorDestroyed);
+
+  mActorDestroyed = true;
+
+  if (!IsInvalidated()) {
+    Invalidate();
+  }
+}
+
+PBackgroundFileHandleParent*
+BackgroundMutableFileParentBase::AllocPBackgroundFileHandleParent(
+                                                          const FileMode& aMode)
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(aMode != FileMode::Readonly &&
+                 aMode != FileMode::Readwrite)) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  nsRefPtr<FileHandle> fileHandle = new FileHandle(this, aMode);
+
+  return fileHandle.forget().take();
+}
+
+bool
+BackgroundMutableFileParentBase::RecvPBackgroundFileHandleConstructor(
+                                            PBackgroundFileHandleParent* aActor,
+                                            const FileMode& aMode)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite);
+
+  FileHandleThreadPool* fileHandleThreadPool =
+    GetFileHandleThreadPoolFor(mStorage);
+  MOZ_ASSERT(fileHandleThreadPool);
+
+  auto* fileHandle = static_cast<FileHandle*>(aActor);
+
+  // Add a placeholder for this file handle immediately.
+  fileHandleThreadPool->Enqueue(fileHandle, nullptr, false);
+
+  fileHandle->SetActive();
+
+  if (NS_WARN_IF(!RegisterFileHandle(fileHandle))) {
+    fileHandle->Abort(/* aForce */ false);
+    return true;
+  }
+
+  return true;
+}
+
+bool
+BackgroundMutableFileParentBase::DeallocPBackgroundFileHandleParent(
+                                            PBackgroundFileHandleParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  nsRefPtr<FileHandle> fileHandle =
+    dont_AddRef(static_cast<FileHandle*>(aActor));
+  return true;
+}
+
+bool
+BackgroundMutableFileParentBase::RecvDeleteMe()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mActorDestroyed);
+
+  return PBackgroundMutableFileParent::Send__delete__(this);
+}
+
+bool
+BackgroundMutableFileParentBase::RecvGetFileId(int64_t* aFileId)
+{
+  AssertIsOnBackgroundThread();
+
+  *aFileId = -1;
+  return true;
+}
+
+/*******************************************************************************
+ * FileHandle
+ ******************************************************************************/
+
+FileHandle::FileHandle(BackgroundMutableFileParentBase* aMutableFile,
+                       FileMode aMode)
+  : mMutableFile(aMutableFile)
+  , mActiveRequestCount(0)
+  , mStorage(aMutableFile->Storage())
+  , mInvalidatedOnAnyThread(false)
+  , mMode(aMode)
+  , mHasBeenActive(false)
+  , mActorDestroyed(false)
+  , mInvalidated(false)
+  , mAborted(false)
+  , mFinishOrAbortReceived(false)
+  , mFinishedOrAborted(false)
+  , mForceAborted(false)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aMutableFile);
+
+#ifdef DEBUG
+  FileHandleThreadPool* fileHandleThreadPool =
+    GetFileHandleThreadPoolFor(mStorage);
+  MOZ_ASSERT(fileHandleThreadPool);
+
+  mThreadPoolEventTarget = fileHandleThreadPool->GetThreadPoolEventTarget();
+#endif
+}
+
+FileHandle::~FileHandle()
+{
+  MOZ_ASSERT(!mActiveRequestCount);
+  MOZ_ASSERT(mActorDestroyed);
+  MOZ_ASSERT_IF(mHasBeenActive, mFinishedOrAborted);
+}
+
+void
+FileHandle::AssertIsOnThreadPool() const
+{
+  MOZ_ASSERT(mThreadPoolEventTarget);
+  DebugOnly<bool> current;
+  MOZ_ASSERT(NS_SUCCEEDED(mThreadPoolEventTarget->IsOnCurrentThread(&current)));
+  MOZ_ASSERT(current);
+}
+
+nsresult
+FileHandle::GetOrCreateStream(nsISupports** aStream)
+{
+  AssertIsOnBackgroundThread();
+
+  if (!mStream) {
+    nsCOMPtr<nsISupports> stream =
+      mMutableFile->CreateStream(mMode == FileMode::Readonly);
+    if (NS_WARN_IF(!stream)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    stream.swap(mStream);
+  }
+
+  nsCOMPtr<nsISupports> stream(mStream);
+  stream.forget(aStream);
+
+  return NS_OK;
+}
+
+void
+FileHandle::Abort(bool aForce)
+{
+  AssertIsOnBackgroundThread();
+
+  mAborted = true;
+
+  if (aForce) {
+    mForceAborted = true;
+  }
+
+  MaybeFinishOrAbort();
+}
+
+void
+FileHandle::NoteActiveRequest()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mActiveRequestCount < UINT64_MAX);
+
+  mActiveRequestCount++;
+}
+
+void
+FileHandle::NoteFinishedRequest()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mActiveRequestCount);
+
+  mActiveRequestCount--;
+
+  MaybeFinishOrAbort();
+}
+
+void
+FileHandle::Invalidate()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread);
+
+  if (!mInvalidated) {
+    mInvalidated = true;
+    mInvalidatedOnAnyThread = true;
+
+    Abort(/* aForce */ true);
+  }
+}
+
+void
+FileHandle::SendCompleteNotification(bool aAborted)
+{
+  AssertIsOnBackgroundThread();
+
+  if (!IsActorDestroyed()) {
+    unused << SendComplete(aAborted);
+  }
+}
+
+bool
+FileHandle::VerifyRequestParams(const FileRequestParams& aParams) const
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
+
+  switch (aParams.type()) {
+    case FileRequestParams::TFileRequestGetMetadataParams: {
+      const FileRequestGetMetadataParams& params =
+        aParams.get_FileRequestGetMetadataParams();
+
+      if (NS_WARN_IF(!params.size() && !params.lastModified())) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
+    case FileRequestParams::TFileRequestReadParams: {
+      const FileRequestReadParams& params =
+        aParams.get_FileRequestReadParams();
+
+      if (NS_WARN_IF(params.offset() == UINT64_MAX)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      if (NS_WARN_IF(!params.size())) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
+    case FileRequestParams::TFileRequestWriteParams: {
+      if (NS_WARN_IF(mMode != FileMode::Readwrite)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      const FileRequestWriteParams& params =
+        aParams.get_FileRequestWriteParams();
+
+
+      if (NS_WARN_IF(!params.dataLength())) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      if (NS_WARN_IF(!VerifyRequestData(params.data()))) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
+    case FileRequestParams::TFileRequestTruncateParams: {
+      if (NS_WARN_IF(mMode != FileMode::Readwrite)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      const FileRequestTruncateParams& params =
+        aParams.get_FileRequestTruncateParams();
+
+      if (NS_WARN_IF(params.offset() == UINT64_MAX)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
+    case FileRequestParams::TFileRequestFlushParams: {
+      if (NS_WARN_IF(mMode != FileMode::Readwrite)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
+    case FileRequestParams::TFileRequestGetFileParams: {
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  return true;
+}
+
+bool
+FileHandle::VerifyRequestData(const FileRequestData& aData) const
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aData.type() != FileRequestData::T__None);
+
+  switch (aData.type()) {
+    case FileRequestData::TFileRequestStringData: {
+      const FileRequestStringData& data =
+        aData.get_FileRequestStringData();
+
+      if (NS_WARN_IF(data.string().IsEmpty())) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
+    case FileRequestData::TFileRequestBlobData: {
+      const FileRequestBlobData& data =
+        aData.get_FileRequestBlobData();
+
+      if (NS_WARN_IF(data.blobChild())) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      if (NS_WARN_IF(!data.blobParent())) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  return true;
+}
+
+void
+FileHandle::FinishOrAbort()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mFinishedOrAborted);
+
+  mFinishedOrAborted = true;
+
+  if (!mHasBeenActive) {
+    return;
+  }
+
+  nsRefPtr<FinishOp> finishOp = new FinishOp(this, mAborted);
+
+  FileHandleThreadPool* fileHandleThreadPool =
+    GetFileHandleThreadPoolFor(mStorage);
+  MOZ_ASSERT(fileHandleThreadPool);
+
+  fileHandleThreadPool->Enqueue(this, finishOp, true);
+}
+
+void
+FileHandle::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+
+  MOZ_ASSERT(!mActorDestroyed);
+
+  mActorDestroyed = true;
+
+  if (!mFinishedOrAborted) {
+    mAborted = true;
+
+    mForceAborted = true;
+
+    MaybeFinishOrAbort();
+  }
+}
+
+bool
+FileHandle::RecvDeleteMe()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!IsActorDestroyed());
+
+  return PBackgroundFileHandleParent::Send__delete__(this);
+}
+
+bool
+FileHandle::RecvFinish()
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(mFinishOrAbortReceived)) {
+    ASSERT_UNLESS_FUZZING();
+    return false;
+  }
+
+  mFinishOrAbortReceived = true;
+
+  MaybeFinishOrAbort();
+  return true;
+}
+
+bool
+FileHandle::RecvAbort()
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(mFinishOrAbortReceived)) {
+    ASSERT_UNLESS_FUZZING();
+    return false;
+  }
+
+  mFinishOrAbortReceived = true;
+
+  Abort(/* aForce */ false);
+  return true;
+}
+
+PBackgroundFileRequestParent*
+FileHandle::AllocPBackgroundFileRequestParent(const FileRequestParams& aParams)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
+
+#ifdef DEBUG
+  // Always verify parameters in DEBUG builds!
+  bool trustParams = false;
+#else
+  PBackgroundParent* backgroundActor = GetBackgroundParent();
+  MOZ_ASSERT(backgroundActor);
+
+  bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
+#endif
+
+  if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  if (NS_WARN_IF(mFinishOrAbortReceived)) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  nsRefPtr<NormalFileHandleOp> actor;
+
+  switch (aParams.type()) {
+    case FileRequestParams::TFileRequestGetMetadataParams:
+      actor = new GetMetadataOp(this, aParams);
+      break;
+
+    case FileRequestParams::TFileRequestReadParams:
+      actor = new ReadOp(this, aParams);
+      break;
+
+    case FileRequestParams::TFileRequestWriteParams:
+      actor = new WriteOp(this, aParams);
+      break;
+
+    case FileRequestParams::TFileRequestTruncateParams:
+      actor = new TruncateOp(this, aParams);
+      break;
+
+    case FileRequestParams::TFileRequestFlushParams:
+      actor = new FlushOp(this, aParams);
+      break;
+
+    case FileRequestParams::TFileRequestGetFileParams:
+      actor = new GetFileOp(this, aParams);
+      break;
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  MOZ_ASSERT(actor);
+
+  // Transfer ownership to IPDL.
+  return actor.forget().take();
+}
+
+bool
+FileHandle::RecvPBackgroundFileRequestConstructor(
+                                           PBackgroundFileRequestParent* aActor,
+                                           const FileRequestParams& aParams)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
+
+  auto* op = static_cast<NormalFileHandleOp*>(aActor);
+
+  if (NS_WARN_IF(!op->Init(this))) {
+    op->Cleanup();
+    return false;
+  }
+
+  op->Enqueue();
+  return true;
+}
+
+bool
+FileHandle::DeallocPBackgroundFileRequestParent(
+                                           PBackgroundFileRequestParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  // Transfer ownership back from IPDL.
+  nsRefPtr<NormalFileHandleOp> actor =
+    dont_AddRef(static_cast<NormalFileHandleOp*>(aActor));
+  return true;
+}
+
+/*******************************************************************************
+ * Local class implementations
+ ******************************************************************************/
+
+void
+FileHandleOp::Enqueue()
+{
+  AssertIsOnOwningThread();
+
+  FileHandleThreadPool* fileHandleThreadPool =
+    GetFileHandleThreadPoolFor(mFileHandle->Storage());
+  MOZ_ASSERT(fileHandleThreadPool);
+
+  fileHandleThreadPool->Enqueue(mFileHandle, this, false);
+
+  mFileHandle->NoteActiveRequest();
+}
+
+void
+FileHandle::
+FinishOp::RunOnThreadPool()
+{
+  AssertIsOnThreadPool();
+  MOZ_ASSERT(mFileHandle);
+
+  nsCOMPtr<nsISupports>& stream = mFileHandle->mStream;
+
+  if (!stream) {
+    return;
+  }
+
+  nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(stream);
+  MOZ_ASSERT(inputStream);
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(inputStream->Close()));
+
+  stream = nullptr;
+}
+
+void
+FileHandle::
+FinishOp::RunOnOwningThread()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileHandle);
+
+  mFileHandle->SendCompleteNotification(mAborted);
+
+  mFileHandle->GetMutableFile()->UnregisterFileHandle(mFileHandle);
+
+  mFileHandle = nullptr;
+}
+
+NormalFileHandleOp::~NormalFileHandleOp()
+{
+  MOZ_ASSERT(!mFileHandle,
+             "NormalFileHandleOp::Cleanup() was not called by a subclass!");
+}
+
+bool
+NormalFileHandleOp::Init(FileHandle* aFileHandle)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileHandle);
+
+  nsresult rv = aFileHandle->GetOrCreateStream(getter_AddRefs(mFileStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+NormalFileHandleOp::Cleanup()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileHandle);
+  MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent);
+
+  mFileHandle = nullptr;
+}
+
+nsresult
+NormalFileHandleOp::SendSuccessResult()
+{
+  AssertIsOnOwningThread();
+
+  if (!IsActorDestroyed()) {
+    FileRequestResponse response;
+    GetResponse(response);
+
+    MOZ_ASSERT(response.type() != FileRequestResponse::T__None);
+
+    if (response.type() == FileRequestResponse::Tnsresult) {
+      MOZ_ASSERT(NS_FAILED(response.get_nsresult()));
+
+      return response.get_nsresult();
+    }
+
+    if (NS_WARN_IF(!PBackgroundFileRequestParent::Send__delete__(this,
+                                                                 response))) {
+      return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+    }
+  }
+
+  DEBUGONLY(mResponseSent = true;)
+
+  return NS_OK;
+}
+
+bool
+NormalFileHandleOp::SendFailureResult(nsresult aResultCode)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(NS_FAILED(aResultCode));
+
+  bool result = false;
+
+  if (!IsActorDestroyed()) {
+    result =
+      PBackgroundFileRequestParent::Send__delete__(this, aResultCode);
+  }
+
+  DEBUGONLY(mResponseSent = true;)
+
+  return result;
+}
+
+void
+NormalFileHandleOp::RunOnThreadPool()
+{
+  AssertIsOnThreadPool();
+  MOZ_ASSERT(mFileHandle);
+  MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
+
+  // There are several cases where we don't actually have to to any work here.
+
+  if (mFileHandleIsAborted) {
+    // This transaction is already set to be aborted.
+    mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
+  } else if (mFileHandle->IsInvalidatedOnAnyThread()) {
+    // This file handle is being invalidated.
+    mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+  } else if (!OperationMayProceed()) {
+    // The operation was canceled in some way, likely because the child process
+    // has crashed.
+    mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+  } else {
+    nsresult rv = DoFileWork(mFileHandle);
+    if (NS_FAILED(rv)) {
+      mResultCode = rv;
+    }
+  }
+}
+
+void
+NormalFileHandleOp::RunOnOwningThread()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileHandle);
+
+  if (NS_WARN_IF(IsActorDestroyed())) {
+    // Don't send any notifications if the actor was destroyed already.
+    if (NS_SUCCEEDED(mResultCode)) {
+      mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+    }
+  } else {
+    if (mFileHandle->IsInvalidated()) {
+      mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+    } else if (mFileHandle->IsAborted()) {
+      // Aborted file handles always see their requests fail with ABORT_ERR,
+      // even if the request succeeded or failed with another error.
+      mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
+    } else if (NS_SUCCEEDED(mResultCode)) {
+      // This may release the IPDL reference.
+      mResultCode = SendSuccessResult();
+    }
+
+    if (NS_FAILED(mResultCode)) {
+      // This should definitely release the IPDL reference.
+      if (!SendFailureResult(mResultCode)) {
+        // Abort the file handle.
+        mFileHandle->Abort(/* aForce */ false);
+      }
+    }
+  }
+
+  mFileHandle->NoteFinishedRequest();
+
+  Cleanup();
+}
+
+void
+NormalFileHandleOp::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  NoteActorDestroyed();
+}
+
+nsresult
+CopyFileHandleOp::DoFileWork(FileHandle* aFileHandle)
+{
+  AssertIsOnThreadPool();
+
+  nsCOMPtr<nsIInputStream> inputStream;
+  nsCOMPtr<nsIOutputStream> outputStream;
+
+  if (mRead) {
+    inputStream = do_QueryInterface(mFileStream);
+    outputStream = do_QueryInterface(mBufferStream);
+  } else {
+    inputStream = do_QueryInterface(mBufferStream);
+    outputStream = do_QueryInterface(mFileStream);
+  }
+
+  MOZ_ASSERT(inputStream);
+  MOZ_ASSERT(outputStream);
+
+  nsCOMPtr<nsISeekableStream> seekableStream =
+    do_QueryInterface(mFileStream);
+
+  nsresult rv;
+
+  if (seekableStream) {
+    if (mOffset == UINT64_MAX) {
+      rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
+    }
+    else {
+      rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  mOffset = 0;
+
+  do {
+    char copyBuffer[kStreamCopyBlockSize];
+
+    uint64_t max = mSize - mOffset;
+    if (max == 0) {
+      break;
+    }
+
+    uint32_t count = sizeof(copyBuffer);
+    if (count > max) {
+      count = max;
+    }
+
+    uint32_t numRead;
+    rv = inputStream->Read(copyBuffer, count, &numRead);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!numRead) {
+      break;
+    }
+
+    uint32_t numWrite;
+    rv = outputStream->Write(copyBuffer, numRead, &numWrite);
+    if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
+      rv = NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR;
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (NS_WARN_IF(numWrite != numRead)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    mOffset += numWrite;
+
+    nsCOMPtr<nsIRunnable> runnable =
+      new ProgressRunnable(this, mOffset, mSize);
+
+    mOwningThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  } while (true);
+
+  MOZ_ASSERT(mOffset == mSize);
+
+  if (mRead) {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(outputStream->Close()));
+  } else {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(inputStream->Close()));
+  }
+
+  return NS_OK;
+}
+
+void
+CopyFileHandleOp::Cleanup()
+{
+  AssertIsOnOwningThread();
+
+  mBufferStream = nullptr;
+
+  NormalFileHandleOp::Cleanup();
+}
+
+NS_IMETHODIMP
+CopyFileHandleOp::
+ProgressRunnable::Run()
+{
+  AssertIsOnBackgroundThread();
+
+  unused << mCopyFileHandleOp->SendProgress(mProgress, mProgressMax);
+
+  mCopyFileHandleOp = nullptr;
+
+  return NS_OK;
+}
+
+GetMetadataOp::GetMetadataOp(FileHandle* aFileHandle,
+                             const FileRequestParams& aParams)
+  : NormalFileHandleOp(aFileHandle)
+  , mParams(aParams.get_FileRequestGetMetadataParams())
+{
+  MOZ_ASSERT(aParams.type() ==
+             FileRequestParams::TFileRequestGetMetadataParams);
+}
+
+nsresult
+GetMetadataOp::DoFileWork(FileHandle* aFileHandle)
+{
+  AssertIsOnThreadPool();
+
+  nsresult rv;
+
+  if (mFileHandle->Mode() == FileMode::Readwrite) {
+    // Force a flush (so all pending writes are flushed to the disk and file
+    // metadata is updated too).
+
+    nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mFileStream);
+    MOZ_ASSERT(ostream);
+
+    rv = ostream->Flush();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  nsCOMPtr<nsIFileMetadata> metadata = do_QueryInterface(mFileStream);
+  MOZ_ASSERT(metadata);
+
+  if (mParams.size()) {
+    int64_t size;
+    rv = metadata->GetSize(&size);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (NS_WARN_IF(size < 0)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    mMetadata.size() = uint64_t(size);
+  } else {
+    mMetadata.size() = void_t();
+  }
+
+  if (mParams.lastModified()) {
+    int64_t lastModified;
+    rv = metadata->GetLastModified(&lastModified);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    mMetadata.lastModified() = lastModified;
+  } else {
+    mMetadata.lastModified() = void_t();
+  }
+
+  return NS_OK;
+}
+
+void
+GetMetadataOp::GetResponse(FileRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  aResponse = FileRequestGetMetadataResponse(mMetadata);
+}
+
+ReadOp::ReadOp(FileHandle* aFileHandle,
+               const FileRequestParams& aParams)
+  : CopyFileHandleOp(aFileHandle)
+  , mParams(aParams.get_FileRequestReadParams())
+{
+  MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestReadParams);
+}
+
+bool
+ReadOp::Init(FileHandle* aFileHandle)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileHandle);
+
+  if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle))) {
+    return false;
+  }
+
+  mBufferStream = MemoryOutputStream::Create(mParams.size());
+  if (NS_WARN_IF(!mBufferStream)) {
+    return false;
+  }
+
+  mOffset = mParams.offset();
+  mSize = mParams.size();
+  mRead = true;
+
+  return true;
+}
+
+void
+ReadOp::GetResponse(FileRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  auto* stream = static_cast<MemoryOutputStream*>(mBufferStream.get());
+
+  aResponse = FileRequestReadResponse(stream->Data());
+}
+
+// static
+already_AddRefed<ReadOp::MemoryOutputStream>
+ReadOp::
+MemoryOutputStream::Create(uint64_t aSize)
+{
+  MOZ_ASSERT(aSize, "Passed zero size!");
+
+  if (NS_WARN_IF(aSize > UINT32_MAX)) {
+    return nullptr;
+  }
+
+  nsRefPtr<MemoryOutputStream> stream = new MemoryOutputStream();
+
+  char* dummy;
+  uint32_t length = stream->mData.GetMutableData(&dummy, aSize, fallible);
+  if (NS_WARN_IF(length != aSize)) {
+    return nullptr;
+  }
+
+  return stream.forget();
+}
+
+NS_IMPL_ISUPPORTS(ReadOp::MemoryOutputStream, nsIOutputStream)
+
+NS_IMETHODIMP
+ReadOp::
+MemoryOutputStream::Close()
+{
+  mData.Truncate(mOffset);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ReadOp::
+MemoryOutputStream::Write(const char* aBuf, uint32_t aCount, uint32_t* _retval)
+{
+  return WriteSegments(NS_CopySegmentToBuffer, (char*)aBuf, aCount, _retval);
+}
+
+NS_IMETHODIMP
+ReadOp::
+MemoryOutputStream::Flush()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ReadOp::
+MemoryOutputStream::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
+                              uint32_t* _retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+ReadOp::
+MemoryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
+                                  uint32_t aCount, uint32_t* _retval)
+{
+  NS_ASSERTION(mData.Length() >= mOffset, "Bad stream state!");
+
+  uint32_t maxCount = mData.Length() - mOffset;
+  if (maxCount == 0) {
+    *_retval = 0;
+    return NS_OK;
+  }
+
+  if (aCount > maxCount) {
+    aCount = maxCount;
+  }
+
+  nsresult rv = aReader(this, aClosure, mData.BeginWriting() + mOffset, 0,
+                        aCount, _retval);
+  if (NS_SUCCEEDED(rv)) {
+    NS_ASSERTION(*_retval <= aCount,
+                 "Reader should not read more than we asked it to read!");
+    mOffset += *_retval;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ReadOp::
+MemoryOutputStream::IsNonBlocking(bool* _retval)
+{
+  *_retval = false;
+  return NS_OK;
+}
+
+WriteOp::WriteOp(FileHandle* aFileHandle,
+                 const FileRequestParams& aParams)
+  : CopyFileHandleOp(aFileHandle)
+  , mParams(aParams.get_FileRequestWriteParams())
+{
+  MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestWriteParams);
+}
+
+bool
+WriteOp::Init(FileHandle* aFileHandle)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileHandle);
+
+  if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle))) {
+    return false;
+  }
+
+  nsCOMPtr<nsIInputStream> inputStream;
+
+  const FileRequestData& data = mParams.data();
+  switch (data.type()) {
+    case FileRequestData::TFileRequestStringData: {
+      const FileRequestStringData& stringData =
+        data.get_FileRequestStringData();
+
+      const nsCString& string = stringData.string();
+
+      nsresult rv =
+        NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return false;
+      }
+
+      break;
+    }
+    case FileRequestData::TFileRequestBlobData: {
+      const FileRequestBlobData& blobData =
+        data.get_FileRequestBlobData();
+
+      auto blobActor = static_cast<BlobParent*>(blobData.blobParent());
+
+      nsRefPtr<BlobImpl> blobImpl = blobActor->GetBlobImpl();
+
+      ErrorResult rv;
+      blobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
+      if (NS_WARN_IF(rv.Failed())) {
+        return false;
+      }
+
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  mBufferStream = inputStream;
+  mOffset = mParams.offset();
+  mSize = mParams.dataLength();
+  mRead = false;
+
+  return true;
+}
+
+void
+WriteOp::GetResponse(FileRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  aResponse = FileRequestWriteResponse();
+}
+
+TruncateOp::TruncateOp(FileHandle* aFileHandle,
+                       const FileRequestParams& aParams)
+  : NormalFileHandleOp(aFileHandle)
+  , mParams(aParams.get_FileRequestTruncateParams())
+{
+  MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestTruncateParams);
+}
+
+nsresult
+TruncateOp::DoFileWork(FileHandle* aFileHandle)
+{
+  AssertIsOnThreadPool();
+
+  nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mFileStream);
+  MOZ_ASSERT(sstream);
+
+  nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mParams.offset());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = sstream->SetEOF();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+TruncateOp::GetResponse(FileRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  aResponse = FileRequestTruncateResponse();
+}
+
+FlushOp::FlushOp(FileHandle* aFileHandle,
+                 const FileRequestParams& aParams)
+  : NormalFileHandleOp(aFileHandle)
+  , mParams(aParams.get_FileRequestFlushParams())
+{
+  MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestFlushParams);
+}
+
+nsresult
+FlushOp::DoFileWork(FileHandle* aFileHandle)
+{
+  AssertIsOnThreadPool();
+
+  nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mFileStream);
+  MOZ_ASSERT(ostream);
+
+  nsresult rv = ostream->Flush();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+FlushOp::GetResponse(FileRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  aResponse = FileRequestFlushResponse();
+}
+
+GetFileOp::GetFileOp(FileHandle* aFileHandle,
+                     const FileRequestParams& aParams)
+  : GetMetadataOp(aFileHandle,
+                  FileRequestGetMetadataParams(true, true))
+  , mBackgroundParent(aFileHandle->GetBackgroundParent())
+{
+  MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestGetFileParams);
+  MOZ_ASSERT(mBackgroundParent);
+}
+
+void
+GetFileOp::GetResponse(FileRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  nsRefPtr<BlobImpl> blobImpl = mFileHandle->GetMutableFile()->CreateBlobImpl();
+  MOZ_ASSERT(blobImpl);
+
+  PBlobParent* actor =
+    BackgroundParent::GetOrCreateActorForBlobImpl(mBackgroundParent, blobImpl);
+  if (NS_WARN_IF(!actor)) {
+    // This can only fail if the child has crashed.
+    aResponse = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+    return;
+  }
+
+  FileRequestGetFileResponse response;
+  response.fileParent() = actor;
+  response.metadata() = mMetadata;
+
+  aResponse = response;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/ActorsParent.h
@@ -0,0 +1,221 @@
+/* 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_filehandle_ActorsParent_h
+#define mozilla_dom_filehandle_ActorsParent_h
+
+#include "mozilla/dom/FileHandleStorage.h"
+#include "mozilla/dom/PBackgroundMutableFileParent.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "nsAutoPtr.h"
+#include "nsClassHashtable.h"
+#include "nsCOMPtr.h"
+#include "nsHashKeys.h"
+#include "nsString.h"
+#include "nsTHashtable.h"
+
+template <class> struct already_AddRefed;
+class nsIFile;
+class nsIRunnable;
+class nsIThreadPool;
+template <class> class nsTArray;
+
+namespace mozilla {
+
+namespace ipc {
+
+class PBackgroundParent;
+
+} // namespace ipc
+
+namespace dom {
+
+class BlobImpl;
+class FileHandle;
+class FileHandleOp;
+
+class FileHandleThreadPool final
+{
+  class FileHandleQueue;
+  struct DelayedEnqueueInfo;
+  class DirectoryInfo;
+  struct StoragesCompleteCallback;
+
+  nsCOMPtr<nsIThreadPool> mThreadPool;
+  nsCOMPtr<nsIEventTarget> mOwningThread;
+
+  nsClassHashtable<nsCStringHashKey, DirectoryInfo> mDirectoryInfos;
+
+  nsTArray<nsAutoPtr<StoragesCompleteCallback>> mCompleteCallbacks;
+
+  bool mShutdownRequested;
+  bool mShutdownComplete;
+
+public:
+  static already_AddRefed<FileHandleThreadPool>
+  Create();
+
+#ifdef DEBUG
+  void
+  AssertIsOnOwningThread() const;
+
+  nsIEventTarget*
+  GetThreadPoolEventTarget() const;
+#else
+  void
+  AssertIsOnOwningThread() const
+  { }
+#endif
+
+  void
+  Enqueue(FileHandle* aFileHandle,
+          FileHandleOp* aFileHandleOp,
+          bool aFinish);
+
+  NS_INLINE_DECL_REFCOUNTING(FileHandleThreadPool)
+
+  void
+  WaitForDirectoriesToComplete(nsTArray<nsCString>&& aDirectoryIds,
+                               nsIRunnable* aCallback);
+
+  void
+  Shutdown();
+
+private:
+  FileHandleThreadPool();
+
+  // Reference counted.
+  ~FileHandleThreadPool();
+
+  nsresult
+  Init();
+
+  void
+  Cleanup();
+
+  void
+  FinishFileHandle(FileHandle* aFileHandle);
+
+  bool
+  MaybeFireCallback(StoragesCompleteCallback* aCallback);
+};
+
+class BackgroundMutableFileParentBase
+  : public PBackgroundMutableFileParent
+{
+  nsTHashtable<nsPtrHashKey<FileHandle>> mFileHandles;
+  nsCString mDirectoryId;
+  nsString mFileName;
+  FileHandleStorage mStorage;
+  bool mInvalidated;
+  bool mActorWasAlive;
+  bool mActorDestroyed;
+
+protected:
+  nsCOMPtr<nsIFile> mFile;
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BackgroundMutableFileParentBase)
+
+  void
+  Invalidate();
+
+  FileHandleStorage
+  Storage() const
+  {
+    return mStorage;
+  }
+
+  const nsCString&
+  DirectoryId() const
+  {
+    return mDirectoryId;
+  }
+
+  const nsString&
+  FileName() const
+  {
+    return mFileName;
+  }
+
+  bool
+  RegisterFileHandle(FileHandle* aFileHandle);
+
+  void
+  UnregisterFileHandle(FileHandle* aFileHandle);
+
+  void
+  SetActorAlive();
+
+  bool
+  IsActorDestroyed() const
+  {
+    mozilla::ipc::AssertIsOnBackgroundThread();
+
+    return mActorWasAlive && mActorDestroyed;
+  }
+
+  bool
+  IsInvalidated() const
+  {
+    mozilla::ipc::AssertIsOnBackgroundThread();
+
+    return mInvalidated;
+  }
+
+  virtual void
+  NoteActiveState()
+  { }
+
+  virtual void
+  NoteInactiveState()
+  { }
+
+  virtual mozilla::ipc::PBackgroundParent*
+  GetBackgroundParent() const = 0;
+
+  virtual already_AddRefed<nsISupports>
+  CreateStream(bool aReadOnly);
+
+  virtual already_AddRefed<BlobImpl>
+  CreateBlobImpl()
+  {
+    return nullptr;
+  }
+
+protected:
+  BackgroundMutableFileParentBase(FileHandleStorage aStorage,
+                                  const nsACString& aDirectoryId,
+                                  const nsAString& aFileName,
+                                  nsIFile* aFile);
+
+  // Reference counted.
+  ~BackgroundMutableFileParentBase();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual PBackgroundFileHandleParent*
+  AllocPBackgroundFileHandleParent(const FileMode& aMode) override;
+
+  virtual bool
+  RecvPBackgroundFileHandleConstructor(PBackgroundFileHandleParent* aActor,
+                                       const FileMode& aMode) override;
+
+  virtual bool
+  DeallocPBackgroundFileHandleParent(PBackgroundFileHandleParent* aActor)
+                                     override;
+
+  virtual bool
+  RecvDeleteMe() override;
+
+  virtual bool
+  RecvGetFileId(int64_t* aFileId) override;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_filehandle_ActorsParent_h
deleted file mode 100644
--- a/dom/filehandle/AsyncHelper.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/* -*- 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 "AsyncHelper.h"
-
-#include "FileService.h"
-#include "MainThreadUtils.h"
-#include "nsDebug.h"
-#include "nsIEventTarget.h"
-#include "nsIRequestObserver.h"
-#include "nsNetUtil.h"
-
-namespace mozilla {
-namespace dom {
-
-NS_IMPL_ISUPPORTS(AsyncHelper, nsIRunnable, nsIRequest)
-
-nsresult
-AsyncHelper::AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt)
-{
-  nsresult rv;
-
-  if (aObserver) {
-    // build proxy for observer events
-    rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), aObserver, aCtxt);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  FileService* service = FileService::GetOrCreate();
-  NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
-
-  nsIEventTarget* target = service->ThreadPoolTarget();
-
-  rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AsyncHelper::Run()
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  if (mObserver) {
-    mObserver->OnStartRequest(this, nullptr);
-  }
-
-  mStatus = DoStreamWork(mStream);
-
-  if (mObserver) {
-    mObserver->OnStopRequest(this, nullptr, mStatus);
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AsyncHelper::GetName(nsACString& aName)
-{
-  NS_WARNING("Shouldn't be called!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-AsyncHelper::IsPending(bool* _retval)
-{
-  NS_WARNING("Shouldn't be called!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-AsyncHelper::GetStatus(nsresult* aStatus)
-{
-  *aStatus = mStatus;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AsyncHelper::Cancel(nsresult aStatus)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AsyncHelper::Suspend()
-{
-  NS_WARNING("Shouldn't be called!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-AsyncHelper::Resume()
-{
-  NS_WARNING("Shouldn't be called!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-AsyncHelper::GetLoadGroup(nsILoadGroup** aLoadGroup)
-{
-  NS_WARNING("Shouldn't be called!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-AsyncHelper::SetLoadGroup(nsILoadGroup* aLoadGroup)
-{
-  NS_WARNING("Shouldn't be called!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-AsyncHelper::GetLoadFlags(nsLoadFlags* aLoadFlags)
-{
-  NS_WARNING("Shouldn't be called!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-AsyncHelper::SetLoadFlags(nsLoadFlags aLoadFlags)
-{
-  NS_WARNING("Shouldn't be called!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/filehandle/AsyncHelper.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_AsyncHelper_h
-#define mozilla_dom_AsyncHelper_h
-
-#include "nsCOMPtr.h"
-#include "nsIRequest.h"
-#include "nsIRunnable.h"
-
-class nsIRequestObserver;
-
-namespace mozilla {
-namespace dom {
-
-/**
- * Must be subclassed. The subclass must implement DoStreamWork.
- * Async operations that are not supported in necko (truncate, flush, etc.)
- * should use this helper. Call AsyncWork to invoke the operation.
- */
-class AsyncHelper : public nsIRunnable,
-                    public nsIRequest
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-  NS_DECL_NSIREQUEST
-
-  nsresult
-  AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt);
-
-protected:
-  explicit AsyncHelper(nsISupports* aStream)
-  : mStream(aStream),
-    mStatus(NS_OK)
-  { }
-
-  virtual ~AsyncHelper()
-  { }
-
-  virtual nsresult
-  DoStreamWork(nsISupports* aStream) = 0;
-
-private:
-  nsCOMPtr<nsISupports> mStream;
-  nsCOMPtr<nsIRequestObserver> mObserver;
-
-  nsresult mStatus;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_AsyncHelper_h
rename from dom/filehandle/FileHandle.cpp
rename to dom/filehandle/FileHandleBase.cpp
--- a/dom/filehandle/FileHandle.cpp
+++ b/dom/filehandle/FileHandleBase.cpp
@@ -1,360 +1,237 @@
 /* -*- 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 "FileHandle.h"
+#include "FileHandleBase.h"
 
-#include "AsyncHelper.h"
-#include "FileHelper.h"
-#include "FileRequest.h"
-#include "FileService.h"
-#include "FileStreamWrappers.h"
-#include "MemoryStreams.h"
-#include "mozilla/dom/EncodingUtils.h"
+#include "ActorsChild.h"
+#include "BackgroundChildImpl.h"
+#include "FileRequestBase.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ErrorResult.h"
 #include "mozilla/dom/File.h"
-#include "MutableFile.h"
-#include "nsContentUtils.h"
+#include "mozilla/dom/PBackgroundFileHandle.h"
+#include "mozilla/dom/UnionConversions.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "MutableFileBase.h"
+#include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsError.h"
-#include "nsIEventTarget.h"
-#include "nsISeekableStream.h"
-#include "nsNetUtil.h"
-#include "nsIAsyncStreamCopier.h"
 #include "nsString.h"
-#include "nsStringStream.h"
-#include "nsThreadUtils.h"
-#include "xpcpublic.h"
-
-#define STREAM_COPY_BLOCK_SIZE 32768
 
 namespace mozilla {
 namespace dom {
 
-namespace {
-
-class ReadHelper : public FileHelper
-{
-public:
-  ReadHelper(FileHandleBase* aFileHandle,
-             FileRequestBase* aFileRequest,
-             uint64_t aLocation,
-             uint64_t aSize)
-  : FileHelper(aFileHandle, aFileRequest),
-    mLocation(aLocation), mSize(aSize)
-  {
-    MOZ_ASSERT(mSize, "Passed zero size!");
-  }
-
-  nsresult
-  Init();
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream) override;
-
-  nsresult
-  GetSuccessResult(JSContext* aCx,
-                   JS::MutableHandle<JS::Value> aVal) override;
-
-protected:
-  uint64_t mLocation;
-  uint64_t mSize;
-
-  nsRefPtr<MemoryOutputStream> mStream;
-};
-
-class ReadTextHelper : public ReadHelper
-{
-public:
-  ReadTextHelper(FileHandleBase* aFileHandle,
-                 FileRequestBase* aFileRequest,
-                 uint64_t aLocation,
-                 uint64_t aSize,
-                 const nsAString& aEncoding)
-  : ReadHelper(aFileHandle, aFileRequest, aLocation, aSize),
-    mEncoding(aEncoding)
-  { }
-
-  nsresult
-  GetSuccessResult(JSContext* aCx,
-                   JS::MutableHandle<JS::Value> aVal) override;
-
-private:
-  nsString mEncoding;
-};
-
-class WriteHelper : public FileHelper
-{
-public:
-  WriteHelper(FileHandleBase* aFileHandle,
-              FileRequestBase* aFileRequest,
-              uint64_t aLocation,
-              nsIInputStream* aStream,
-              uint64_t aLength)
-  : FileHelper(aFileHandle, aFileRequest),
-    mLocation(aLocation), mStream(aStream), mLength(aLength)
-  {
-    MOZ_ASSERT(mLength, "Passed zero length!");
-  }
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream);
-
-private:
-  uint64_t mLocation;
-  nsCOMPtr<nsIInputStream> mStream;
-  uint64_t mLength;
-};
-
-class TruncateHelper : public FileHelper
-{
-public:
-  TruncateHelper(FileHandleBase* aFileHandle,
-                 FileRequestBase* aFileRequest,
-                 uint64_t aOffset)
-  : FileHelper(aFileHandle, aFileRequest),
-    mOffset(aOffset)
-  { }
+using namespace mozilla::ipc;
 
-  nsresult
-  DoAsyncRun(nsISupports* aStream);
-
-private:
-  class AsyncTruncator : public AsyncHelper
-  {
-  public:
-    AsyncTruncator(nsISupports* aStream, int64_t aOffset)
-    : AsyncHelper(aStream),
-      mOffset(aOffset)
-    { }
-  protected:
-    nsresult
-    DoStreamWork(nsISupports* aStream) override;
-
-    uint64_t mOffset;
-  };
-
-  uint64_t mOffset;
-};
-
-class FlushHelper : public FileHelper
+FileHandleBase::FileHandleBase(DEBUGONLY(PRThread* aOwningThread,)
+                               FileMode aMode)
+  : RefCountedThreadObject(DEBUGONLY(aOwningThread))
+  , mBackgroundActor(nullptr)
+  , mLocation(0)
+  , mPendingRequestCount(0)
+  , mReadyState(INITIAL)
+  , mMode(aMode)
+  , mAborted(false)
+  , mCreating(false)
+  DEBUGONLY(, mSentFinishOrAbort(false))
+  DEBUGONLY(, mFiredCompleteOrAbort(false))
 {
-public:
-  FlushHelper(FileHandleBase* aFileHandle,
-              FileRequestBase* aFileRequest)
-  : FileHelper(aFileHandle, aFileRequest)
-  { }
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream);
-
-private:
-  class AsyncFlusher : public AsyncHelper
-  {
-  public:
-    explicit AsyncFlusher(nsISupports* aStream)
-    : AsyncHelper(aStream)
-    { }
-  protected:
-    nsresult
-    DoStreamWork(nsISupports* aStream) override;
-  };
-};
-
-class OpenStreamHelper : public FileHelper
-{
-public:
-  OpenStreamHelper(FileHandleBase* aFileHandle,
-                   bool aWholeFile,
-                   uint64_t aStart,
-                   uint64_t aLength)
-  : FileHelper(aFileHandle, nullptr),
-    mWholeFile(aWholeFile), mStart(aStart), mLength(aLength)
-  { }
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream);
-
-  nsCOMPtr<nsIInputStream>&
-  Result()
-  {
-    return mStream;
-  }
-
-private:
-  bool mWholeFile;
-  uint64_t mStart;
-  uint64_t mLength;
-
-  nsCOMPtr<nsIInputStream> mStream;
-};
-
-} // namespace
-
-FileHandleBase::FileHandleBase(FileMode aMode,
-                               RequestMode aRequestMode)
-: mReadyState(INITIAL),
-  mMode(aMode),
-  mRequestMode(aRequestMode),
-  mLocation(0),
-  mPendingRequests(0),
-  mAborted(false),
-  mCreating(false)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
 }
 
 FileHandleBase::~FileHandleBase()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mPendingRequestCount);
+  MOZ_ASSERT(!mCreating);
+  MOZ_ASSERT(mSentFinishOrAbort);
+  MOZ_ASSERT_IF(mBackgroundActor, mFiredCompleteOrAbort);
+
+  if (mBackgroundActor) {
+    mBackgroundActor->SendDeleteMeInternal();
+
+    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
+  }
+}
+
+// static
+FileHandleBase*
+FileHandleBase::GetCurrent()
+{
+  MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
+
+  BackgroundChildImpl::ThreadLocal* threadLocal =
+    BackgroundChildImpl::GetThreadLocalForCurrentThread();
+  MOZ_ASSERT(threadLocal);
+
+  return threadLocal->mCurrentFileHandle;
+}
+
+void
+FileHandleBase::SetBackgroundActor(BackgroundFileHandleChild* aActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(!mBackgroundActor);
+
+  mBackgroundActor = aActor;
+}
+
+void
+FileHandleBase::StartRequest(FileRequestBase* aFileRequest,
+                             const FileRequestParams& aParams)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileRequest);
+  MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
+
+  BackgroundFileRequestChild* actor =
+    new BackgroundFileRequestChild(DEBUGONLY(mBackgroundActor->OwningThread(),)
+                                   aFileRequest);
+
+  mBackgroundActor->SendPBackgroundFileRequestConstructor(actor, aParams);
+
+  // Balanced in BackgroundFileRequestChild::Recv__delete__().
+  OnNewRequest();
 }
 
 void
 FileHandleBase::OnNewRequest()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  if (!mPendingRequests) {
-    MOZ_ASSERT(mReadyState == INITIAL, "Reusing a file handle!");
+  AssertIsOnOwningThread();
+
+  if (!mPendingRequestCount) {
+    MOZ_ASSERT(mReadyState == INITIAL);
     mReadyState = LOADING;
   }
-  ++mPendingRequests;
+
+  ++mPendingRequestCount;
 }
 
 void
-FileHandleBase::OnRequestFinished()
+FileHandleBase::OnRequestFinished(bool aActorDestroyedNormally)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(mPendingRequests, "Mismatched calls!");
-  --mPendingRequests;
-  if (!mPendingRequests) {
-    MOZ_ASSERT(mAborted || mReadyState == LOADING, "Bad state!");
-    mReadyState = FileHandleBase::FINISHING;
-    Finish();
-  }
-}
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mPendingRequestCount);
 
-nsresult
-FileHandleBase::CreateParallelStream(nsISupports** aStream)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-  MutableFileBase* mutableFile = MutableFile();
+  --mPendingRequestCount;
 
-  if (mutableFile->IsInvalid()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  nsCOMPtr<nsISupports> stream =
-    mutableFile->CreateStream(mMode == FileMode::Readonly);
-  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
+  if (!mPendingRequestCount && !MutableFile()->IsInvalidated()) {
+    mReadyState = FINISHING;
 
-  mParallelStreams.AppendElement(stream);
-
-  stream.forget(aStream);
-  return NS_OK;
-}
-
-nsresult
-FileHandleBase::GetOrCreateStream(nsISupports** aStream)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-  MutableFileBase* mutableFile = MutableFile();
-
-  if (mutableFile->IsInvalid()) {
-    return NS_ERROR_NOT_AVAILABLE;
+    if (aActorDestroyedNormally) {
+      if (!mAborted) {
+        SendFinish();
+      } else {
+        SendAbort();
+      }
+    } else {
+      // Don't try to send any more messages to the parent if the request actor
+      // was killed.
+#ifdef DEBUG
+      MOZ_ASSERT(!mSentFinishOrAbort);
+      mSentFinishOrAbort = true;
+#endif
+    }
   }
-
-  if (!mStream) {
-    nsCOMPtr<nsISupports> stream =
-      mutableFile->CreateStream(mMode == FileMode::Readonly);
-    NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
-
-    stream.swap(mStream);
-  }
-
-  nsCOMPtr<nsISupports> stream(mStream);
-  stream.forget(aStream);
-
-  return NS_OK;
 }
 
 bool
 FileHandleBase::IsOpen() const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
 
   // If we haven't started anything then we're open.
   if (mReadyState == INITIAL) {
-    MOZ_ASSERT(FileHelper::GetCurrentFileHandle() != this,
-               "This should be some other file handle (or null)!");
     return true;
   }
 
   // If we've already started then we need to check to see if we still have the
   // mCreating flag set. If we do (i.e. we haven't returned to the event loop
   // from the time we were created) then we are open. Otherwise check the
   // currently running file handles to see if it's the same. We only allow other
   // requests to be made if this file handle is currently running.
-  if (mReadyState == LOADING) {
-    if (mCreating) {
-      return true;
-    }
-
-    if (FileHelper::GetCurrentFileHandle() == this) {
-      return true;
-    }
+  if (mReadyState == LOADING && (mCreating || GetCurrent() == this)) {
+    return true;
   }
 
   return false;
 }
 
+void
+FileHandleBase::Abort()
+{
+  AssertIsOnOwningThread();
+
+  if (IsFinishingOrDone()) {
+    // Already started (and maybe finished) the finish or abort so there is
+    // nothing to do here.
+    return;
+  }
+
+  const bool isInvalidated = MutableFile()->IsInvalidated();
+  bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
+
+#ifdef DEBUG
+  if (isInvalidated) {
+    mSentFinishOrAbort = true;
+  }
+#endif
+
+  mAborted = true;
+  mReadyState = DONE;
+
+  // Fire the abort event if there are no outstanding requests. Otherwise the
+  // abort event will be fired when all outstanding requests finish.
+  if (needToSendAbort) {
+    SendAbort();
+  }
+}
+
 already_AddRefed<FileRequestBase>
 FileHandleBase::Read(uint64_t aSize, bool aHasEncoding,
                      const nsAString& aEncoding, ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
 
   // State and argument checking for read
   if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
     return nullptr;
   }
 
   // Do nothing if the window is closed
   if (!CheckWindow()) {
     return nullptr;
   }
 
-  nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
+  FileRequestReadParams params;
+  params.offset() = mLocation;
+  params.size() = aSize;
 
-  nsRefPtr<ReadHelper> helper;
+  nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
   if (aHasEncoding) {
-    helper = new ReadTextHelper(this, fileRequest, mLocation, aSize, aEncoding);
-  } else {
-    helper = new ReadHelper(this, fileRequest, mLocation, aSize);
+    fileRequest->SetEncoding(aEncoding);
   }
 
-  if (NS_WARN_IF(NS_FAILED(helper->Init())) ||
-      NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
+  StartRequest(fileRequest, params);
 
   mLocation += aSize;
 
   return fileRequest.forget();
 }
 
 already_AddRefed<FileRequestBase>
 FileHandleBase::Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
 
   // State checking for write
   if (!CheckStateForWrite(aRv)) {
     return nullptr;
   }
 
   // Getting location and additional state checking for truncate
   uint64_t location;
@@ -370,137 +247,96 @@ FileHandleBase::Truncate(const Optional<
     location = mLocation;
   }
 
   // Do nothing if the window is closed
   if (!CheckWindow()) {
     return nullptr;
   }
 
+  FileRequestTruncateParams params;
+  params.offset() = location;
+
   nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
 
-  nsRefPtr<TruncateHelper> helper =
-    new TruncateHelper(this, fileRequest, location);
-
-  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
+  StartRequest(fileRequest, params);
 
   if (aSize.WasPassed()) {
     mLocation = aSize.Value();
   }
 
   return fileRequest.forget();
 }
 
 already_AddRefed<FileRequestBase>
 FileHandleBase::Flush(ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
 
   // State checking for write
   if (!CheckStateForWrite(aRv)) {
     return nullptr;
   }
 
   // Do nothing if the window is closed
   if (!CheckWindow()) {
     return nullptr;
   }
 
+  FileRequestFlushParams params;
+
   nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
 
-  nsRefPtr<FlushHelper> helper = new FlushHelper(this, fileRequest);
-
-  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
+  StartRequest(fileRequest, params);
 
   return fileRequest.forget();
 }
 
 void
 FileHandleBase::Abort(ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
 
   // This method is special enough for not using generic state checking methods.
 
-  // We can't use IsOpen here since we need it to be possible to call Abort()
-  // even from outside of transaction callbacks.
-  if (mReadyState != FileHandleBase::INITIAL &&
-      mReadyState != FileHandleBase::LOADING) {
+  if (IsFinishingOrDone()) {
     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
     return;
   }
 
-  bool needToFinish = mReadyState == INITIAL;
+  Abort();
+}
 
-  mAborted = true;
+void
+FileHandleBase::HandleCompleteOrAbort(bool aAborted)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mFiredCompleteOrAbort);
+
   mReadyState = DONE;
 
-  // Fire the abort event if there are no outstanding requests. Otherwise the
-  // abort event will be fired when all outstanding requests finish.
-  if (needToFinish) {
-    aRv = Finish();
-  }
+  DEBUGONLY(mFiredCompleteOrAbort = true;)
 }
 
 void
 FileHandleBase::OnReturnToEventLoop()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
 
   // We're back at the event loop, no longer newborn.
   mCreating = false;
 
-  // Maybe set the readyState to DONE if there were no requests generated.
+  // Maybe finish if there were no requests generated.
   if (mReadyState == INITIAL) {
     mReadyState = DONE;
 
-    if (NS_FAILED(Finish())) {
-      NS_WARNING("Failed to finish!");
-    }
+    SendFinish();
   }
 }
 
-nsresult
-FileHandleBase::OpenInputStream(bool aWholeFile, uint64_t aStart,
-                                uint64_t aLength, nsIInputStream** aResult)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(mRequestMode == PARALLEL,
-             "Don't call me in other than parallel mode!");
-
-  // Common state checking
-  ErrorResult error;
-  if (!CheckState(error)) {
-    return error.StealNSResult();
-  }
-
-  // Do nothing if the window is closed
-  if (!CheckWindow()) {
-    return NS_OK;
-  }
-
-  nsRefPtr<OpenStreamHelper> helper =
-    new OpenStreamHelper(this, aWholeFile, aStart, aLength);
-
-  nsresult rv = helper->Enqueue();
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-
-  nsCOMPtr<nsIInputStream>& result = helper->Result();
-  NS_ENSURE_TRUE(result, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-
-  result.forget(aResult);
-  return NS_OK;
-}
-
 bool
 FileHandleBase::CheckState(ErrorResult& aRv)
 {
   if (!IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
     return false;
   }
 
@@ -542,360 +378,259 @@ FileHandleBase::CheckStateForWrite(Error
   if (mMode != FileMode::Readwrite) {
     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
     return false;
   }
 
   return true;
 }
 
+bool
+FileHandleBase::CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv)
+{
+  // State checking for write
+  if (!CheckStateForWrite(aRv)) {
+    return false;
+  }
+
+  // Additional state checking for write
+  if (!aAppend && mLocation == UINT64_MAX) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+    return false;
+  }
+
+  return true;
+}
+
 already_AddRefed<FileRequestBase>
-FileHandleBase::WriteInternal(nsIInputStream* aInputStream,
-                              uint64_t aInputLength, bool aAppend,
+FileHandleBase::WriteOrAppend(
+                       const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue,
+                       bool aAppend,
+                       ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
+
+  if (aValue.IsString()) {
+    return WriteOrAppend(aValue.GetAsString(), aAppend, aRv);
+  }
+
+  if (aValue.IsArrayBuffer()) {
+    return WriteOrAppend(aValue.GetAsArrayBuffer(), aAppend, aRv);
+  }
+
+  if (aValue.IsArrayBufferView()) {
+    return WriteOrAppend(aValue.GetAsArrayBufferView(), aAppend, aRv);
+  }
+
+  MOZ_ASSERT(aValue.IsBlob());
+  return WriteOrAppend(aValue.GetAsBlob(), aAppend, aRv);
+}
+
+already_AddRefed<FileRequestBase>
+FileHandleBase::WriteOrAppend(const nsAString& aValue,
+                              bool aAppend,
+                              ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
+
+  // State checking for write or append
+  if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
+    return nullptr;
+  }
+
+  NS_ConvertUTF16toUTF8 cstr(aValue);
+
+  uint64_t dataLength = cstr.Length();;
+  if (!dataLength) {
+    return nullptr;
+  }
+
+  FileRequestStringData stringData(cstr);
+
+  // Do nothing if the window is closed
+  if (!CheckWindow()) {
+    return nullptr;
+  }
+
+  return WriteInternal(stringData, dataLength, aAppend, aRv);
+}
+
+already_AddRefed<FileRequestBase>
+FileHandleBase::WriteOrAppend(const ArrayBuffer& aValue,
+                              bool aAppend,
                               ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
+
+  // State checking for write or append
+  if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
+    return nullptr;
+  }
+
+  aValue.ComputeLengthAndData();
+
+  uint64_t dataLength = aValue.Length();;
+  if (!dataLength) {
+    return nullptr;
+  }
+
+  const char* data = reinterpret_cast<const char*>(aValue.Data());
+
+  FileRequestStringData stringData;
+  if (NS_WARN_IF(!stringData.string().Assign(data, aValue.Length(),
+                                             fallible_t()))) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  // Do nothing if the window is closed
+  if (!CheckWindow()) {
+    return nullptr;
+  }
+
+  return WriteInternal(stringData, dataLength, aAppend, aRv);
+}
+
+already_AddRefed<FileRequestBase>
+FileHandleBase::WriteOrAppend(const ArrayBufferView& aValue,
+                              bool aAppend,
+                              ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
+
+  // State checking for write or append
+  if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
+    return nullptr;
+  }
+
+  aValue.ComputeLengthAndData();
+
+  uint64_t dataLength = aValue.Length();;
+  if (!dataLength) {
+    return nullptr;
+  }
+
+  const char* data = reinterpret_cast<const char*>(aValue.Data());
+
+  FileRequestStringData stringData;
+  if (NS_WARN_IF(!stringData.string().Assign(data, aValue.Length(),
+                                             fallible_t()))) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  // Do nothing if the window is closed
+  if (!CheckWindow()) {
+    return nullptr;
+  }
+
+  return WriteInternal(stringData, dataLength, aAppend, aRv);
+}
+
+already_AddRefed<FileRequestBase>
+FileHandleBase::WriteOrAppend(Blob& aValue,
+                              bool aAppend,
+                              ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
+
+  // State checking for write or append
+  if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
+    return nullptr;
+  }
+
+  ErrorResult rv;
+  uint64_t dataLength = aValue.GetSize(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  if (!dataLength) {
+    return nullptr;
+  }
+
+  PBackgroundChild* backgroundActor = BackgroundChild::GetForCurrentThread();
+  MOZ_ASSERT(backgroundActor);
+
+  PBlobChild* blobActor =
+    BackgroundChild::GetOrCreateActorForBlob(backgroundActor, &aValue);
+  if (NS_WARN_IF(!blobActor)) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  FileRequestBlobData blobData;
+  blobData.blobChild() = blobActor;
+
+  // Do nothing if the window is closed
+  if (!CheckWindow()) {
+    return nullptr;
+  }
+
+  return WriteInternal(blobData, dataLength, aAppend, aRv);
+}
+
+already_AddRefed<FileRequestBase>
+FileHandleBase::WriteInternal(const FileRequestData& aData,
+                              uint64_t aDataLength,
+                              bool aAppend,
+                              ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
 
   DebugOnly<ErrorResult> error;
   MOZ_ASSERT(CheckStateForWrite(error));
   MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
-  MOZ_ASSERT(aInputStream);
-  MOZ_ASSERT(aInputLength);
+  MOZ_ASSERT(aDataLength);
   MOZ_ASSERT(CheckWindow());
 
+  FileRequestWriteParams params;
+  params.offset() = aAppend ? UINT64_MAX : mLocation;
+  params.data() = aData;
+  params.dataLength() = aDataLength;
+
   nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
-
-  uint64_t location = aAppend ? UINT64_MAX : mLocation;
+  MOZ_ASSERT(fileRequest);
 
-  nsRefPtr<WriteHelper> helper =
-    new WriteHelper(this, fileRequest, location, aInputStream, aInputLength);
-
-  if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
-    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return nullptr;
-  }
+  StartRequest(fileRequest, params);
 
   if (aAppend) {
     mLocation = UINT64_MAX;
   }
   else {
-    mLocation += aInputLength;
+    mLocation += aDataLength;
   }
 
   return fileRequest.forget();
 }
 
-nsresult
-FileHandleBase::Finish()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-  nsRefPtr<FinishHelper> helper(new FinishHelper(this));
-
-  FileService* service = FileService::Get();
-  MOZ_ASSERT(service, "This should never be null");
-
-  nsIEventTarget* target = service->ThreadPoolTarget();
-
-  nsresult rv = target->Dispatch(helper, NS_DISPATCH_NORMAL);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-// static
-already_AddRefed<nsIInputStream>
-FileHandleBase::GetInputStream(const ArrayBuffer& aValue,
-                               uint64_t* aInputLength, ErrorResult& aRv)
+void
+FileHandleBase::SendFinish()
 {
-  aValue.ComputeLengthAndData();
-  const char* data = reinterpret_cast<const char*>(aValue.Data());
-  uint32_t length = aValue.Length();
-
-  nsCOMPtr<nsIInputStream> stream;
-  aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, length,
-                              NS_ASSIGNMENT_COPY);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  *aInputLength = length;
-  return stream.forget();
-}
-
-// static
-already_AddRefed<nsIInputStream>
-FileHandleBase::GetInputStream(const Blob& aValue, uint64_t* aInputLength,
-                               ErrorResult& aRv)
-{
-  Blob& file = const_cast<Blob&>(aValue);
-  uint64_t length = file.GetSize(aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIInputStream> stream;
-  file.GetInternalStream(getter_AddRefs(stream), aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  *aInputLength = length;
-  return stream.forget();
-}
-
-// static
-already_AddRefed<nsIInputStream>
-FileHandleBase::GetInputStream(const nsAString& aValue, uint64_t* aInputLength,
-                               ErrorResult& aRv)
-{
-  NS_ConvertUTF16toUTF8 cstr(aValue);
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mAborted);
+  MOZ_ASSERT(IsFinishingOrDone());
+  MOZ_ASSERT(!mSentFinishOrAbort);
+  MOZ_ASSERT(!mPendingRequestCount);
 
-  nsCOMPtr<nsIInputStream> stream;
-  aRv = NS_NewCStringInputStream(getter_AddRefs(stream), cstr);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  *aInputLength = cstr.Length();
-  return stream.forget();
-}
-
-FinishHelper::FinishHelper(FileHandleBase* aFileHandle)
-: mFileHandle(aFileHandle),
-  mAborted(aFileHandle->mAborted)
-{
-  mParallelStreams.SwapElements(aFileHandle->mParallelStreams);
-  mStream.swap(aFileHandle->mStream);
-}
-
-NS_IMPL_ISUPPORTS(FinishHelper, nsIRunnable)
-
-NS_IMETHODIMP
-FinishHelper::Run()
-{
-  if (NS_IsMainThread()) {
-    mFileHandle->mReadyState = FileHandleBase::DONE;
-
-    FileService* service = FileService::Get();
-    if (service) {
-      service->NotifyFileHandleCompleted(mFileHandle);
-    }
-
-    nsresult rv = mFileHandle->OnCompleteOrAbort(mAborted);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    mFileHandle = nullptr;
+  MOZ_ASSERT(mBackgroundActor);
+  mBackgroundActor->SendFinish();
 
-    return NS_OK;
-  }
-
-  if (mFileHandle->MutableFile()->IsInvalid()) {
-    mAborted = true;
-  }
-
-  for (uint32_t index = 0; index < mParallelStreams.Length(); index++) {
-    nsCOMPtr<nsIInputStream> stream =
-      do_QueryInterface(mParallelStreams[index]);
-
-    if (NS_FAILED(stream->Close())) {
-      NS_WARNING("Failed to close stream!");
-    }
-
-    mParallelStreams[index] = nullptr;
-  }
-
-  if (mStream) {
-    nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
-
-    if (NS_FAILED(stream->Close())) {
-      NS_WARNING("Failed to close stream!");
-    }
-
-    mStream = nullptr;
-  }
-
-  return NS_DispatchToMainThread(this);
-}
-
-nsresult
-ReadHelper::Init()
-{
-  mStream = MemoryOutputStream::Create(mSize);
-  NS_ENSURE_TRUE(mStream, NS_ERROR_FAILURE);
-
-  return NS_OK;
+  DEBUGONLY(mSentFinishOrAbort = true;)
 }
 
-nsresult
-ReadHelper::DoAsyncRun(nsISupports* aStream)
-{
-  MOZ_ASSERT(aStream, "Passed a null stream!");
-
-  uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
-
-  nsCOMPtr<nsIInputStream> istream =
-    new FileInputStreamWrapper(aStream, this, mLocation, mSize, flags);
-
-  FileService* service = FileService::Get();
-  MOZ_ASSERT(service, "This should never be null");
-
-  nsIEventTarget* target = service->ThreadPoolTarget();
-
-  nsCOMPtr<nsIAsyncStreamCopier> copier;
-  nsresult rv =
-    NS_NewAsyncStreamCopier(getter_AddRefs(copier), istream, mStream, target,
-                            false, true, STREAM_COPY_BLOCK_SIZE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = copier->AsyncCopy(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mRequest = do_QueryInterface(copier);
-
-  return NS_OK;
-}
-
-nsresult
-ReadHelper::GetSuccessResult(JSContext* aCx,
-                             JS::MutableHandle<JS::Value> aVal)
+void
+FileHandleBase::SendAbort()
 {
-  JS::Rooted<JSObject*> arrayBuffer(aCx);
-  nsresult rv =
-    nsContentUtils::CreateArrayBuffer(aCx, mStream->Data(), arrayBuffer.address());
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  aVal.setObject(*arrayBuffer);
-  return NS_OK;
-}
-
-nsresult
-ReadTextHelper::GetSuccessResult(JSContext* aCx,
-                                 JS::MutableHandle<JS::Value> aVal)
-{
-  nsAutoCString encoding;
-  const nsCString& data = mStream->Data();
-  // The BOM sniffing is baked into the "decode" part of the Encoding
-  // Standard, which the File API references.
-  if (!nsContentUtils::CheckForBOM(
-        reinterpret_cast<const unsigned char *>(data.get()),
-        data.Length(),
-        encoding)) {
-    // BOM sniffing failed. Try the API argument.
-    if (!EncodingUtils::FindEncodingForLabel(mEncoding, encoding)) {
-      // API argument failed. Since we are dealing with a file system file,
-      // we don't have a meaningful type attribute for the blob available,
-      // so proceeding to the next step, which is defaulting to UTF-8.
-      encoding.AssignLiteral("UTF-8");
-    }
-  }
-
-  nsString tmpString;
-  nsresult rv = nsContentUtils::ConvertStringFromEncoding(encoding, data,
-                                                          tmpString);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!xpc::StringToJsval(aCx, tmpString, aVal)) {
-    NS_WARNING("Failed to convert string!");
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
-nsresult
-WriteHelper::DoAsyncRun(nsISupports* aStream)
-{
-  MOZ_ASSERT(aStream, "Passed a null stream!");
-
-  uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mAborted);
+  MOZ_ASSERT(IsFinishingOrDone());
+  MOZ_ASSERT(!mSentFinishOrAbort);
 
-  nsCOMPtr<nsIOutputStream> ostream =
-    new FileOutputStreamWrapper(aStream, this, mLocation, mLength, flags);
-
-  FileService* service = FileService::Get();
-  MOZ_ASSERT(service, "This should never be null");
-
-  nsIEventTarget* target = service->ThreadPoolTarget();
-
-  nsCOMPtr<nsIAsyncStreamCopier> copier;
-  nsresult rv =
-    NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, ostream, target,
-                            true, false, STREAM_COPY_BLOCK_SIZE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = copier->AsyncCopy(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mRequest = do_QueryInterface(copier);
-
-  return NS_OK;
-}
-
-nsresult
-TruncateHelper::DoAsyncRun(nsISupports* aStream)
-{
-  MOZ_ASSERT(aStream, "Passed a null stream!");
-
-  nsRefPtr<AsyncTruncator> truncator = new AsyncTruncator(aStream, mOffset);
-
-  nsresult rv = truncator->AsyncWork(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-TruncateHelper::AsyncTruncator::DoStreamWork(nsISupports* aStream)
-{
-  nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(aStream);
-
-  nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
-  NS_ENSURE_SUCCESS(rv, rv);
+  MOZ_ASSERT(mBackgroundActor);
+  mBackgroundActor->SendAbort();
 
-  rv = sstream->SetEOF();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-FlushHelper::DoAsyncRun(nsISupports* aStream)
-{
-  MOZ_ASSERT(aStream, "Passed a null stream!");
-
-  nsRefPtr<AsyncFlusher> flusher = new AsyncFlusher(aStream);
-
-  nsresult rv = flusher->AsyncWork(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-FlushHelper::AsyncFlusher::DoStreamWork(nsISupports* aStream)
-{
-  nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
-
-  nsresult rv = ostream->Flush();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-OpenStreamHelper::DoAsyncRun(nsISupports* aStream)
-{
-  MOZ_ASSERT(aStream, "Passed a null stream!");
-
-  uint32_t flags = FileStreamWrapper::NOTIFY_CLOSE |
-                   FileStreamWrapper::NOTIFY_DESTROY;
-
-  mStream = mWholeFile ?
-    new FileInputStreamWrapper(aStream, this, 0, mLength, flags) :
-    new FileInputStreamWrapper(aStream, this, mStart, mLength, flags);
-
-  return NS_OK;
+  DEBUGONLY(mSentFinishOrAbort = true;)
 }
 
 } // namespace dom
 } // namespace mozilla
rename from dom/filehandle/FileHandle.h
rename to dom/filehandle/FileHandleBase.h
--- a/dom/filehandle/FileHandle.h
+++ b/dom/filehandle/FileHandleBase.h
@@ -2,148 +2,161 @@
 /* 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/. */
 
 #ifndef mozilla_dom_FileHandle_h
 #define mozilla_dom_FileHandle_h
 
-#include "MainThreadUtils.h"
-#include "mozilla/Assertions.h"
+#include "FileHandleCommon.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/FileModeBinding.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/TypedArray.h"
-#include "mozilla/ErrorResult.h"
-#include "nsAutoPtr.h"
-#include "nsCOMPtr.h"
-#include "nsIInputStream.h"
-#include "nsIRunnable.h"
-#include "nsTArray.h"
 
+template <class> struct already_AddRefed;
 class nsAString;
+struct PRThread;
 
 namespace mozilla {
+
+class ErrorResult;
+
 namespace dom {
 
+class BackgroundFileHandleChild;
 class Blob;
-class FileHelper;
 class FileRequestBase;
-class FileService;
-class FinishHelper;
-class MetadataHelper;
+class FileRequestData;
+class FileRequestParams;
 class MutableFileBase;
+class StringOrArrayBufferOrArrayBufferViewOrBlob;
 
 /**
  * This class provides a base for FileHandle implementations.
  */
 class FileHandleBase
+  : public RefCountedThreadObject
 {
 public:
-  enum RequestMode
-  {
-    NORMAL = 0, // Sequential
-    PARALLEL
-  };
-
   enum ReadyState
   {
     INITIAL = 0,
     LOADING,
     FINISHING,
     DONE
   };
 
 private:
-  friend class FileHelper;
-  friend class FileService;
-  friend class FinishHelper;
-  friend class MetadataHelper;
+  BackgroundFileHandleChild* mBackgroundActor;
+
+  uint64_t mLocation;
+
+  uint32_t mPendingRequestCount;
 
   ReadyState mReadyState;
   FileMode mMode;
-  RequestMode mRequestMode;
-  uint64_t mLocation;
-  uint32_t mPendingRequests;
-
-  nsTArray<nsCOMPtr<nsISupports>> mParallelStreams;
-  nsCOMPtr<nsISupports> mStream;
 
   bool mAborted;
   bool mCreating;
 
+  DEBUGONLY(bool mSentFinishOrAbort;)
+  DEBUGONLY(bool mFiredCompleteOrAbort;)
+
 public:
-  NS_IMETHOD_(MozExternalRefCountType)
-  AddRef() = 0;
+  static FileHandleBase*
+  GetCurrent();
+
+  void
+  SetBackgroundActor(BackgroundFileHandleChild* aActor);
 
-  NS_IMETHOD_(MozExternalRefCountType)
-  Release() = 0;
+  void
+  ClearBackgroundActor()
+  {
+    AssertIsOnOwningThread();
+
+    mBackgroundActor = nullptr;
+  }
 
-  nsresult
-  CreateParallelStream(nsISupports** aStream);
+  void
+  StartRequest(FileRequestBase* aFileRequest, const FileRequestParams& aParams);
 
-  nsresult
-  GetOrCreateStream(nsISupports** aStream);
+  void
+  OnNewRequest();
+
+  void
+  OnRequestFinished(bool aActorDestroyedNormally);
 
   bool
   IsOpen() const;
 
   bool
+  IsFinishingOrDone() const
+  {
+    AssertIsOnOwningThread();
+
+    return mReadyState == FINISHING || mReadyState == DONE;
+  }
+
+  bool
+  IsDone() const
+  {
+    AssertIsOnOwningThread();
+
+    return mReadyState == DONE;
+  }
+
+  bool
   IsAborted() const
   {
+    AssertIsOnOwningThread();
     return mAborted;
   }
 
   void
   SetCreating()
   {
     mCreating = true;
   }
 
-  virtual MutableFileBase*
-  MutableFile() const = 0;
-
-  nsresult
-  OpenInputStream(bool aWholeFile, uint64_t aStart, uint64_t aLength,
-                  nsIInputStream** aResult);
+  void
+  Abort();
 
   // Shared WebIDL (IndexedDB FileHandle and FileSystem FileHandle)
   FileMode
   Mode() const
   {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
+    AssertIsOnOwningThread();
     return mMode;
   }
 
   bool
   Active() const
   {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
+    AssertIsOnOwningThread();
     return IsOpen();
   }
 
   Nullable<uint64_t>
   GetLocation() const
   {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+    AssertIsOnOwningThread();
 
     if (mLocation == UINT64_MAX) {
       return Nullable<uint64_t>();
     }
 
     return Nullable<uint64_t>(mLocation);
   }
 
   void
   SetLocation(const Nullable<uint64_t>& aLocation)
   {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+    AssertIsOnOwningThread();
 
     // Null means the end-of-file.
     if (aLocation.IsNull()) {
       mLocation = UINT64_MAX;
     } else {
       mLocation = aLocation.Value();
     }
   }
@@ -156,119 +169,78 @@ public:
   Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv);
 
   already_AddRefed<FileRequestBase>
   Flush(ErrorResult& aRv);
 
   void
   Abort(ErrorResult& aRv);
 
+  //  Must be overridden in subclasses.
+  virtual MutableFileBase*
+  MutableFile() const = 0;
+
+  //  May be overridden in subclasses.
+  virtual void
+  HandleCompleteOrAbort(bool aAborted);
+
 protected:
-  FileHandleBase(FileMode aMode,
-                 RequestMode aRequestMode);
+  FileHandleBase(DEBUGONLY(PRThread* aOwningThread,)
+                 FileMode aMode);
+
   ~FileHandleBase();
 
   void
-  OnNewRequest();
-
-  void
-  OnRequestFinished();
-
-  void
   OnReturnToEventLoop();
 
-  virtual nsresult
-  OnCompleteOrAbort(bool aAborted) = 0;
-
   bool
   CheckState(ErrorResult& aRv);
 
   bool
   CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv);
 
   bool
   CheckStateForWrite(ErrorResult& aRv);
 
+  bool
+  CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv);
+
+  already_AddRefed<FileRequestBase>
+  WriteOrAppend(const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue,
+                bool aAppend,
+                ErrorResult& aRv);
+
+  //  Must be overridden in subclasses.
   virtual bool
   CheckWindow() = 0;
 
+  //  Must be overridden in subclasses.
   virtual already_AddRefed<FileRequestBase>
   GenerateFileRequest() = 0;
 
-  template<class T>
+private:
   already_AddRefed<FileRequestBase>
-  WriteOrAppend(const T& aValue, bool aAppend, ErrorResult& aRv)
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    // State checking for write
-    if (!CheckStateForWrite(aRv)) {
-      return nullptr;
-    }
-
-    // Additional state checking for write
-    if (!aAppend && mLocation == UINT64_MAX) {
-      aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
-      return nullptr;
-    }
+  WriteOrAppend(const nsAString& aValue, bool aAppend, ErrorResult& aRv);
 
-    uint64_t length;
-    nsCOMPtr<nsIInputStream> stream = GetInputStream(aValue, &length, aRv);
-    if (aRv.Failed()) {
-      return nullptr;
-    }
+  already_AddRefed<FileRequestBase>
+  WriteOrAppend(const ArrayBuffer& aValue, bool aAppend, ErrorResult& aRv);
 
-    if (!length) {
-      return nullptr;
-    }
-
-    // Do nothing if the window is closed
-    if (!CheckWindow()) {
-      return nullptr;
-    }
-
-    return WriteInternal(stream, length, aAppend, aRv);
-  }
+  already_AddRefed<FileRequestBase>
+  WriteOrAppend(const ArrayBufferView& aValue, bool aAppend, ErrorResult& aRv);
 
   already_AddRefed<FileRequestBase>
-  WriteInternal(nsIInputStream* aInputStream, uint64_t aInputLength,
+  WriteOrAppend(Blob& aValue, bool aAppend, ErrorResult& aRv);
+
+  already_AddRefed<FileRequestBase>
+  WriteInternal(const FileRequestData& aData, uint64_t aDataLength,
                 bool aAppend, ErrorResult& aRv);
 
-  nsresult
-  Finish();
-
-  static already_AddRefed<nsIInputStream>
-  GetInputStream(const ArrayBuffer& aValue, uint64_t* aInputLength,
-                 ErrorResult& aRv);
-
-  static already_AddRefed<nsIInputStream>
-  GetInputStream(const Blob& aValue, uint64_t* aInputLength,
-                 ErrorResult& aRv);
-
-  static already_AddRefed<nsIInputStream>
-  GetInputStream(const nsAString& aValue, uint64_t* aInputLength,
-                 ErrorResult& aRv);
-};
+  void
+  SendFinish();
 
-class FinishHelper final : public nsIRunnable
-{
-  friend class FileHandleBase;
-
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-
-private:
-  explicit FinishHelper(FileHandleBase* aFileHandle);
-  ~FinishHelper()
-  { }
-
-  nsRefPtr<FileHandleBase> mFileHandle;
-  nsTArray<nsCOMPtr<nsISupports>> mParallelStreams;
-  nsCOMPtr<nsISupports> mStream;
-
-  bool mAborted;
+  void
+  SendAbort();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileHandle_h
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/FileHandleCommon.cpp
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "FileHandleCommon.h"
+
+#include "mozilla/Assertions.h"
+#include "prthread.h"
+
+namespace mozilla {
+namespace dom {
+
+#ifdef DEBUG
+
+void
+ThreadObject::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+  MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+}
+
+PRThread*
+ThreadObject::OwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+  return mOwningThread;
+}
+
+#endif // DEBUG
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/FileHandleCommon.h
@@ -0,0 +1,73 @@
+/* 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_FileHandleCommon_h
+#define mozilla_dom_FileHandleCommon_h
+
+#include "nscore.h"
+
+#ifdef DEBUG
+#define DEBUGONLY(...)  __VA_ARGS__
+#else
+#define DEBUGONLY(...)  /* nothing */
+#endif
+
+struct PRThread;
+
+namespace mozilla {
+namespace dom {
+
+class RefCountedObject
+{
+public:
+  NS_IMETHOD_(MozExternalRefCountType)
+  AddRef() = 0;
+
+  NS_IMETHOD_(MozExternalRefCountType)
+  Release() = 0;
+
+protected:
+  virtual ~RefCountedObject()
+  { }
+};
+
+class ThreadObject
+{
+  DEBUGONLY(PRThread* mOwningThread;)
+
+public:
+  explicit ThreadObject(DEBUGONLY(PRThread* aOwningThread))
+    DEBUGONLY(: mOwningThread(aOwningThread))
+  { }
+
+  virtual ~ThreadObject()
+  { }
+
+#ifdef DEBUG
+  void
+  AssertIsOnOwningThread() const;
+
+  PRThread*
+  OwningThread() const;
+#else
+  void
+  AssertIsOnOwningThread() const
+  { }
+#endif
+};
+
+class RefCountedThreadObject
+  : public RefCountedObject
+  , public ThreadObject
+{
+public:
+  explicit RefCountedThreadObject(DEBUGONLY(PRThread* aOwningThread))
+    : ThreadObject(DEBUGONLY(aOwningThread))
+  { }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_FileHandleCommon_h
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/FileHandleStorage.h
@@ -0,0 +1,24 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_FileHandleStorage_h
+#define mozilla_dom_FileHandleStorage_h
+
+namespace mozilla {
+namespace dom {
+
+enum FileHandleStorage
+{
+  FILE_HANDLE_STORAGE_IDB = 0,
+  // A placeholder for bug 997471
+  //FILE_HANDLE_STORAGE_FS
+  FILE_HANDLE_STORAGE_MAX
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_FileHandleStorage_h
deleted file mode 100644
--- a/dom/filehandle/FileHelper.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-/* -*- 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 "FileHelper.h"
-
-#include "FileHandle.h"
-#include "FileRequest.h"
-#include "FileService.h"
-#include "js/Value.h"
-#include "MainThreadUtils.h"
-#include "mozilla/Assertions.h"
-#include "MutableFile.h"
-#include "nsDebug.h"
-#include "nsError.h"
-#include "nsIRequest.h"
-
-namespace mozilla {
-namespace dom {
-
-namespace {
-
-FileHandleBase* gCurrentFileHandle = nullptr;
-
-} // namespace
-
-FileHelper::FileHelper(FileHandleBase* aFileHandle,
-                       FileRequestBase* aFileRequest)
-: mMutableFile(aFileHandle->MutableFile()),
-  mFileHandle(aFileHandle),
-  mFileRequest(aFileRequest),
-  mResultCode(NS_OK),
-  mFinished(false)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-}
-
-FileHelper::~FileHelper()
-{
-  MOZ_ASSERT(!mMutableFile && !mFileHandle && !mFileRequest && !mListener &&
-             !mRequest, "Should have cleared this!");
-}
-
-NS_IMPL_ISUPPORTS(FileHelper, nsIRequestObserver)
-
-nsresult
-FileHelper::Enqueue()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  FileService* service = FileService::GetOrCreate();
-  NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
-
-  nsresult rv = service->Enqueue(mFileHandle, this);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (mFileHandle) {
-    mFileHandle->OnNewRequest();
-  }
-
-  return NS_OK;
-}
-
-nsresult
-FileHelper::AsyncRun(FileHelperListener* aListener)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  // Assign the listener early, so we can use it synchronously if the code
-  // below fails.
-  mListener = aListener;
-
-  nsresult rv;
-
-  nsCOMPtr<nsISupports> stream;
-  if (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) {
-    rv = mFileHandle->CreateParallelStream(getter_AddRefs(stream));
-  }
-  else {
-    rv = mFileHandle->GetOrCreateStream(getter_AddRefs(stream));
-  }
-
-  if (NS_SUCCEEDED(rv)) {
-    NS_ASSERTION(stream, "This should never be null!");
-
-    rv = DoAsyncRun(stream);
-  }
-
-  if (NS_FAILED(rv)) {
-    mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
-    Finish();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FileHelper::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FileHelper::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
-                          nsresult aStatus)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (NS_FAILED(aStatus)) {
-    if (aStatus == NS_ERROR_FILE_NO_DEVICE_SPACE) {
-      mResultCode = NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR;
-    }
-    else {
-      mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
-    }
-  }
-
-  Finish();
-
-  return NS_OK;
-}
-
-void
-FileHelper::OnStreamProgress(uint64_t aProgress, uint64_t aProgressMax)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (mFileHandle->IsAborted()) {
-    NS_ASSERTION(mRequest, "Should have a request!\n");
-
-    nsresult rv = mRequest->Cancel(NS_BINDING_ABORTED);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to cancel the request!");
-    }
-
-    return;
-  }
-
-  if (mFileRequest) {
-    mFileRequest->OnProgress(aProgress, aProgressMax);
-  }
-}
-
-// static
-FileHandleBase*
-FileHelper::GetCurrentFileHandle()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  return gCurrentFileHandle;
-}
-
-nsresult
-FileHelper::GetSuccessResult(JSContext* aCx,
-                             JS::MutableHandle<JS::Value> aVal)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  aVal.setUndefined();
-  return NS_OK;
-}
-
-void
-FileHelper::ReleaseObjects()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  mMutableFile = nullptr;
-  mFileHandle = nullptr;
-  mFileRequest = nullptr;
-  mListener = nullptr;
-  mRequest = nullptr;
-}
-
-void
-FileHelper::Finish()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (mFinished) {
-    return;
-  }
-
-  mFinished = true;
-
-  if (mFileHandle->IsAborted()) {
-    // Always fire a "error" event with ABORT_ERR if the transaction was
-    // aborted, even if the request succeeded or failed with another error.
-    mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
-  }
-
-  FileHandleBase* oldFileHandle = gCurrentFileHandle;
-  gCurrentFileHandle = mFileHandle;
-
-  if (mFileRequest) {
-    nsresult rv = mFileRequest->NotifyHelperCompleted(this);
-    if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
-      mResultCode = rv;
-    }
-  }
-
-  MOZ_ASSERT(gCurrentFileHandle == mFileHandle, "Should be unchanged!");
-  gCurrentFileHandle = oldFileHandle;
-
-  mFileHandle->OnRequestFinished();
-
-  mListener->OnFileHelperComplete(this);
-
-  ReleaseObjects();
-
-  MOZ_ASSERT(!(mMutableFile || mFileHandle || mFileRequest || mListener ||
-               mRequest), "Subclass didn't call FileHelper::ReleaseObjects!");
-
-}
-
-void
-FileHelper::OnStreamClose()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  Finish();
-}
-
-void
-FileHelper::OnStreamDestroy()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  Finish();
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/filehandle/FileHelper.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_FileHelper_h
-#define mozilla_dom_FileHelper_h
-
-#include "js/TypeDecls.h"
-#include "nsAutoPtr.h"
-#include "nsCOMPtr.h"
-#include "nsIRequestObserver.h"
-
-namespace mozilla {
-namespace dom {
-
-class FileHandleBase;
-class FileHelper;
-class FileRequestBase;
-class FileOutputStreamWrapper;
-class MutableFileBase;
-
-class FileHelperListener
-{
-public:
-  NS_IMETHOD_(MozExternalRefCountType)
-  AddRef() = 0;
-
-  NS_IMETHOD_(MozExternalRefCountType)
-  Release() = 0;
-
-  virtual void
-  OnFileHelperComplete(FileHelper* aFileHelper) = 0;
-};
-
-/**
- * Must be subclassed. The subclass must implement DoAsyncRun. It may then
- * choose to implement GetSuccessResult to properly set the result of the
- * success event. Call Enqueue to start the file operation.
- */
-class FileHelper : public nsIRequestObserver
-{
-  friend class FileOutputStreamWrapper;
-
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIREQUESTOBSERVER
-
-  nsresult
-  ResultCode() const
-  {
-    return mResultCode;
-  }
-
-  nsresult
-  Enqueue();
-
-  nsresult
-  AsyncRun(FileHelperListener* aListener);
-
-  virtual nsresult
-  GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal);
-
-  void
-  OnStreamProgress(uint64_t aProgress, uint64_t aProgressMax);
-
-  void
-  OnStreamClose();
-
-  void
-  OnStreamDestroy();
-
-  static FileHandleBase*
-  GetCurrentFileHandle();
-
-protected:
-  FileHelper(FileHandleBase* aFileHandle, FileRequestBase* aRequest);
-
-  virtual ~FileHelper();
-
-  virtual nsresult
-  DoAsyncRun(nsISupports* aStream) = 0;
-
-  virtual void
-  ReleaseObjects();
-
-  void
-  Finish();
-
-  nsRefPtr<MutableFileBase> mMutableFile;
-  nsRefPtr<FileHandleBase> mFileHandle;
-  nsRefPtr<FileRequestBase> mFileRequest;
-
-  nsRefPtr<FileHelperListener> mListener;
-  nsCOMPtr<nsIRequest> mRequest;
-
-private:
-  nsresult mResultCode;
-  bool mFinished;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_FileHelper_h
deleted file mode 100644
--- a/dom/filehandle/FileRequest.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- 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 "FileRequest.h"
-
-#include "MainThreadUtils.h"
-#include "mozilla/Assertions.h"
-
-namespace mozilla {
-namespace dom {
-
-FileRequestBase::FileRequestBase()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-}
-
-FileRequestBase::~FileRequestBase()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-}
-
-} // namespace dom
-} // namespace mozilla
rename from dom/filehandle/FileRequest.h
rename to dom/filehandle/FileRequestBase.h
--- a/dom/filehandle/FileRequest.h
+++ b/dom/filehandle/FileRequestBase.h
@@ -2,43 +2,92 @@
 /* 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/. */
 
 #ifndef mozilla_dom_FileRequest_h
 #define mozilla_dom_FileRequest_h
 
-#include "nscore.h"
+#include "FileHandleCommon.h"
+#include "js/TypeDecls.h"
+#include "nsString.h"
+
+struct PRThread;
 
 namespace mozilla {
 namespace dom {
 
-class FileHelper;
+class FileHandleBase;
 
 /**
  * This class provides a base for FileRequest implementations.
  */
 class FileRequestBase
+  : public RefCountedThreadObject
 {
+  nsString mEncoding;
+
+  bool mHasEncoding;
+
 public:
-  NS_IMETHOD_(MozExternalRefCountType)
-  AddRef() = 0;
+  class ResultCallback;
+
+  void
+  SetEncoding(const nsAString& aEncoding)
+  {
+    mEncoding = aEncoding;
+    mHasEncoding = true;
+  }
 
-  NS_IMETHOD_(MozExternalRefCountType)
-  Release() = 0;
+  const nsAString&
+  GetEncoding() const
+  {
+    return mEncoding;
+  }
+
+  bool
+  HasEncoding() const
+  {
+    return mHasEncoding;
+  }
+
+  virtual FileHandleBase*
+  FileHandle() const = 0;
 
   virtual void
   OnProgress(uint64_t aProgress, uint64_t aProgressMax) = 0;
 
-  virtual nsresult
-  NotifyHelperCompleted(FileHelper* aFileHelper) = 0;
+  virtual void
+  SetResultCallback(ResultCallback* aCallback) = 0;
+
+  virtual void
+  SetError(nsresult aError) = 0;
 
 protected:
-  FileRequestBase();
+  FileRequestBase(DEBUGONLY(PRThread* aOwningThread))
+    : RefCountedThreadObject(DEBUGONLY(aOwningThread))
+    , mHasEncoding(false)
+  {
+    AssertIsOnOwningThread();
+  }
 
-  virtual ~FileRequestBase();
+  virtual ~FileRequestBase()
+  {
+    AssertIsOnOwningThread();
+  }
+};
+
+class NS_NO_VTABLE FileRequestBase::ResultCallback
+{
+public:
+  virtual nsresult
+  GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) = 0;
+
+protected:
+  ResultCallback()
+  { }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileRequest_h
deleted file mode 100644
--- a/dom/filehandle/FileService.cpp
+++ /dev/null
@@ -1,479 +0,0 @@
-/* -*- 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 "FileService.h"
-
-#include "FileHandle.h"
-#include "MainThreadUtils.h"
-#include "mozilla/Assertions.h"
-#include "MutableFile.h"
-#include "nsError.h"
-#include "nsIEventTarget.h"
-#include "nsNetCID.h"
-#include "nsServiceManagerUtils.h"
-#include "nsThreadPool.h"
-#include "nsThreadUtils.h"
-
-namespace mozilla {
-namespace dom {
-
-namespace {
-
-const uint32_t kThreadLimit = 5;
-const uint32_t kIdleThreadLimit = 1;
-const uint32_t kIdleThreadTimeoutMs = 30000;
-
-StaticAutoPtr<FileService> gInstance;
-bool gShutdown = false;
-
-} // namespace
-
-FileService::FileService()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!gInstance);
-}
-
-FileService::~FileService()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-}
-
-nsresult
-FileService::Init()
-{
-  mThreadPool = new nsThreadPool();
-
-  nsresult rv = mThreadPool->SetName(NS_LITERAL_CSTRING("FileHandleTrans"));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mThreadPool->SetThreadLimit(kThreadLimit);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-nsresult
-FileService::Cleanup()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsIThread* thread = NS_GetCurrentThread();
-  MOZ_ASSERT(thread);
-
-  nsresult rv = mThreadPool->Shutdown();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Make sure the service is still accessible while any generated callbacks
-  // are processed.
-  rv = NS_ProcessPendingEvents(thread);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!mCompleteCallbacks.IsEmpty()) {
-    // Run all callbacks manually now.
-    for (uint32_t index = 0; index < mCompleteCallbacks.Length(); index++) {
-      mCompleteCallbacks[index].mCallback->Run();
-    }
-    mCompleteCallbacks.Clear();
-
-    // And make sure they get processed.
-    rv = NS_ProcessPendingEvents(thread);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return NS_OK;
-}
-
-// static
-FileService*
-FileService::GetOrCreate()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (gShutdown) {
-    NS_WARNING("Calling GetOrCreate() after shutdown!");
-    return nullptr;
-  }
-
-  if (!gInstance) {
-    nsAutoPtr<FileService> service(new FileService());
-
-    nsresult rv = service->Init();
-    NS_ENSURE_SUCCESS(rv, nullptr);
-
-    gInstance = service.forget();
-  }
-
-  return gInstance;
-}
-
-// static
-FileService*
-FileService::Get()
-{
-  // Does not return an owning reference.
-  return gInstance;
-}
-
-// static
-void
-FileService::Shutdown()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  gShutdown = true;
-
-  if (gInstance) {
-    if (NS_FAILED(gInstance->Cleanup())) {
-      NS_WARNING("Failed to shutdown file service!");
-    }
-    gInstance = nullptr;
-  }
-}
-
-// static
-bool
-FileService::IsShuttingDown()
-{
-  return gShutdown;
-}
-
-nsresult
-FileService::Enqueue(FileHandleBase* aFileHandle, FileHelper* aFileHelper)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(aFileHandle, "Null pointer!");
-
-  MutableFileBase* mutableFile = aFileHandle->MutableFile();
-
-  if (mutableFile->IsInvalid()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  const nsACString& storageId = mutableFile->mStorageId;
-  const nsAString& fileName = mutableFile->mFileName;
-  bool modeIsWrite = aFileHandle->mMode == FileMode::Readwrite;
-
-  StorageInfo* storageInfo;
-  if (!mStorageInfos.Get(storageId, &storageInfo)) {
-    nsAutoPtr<StorageInfo> newStorageInfo(new StorageInfo());
-
-    mStorageInfos.Put(storageId, newStorageInfo);
-
-    storageInfo = newStorageInfo.forget();
-  }
-
-  FileHandleQueue* existingFileHandleQueue =
-    storageInfo->GetFileHandleQueue(aFileHandle);
-
-  if (existingFileHandleQueue) {
-    existingFileHandleQueue->Enqueue(aFileHelper);
-    return NS_OK;
-  }
-
-  bool lockedForReading = storageInfo->IsFileLockedForReading(fileName);
-  bool lockedForWriting = storageInfo->IsFileLockedForWriting(fileName);
-
-  if (modeIsWrite) {
-    if (!lockedForWriting) {
-      storageInfo->LockFileForWriting(fileName);
-    }
-  }
-  else {
-    if (!lockedForReading) {
-      storageInfo->LockFileForReading(fileName);
-    }
-  }
-
-  if (lockedForWriting || (lockedForReading && modeIsWrite)) {
-    storageInfo->CreateDelayedEnqueueInfo(aFileHandle, aFileHelper);
-  }
-  else {
-    FileHandleQueue* fileHandleQueue =
-      storageInfo->CreateFileHandleQueue(aFileHandle);
-
-    if (aFileHelper) {
-      // Enqueue() will queue the file helper if there's already something
-      // running. That can't fail, so no need to eventually remove
-      // storageInfo from the hash table.
-      //
-      // If the file helper is free to run then AsyncRun() is called on the
-      // file helper. AsyncRun() is responsible for calling all necessary
-      // callbacks when something fails. We're propagating the error here,
-      // however there's no need to eventually remove storageInfo from
-      // the hash table. Code behind AsyncRun() will take care of it. The last
-      // item in the code path is NotifyFileHandleCompleted() which removes
-      // storageInfo from the hash table if there are no file handles for
-      // the file storage.
-      nsresult rv = fileHandleQueue->Enqueue(aFileHelper);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-  }
-
-  return NS_OK;
-}
-
-void
-FileService::NotifyFileHandleCompleted(FileHandleBase* aFileHandle)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(aFileHandle, "Null pointer!");
-
-  MutableFileBase* mutableFile = aFileHandle->MutableFile();
-  const nsACString& storageId = mutableFile->mStorageId;
-
-  StorageInfo* storageInfo;
-  if (!mStorageInfos.Get(storageId, &storageInfo)) {
-    NS_ERROR("We don't know anyting about this file handle?!");
-    return;
-  }
-
-  storageInfo->RemoveFileHandleQueue(aFileHandle);
-
-  if (!storageInfo->HasRunningFileHandles()) {
-    mStorageInfos.Remove(storageId);
-
-    // See if we need to fire any complete callbacks.
-    uint32_t index = 0;
-    while (index < mCompleteCallbacks.Length()) {
-      if (MaybeFireCallback(mCompleteCallbacks[index])) {
-        mCompleteCallbacks.RemoveElementAt(index);
-      }
-      else {
-        index++;
-      }
-    }
-  }
-}
-
-void
-FileService::WaitForStoragesToComplete(nsTArray<nsCString>& aStorageIds,
-                                       nsIRunnable* aCallback)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!aStorageIds.IsEmpty());
-  MOZ_ASSERT(aCallback);
-
-  StoragesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
-  callback->mCallback = aCallback;
-  callback->mStorageIds.SwapElements(aStorageIds);
-
-  if (MaybeFireCallback(*callback)) {
-    mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
-  }
-}
-
-nsIEventTarget*
-FileService::ThreadPoolTarget() const
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mThreadPool);
-
-  return mThreadPool;
-}
-
-bool
-FileService::MaybeFireCallback(StoragesCompleteCallback& aCallback)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  for (uint32_t index = 0; index < aCallback.mStorageIds.Length(); index++) {
-    if (mStorageInfos.Get(aCallback.mStorageIds[index], nullptr)) {
-      return false;
-    }
-  }
-
-  aCallback.mCallback->Run();
-  return true;
-}
-
-FileService::FileHandleQueue::FileHandleQueue(FileHandleBase* aFileHandle)
-: mFileHandle(aFileHandle)
-{
-  MOZ_ASSERT(aFileHandle, "Null pointer!");
-}
-
-FileService::FileHandleQueue::~FileHandleQueue()
-{
-}
-
-NS_IMPL_ADDREF(FileService::FileHandleQueue)
-NS_IMPL_RELEASE(FileService::FileHandleQueue)
-
-nsresult
-FileService::FileHandleQueue::Enqueue(FileHelper* aFileHelper)
-{
-  mQueue.AppendElement(aFileHelper);
-
-  nsresult rv;
-  if (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) {
-    rv = aFileHelper->AsyncRun(this);
-  }
-  else {
-    rv = ProcessQueue();
-  }
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-void
-FileService::
-FileHandleQueue::OnFileHelperComplete(FileHelper* aFileHelper)
-{
-  if (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) {
-    int32_t index = mQueue.IndexOf(aFileHelper);
-    NS_ASSERTION(index != -1, "We don't know anything about this helper!");
-
-    mQueue.RemoveElementAt(index);
-  }
-  else {
-    NS_ASSERTION(mCurrentHelper == aFileHelper, "How can this happen?!");
-
-    mCurrentHelper = nullptr;
-
-    nsresult rv = ProcessQueue();
-    if (NS_FAILED(rv)) {
-      return;
-    }
-  }
-}
-
-nsresult
-FileService::FileHandleQueue::ProcessQueue()
-{
-  if (mQueue.IsEmpty() || mCurrentHelper) {
-    return NS_OK;
-  }
-
-  mCurrentHelper = mQueue[0];
-  mQueue.RemoveElementAt(0);
-
-  nsresult rv = mCurrentHelper->AsyncRun(this);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-FileService::DelayedEnqueueInfo::DelayedEnqueueInfo()
-{
-}
-
-FileService::DelayedEnqueueInfo::~DelayedEnqueueInfo()
-{
-}
-
-FileService::FileHandleQueue*
-FileService::StorageInfo::CreateFileHandleQueue(FileHandleBase* aFileHandle)
-{
-  nsRefPtr<FileHandleQueue>* fileHandleQueue =
-    mFileHandleQueues.AppendElement();
-  *fileHandleQueue = new FileHandleQueue(aFileHandle);
-  return fileHandleQueue->get();
-}
-
-FileService::FileHandleQueue*
-FileService::StorageInfo::GetFileHandleQueue(FileHandleBase* aFileHandle)
-{
-  uint32_t count = mFileHandleQueues.Length();
-  for (uint32_t index = 0; index < count; index++) {
-    nsRefPtr<FileHandleQueue>& fileHandleQueue = mFileHandleQueues[index];
-    if (fileHandleQueue->mFileHandle == aFileHandle) {
-      return fileHandleQueue;
-    }
-  }
-  return nullptr;
-}
-
-void
-FileService::StorageInfo::RemoveFileHandleQueue(FileHandleBase* aFileHandle)
-{
-  for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
-    if (mDelayedEnqueueInfos[index].mFileHandle == aFileHandle) {
-      MOZ_ASSERT(!mDelayedEnqueueInfos[index].mFileHelper, "Should be null!");
-      mDelayedEnqueueInfos.RemoveElementAt(index);
-      return;
-    }
-  }
-
-  uint32_t fileHandleCount = mFileHandleQueues.Length();
-
-  // We can't just remove entries from lock hash tables, we have to rebuild
-  // them instead. Multiple FileHandle objects may lock the same file
-  // (one entry can represent multiple locks).
-
-  mFilesReading.Clear();
-  mFilesWriting.Clear();
-
-  for (uint32_t index = 0, count = fileHandleCount; index < count; index++) {
-    FileHandleBase* fileHandle = mFileHandleQueues[index]->mFileHandle;
-    if (fileHandle == aFileHandle) {
-      MOZ_ASSERT(count == fileHandleCount, "More than one match?!");
-
-      mFileHandleQueues.RemoveElementAt(index);
-      index--;
-      count--;
-
-      continue;
-    }
-
-    const nsAString& fileName = fileHandle->MutableFile()->mFileName;
-
-    if (fileHandle->mMode == FileMode::Readwrite) {
-      if (!IsFileLockedForWriting(fileName)) {
-        LockFileForWriting(fileName);
-      }
-    }
-    else {
-      if (!IsFileLockedForReading(fileName)) {
-        LockFileForReading(fileName);
-      }
-    }
-  }
-
-  MOZ_ASSERT(mFileHandleQueues.Length() == fileHandleCount - 1,
-             "Didn't find the file handle we were looking for!");
-
-  nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos;
-  delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos);
-
-  for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) {
-    DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
-    if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mFileHandle,
-                                     delayedEnqueueInfo.mFileHelper))) {
-      NS_WARNING("Enqueue failed!");
-    }
-  }
-}
-
-FileService::DelayedEnqueueInfo*
-FileService::StorageInfo::CreateDelayedEnqueueInfo(FileHandleBase* aFileHandle,
-                                                   FileHelper* aFileHelper)
-{
-  DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
-  info->mFileHandle = aFileHandle;
-  info->mFileHelper = aFileHelper;
-  return info;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/filehandle/FileService.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_FileService_h
-#define mozilla_dom_FileService_h
-
-#include "mozilla/Attributes.h"
-#include "mozilla/dom/FileHelper.h"
-#include "mozilla/StaticPtr.h"
-#include "nsClassHashtable.h"
-#include "nsAutoPtr.h"
-#include "nsCOMPtr.h"
-#include "nsDebug.h"
-#include "nsHashKeys.h"
-#include "nsTArray.h"
-#include "nsTHashtable.h"
-
-class nsAString;
-class nsIEventTarget;
-class nsIRunnable;
-class nsThreadPool;
-
-namespace mozilla {
-namespace dom {
-
-class FileHandleBase;
-
-class FileService final
-{
-  friend class nsAutoPtr<FileService>;
-  friend class StaticAutoPtr<FileService>;
-
-public:
-  // Returns a non-owning reference!
-  static FileService*
-  GetOrCreate();
-
-  // Returns a non-owning reference!
-  static FileService*
-  Get();
-
-  static void
-  Shutdown();
-
-  // Returns true if we've begun the shutdown process.
-  static bool
-  IsShuttingDown();
-
-  nsresult
-  Enqueue(FileHandleBase* aFileHandle, FileHelper* aFileHelper);
-
-  void
-  NotifyFileHandleCompleted(FileHandleBase* aFileHandle);
-
-  void
-  WaitForStoragesToComplete(nsTArray<nsCString>& aStorageIds,
-                            nsIRunnable* aCallback);
-
-  nsIEventTarget*
-  ThreadPoolTarget() const;
-
-private:
-  class FileHandleQueue final : public FileHelperListener
-  {
-    friend class FileService;
-
-  public:
-    NS_IMETHOD_(MozExternalRefCountType)
-    AddRef() override;
-
-    NS_IMETHOD_(MozExternalRefCountType)
-    Release() override;
-
-    inline nsresult
-    Enqueue(FileHelper* aFileHelper);
-
-    virtual void
-    OnFileHelperComplete(FileHelper* aFileHelper) override;
-
-  private:
-    inline
-    explicit FileHandleQueue(FileHandleBase* aFileHandle);
-
-    ~FileHandleQueue();
-
-    nsresult
-    ProcessQueue();
-
-    ThreadSafeAutoRefCnt mRefCnt;
-    NS_DECL_OWNINGTHREAD
-    nsRefPtr<FileHandleBase> mFileHandle;
-    nsTArray<nsRefPtr<FileHelper> > mQueue;
-    nsRefPtr<FileHelper> mCurrentHelper;
-  };
-
-  struct DelayedEnqueueInfo
-  {
-    DelayedEnqueueInfo();
-    ~DelayedEnqueueInfo();
-
-    nsRefPtr<FileHandleBase> mFileHandle;
-    nsRefPtr<FileHelper> mFileHelper;
-  };
-
-  class StorageInfo
-  {
-    friend class FileService;
-
-  public:
-    inline FileHandleQueue*
-    CreateFileHandleQueue(FileHandleBase* aFileHandle);
-
-    inline FileHandleQueue*
-    GetFileHandleQueue(FileHandleBase* aFileHandle);
-
-    void
-    RemoveFileHandleQueue(FileHandleBase* aFileHandle);
-
-    bool
-    HasRunningFileHandles()
-    {
-      return !mFileHandleQueues.IsEmpty();
-    }
-
-    inline DelayedEnqueueInfo*
-    CreateDelayedEnqueueInfo(FileHandleBase* aFileHandle,
-                             FileHelper* aFileHelper);
-
-    void
-    LockFileForReading(const nsAString& aFileName)
-    {
-      mFilesReading.PutEntry(aFileName);
-    }
-
-    void
-    LockFileForWriting(const nsAString& aFileName)
-    {
-      mFilesWriting.PutEntry(aFileName);
-    }
-
-    bool
-    IsFileLockedForReading(const nsAString& aFileName)
-    {
-      return mFilesReading.Contains(aFileName);
-    }
-
-    bool
-    IsFileLockedForWriting(const nsAString& aFileName)
-    {
-      return mFilesWriting.Contains(aFileName);
-    }
-
-  private:
-    StorageInfo()
-    {
-    }
-
-    nsTArray<nsRefPtr<FileHandleQueue>> mFileHandleQueues;
-    nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos;
-    nsTHashtable<nsStringHashKey> mFilesReading;
-    nsTHashtable<nsStringHashKey> mFilesWriting;
-  };
-
-  struct StoragesCompleteCallback
-  {
-    nsTArray<nsCString> mStorageIds;
-    nsCOMPtr<nsIRunnable> mCallback;
-  };
-
-  FileService();
-  ~FileService();
-
-  nsresult
-  Init();
-
-  nsresult
-  Cleanup();
-
-  bool
-  MaybeFireCallback(StoragesCompleteCallback& aCallback);
-
-  nsRefPtr<nsThreadPool> mThreadPool;
-  nsClassHashtable<nsCStringHashKey, StorageInfo> mStorageInfos;
-  nsTArray<StoragesCompleteCallback> mCompleteCallbacks;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_FileService_h
deleted file mode 100644
--- a/dom/filehandle/FileStreamWrappers.cpp
+++ /dev/null
@@ -1,415 +0,0 @@
-/* -*- 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 "FileStreamWrappers.h"
-
-#include "FileHelper.h"
-#include "MainThreadUtils.h"
-#include "mozilla/Assertions.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/ipc/InputStreamParams.h"
-#include "MutableFile.h"
-#include "nsDebug.h"
-#include "nsError.h"
-#include "nsIRunnable.h"
-#include "nsISeekableStream.h"
-#include "nsThreadUtils.h"
-#include "nsQueryObject.h"
-
-#ifdef DEBUG
-#include "nsXULAppAPI.h"
-#endif
-
-namespace mozilla {
-namespace dom {
-
-namespace {
-
-class ProgressRunnable final : public nsIRunnable
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-
-  ProgressRunnable(FileHelper* aFileHelper,
-                   uint64_t aProgress,
-                   uint64_t aProgressMax)
-  : mFileHelper(aFileHelper),
-    mProgress(aProgress),
-    mProgressMax(aProgressMax)
-  {
-  }
-
-private:
-  ~ProgressRunnable() {}
-
-  nsRefPtr<FileHelper> mFileHelper;
-  uint64_t mProgress;
-  uint64_t mProgressMax;
-};
-
-class CloseRunnable final : public nsIRunnable
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-
-  explicit CloseRunnable(FileHelper* aFileHelper)
-  : mFileHelper(aFileHelper)
-  { }
-
-private:
-  ~CloseRunnable() {}
-
-  nsRefPtr<FileHelper> mFileHelper;
-};
-
-class DestroyRunnable final : public nsIRunnable
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-
-  explicit DestroyRunnable(FileHelper* aFileHelper)
-  : mFileHelper(aFileHelper)
-  { }
-
-private:
-  ~DestroyRunnable() {}
-
-  nsRefPtr<FileHelper> mFileHelper;
-};
-
-} // namespace
-
-FileStreamWrapper::FileStreamWrapper(nsISupports* aFileStream,
-                                     FileHelper* aFileHelper,
-                                     uint64_t aOffset,
-                                     uint64_t aLimit,
-                                     uint32_t aFlags)
-: mFileStream(aFileStream),
-  mFileHelper(aFileHelper),
-  mOffset(aOffset),
-  mLimit(aLimit),
-  mFlags(aFlags),
-  mFirstTime(true)
-{
-  NS_ASSERTION(mFileStream, "Must have a file stream!");
-  NS_ASSERTION(mFileHelper, "Must have a file helper!");
-}
-
-FileStreamWrapper::~FileStreamWrapper()
-{
-  if (mFlags & NOTIFY_DESTROY) {
-    if (NS_IsMainThread()) {
-      mFileHelper->OnStreamDestroy();
-    }
-    else {
-      nsCOMPtr<nsIRunnable> runnable =
-        new DestroyRunnable(mFileHelper);
-
-      nsresult rv = NS_DispatchToMainThread(runnable);
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Failed to dispatch to the main thread!");
-      }
-    }
-  }
-}
-
-NS_IMPL_ISUPPORTS0(FileStreamWrapper)
-
-FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream,
-                                               FileHelper* aFileHelper,
-                                               uint64_t aOffset,
-                                               uint64_t aLimit,
-                                               uint32_t aFlags)
-: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
-{
-  mInputStream = do_QueryInterface(mFileStream);
-  NS_ASSERTION(mInputStream, "This should always succeed!");
-}
-
-NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper,
-                            FileStreamWrapper,
-                            nsIInputStream,
-                            nsIIPCSerializableInputStream)
-
-NS_IMETHODIMP
-FileInputStreamWrapper::Close()
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  if (mFlags & NOTIFY_CLOSE) {
-    nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
-
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-      NS_WARNING("Failed to dispatch to the main thread!");
-    }
-  }
-
-  mOffset = 0;
-  mLimit = 0;
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FileInputStreamWrapper::Available(uint64_t* _retval)
-{
-  // Performing sync IO on the main thread is generally not allowed.
-  // However, the input stream wrapper is also used to track reads performed by
-  // other APIs like FileReader, XHR, etc.
-  // In that case nsInputStreamChannel::OpenContentStream() calls Available()
-  // before setting the content length property. This property is not important
-  // to perform reads properly, so we can just return NS_BASE_STREAM_CLOSED
-  // here. It causes OpenContentStream() to set the content length property to
-  // zero.
-
-  if (NS_IsMainThread()) {
-    return NS_BASE_STREAM_CLOSED;
-  }
-
-  return mInputStream->Available(_retval);
-}
-
-NS_IMETHODIMP
-FileInputStreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  nsresult rv;
-
-  if (mFirstTime) {
-    mFirstTime = false;
-
-    if (mOffset != UINT64_MAX) {
-      nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
-      if (seekable) {
-        rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-    }
-
-    mOffset = 0;
-  }
-
-  uint64_t max = mLimit - mOffset;
-  if (max == 0) {
-    *_retval = 0;
-    return NS_OK;
-  }
-
-  if (aCount > max) {
-    aCount = max;
-  }
-
-  rv = mInputStream->Read(aBuf, aCount, _retval);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mOffset += *_retval;
-
-  if (mFlags & NOTIFY_PROGRESS) {
-    nsCOMPtr<nsIRunnable> runnable =
-      new ProgressRunnable(mFileHelper, mOffset, mLimit);
-
-    rv = NS_DispatchToMainThread(runnable);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to dispatch to the main thread!");
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FileInputStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
-                                     uint32_t aCount, uint32_t* _retval)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-FileInputStreamWrapper::IsNonBlocking(bool* _retval)
-{
-  *_retval = false;
-  return NS_OK;
-}
-
-void
-FileInputStreamWrapper::Serialize(InputStreamParams& aParams,
-                                  FileDescriptorArray& /* aFDs */)
-{
-  MOZ_ASSERT(XRE_IsParentProcess());
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsCOMPtr<nsIInputStream> thisStream = do_QueryObject(this);
-
-  aParams = mozilla::ipc::SameProcessInputStreamParams(
-    reinterpret_cast<intptr_t>(thisStream.forget().take()));
-}
-
-bool
-FileInputStreamWrapper::Deserialize(const InputStreamParams& /* aParams */,
-                                    const FileDescriptorArray& /* aFDs */)
-{
-  MOZ_CRASH("Should never get here!");
-}
-
-FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream,
-                                                 FileHelper* aFileHelper,
-                                                 uint64_t aOffset,
-                                                 uint64_t aLimit,
-                                                 uint32_t aFlags)
-: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
-{
-  mOutputStream = do_QueryInterface(mFileStream);
-  NS_ASSERTION(mOutputStream, "This should always succeed!");
-}
-
-NS_IMPL_ISUPPORTS_INHERITED(FileOutputStreamWrapper,
-                            FileStreamWrapper,
-                            nsIOutputStream)
-
-NS_IMETHODIMP
-FileOutputStreamWrapper::Close()
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  nsresult rv = NS_OK;
-
-  if (mFlags & NOTIFY_CLOSE) {
-    nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
-
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-      NS_WARNING("Failed to dispatch to the main thread!");
-    }
-  }
-
-  mOffset = 0;
-  mLimit = 0;
-
-  return rv;
-}
-
-NS_IMETHODIMP
-FileOutputStreamWrapper::Write(const char* aBuf, uint32_t aCount,
-                               uint32_t* _retval)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-  nsresult rv;
-
-  if (mFirstTime) {
-    mFirstTime = false;
-
-    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mOutputStream);
-    if (seekable) {
-      if (mOffset == UINT64_MAX) {
-        rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
-      }
-      else {
-        rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
-      }
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    mOffset = 0;
-  }
-
-  uint64_t max = mLimit - mOffset;
-  if (max == 0) {
-    *_retval = 0;
-    return NS_OK;
-  }
-
-  if (aCount > max) {
-    aCount = max;
-  }
-
-  rv = mOutputStream->Write(aBuf, aCount, _retval);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mOffset += *_retval;
-
-  if (mFlags & NOTIFY_PROGRESS) {
-    nsCOMPtr<nsIRunnable> runnable =
-      new ProgressRunnable(mFileHelper, mOffset, mLimit);
-
-    NS_DispatchToMainThread(runnable);
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FileOutputStreamWrapper::Flush()
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FileOutputStreamWrapper::WriteFrom(nsIInputStream* aFromStream,
-                                   uint32_t aCount, uint32_t* _retval)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-FileOutputStreamWrapper::WriteSegments(nsReadSegmentFun aReader,
-                                       void* aClosure, uint32_t aCount,
-                                       uint32_t* _retval)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-FileOutputStreamWrapper::IsNonBlocking(bool* _retval)
-{
-  *_retval = false;
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(ProgressRunnable, nsIRunnable)
-
-NS_IMETHODIMP
-ProgressRunnable::Run()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  mFileHelper->OnStreamProgress(mProgress, mProgressMax);
-  mFileHelper = nullptr;
-
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(CloseRunnable, nsIRunnable)
-
-NS_IMETHODIMP
-CloseRunnable::Run()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  mFileHelper->OnStreamClose();
-  mFileHelper = nullptr;
-
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(DestroyRunnable, nsIRunnable)
-
-NS_IMETHODIMP
-DestroyRunnable::Run()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  mFileHelper->OnStreamDestroy();
-  mFileHelper = nullptr;
-
-  return NS_OK;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/filehandle/FileStreamWrappers.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_FileStreamWrappers_h
-#define mozilla_dom_FileStreamWrappers_h
-
-#include "nsAutoPtr.h"
-#include "nsCOMPtr.h"
-#include "nsIInputStream.h"
-#include "nsIOutputStream.h"
-#include "nsIIPCSerializableInputStream.h"
-
-namespace mozilla {
-namespace ipc {
-class InputStreamParams;
-} // namespace ipc
-
-namespace dom {
-
-class FileHelper;
-
-class FileStreamWrapper : public nsISupports
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  FileStreamWrapper(nsISupports* aFileStream,
-                    FileHelper* aFileHelper,
-                    uint64_t aOffset,
-                    uint64_t aLimit,
-                    uint32_t aFlags);
-
-  enum {
-    NOTIFY_PROGRESS = 1 << 0,
-    NOTIFY_CLOSE = 1 << 1,
-    NOTIFY_DESTROY = 1 << 2
-  };
-
-protected:
-  virtual ~FileStreamWrapper();
-
-  nsCOMPtr<nsISupports> mFileStream;
-  nsRefPtr<FileHelper> mFileHelper;
-  uint64_t mOffset;
-  uint64_t mLimit;
-  uint32_t mFlags;
-  bool mFirstTime;
-};
-
-class FileInputStreamWrapper : public FileStreamWrapper,
-                               public nsIInputStream,
-                               public nsIIPCSerializableInputStream
-{
-  typedef mozilla::ipc::InputStreamParams InputStreamParams;
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIINPUTSTREAM
-  NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
-
-  FileInputStreamWrapper(nsISupports* aFileStream,
-                         FileHelper* aFileHelper,
-                         uint64_t aOffset,
-                         uint64_t aLimit,
-                         uint32_t aFlags);
-
-protected:
-  virtual ~FileInputStreamWrapper()
-  { }
-
-private:
-  nsCOMPtr<nsIInputStream> mInputStream;
-};
-
-class FileOutputStreamWrapper : public FileStreamWrapper,
-                                public nsIOutputStream
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIOUTPUTSTREAM
-
-  FileOutputStreamWrapper(nsISupports* aFileStream,
-                          FileHelper* aFileHelper,
-                          uint64_t aOffset,
-                          uint64_t aLimit,
-                          uint32_t aFlags);
-
-protected:
-  virtual ~FileOutputStreamWrapper()
-  { }
-
-private:
-  nsCOMPtr<nsIOutputStream> mOutputStream;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_FileStreamWrappers_h
deleted file mode 100644
--- a/dom/filehandle/MemoryStreams.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- 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 "MemoryStreams.h"
-
-#include "nsAutoPtr.h"
-#include "nsCOMPtr.h"
-#include "nsDebug.h"
-#include "nsError.h"
-#include "nsStreamUtils.h"
-
-namespace mozilla {
-namespace dom {
-
-// static
-already_AddRefed<MemoryOutputStream>
-MemoryOutputStream::Create(uint64_t aSize)
-{
-  NS_ASSERTION(aSize, "Passed zero size!");
-
-  NS_ENSURE_TRUE(aSize <= UINT32_MAX, nullptr);
-
-  nsRefPtr<MemoryOutputStream> stream = new MemoryOutputStream();
-
-  char* dummy;
-  uint32_t length = stream->mData.GetMutableData(&dummy, aSize, fallible);
-  NS_ENSURE_TRUE(length == aSize, nullptr);
-
-  return stream.forget();
-}
-
-NS_IMPL_ISUPPORTS(MemoryOutputStream, nsIOutputStream)
-
-NS_IMETHODIMP
-MemoryOutputStream::Close()
-{
-  mData.Truncate(mOffset);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MemoryOutputStream::Write(const char* aBuf, uint32_t aCount, uint32_t* _retval)
-{
-  return WriteSegments(NS_CopySegmentToBuffer, (char*)aBuf, aCount, _retval);
-}
-
-NS_IMETHODIMP
-MemoryOutputStream::Flush()
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MemoryOutputStream::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
-                              uint32_t* _retval)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-MemoryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
-                                  uint32_t aCount, uint32_t* _retval)
-{
-  NS_ASSERTION(mData.Length() >= mOffset, "Bad stream state!");
-
-  uint32_t maxCount = mData.Length() - mOffset;
-  if (maxCount == 0) {
-    *_retval = 0;
-    return NS_OK;
-  }
-
-  if (aCount > maxCount) {
-    aCount = maxCount;
-  }
-
-  nsresult rv = aReader(this, aClosure, mData.BeginWriting() + mOffset, 0,
-                        aCount, _retval);
-  if (NS_SUCCEEDED(rv)) {
-    NS_ASSERTION(*_retval <= aCount,
-                 "Reader should not read more than we asked it to read!");
-    mOffset += *_retval;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MemoryOutputStream::IsNonBlocking(bool* _retval)
-{
-  *_retval = false;
-  return NS_OK;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/filehandle/MemoryStreams.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_MemoryStreams_h
-#define mozilla_dom_MemoryStreams_h
-
-#include "nsIOutputStream.h"
-#include "nsString.h"
-
-template <class> struct already_AddRefed;
-
-namespace mozilla {
-namespace dom {
-
-class MemoryOutputStream : public nsIOutputStream
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIOUTPUTSTREAM
-
-  static already_AddRefed<MemoryOutputStream>
-  Create(uint64_t aSize);
-
-  const nsCString&
-  Data() const
-  {
-    return mData;
-  }
-
-private:
-  MemoryOutputStream()
-  : mOffset(0)
-  { }
-
-  virtual ~MemoryOutputStream()
-  { }
-
-  nsCString mData;
-  uint64_t mOffset;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_MemoryStreams_h
deleted file mode 100644
--- a/dom/filehandle/MetadataHelper.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/* -*- 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 "MetadataHelper.h"
-
-#include "FileHandle.h"
-#include "js/Value.h"
-#include "js/RootingAPI.h"
-#include "jsapi.h"
-#include "js/Date.h"
-#include "mozilla/dom/FileModeBinding.h"
-#include "nsDebug.h"
-#include "nsIFileStreams.h"
-#include "nsIOutputStream.h"
-
-namespace mozilla {
-namespace dom {
-
-nsresult
-MetadataHelper::DoAsyncRun(nsISupports* aStream)
-{
-  bool readWrite = mFileHandle->mMode == FileMode::Readwrite;
-
-  nsRefPtr<AsyncMetadataGetter> getter =
-    new AsyncMetadataGetter(aStream, mParams, readWrite);
-
-  nsresult rv = getter->AsyncWork(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-MetadataHelper::GetSuccessResult(JSContext* aCx,
-                                 JS::MutableHandle<JS::Value> aVal)
-{
-  JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
-  NS_ENSURE_TRUE(obj, NS_ERROR_OUT_OF_MEMORY);
-
-  if (mParams->SizeRequested()) {
-    JS::Rooted<JS::Value> val(aCx, JS_NumberValue(mParams->Size()));
-
-    if (!JS_DefineProperty(aCx, obj, "size", val, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  if (mParams->LastModifiedRequested()) {
-    double msec = mParams->LastModified();
-    JSObject *date = JS::NewDateObject(aCx, JS::TimeClip(msec));
-    NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY);
-
-    JS::Rooted<JS::Value> dateRoot(aCx, JS::ObjectValue(*date));
-    if (!JS_DefineProperty(aCx, obj, "lastModified", dateRoot,
-                           JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  aVal.setObject(*obj);
-  return NS_OK;
-}
-
-nsresult
-MetadataHelper::AsyncMetadataGetter::DoStreamWork(nsISupports* aStream)
-{
-  nsresult rv;
-
-  if (mReadWrite) {
-    // Force a flush (so all pending writes are flushed to the disk and file
-    // metadata is updated too).
-
-    nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
-    NS_ASSERTION(ostream, "This should always succeed!");
-
-    rv = ostream->Flush();
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  nsCOMPtr<nsIFileMetadata> metadata = do_QueryInterface(aStream);
-
-  if (mParams->SizeRequested()) {
-    int64_t size;
-    rv = metadata->GetSize(&size);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    NS_ENSURE_TRUE(size >= 0, NS_ERROR_FAILURE);
-
-    mParams->mSize = uint64_t(size);
-  }
-
-  if (mParams->LastModifiedRequested()) {
-    int64_t lastModified;
-    rv = metadata->GetLastModified(&lastModified);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mParams->mLastModified = lastModified;
-  }
-
-  return NS_OK;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/filehandle/MetadataHelper.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_MetadataHelper_h
-#define mozilla_dom_MetadataHelper_h
-
-#include "FileHelper.h"
-#include "js/TypeDecls.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/dom/AsyncHelper.h"
-#include "nsAutoPtr.h"
-
-namespace mozilla {
-namespace dom {
-
-class MetadataHelper;
-
-class MetadataParameters final
-{
-  friend class MetadataHelper;
-
-public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataParameters)
-
-  MetadataParameters(bool aSizeRequested, bool aLastModifiedRequested)
-    : mSizeRequested(aSizeRequested)
-    , mLastModifiedRequested(aLastModifiedRequested)
-  {
-  }
-
-  bool
-  IsConfigured() const
-  {
-    return mSizeRequested || mLastModifiedRequested;
-  }
-
-  bool
-  SizeRequested() const
-  {
-    return mSizeRequested;
-  }
-
-  bool
-  LastModifiedRequested() const
-  {
-    return mLastModifiedRequested;
-  }
-
-  uint64_t
-  Size() const
-  {
-    return mSize;
-  }
-
-  int64_t
-  LastModified() const
-  {
-    return mLastModified;
-  }
-
-private:
-  // Private destructor, to discourage deletion outside of Release():
-  ~MetadataParameters()
-  {
-  }
-
-  uint64_t mSize;
-  int64_t mLastModified;
-  bool mSizeRequested;
-  bool mLastModifiedRequested;
-};
-
-class MetadataHelper : public FileHelper
-{
-public:
-  MetadataHelper(FileHandleBase* aFileHandle,
-                 FileRequestBase* aFileRequest,
-                 MetadataParameters* aParams)
-  : FileHelper(aFileHandle, aFileRequest),
-    mParams(aParams)
-  { }
-
-  nsresult
-  DoAsyncRun(nsISupports* aStream) override;
-
-  nsresult
-  GetSuccessResult(JSContext* aCx,
-                   JS::MutableHandle<JS::Value> aVal) override;
-
-protected:
-  class AsyncMetadataGetter : public AsyncHelper
-  {
-  public:
-    AsyncMetadataGetter(nsISupports* aStream, MetadataParameters* aParams,
-                        bool aReadWrite)
-    : AsyncHelper(aStream),
-      mParams(aParams), mReadWrite(aReadWrite)
-    { }
-
-  protected:
-    nsresult
-    DoStreamWork(nsISupports* aStream) override;
-
-  private:
-    nsRefPtr<MetadataParameters> mParams;
-    bool mReadWrite;
-  };
-
-  nsRefPtr<MetadataParameters> mParams;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_MetadataHelper_h
rename from dom/filehandle/MutableFile.cpp
rename to dom/filehandle/MutableFileBase.cpp
--- a/dom/filehandle/MutableFile.cpp
+++ b/dom/filehandle/MutableFileBase.cpp
@@ -1,52 +1,36 @@
 /* -*- 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 "MutableFile.h"
+#include "MutableFileBase.h"
 
-#include "nsDebug.h"
-#include "nsIFile.h"
-#include "nsIFileStreams.h"
-#include "nsIInputStream.h"
-#include "nsNetUtil.h"
+#include "ActorsChild.h"
+#include "mozilla/Assertions.h"
+#include "prthread.h"
 
 namespace mozilla {
 namespace dom {
 
-MutableFileBase::MutableFileBase()
+MutableFileBase::MutableFileBase(DEBUGONLY(PRThread* aOwningThread,)
+                                 BackgroundMutableFileChildBase* aActor)
+  : RefCountedThreadObject(DEBUGONLY(aOwningThread))
+  , mBackgroundActor(aActor)
 {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
 }
 
 MutableFileBase::~MutableFileBase()
 {
-}
-
-// virtual
-already_AddRefed<nsISupports>
-MutableFileBase::CreateStream(bool aReadOnly)
-{
-  nsresult rv;
+  AssertIsOnOwningThread();
 
-  if (aReadOnly) {
-    nsCOMPtr<nsIInputStream> stream;
-    rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1,
-                                    nsIFileInputStream::DEFER_OPEN);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return nullptr;
-    }
-    return stream.forget();
+  if (mBackgroundActor) {
+    mBackgroundActor->SendDeleteMeInternal();
+    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
   }
-
-  nsCOMPtr<nsIFileStream> stream;
-  rv = NS_NewLocalFileStream(getter_AddRefs(stream), mFile, -1, -1,
-                             nsIFileStream::DEFER_OPEN);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-  return stream.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
rename from dom/filehandle/MutableFile.h
rename to dom/filehandle/MutableFileBase.h
--- a/dom/filehandle/MutableFile.h
+++ b/dom/filehandle/MutableFileBase.h
@@ -2,65 +2,75 @@
 /* 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/. */
 
 #ifndef mozilla_dom_MutableFile_h
 #define mozilla_dom_MutableFile_h
 
-#include "nsCOMPtr.h"
-#include "nsString.h"
+#include "mozilla/dom/FileHandleCommon.h"
+#include "nscore.h"
 
-class nsIFile;
-class nsIOfflineStorage;
+template <class> struct already_AddRefed;
+class nsISupports;
+class nsString;
+struct PRThread;
 
 namespace mozilla {
 namespace dom {
 
-class FileService;
+class BackgroundMutableFileChildBase;
+class BlobImpl;
+class File;
+class FileHandleBase;
 
 /**
  * This class provides a base for MutableFile implementations.
- * The subclasses can override implementation of IsInvalid and CreateStream.
  * (for example IDBMutableFile provides IndexedDB specific implementation).
  */
 class MutableFileBase
+  : public RefCountedThreadObject
 {
-  friend class FileService;
+protected:
+  BackgroundMutableFileChildBase* mBackgroundActor;
 
 public:
-  NS_IMETHOD_(MozExternalRefCountType)
-  AddRef() = 0;
+  BackgroundMutableFileChildBase*
+  GetBackgroundActor() const
+  {
+    AssertIsOnOwningThread();
 
-  NS_IMETHOD_(MozExternalRefCountType)
-  Release() = 0;
-
-  virtual bool
-  IsInvalid()
-  {
-    return false;
+    return mBackgroundActor;
   }
 
-  // A temporary method that will be removed along with nsIOfflineStorage
-  // interface.
-  virtual nsIOfflineStorage*
-  Storage() = 0;
+  void
+  ClearBackgroundActor()
+  {
+    AssertIsOnOwningThread();
+
+    mBackgroundActor = nullptr;
+  }
 
-  virtual already_AddRefed<nsISupports>
-  CreateStream(bool aReadOnly);
+  virtual const nsString&
+  Name() const = 0;
+
+  virtual const nsString&
+  Type() const = 0;
 
+  virtual bool
+  IsInvalidated() = 0;
+
+  virtual already_AddRefed<File>
+  CreateFileFor(BlobImpl* aBlobImpl,
+                FileHandleBase* aFileHandle) = 0;
 
 protected:
-  MutableFileBase();
+  MutableFileBase(DEBUGONLY(PRThread* aOwningThread,)
+                  BackgroundMutableFileChildBase* aActor);
 
   virtual ~MutableFileBase();
-
-  nsCOMPtr<nsIFile> mFile;
-
-  nsCString mStorageId;
-  nsString mFileName;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MutableFile_h
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/PBackgroundFileHandle.ipdl
@@ -0,0 +1,91 @@
+/* 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 PBackgroundFileRequest;
+include protocol PBackgroundMutableFile;
+include protocol PBlob;
+
+namespace mozilla {
+namespace dom {
+
+struct FileRequestGetMetadataParams
+{
+  bool size;
+  bool lastModified;
+};
+
+struct FileRequestReadParams
+{
+  uint64_t offset;
+  uint64_t size;
+};
+
+struct FileRequestStringData
+{
+  nsCString string;
+};
+
+struct FileRequestBlobData
+{
+  PBlob blob;
+};
+
+union FileRequestData
+{
+  FileRequestStringData;
+  FileRequestBlobData;
+};
+
+struct FileRequestWriteParams
+{
+  uint64_t offset;
+  FileRequestData data;
+  uint64_t dataLength;
+};
+
+struct FileRequestTruncateParams
+{
+  uint64_t offset;
+};
+
+struct FileRequestFlushParams
+{
+};
+
+struct FileRequestGetFileParams
+{
+};
+
+union FileRequestParams
+{
+  FileRequestGetMetadataParams;
+  FileRequestReadParams;
+  FileRequestWriteParams;
+  FileRequestTruncateParams;
+  FileRequestFlushParams;
+  FileRequestGetFileParams;
+};
+
+protocol PBackgroundFileHandle
+{
+  manager PBackgroundMutableFile;
+
+  manages PBackgroundFileRequest;
+
+parent:
+  DeleteMe();
+
+  Finish();
+  Abort();
+
+  PBackgroundFileRequest(FileRequestParams params);
+
+child:
+  __delete__();
+
+  Complete(bool aborted);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/PBackgroundFileRequest.ipdl
@@ -0,0 +1,83 @@
+/* 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 PBackgroundFileHandle;
+include protocol PBlob;
+
+using struct mozilla::void_t
+  from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+union FileRequestSize
+{
+  void_t;
+  uint64_t;
+};
+
+union FileRequestLastModified
+{
+  void_t;
+  int64_t;
+};
+
+struct FileRequestMetadata
+{
+  FileRequestSize size;
+  FileRequestLastModified lastModified;
+};
+
+struct FileRequestGetMetadataResponse
+{
+  FileRequestMetadata metadata;
+};
+
+struct FileRequestReadResponse
+{
+  nsCString data;
+};
+
+struct FileRequestWriteResponse
+{
+};
+
+struct FileRequestTruncateResponse
+{
+};
+
+struct FileRequestFlushResponse
+{
+};
+
+struct FileRequestGetFileResponse
+{
+  PBlob file;
+  FileRequestMetadata metadata;
+};
+
+union FileRequestResponse
+{
+  nsresult;
+  FileRequestGetMetadataResponse;
+  FileRequestReadResponse;
+  FileRequestWriteResponse;
+  FileRequestTruncateResponse;
+  FileRequestFlushResponse;
+  FileRequestGetFileResponse;
+};
+
+protocol PBackgroundFileRequest
+{
+  manager PBackgroundFileHandle;
+
+child:
+  __delete__(FileRequestResponse response);
+
+  Progress(uint64_t progress,
+           uint64_t progressMax);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/PBackgroundMutableFile.ipdl
@@ -0,0 +1,36 @@
+/* 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 PBackgroundFileHandle;
+include protocol PBackgroundIDBDatabase;
+
+include "mozilla/dom/filehandle/SerializationHelpers.h";
+
+using mozilla::dom::FileMode
+  from "mozilla/dom/FileModeBinding.h";
+
+namespace mozilla {
+namespace dom {
+
+sync protocol PBackgroundMutableFile
+{
+  manager PBackgroundIDBDatabase;
+
+  manages PBackgroundFileHandle;
+
+parent:
+  DeleteMe();
+
+  PBackgroundFileHandle(FileMode mode);
+
+  // Use only for testing!
+  sync GetFileId()
+    returns (int64_t fileId);
+
+child:
+  __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/filehandle/SerializationHelpers.h
@@ -0,0 +1,23 @@
+/* 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_filehandle_SerializationHelpers_h
+#define mozilla_dom_filehandle_SerializationHelpers_h
+
+#include "ipc/IPCMessageUtils.h"
+
+#include "mozilla/dom/FileModeBinding.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::dom::FileMode> :
+  public ContiguousEnumSerializer<mozilla::dom::FileMode,
+                                  mozilla::dom::FileMode::Readonly,
+                                  mozilla::dom::FileMode::EndGuard_>
+{ };
+
+} // namespace IPC
+
+#endif // mozilla_dom_filehandle_SerializationHelpers_h
--- a/dom/filehandle/moz.build
+++ b/dom/filehandle/moz.build
@@ -1,34 +1,40 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+EXPORTS.mozilla.dom.filehandle += [
+    'ActorsChild.h',
+    'ActorsParent.h',
+    'SerializationHelpers.h',
+]
+
 EXPORTS.mozilla.dom += [
-    'AsyncHelper.h',
-    'FileHandle.h',
-    'FileHelper.h',
-    'FileRequest.h',
-    'FileService.h',
-    'MetadataHelper.h',
-    'MutableFile.h',
+    'FileHandleBase.h',
+    'FileHandleCommon.h',
+    'FileHandleStorage.h',
+    'FileRequestBase.h',
+    'MutableFileBase.h',
 ]
 
 UNIFIED_SOURCES += [
-    'AsyncHelper.cpp',
-    'FileHandle.cpp',
-    'FileHelper.cpp',
-    'FileRequest.cpp',
-    'FileService.cpp',
-    'FileStreamWrappers.cpp',
-    'MemoryStreams.cpp',
-    'MetadataHelper.cpp',
-    'MutableFile.cpp',
+    'ActorsChild.cpp',
+    'ActorsParent.cpp',
+    'FileHandleBase.cpp',
+    'FileHandleCommon.cpp',
+    'MutableFileBase.cpp',
+]
+
+IPDL_SOURCES += [
+    'PBackgroundFileHandle.ipdl',
+    'PBackgroundFileRequest.ipdl',
+    'PBackgroundMutableFile.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '../base',
 ]
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -373,18 +373,17 @@ HTMLInputElement::nsFilePickerShownCallb
     return NS_OK;
   }
 
   int16_t mode;
   mFilePicker->GetMode(&mode);
 
   // Collect new selected filenames
   nsTArray<nsRefPtr<File>> newFiles;
-  if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple) ||
-      mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder)) {
+  if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)) {
     nsCOMPtr<nsISimpleEnumerator> iter;
     nsresult rv = mFilePicker->GetDomfiles(getter_AddRefs(iter));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!iter) {
       return NS_OK;
     }
 
@@ -396,17 +395,18 @@ HTMLInputElement::nsFilePickerShownCallb
       nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(tmp);
       MOZ_ASSERT(domBlob,
                  "Null file object from FilePicker's file enumerator?");
       if (domBlob) {
         newFiles.AppendElement(static_cast<File*>(domBlob.get()));
       }
     }
   } else {
-    MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen));
+    MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen) ||
+               mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder));
     nsCOMPtr<nsISupports> tmp;
     nsresult rv = mFilePicker->GetDomfile(getter_AddRefs(tmp));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(tmp);
     if (blob) {
       nsRefPtr<File> file = static_cast<Blob*>(blob.get())->ToFile();
       newFiles.AppendElement(file);
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -2,21 +2,21 @@
 /* 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 "ActorsChild.h"
 
 #include "BackgroundChildImpl.h"
-#include "FileManager.h"
 #include "IDBDatabase.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBIndex.h"
+#include "IDBMutableFile.h"
 #include "IDBObjectStore.h"
 #include "IDBMutableFile.h"
 #include "IDBRequest.h"
 #include "IDBTransaction.h"
 #include "IndexedDatabase.h"
 #include "IndexedDatabaseInlines.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/Maybe.h"
@@ -592,52 +592,93 @@ protected:
 
 void
 ConvertActorsToBlobs(IDBDatabase* aDatabase,
                      const SerializedStructuredCloneReadInfo& aCloneReadInfo,
                      nsTArray<StructuredCloneFile>& aFiles)
 {
   MOZ_ASSERT(aFiles.IsEmpty());
 
-  const nsTArray<PBlobChild*>& blobs = aCloneReadInfo.blobsChild();
-  const nsTArray<intptr_t>& fileInfos = aCloneReadInfo.fileInfos();
-
-  MOZ_ASSERT_IF(IndexedDatabaseManager::IsMainProcess(),
-                blobs.Length() == fileInfos.Length());
-  MOZ_ASSERT_IF(!IndexedDatabaseManager::IsMainProcess(), fileInfos.IsEmpty());
+  const nsTArray<BlobOrMutableFile>& blobs = aCloneReadInfo.blobs();
 
   if (!blobs.IsEmpty()) {
     const uint32_t count = blobs.Length();
     aFiles.SetCapacity(count);
 
     for (uint32_t index = 0; index < count; index++) {
-      BlobChild* actor = static_cast<BlobChild*>(blobs[index]);
-
-      nsRefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
-      MOZ_ASSERT(blobImpl);
-
-      nsRefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl);
-
-      nsRefPtr<FileInfo> fileInfo;
-      if (!fileInfos.IsEmpty()) {
-        fileInfo = dont_AddRef(reinterpret_cast<FileInfo*>(fileInfos[index]));
-
-        MOZ_ASSERT(fileInfo);
-        MOZ_ASSERT(fileInfo->Id() > 0);
-
-        blob->AddFileInfo(fileInfo);
+      const BlobOrMutableFile& blobOrMutableFile = blobs[index];
+
+      switch (blobOrMutableFile.type()) {
+        case BlobOrMutableFile::TPBlobChild: {
+          auto* actor =
+            static_cast<BlobChild*>(blobOrMutableFile.get_PBlobChild());
+
+          nsRefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
+          MOZ_ASSERT(blobImpl);
+
+          nsRefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl);
+
+          aDatabase->NoteReceivedBlob(blob);
+
+          StructuredCloneFile* file = aFiles.AppendElement();
+          MOZ_ASSERT(file);
+
+          file->mMutable = false;
+          file->mBlob.swap(blob);
+
+          break;
+        }
+
+        case BlobOrMutableFile::TNullableMutableFile: {
+          const NullableMutableFile& nullableMutableFile =
+            blobOrMutableFile.get_NullableMutableFile();
+
+          switch (nullableMutableFile.type()) {
+            case NullableMutableFile::Tnull_t: {
+              StructuredCloneFile* file = aFiles.AppendElement();
+              MOZ_ASSERT(file);
+
+              file->mMutable = true;
+
+              break;
+            }
+
+            case NullableMutableFile::TPBackgroundMutableFileChild: {
+              auto* actor =
+                static_cast<BackgroundMutableFileChild*>(
+                  nullableMutableFile.get_PBackgroundMutableFileChild());
+              MOZ_ASSERT(actor);
+
+              actor->EnsureDOMObject();
+
+              auto* mutableFile =
+                static_cast<IDBMutableFile*>(actor->GetDOMObject());
+              MOZ_ASSERT(mutableFile);
+
+              StructuredCloneFile* file = aFiles.AppendElement();
+              MOZ_ASSERT(file);
+
+              file->mMutable = true;
+              file->mMutableFile = mutableFile;
+
+              actor->ReleaseDOMObject();
+
+              break;
+            }
+
+            default:
+              MOZ_CRASH("Should never get here!");
+          }
+
+          break;
+        }
+
+        default:
+          MOZ_CRASH("Should never get here!");
       }
-
-      aDatabase->NoteReceivedBlob(blob);
-
-      StructuredCloneFile* file = aFiles.AppendElement();
-      MOZ_ASSERT(file);
-
-      file->mBlob.swap(blob);
-      file->mFileInfo.swap(fileInfo);
     }
   }
 }
 
 void
 DispatchErrorEvent(IDBRequest* aRequest,
                    nsresult aErrorCode,
                    IDBTransaction* aTransaction = nullptr,
@@ -1120,16 +1161,23 @@ BackgroundFactoryChild::AssertIsOnOwning
 {
   MOZ_ASSERT(mOwningThread);
 
   bool current;
   MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)));
   MOZ_ASSERT(current);
 }
 
+nsIEventTarget*
+BackgroundFactoryChild::OwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+  return mOwningThread;
+}
+
 #endif // DEBUG
 
 void
 BackgroundFactoryChild::SendDeleteMeInternal()
 {
   AssertIsOnOwningThread();
 
   if (mFactory) {
@@ -1521,17 +1569,17 @@ BackgroundDatabaseChild::EnsureDOMObject
 
   if (mTemporaryStrongDatabase) {
     MOZ_ASSERT(!mSpec);
     return;
   }
 
   MOZ_ASSERT(mSpec);
 
-  auto request = mOpenRequestActor->GetDOMObject();
+  auto request = mOpenRequestActor->GetOpenDBRequest();
   MOZ_ASSERT(request);
 
   auto factory =
     static_cast<BackgroundFactoryChild*>(Manager())->GetDOMObject();
   MOZ_ASSERT(factory);
 
   mTemporaryStrongDatabase =
     IDBDatabase::Create(request, factory, this, mSpec);
@@ -1588,16 +1636,34 @@ BackgroundDatabaseChild::DeallocPBackgro
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
+PBackgroundIDBDatabaseRequestChild*
+BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseRequestChild(
+                                           const DatabaseRequestParams& aParams)
+{
+  MOZ_CRASH("PBackgroundIDBDatabaseRequestChild actors should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseRequestChild(
+                                     PBackgroundIDBDatabaseRequestChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundDatabaseRequestChild*>(aActor);
+  return true;
+}
+
 PBackgroundIDBTransactionChild*
 BackgroundDatabaseChild::AllocPBackgroundIDBTransactionChild(
                                     const nsTArray<nsString>& aObjectStoreNames,
                                     const Mode& aMode)
 {
   MOZ_CRASH("PBackgroundIDBTransactionChild actors should be manually "
             "constructed!");
 }
@@ -1693,16 +1759,44 @@ BackgroundDatabaseChild::DeallocPBackgro
                             PBackgroundIDBVersionChangeTransactionChild* aActor)
 {
   MOZ_ASSERT(aActor);
 
   delete static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
   return true;
 }
 
+PBackgroundMutableFileChild*
+BackgroundDatabaseChild::AllocPBackgroundMutableFileChild(const nsString& aName,
+                                                          const nsString& aType)
+{
+  AssertIsOnOwningThread();
+
+#ifdef DEBUG
+  nsCOMPtr<nsIThread> owningThread = do_QueryInterface(OwningThread());
+
+  PRThread* owningPRThread;
+  owningThread->GetPRThread(&owningPRThread);
+#endif
+
+  return new BackgroundMutableFileChild(DEBUGONLY(owningPRThread,)
+                                        aName,
+                                        aType);
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundMutableFileChild(
+                                            PBackgroundMutableFileChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundMutableFileChild*>(aActor);
+  return true;
+}
+
 bool
 BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion,
                                            const NullableVersion& aNewVersion)
 {
   AssertIsOnOwningThread();
 
   MaybeCollectGarbageOnIPCMessage();
 
@@ -1786,16 +1880,101 @@ BackgroundDatabaseChild::RecvInvalidate(
   if (mDatabase) {
     mDatabase->Invalidate();
   }
 
   return true;
 }
 
 /*******************************************************************************
+ * BackgroundDatabaseRequestChild
+ ******************************************************************************/
+
+BackgroundDatabaseRequestChild::BackgroundDatabaseRequestChild(
+                                                         IDBDatabase* aDatabase,
+                                                         IDBRequest* aRequest)
+  : BackgroundRequestChildBase(aRequest)
+  , mDatabase(aDatabase)
+{
+  // Can't assert owning thread here because IPDL has not yet set our manager!
+  MOZ_ASSERT(aDatabase);
+  aDatabase->AssertIsOnOwningThread();
+  MOZ_ASSERT(aRequest);
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseRequestChild);
+}
+
+BackgroundDatabaseRequestChild::~BackgroundDatabaseRequestChild()
+{
+  MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseRequestChild);
+}
+
+bool
+BackgroundDatabaseRequestChild::HandleResponse(nsresult aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_FAILED(aResponse));
+  MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+
+  mRequest->Reset();
+
+  DispatchErrorEvent(mRequest, aResponse);
+
+  return true;
+}
+
+bool
+BackgroundDatabaseRequestChild::HandleResponse(
+                                     const CreateFileRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  mRequest->Reset();
+
+  auto mutableFileActor =
+    static_cast<BackgroundMutableFileChild*>(aResponse.mutableFileChild());
+  MOZ_ASSERT(mutableFileActor);
+
+  mutableFileActor->EnsureDOMObject();
+
+  auto mutableFile =
+    static_cast<IDBMutableFile*>(mutableFileActor->GetDOMObject());
+  MOZ_ASSERT(mutableFile);
+
+  ResultHelper helper(mRequest, nullptr, mutableFile);
+
+  DispatchSuccessEvent(&helper);
+
+  mutableFileActor->ReleaseDOMObject();
+
+  return true;
+}
+
+bool
+BackgroundDatabaseRequestChild::Recv__delete__(
+                                       const DatabaseRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  switch (aResponse.type()) {
+    case DatabaseRequestResponse::Tnsresult:
+      return HandleResponse(aResponse.get_nsresult());
+
+    case DatabaseRequestResponse::TCreateFileRequestResponse:
+      return HandleResponse(aResponse.get_CreateFileRequestResponse());
+
+    default:
+      MOZ_CRASH("Unknown response type!");
+  }
+
+  MOZ_CRASH("Should never get here!");
+}
+
+/*******************************************************************************
  * BackgroundTransactionBase
  ******************************************************************************/
 
 BackgroundTransactionBase::BackgroundTransactionBase()
 : mTransaction(nullptr)
 {
   MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase);
 }
@@ -2090,16 +2269,50 @@ BackgroundVersionChangeTransactionChild:
 {
   MOZ_ASSERT(aActor);
 
   delete static_cast<BackgroundCursorChild*>(aActor);
   return true;
 }
 
 /*******************************************************************************
+ * BackgroundMutableFileChild
+ ******************************************************************************/
+
+BackgroundMutableFileChild::BackgroundMutableFileChild(
+                                             DEBUGONLY(PRThread* aOwningThread,)
+                                             const nsAString& aName,
+                                             const nsAString& aType)
+  : BackgroundMutableFileChildBase(DEBUGONLY(aOwningThread))
+  , mName(aName)
+  , mType(aType)
+{
+  // Can't assert owning thread here because IPDL has not yet set our manager!
+  MOZ_COUNT_CTOR(indexedDB::BackgroundMutableFileChild);
+}
+
+BackgroundMutableFileChild::~BackgroundMutableFileChild()
+{
+  MOZ_COUNT_DTOR(indexedDB::BackgroundMutableFileChild);
+}
+
+already_AddRefed<MutableFileBase>
+BackgroundMutableFileChild::CreateMutableFile()
+{
+  auto database =
+    static_cast<BackgroundDatabaseChild*>(Manager())->GetDOMObject();
+  MOZ_ASSERT(database);
+
+  nsRefPtr<IDBMutableFile> mutableFile =
+    new IDBMutableFile(database, this, mName, mType);
+
+  return mutableFile.forget();
+}
+
+/*******************************************************************************
  * BackgroundRequestChild
  ******************************************************************************/
 
 BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest)
   : BackgroundRequestChildBase(aRequest)
   , mTransaction(aRequest->GetTransaction())
 {
   MOZ_ASSERT(mTransaction);
@@ -2112,26 +2325,16 @@ BackgroundRequestChild::~BackgroundReque
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!mTransaction);
 
   MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild);
 }
 
 void
-BackgroundRequestChild::HoldFileInfosUntilComplete(
-                                       nsTArray<nsRefPtr<FileInfo>>& aFileInfos)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(mFileInfos.IsEmpty());
-
-  mFileInfos.SwapElements(aFileInfos);
-}
-
-void
 BackgroundRequestChild::HandleResponse(nsresult aResponse)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(NS_FAILED(aResponse));
   MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
   MOZ_ASSERT(mTransaction);
 
   DispatchErrorEvent(mRequest, aResponse, mTransaction);
@@ -2163,17 +2366,16 @@ BackgroundRequestChild::HandleResponse(
 {
   AssertIsOnOwningThread();
 
   // XXX Fix this somehow...
   auto& serializedCloneInfo =
     const_cast<SerializedStructuredCloneReadInfo&>(aResponse);
 
   StructuredCloneReadInfo cloneReadInfo(Move(serializedCloneInfo));
-  cloneReadInfo.mDatabase = mTransaction->Database();
 
   ConvertActorsToBlobs(mTransaction->Database(),
                        aResponse,
                        cloneReadInfo.mFiles);
 
   ResultHelper helper(mRequest, mTransaction, &cloneReadInfo);
 
   DispatchSuccessEvent(&helper);
@@ -2198,18 +2400,16 @@ BackgroundRequestChild::HandleResponse(
       // XXX Fix this somehow...
       auto& serializedCloneInfo =
         const_cast<SerializedStructuredCloneReadInfo&>(aResponse[index]);
 
       StructuredCloneReadInfo* cloneReadInfo = cloneReadInfos.AppendElement();
 
       *cloneReadInfo = Move(serializedCloneInfo);
 
-      cloneReadInfo->mDatabase = mTransaction->Database();
-
       ConvertActorsToBlobs(database,
                            serializedCloneInfo,
                            cloneReadInfo->mFiles);
     }
   }
 
   ResultHelper helper(mRequest, mTransaction, &cloneReadInfos);
 
@@ -2809,36 +3009,16 @@ BackgroundCursorChild::RecvResponse(cons
       MOZ_CRASH("Should never get here!");
   }
 
   mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true);
 
   return true;
 }
 
-// XXX This doesn't belong here. However, we're not yet porting MutableFile
-//     stuff to PBackground so this is necessary for the time being.
-void
-DispatchMutableFileResult(IDBRequest* aRequest,
-                          nsresult aResultCode,
-                          IDBMutableFile* aMutableFile)
-{
-  MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aRequest);
-  MOZ_ASSERT_IF(NS_SUCCEEDED(aResultCode), aMutableFile);
-
-  if (NS_SUCCEEDED(aResultCode)) {
-    ResultHelper helper(aRequest, nullptr, aMutableFile);
-    DispatchSuccessEvent(&helper);
-  } else {
-    DispatchErrorEvent(aRequest, aResultCode);
-  }
-}
-
 NS_IMPL_ISUPPORTS(BackgroundCursorChild::DelayedActionRunnable,
                   nsIRunnable,
                   nsICancelableRunnable)
 
 NS_IMETHODIMP
 BackgroundCursorChild::
 DelayedActionRunnable::Run()
 {
--- a/dom/indexedDB/ActorsChild.h
+++ b/dom/indexedDB/ActorsChild.h
@@ -5,18 +5,20 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_indexeddb_actorschild_h__
 #define mozilla_dom_indexeddb_actorschild_h__
 
 #include "IDBTransaction.h"
 #include "js/RootingAPI.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/filehandle/ActorsChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
@@ -31,17 +33,16 @@ namespace ipc {
 
 class BackgroundChildImpl;
 
 } // namespace ipc
 
 namespace dom {
 namespace indexedDB {
 
-class FileInfo;
 class IDBCursor;
 class IDBDatabase;
 class IDBFactory;
 class IDBMutableFile;
 class IDBOpenDBRequest;
 class IDBRequest;
 class Key;
 class PermissionRequestChild;
@@ -150,21 +151,25 @@ class BackgroundFactoryChild final
 
   IDBFactory* mFactory;
 
 #ifdef DEBUG
   nsCOMPtr<nsIEventTarget> mOwningThread;
 #endif
 
 public:
+#ifdef DEBUG
+  void
+  AssertIsOnOwningThread() const;
+
+  nsIEventTarget*
+  OwningThread() const;
+#else
   void
   AssertIsOnOwningThread() const
-#ifdef DEBUG
-  ;
-#else
   { }
 #endif
 
   IDBFactory*
   GetDOMObject() const
   {
     AssertIsOnOwningThread();
     return mFactory;
@@ -316,16 +321,24 @@ class BackgroundDatabaseChild final
 
 public:
   void
   AssertIsOnOwningThread() const
   {
     static_cast<BackgroundFactoryChild*>(Manager())->AssertIsOnOwningThread();
   }
 
+#ifdef DEBUG
+  nsIEventTarget*
+  OwningThread() const
+  {
+    return static_cast<BackgroundFactoryChild*>(Manager())->OwningThread();
+  }
+#endif
+
   const DatabaseSpec*
   Spec() const
   {
     AssertIsOnOwningThread();
     return mSpec;
   }
 
   IDBDatabase*
@@ -360,16 +373,25 @@ private:
   AllocPBackgroundIDBDatabaseFileChild(PBlobChild* aBlobChild)
                                        override;
 
   virtual bool
   DeallocPBackgroundIDBDatabaseFileChild(
                                         PBackgroundIDBDatabaseFileChild* aActor)
                                         override;
 
+  virtual PBackgroundIDBDatabaseRequestChild*
+  AllocPBackgroundIDBDatabaseRequestChild(const DatabaseRequestParams& aParams)
+                                          override;
+
+  virtual bool
+  DeallocPBackgroundIDBDatabaseRequestChild(
+                                     PBackgroundIDBDatabaseRequestChild* aActor)
+                                     override;
+
   virtual PBackgroundIDBTransactionChild*
   AllocPBackgroundIDBTransactionChild(
                                     const nsTArray<nsString>& aObjectStoreNames,
                                     const Mode& aMode)
                                     override;
 
   virtual bool
   DeallocPBackgroundIDBTransactionChild(PBackgroundIDBTransactionChild* aActor)
@@ -392,28 +414,64 @@ private:
                             const int64_t& aNextIndexId)
                             override;
 
   virtual bool
   DeallocPBackgroundIDBVersionChangeTransactionChild(
                             PBackgroundIDBVersionChangeTransactionChild* aActor)
                             override;
 
+  virtual PBackgroundMutableFileChild*
+  AllocPBackgroundMutableFileChild(const nsString& aName,
+                                   const nsString& aType) override;
+
+  virtual bool
+  DeallocPBackgroundMutableFileChild(PBackgroundMutableFileChild* aActor)
+                                     override;
+
   virtual bool
   RecvVersionChange(const uint64_t& aOldVersion,
                     const NullableVersion& aNewVersion)
                     override;
 
   virtual bool
   RecvInvalidate() override;
 
   bool
   SendDeleteMe() = delete;
 };
 
+class BackgroundDatabaseRequestChild final
+  : public BackgroundRequestChildBase
+  , public PBackgroundIDBDatabaseRequestChild
+{
+  friend class BackgroundDatabaseChild;
+  friend class IDBDatabase;
+
+  nsRefPtr<IDBDatabase> mDatabase;
+
+private:
+  // Only created by IDBDatabase.
+  BackgroundDatabaseRequestChild(IDBDatabase* aDatabase,
+                                 IDBRequest* aRequest);
+
+  // Only destroyed by BackgroundDatabaseChild.
+  ~BackgroundDatabaseRequestChild();
+
+  bool
+  HandleResponse(nsresult aResponse);
+
+  bool
+  HandleResponse(const CreateFileRequestResponse& aResponse);
+
+  // IPDL methods are only called by IPDL.
+  virtual bool
+  Recv__delete__(const DatabaseRequestResponse& aResponse) override;
+};
+
 class BackgroundVersionChangeTransactionChild;
 
 class BackgroundTransactionBase
 {
   friend class BackgroundVersionChangeTransactionChild;
 
   // mTemporaryStrongTransaction is strong and is only valid until the end of
   // NoteComplete() member function or until the NoteActorDestroyed() member
@@ -560,30 +618,47 @@ private:
   virtual bool
   DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor)
                                    override;
 
   bool
   SendDeleteMe() = delete;
 };
 
+class BackgroundMutableFileChild final
+  : public mozilla::dom::BackgroundMutableFileChildBase
+{
+  friend class BackgroundDatabaseChild;
+
+  nsString mName;
+  nsString mType;
+
+private:
+  // Only constructed by BackgroundDatabaseChild.
+  BackgroundMutableFileChild(DEBUGONLY(PRThread* aOwningThread,)
+                             const nsAString& aName,
+                             const nsAString& aType);
+
+  // Only destroyed by BackgroundDatabaseChild.
+  ~BackgroundMutableFileChild();
+
+  // BackgroundMutableFileChildBase
+  virtual already_AddRefed<MutableFileBase>
+  CreateMutableFile() override;
+};
+
 class BackgroundRequestChild final
   : public BackgroundRequestChildBase
   , public PBackgroundIDBRequestChild
 {
   friend class BackgroundTransactionChild;
   friend class BackgroundVersionChangeTransactionChild;
   friend class IDBTransaction;
 
   nsRefPtr<IDBTransaction> mTransaction;
-  nsTArray<nsRefPtr<FileInfo>> mFileInfos;
-
-public:
-  void
-  HoldFileInfosUntilComplete(nsTArray<nsRefPtr<FileInfo>>& aFileInfos);
 
 private:
   // Only created by IDBTransaction.
   explicit
   BackgroundRequestChild(IDBRequest* aRequest);
 
   // Only destroyed by BackgroundTransactionChild or
   // BackgroundVersionChangeTransactionChild.
@@ -749,20 +824,13 @@ private:
   // Force callers to use SendContinueInternal.
   bool
   SendContinue(const CursorRequestParams& aParams, const Key& aKey) = delete;
 
   bool
   SendDeleteMe() = delete;
 };
 
-// XXX This doesn't belong here. However, we're not yet porting MutableFile
-//     stuff to PBackground so this is necessary for the time being.
-void
-DispatchMutableFileResult(IDBRequest* aRequest,
-                          nsresult aResultCode,
-                          IDBMutableFile* aMutableFile);
-
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_indexeddb_actorschild_h__
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -27,22 +27,23 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/storage.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/File.h"
-#include "mozilla/dom/FileService.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/filehandle/ActorsParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/quota/Client.h"
@@ -79,16 +80,17 @@
 #include "nsISupports.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsPriority.h"
 #include "nsIThread.h"
 #include "nsITimer.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
+#include "nsQueryObject.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsThreadPool.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMCID.h"
 #include "PermissionRequestBase.h"
 #include "ProfilerHelpers.h"
 #include "prsystem.h"
@@ -108,48 +110,52 @@
   MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(),                           \
          LogLevel::Debug,                                                         \
          _args )
 
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 #define IDB_MOBILE
 #endif
 
+#define BLOB_IMPL_STORED_FILE_IID \
+  {0x6b505c84, 0x2c60, 0x4ffb, {0x8b, 0x91, 0xfe, 0x22, 0xb1, 0xec, 0x75, 0xe2}}
+
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 using namespace mozilla::dom::quota;
 using namespace mozilla::ipc;
 
 namespace {
 
 class ConnectionPool;
 class Cursor;
 class Database;
 struct DatabaseActorInfo;
 class DatabaseLogging