Bug 1370519 - Part 2: Merge FileHandle base classes with IndexedDB specific classes (except parent actors); r=btseng
authorJan Varga <jan.varga@gmail.com>
Wed, 07 Jun 2017 12:36:42 +0200
changeset 410795 398e771204b76ab4fd049c54ce4025a6d14b2d5b
parent 410794 1637d5c1dd6c9a1752e68fe7f2b84420d816db0b
child 410796 e5f90f396476ef1efdfe421c5c903343ca9904a3
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbtseng
bugs1370519
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1370519 - Part 2: Merge FileHandle base classes with IndexedDB specific classes (except parent actors); r=btseng
dom/filehandle/moz.build
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/ActorsChild.h
dom/indexedDB/IDBFileHandle.cpp
dom/indexedDB/IDBFileHandle.h
dom/indexedDB/IDBFileRequest.cpp
dom/indexedDB/IDBFileRequest.h
dom/indexedDB/IDBMutableFile.cpp
dom/indexedDB/IDBMutableFile.h
ipc/glue/BackgroundChildImpl.h
--- a/dom/filehandle/moz.build
+++ b/dom/filehandle/moz.build
@@ -3,35 +3,28 @@
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM")
 
 EXPORTS.mozilla.dom.filehandle += [
-    'ActorsChild.h',
     'ActorsParent.h',
     'SerializationHelpers.h',
 ]
 
 EXPORTS.mozilla.dom += [
-    'FileHandleBase.h',
     'FileHandleCommon.h',
     'FileHandleStorage.h',
-    'FileRequestBase.h',
-    'MutableFileBase.h',
 ]
 
 UNIFIED_SOURCES += [
-    'ActorsChild.cpp',
     'ActorsParent.cpp',
-    'FileHandleBase.cpp',
     'FileHandleCommon.cpp',
-    'MutableFileBase.cpp',
 ]
 
 IPDL_SOURCES += [
     'PBackgroundFileHandle.ipdl',
     'PBackgroundFileRequest.ipdl',
     'PBackgroundMutableFile.ipdl',
 ]
 
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -2,36 +2,39 @@
 /* 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 "FileSnapshot.h"
 #include "IDBDatabase.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
+#include "IDBFileHandle.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/CycleCollectedJSRuntime.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
+#include "mozilla/dom/ipc/PendingIPCBlobChild.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/TaskQueue.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIBFCacheEntry.h"
 #include "nsIDocument.h"
@@ -1136,16 +1139,364 @@ mozilla::ipc::IPCResult
 WorkerPermissionRequestChildProcessActor::Recv__delete__(
                                               const uint32_t& /* aPermission */)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mChallenge->OperationCompleted();
   return IPC_OK();
 }
 
+class MOZ_STACK_CLASS AutoSetCurrentFileHandle final
+{
+  typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl;
+
+  IDBFileHandle* const mFileHandle;
+  IDBFileHandle* mPreviousFileHandle;
+  IDBFileHandle** mThreadLocalSlot;
+
+public:
+  explicit AutoSetCurrentFileHandle(IDBFileHandle* 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;
+    }
+  }
+
+  IDBFileHandle*
+  FileHandle() const
+  {
+    return mFileHandle;
+  }
+};
+
+class MOZ_STACK_CLASS FileHandleResultHelper final
+  : public IDBFileRequest::ResultCallback
+{
+  IDBFileRequest* 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:
+  FileHandleResultHelper(IDBFileRequest* aFileRequest,
+                         IDBFileHandle* aFileHandle,
+                         File* aResult)
+    : mFileRequest(aFileRequest)
+    , mAutoFileHandle(aFileHandle)
+    , mResultType(ResultTypeFile)
+  {
+    MOZ_ASSERT(aFileRequest);
+    MOZ_ASSERT(aFileHandle);
+    MOZ_ASSERT(aResult);
+
+    mResult.mFile = aResult;
+  }
+
+  FileHandleResultHelper(IDBFileRequest* aFileRequest,
+                         IDBFileHandle* aFileHandle,
+                         const nsCString* aResult)
+    : mFileRequest(aFileRequest)
+    , mAutoFileHandle(aFileHandle)
+    , mResultType(ResultTypeString)
+  {
+    MOZ_ASSERT(aFileRequest);
+    MOZ_ASSERT(aFileHandle);
+    MOZ_ASSERT(aResult);
+
+    mResult.mString = aResult;
+  }
+
+  FileHandleResultHelper(IDBFileRequest* aFileRequest,
+                         IDBFileHandle* aFileHandle,
+                         const FileRequestMetadata* aResult)
+    : mFileRequest(aFileRequest)
+    , mAutoFileHandle(aFileHandle)
+    , mResultType(ResultTypeMetadata)
+  {
+    MOZ_ASSERT(aFileRequest);
+    MOZ_ASSERT(aFileHandle);
+    MOZ_ASSERT(aResult);
+
+    mResult.mMetadata = aResult;
+  }
+
+
+  FileHandleResultHelper(IDBFileRequest* aFileRequest,
+                         IDBFileHandle* 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;
+  }
+
+  IDBFileRequest*
+  FileRequest() const
+  {
+    return mFileRequest;
+  }
+
+  IDBFileHandle*
+  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(IDBFileHandle* aFileHandle,
+                   const FileRequestGetFileResponse& aResponse)
+{
+  auto* actor = static_cast<PendingIPCBlobChild*>(aResponse.fileChild());
+
+  IDBMutableFile* mutableFile = aFileHandle->GetMutableFile();
+  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);
+
+  RefPtr<BlobImpl> blobImpl =
+    actor->SetPendingInfoAndDeleteActor(mutableFile->Name(),
+                                        mutableFile->Type(),
+                                        size.get_uint64_t(),
+                                        lastModified.get_int64_t());
+  MOZ_ASSERT(blobImpl);
+
+  RefPtr<BlobImpl> blobImplSnapshot =
+    new BlobImplSnapshot(blobImpl, static_cast<IDBFileHandle*>(aFileHandle));
+
+  RefPtr<File> file = File::Create(mutableFile->GetOwner(), blobImplSnapshot);
+  return file.forget();
+}
+
+void
+DispatchFileHandleErrorEvent(IDBFileRequest* aFileRequest,
+                             nsresult aErrorCode,
+                             IDBFileHandle* 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);
+
+  RefPtr<IDBFileRequest> fileRequest = aFileRequest;
+  RefPtr<IDBFileHandle> fileHandle = aFileHandle;
+
+  AutoSetCurrentFileHandle ascfh(aFileHandle);
+
+  fileRequest->FireError(aErrorCode);
+
+  MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted());
+}
+
+void
+DispatchFileHandleSuccessEvent(FileHandleResultHelper* aResultHelper)
+{
+  MOZ_ASSERT(aResultHelper);
+
+  RefPtr<IDBFileRequest> fileRequest = aResultHelper->FileRequest();
+  MOZ_ASSERT(fileRequest);
+  fileRequest->AssertIsOnOwningThread();
+
+  RefPtr<IDBFileHandle> fileHandle = aResultHelper->FileHandle();
+  MOZ_ASSERT(fileHandle);
+
+  if (fileHandle->IsAborted()) {
+    fileRequest->FireError(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR);
+    return;
+  }
+
+  MOZ_ASSERT(fileHandle->IsOpen());
+
+  fileRequest->SetResultCallback(aResultHelper);
+
+  MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted());
+}
+
 } // namespace
 
 /*******************************************************************************
  * Actor class declarations
  ******************************************************************************/
 
 // CancelableRunnable is used to make workers happy.
 class BackgroundRequestChild::PreprocessHelper final
@@ -1956,26 +2307,17 @@ BackgroundDatabaseChild::DeallocPBackgro
 }
 
 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);
+  return new BackgroundMutableFileChild(aName, aType);
 }
 
 bool
 BackgroundDatabaseChild::DeallocPBackgroundMutableFileChild(
                                             PBackgroundMutableFileChild* aActor)
 {
   MOZ_ASSERT(aActor);
 
@@ -2483,44 +2825,118 @@ BackgroundVersionChangeTransactionChild:
   delete static_cast<BackgroundCursorChild*>(aActor);
   return true;
 }
 
 /*******************************************************************************
  * BackgroundMutableFileChild
  ******************************************************************************/
 
-BackgroundMutableFileChild::BackgroundMutableFileChild(
-                                             DEBUGONLY(PRThread* aOwningThread,)
-                                             const nsAString& aName,
-                                             const nsAString& aType)
-  : BackgroundMutableFileChildBase(DEBUGONLY(aOwningThread))
+BackgroundMutableFileChild::BackgroundMutableFileChild(const nsAString& aName,
+                                                       const nsAString& aType)
+  : mMutableFile(nullptr)
   , 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()
+#ifdef DEBUG
+
+void
+BackgroundMutableFileChild::AssertIsOnOwningThread() const
 {
+  static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundMutableFileChild::EnsureDOMObject()
+{
+  AssertIsOnOwningThread();
+
+  if (mTemporaryStrongMutableFile) {
+    return;
+  }
+
   auto database =
     static_cast<BackgroundDatabaseChild*>(Manager())->GetDOMObject();
   MOZ_ASSERT(database);
 
-  RefPtr<IDBMutableFile> mutableFile =
+  mTemporaryStrongMutableFile =
     new IDBMutableFile(database, this, mName, mType);
 
-  return mutableFile.forget();
+  MOZ_ASSERT(mTemporaryStrongMutableFile);
+  mTemporaryStrongMutableFile->AssertIsOnOwningThread();
+
+  mMutableFile = mTemporaryStrongMutableFile;
+}
+
+void
+BackgroundMutableFileChild::ReleaseDOMObject()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mTemporaryStrongMutableFile);
+  mTemporaryStrongMutableFile->AssertIsOnOwningThread();
+  MOZ_ASSERT(mMutableFile == mTemporaryStrongMutableFile);
+
+  mTemporaryStrongMutableFile = nullptr;
+}
+
+void
+BackgroundMutableFileChild::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mTemporaryStrongMutableFile);
+
+  if (mMutableFile) {
+    mMutableFile->ClearBackgroundActor();
+    mMutableFile = nullptr;
+
+    MOZ_ALWAYS_TRUE(PBackgroundMutableFileChild::SendDeleteMe());
+  }
+}
+
+void
+BackgroundMutableFileChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  if (mMutableFile) {
+    mMutableFile->ClearBackgroundActor();
+#ifdef DEBUG
+    mMutableFile = nullptr;
+#endif
+  }
+}
+
+PBackgroundFileHandleChild*
+BackgroundMutableFileChild::AllocPBackgroundFileHandleChild(
+                                                          const FileMode& aMode)
+{
+  MOZ_CRASH("PBackgroundFileHandleChild actors should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundMutableFileChild::DeallocPBackgroundFileHandleChild(
+                                             PBackgroundFileHandleChild* aActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundFileHandleChild*>(aActor);
+  return true;
 }
 
 /*******************************************************************************
  * BackgroundRequestChild
  ******************************************************************************/
 
 BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest)
   : BackgroundRequestChildBase(aRequest)
@@ -3681,16 +4097,303 @@ DelayedActionRunnable::Cancel()
 
   // This must always run to clean up our state.
   Run();
 
   return NS_OK;
 }
 
 /*******************************************************************************
+ * BackgroundFileHandleChild
+ ******************************************************************************/
+
+BackgroundFileHandleChild::BackgroundFileHandleChild(IDBFileHandle* aFileHandle)
+  : mTemporaryStrongFileHandle(aFileHandle)
+  , mFileHandle(aFileHandle)
+{
+  MOZ_ASSERT(aFileHandle);
+  aFileHandle->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(BackgroundFileHandleChild);
+}
+
+BackgroundFileHandleChild::~BackgroundFileHandleChild()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(BackgroundFileHandleChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundFileHandleChild::AssertIsOnOwningThread() const
+{
+  static_cast<BackgroundMutableFileChild*>(Manager())->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+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();
+}
+
+mozilla::ipc::IPCResult
+BackgroundFileHandleChild::RecvComplete(const bool& aAborted)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileHandle);
+
+  mFileHandle->FireCompleteOrAbortEvents(aAborted);
+
+  NoteComplete();
+  return IPC_OK();
+}
+
+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(
+                                                   IDBFileRequest* aFileRequest)
+  : mFileRequest(aFileRequest)
+  , mFileHandle(aFileRequest->GetFileHandle())
+  , mActorDestroyed(false)
+{
+  MOZ_ASSERT(aFileRequest);
+  aFileRequest->AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileHandle);
+  mFileHandle->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(BackgroundFileRequestChild);
+}
+
+BackgroundFileRequestChild::~BackgroundFileRequestChild()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mFileHandle);
+
+  MOZ_COUNT_DTOR(BackgroundFileRequestChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundFileRequestChild::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mFileRequest);
+  mFileRequest->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+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);
+
+  DispatchFileHandleErrorEvent(mFileRequest, aResponse, mFileHandle);
+}
+
+void
+BackgroundFileRequestChild::HandleResponse(
+                                    const FileRequestGetFileResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  RefPtr<File> file = ConvertActorToFile(mFileHandle, aResponse);
+
+  FileHandleResultHelper helper(mFileRequest, mFileHandle, file);
+
+  DispatchFileHandleSuccessEvent(&helper);
+}
+
+void
+BackgroundFileRequestChild::HandleResponse(const nsCString& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  FileHandleResultHelper helper(mFileRequest, mFileHandle, &aResponse);
+
+  DispatchFileHandleSuccessEvent(&helper);
+}
+
+void
+BackgroundFileRequestChild::HandleResponse(const FileRequestMetadata& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  FileHandleResultHelper helper(mFileRequest, mFileHandle, &aResponse);
+
+  DispatchFileHandleSuccessEvent(&helper);
+}
+
+void
+BackgroundFileRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse)
+{
+  AssertIsOnOwningThread();
+
+  FileHandleResultHelper helper(mFileRequest, mFileHandle, &aResponse);
+
+  DispatchFileHandleSuccessEvent(&helper);
+}
+
+void
+BackgroundFileRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_ASSERT(!mActorDestroyed);
+
+  mActorDestroyed = true;
+
+  if (mFileHandle) {
+    mFileHandle->AssertIsOnOwningThread();
+
+    mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */
+                                   aWhy == Deletion);
+
+#ifdef DEBUG
+    mFileHandle = nullptr;
+#endif
+  }
+}
+
+mozilla::ipc::IPCResult
+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 IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+BackgroundFileRequestChild::RecvProgress(const uint64_t& aProgress,
+                                         const uint64_t& aProgressMax)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileRequest);
+
+  mFileRequest->FireProgressEvent(aProgress, aProgressMax);
+
+  return IPC_OK();
+}
+
+/*******************************************************************************
  * BackgroundUtilsChild
  ******************************************************************************/
 
 BackgroundUtilsChild::BackgroundUtilsChild(IndexedDatabaseManager* aManager)
   : mManager(aManager)
 #ifdef DEBUG
   , mOwningThread(NS_GetCurrentThread())
 #endif
--- a/dom/indexedDB/ActorsChild.h
+++ b/dom/indexedDB/ActorsChild.h
@@ -5,27 +5,29 @@
  * 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 "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
+#include "mozilla/dom/PBackgroundFileHandleChild.h"
+#include "mozilla/dom/PBackgroundFileRequestChild.h"
+#include "mozilla/dom/PBackgroundMutableFileChild.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 
 class nsIEventTarget;
 struct nsID;
 struct PRThread;
 
@@ -40,16 +42,18 @@ class BackgroundChildImpl;
 
 } // namespace ipc
 
 namespace dom {
 
 class IDBCursor;
 class IDBDatabase;
 class IDBFactory;
+class IDBFileHandle;
+class IDBFileRequest;
 class IDBMutableFile;
 class IDBOpenDBRequest;
 class IDBRequest;
 class IndexedDatabaseManager;
 
 namespace indexedDB {
 
 class Key;
@@ -616,35 +620,72 @@ private:
   DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor)
                                    override;
 
   bool
   SendDeleteMe() = delete;
 };
 
 class BackgroundMutableFileChild final
-  : public mozilla::dom::BackgroundMutableFileChildBase
+  : public PBackgroundMutableFileChild
 {
   friend class BackgroundDatabaseChild;
+  friend IDBMutableFile;
 
+  RefPtr<IDBMutableFile> mTemporaryStrongMutableFile;
+  IDBMutableFile* mMutableFile;
   nsString mName;
   nsString mType;
 
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  void
+  EnsureDOMObject();
+
+  IDBMutableFile*
+  GetDOMObject() const
+  {
+    AssertIsOnOwningThread();
+    return mMutableFile;
+  }
+
+  void
+  ReleaseDOMObject();
+
 private:
   // Only constructed by BackgroundDatabaseChild.
-  BackgroundMutableFileChild(DEBUGONLY(PRThread* aOwningThread,)
-                             const nsAString& aName,
+  BackgroundMutableFileChild(const nsAString& aName,
                              const nsAString& aType);
 
   // Only destroyed by BackgroundDatabaseChild.
   ~BackgroundMutableFileChild();
 
-  // BackgroundMutableFileChildBase
-  virtual already_AddRefed<MutableFileBase>
-  CreateMutableFile() override;
+  void
+  SendDeleteMeInternal();
+
+  // 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 BackgroundRequestChild final
   : public BackgroundRequestChildBase
   , public PBackgroundIDBRequestChild
 {
   friend class BackgroundTransactionChild;
   friend class BackgroundVersionChangeTransactionChild;
@@ -832,16 +873,127 @@ private:
   // Force callers to use SendContinueInternal.
   bool
   SendContinue(const CursorRequestParams& aParams) = delete;
 
   bool
   SendDeleteMe() = delete;
 };
 
+class BackgroundFileHandleChild
+  : public PBackgroundFileHandleChild
+{
+  friend class BackgroundMutableFileChild;
+  friend IDBMutableFile;
+
+  // mTemporaryStrongFileHandle is strong and is only valid until the end of
+  // NoteComplete() member function or until the NoteActorDestroyed() member
+  // function is called.
+  RefPtr<IDBFileHandle> mTemporaryStrongFileHandle;
+
+  // mFileHandle is weak and is valid until the NoteActorDestroyed() member
+  // function is called.
+  IDBFileHandle* mFileHandle;
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  void
+  SendDeleteMeInternal();
+
+private:
+  // Only created by IDBMutableFile.
+  explicit BackgroundFileHandleChild(IDBFileHandle* aFileHandle);
+
+  ~BackgroundFileHandleChild();
+
+  void
+  NoteActorDestroyed();
+
+  void
+  NoteComplete();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  mozilla::ipc::IPCResult
+  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 PBackgroundFileRequestChild
+{
+  friend class BackgroundFileHandleChild;
+  friend IDBFileHandle;
+
+  RefPtr<IDBFileRequest> mFileRequest;
+  RefPtr<IDBFileHandle> mFileHandle;
+  bool mActorDestroyed;
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+private:
+  // Only created by IDBFileHandle.
+  explicit BackgroundFileRequestChild(IDBFileRequest* 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 mozilla::ipc::IPCResult
+  Recv__delete__(const FileRequestResponse& aResponse) override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvProgress(const uint64_t& aProgress,
+               const uint64_t& aProgressMax) override;
+};
+
 class BackgroundUtilsChild final
   : public PBackgroundIndexedDBUtilsChild
 {
   friend class mozilla::ipc::BackgroundChildImpl;
   friend IndexedDatabaseManager;
 
   IndexedDatabaseManager* mManager;
 
--- a/dom/indexedDB/IDBFileHandle.cpp
+++ b/dom/indexedDB/IDBFileHandle.cpp
@@ -1,75 +1,292 @@
 /* -*- 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 "IDBFileHandle.h"
 
+#include "ActorsChild.h"
+#include "BackgroundChildImpl.h"
 #include "IDBEvents.h"
 #include "IDBMutableFile.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/dom/File.h"
 #include "mozilla/dom/IDBFileHandleBinding.h"
-#include "mozilla/dom/filehandle/ActorsChild.h"
+#include "mozilla/dom/IPCBlobUtils.h"
+#include "mozilla/dom/PBackgroundFileHandle.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/ipc/BackgroundChild.h"
 #include "nsContentUtils.h"
 #include "nsQueryObject.h"
 #include "nsServiceManagerUtils.h"
 #include "nsWidgetsCID.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace mozilla::dom::indexedDB;
+using namespace mozilla::ipc;
 
-IDBFileHandle::IDBFileHandle(FileMode aMode,
-                             IDBMutableFile* aMutableFile)
-  : FileHandleBase(DEBUGONLY(aMutableFile->OwningThread(),)
-                   aMode)
-  , mMutableFile(aMutableFile)
+namespace {
+
+already_AddRefed<IDBFileRequest>
+GenerateFileRequest(IDBFileHandle* aFileHandle)
 {
-  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileHandle);
+  aFileHandle->AssertIsOnOwningThread();
+
+  RefPtr<IDBFileRequest> fileRequest =
+    IDBFileRequest::Create(aFileHandle, /* aWrapAsDOMRequest */ false);
+  MOZ_ASSERT(fileRequest);
+
+  return fileRequest.forget();
+}
+
+} // namespace
+
+IDBFileHandle::IDBFileHandle(IDBMutableFile* aMutableFile,
+                             FileMode aMode)
+  : mMutableFile(aMutableFile)
+  , mBackgroundActor(nullptr)
+  , mLocation(0)
+  , mPendingRequestCount(0)
+  , mReadyState(INITIAL)
+  , mMode(aMode)
+  , mAborted(false)
+  , mCreating(false)
+#ifdef DEBUG
+  , mSentFinishOrAbort(false)
+  , mFiredCompleteOrAbort(false)
+#endif
+{
+  MOZ_ASSERT(aMutableFile);
+  aMutableFile->AssertIsOnOwningThread();
 }
 
 IDBFileHandle::~IDBFileHandle()
 {
   AssertIsOnOwningThread();
+  MOZ_ASSERT(!mPendingRequestCount);
+  MOZ_ASSERT(!mCreating);
+  MOZ_ASSERT(mSentFinishOrAbort);
+  MOZ_ASSERT_IF(mBackgroundActor, mFiredCompleteOrAbort);
 
   mMutableFile->UnregisterFileHandle(this);
+
+  if (mBackgroundActor) {
+    mBackgroundActor->SendDeleteMeInternal();
+
+    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
+  }
 }
 
 // static
 already_AddRefed<IDBFileHandle>
 IDBFileHandle::Create(IDBMutableFile* aMutableFile,
                       FileMode aMode)
 {
   MOZ_ASSERT(aMutableFile);
   aMutableFile->AssertIsOnOwningThread();
   MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite);
 
   RefPtr<IDBFileHandle> fileHandle =
-    new IDBFileHandle(aMode, aMutableFile);
+    new IDBFileHandle(aMutableFile, aMode);
 
   fileHandle->BindToOwner(aMutableFile);
 
   // XXX Fix!
   MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
 
   nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
   nsContentUtils::RunInMetastableState(runnable.forget());
 
-  fileHandle->SetCreating();
+  fileHandle->mCreating = true;
 
   aMutableFile->RegisterFileHandle(fileHandle);
 
   return fileHandle.forget();
 }
 
+// static
+IDBFileHandle*
+IDBFileHandle::GetCurrent()
+{
+  MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
+
+  BackgroundChildImpl::ThreadLocal* threadLocal =
+    BackgroundChildImpl::GetThreadLocalForCurrentThread();
+  MOZ_ASSERT(threadLocal);
+
+  return threadLocal->mCurrentFileHandle;
+}
+
+#ifdef DEBUG
+
+void
+IDBFileHandle::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mMutableFile);
+  mMutableFile->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+IDBFileHandle::SetBackgroundActor(BackgroundFileHandleChild* aActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(!mBackgroundActor);
+
+  mBackgroundActor = aActor;
+}
+
+void
+IDBFileHandle::StartRequest(IDBFileRequest* aFileRequest,
+                            const FileRequestParams& aParams)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileRequest);
+  MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
+
+  BackgroundFileRequestChild* actor =
+    new BackgroundFileRequestChild(aFileRequest);
+
+  mBackgroundActor->SendPBackgroundFileRequestConstructor(actor, aParams);
+
+  // Balanced in BackgroundFileRequestChild::Recv__delete__().
+  OnNewRequest();
+}
+
+void
+IDBFileHandle::OnNewRequest()
+{
+  AssertIsOnOwningThread();
+
+  if (!mPendingRequestCount) {
+    MOZ_ASSERT(mReadyState == INITIAL);
+    mReadyState = LOADING;
+  }
+
+  ++mPendingRequestCount;
+}
+
+void
+IDBFileHandle::OnRequestFinished(bool aActorDestroyedNormally)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mPendingRequestCount);
+
+  --mPendingRequestCount;
+
+  if (!mPendingRequestCount && !mMutableFile->IsInvalidated()) {
+    mReadyState = FINISHING;
+
+    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
+    }
+  }
+}
+
+void
+IDBFileHandle::FireCompleteOrAbortEvents(bool aAborted)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mFiredCompleteOrAbort);
+
+  mReadyState = DONE;
+
+#ifdef DEBUG
+  mFiredCompleteOrAbort = true;
+#endif
+
+  nsCOMPtr<nsIDOMEvent> event;
+  if (aAborted) {
+    event = CreateGenericEvent(this, nsDependentString(kAbortEventType),
+                               eDoesBubble, eNotCancelable);
+  } else {
+    event = CreateGenericEvent(this, nsDependentString(kCompleteEventType),
+                               eDoesNotBubble, eNotCancelable);
+  }
+  if (NS_WARN_IF(!event)) {
+    return;
+  }
+
+  bool dummy;
+  if (NS_FAILED(DispatchEvent(event, &dummy))) {
+    NS_WARNING("DispatchEvent failed!");
+  }
+}
+
+bool
+IDBFileHandle::IsOpen() const
+{
+  AssertIsOnOwningThread();
+
+  // If we haven't started anything then we're open.
+  if (mReadyState == INITIAL) {
+    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 && (mCreating || GetCurrent() == this)) {
+    return true;
+  }
+
+  return false;
+}
+
+void
+IDBFileHandle::Abort()
+{
+  AssertIsOnOwningThread();
+
+  if (IsFinishingOrDone()) {
+    // Already started (and maybe finished) the finish or abort so there is
+    // nothing to do here.
+    return;
+  }
+
+  const bool isInvalidated = mMutableFile->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<IDBFileRequest>
 IDBFileHandle::GetMetadata(const IDBFileMetadataParameters& aParameters,
                            ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
 
   // Common state checking
   if (!CheckState(aRv)) {
@@ -86,21 +303,448 @@ IDBFileHandle::GetMetadata(const IDBFile
   if (!CheckWindow()) {
     return nullptr;
   }
 
   FileRequestGetMetadataParams params;
   params.size() = aParameters.mSize;
   params.lastModified() = aParameters.mLastModified;
 
-  RefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
+  RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
+
+  StartRequest(fileRequest, params);
+
+  return fileRequest.forget();
+}
+
+already_AddRefed<IDBFileRequest>
+IDBFileHandle::Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
+
+  // State checking for write
+  if (!CheckStateForWrite(aRv)) {
+    return nullptr;
+  }
+
+  // Getting location and additional state checking for truncate
+  uint64_t location;
+  if (aSize.WasPassed()) {
+    // Just in case someone calls us from C++
+    MOZ_ASSERT(aSize.Value() != UINT64_MAX, "Passed wrong size!");
+    location = aSize.Value();
+  } else {
+    if (mLocation == UINT64_MAX) {
+      aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+      return nullptr;
+    }
+    location = mLocation;
+  }
+
+  // Do nothing if the window is closed
+  if (!CheckWindow()) {
+    return nullptr;
+  }
+
+  FileRequestTruncateParams params;
+  params.offset() = location;
+
+  RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
+
+  StartRequest(fileRequest, params);
+
+  if (aSize.WasPassed()) {
+    mLocation = aSize.Value();
+  }
+
+  return fileRequest.forget();
+}
+
+already_AddRefed<IDBFileRequest>
+IDBFileHandle::Flush(ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
+
+  // State checking for write
+  if (!CheckStateForWrite(aRv)) {
+    return nullptr;
+  }
+
+  // Do nothing if the window is closed
+  if (!CheckWindow()) {
+    return nullptr;
+  }
+
+  FileRequestFlushParams params;
+
+  RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
+
+  StartRequest(fileRequest, params);
+
+  return fileRequest.forget();
+}
+
+void
+IDBFileHandle::Abort(ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
+
+  // This method is special enough for not using generic state checking methods.
+
+  if (IsFinishingOrDone()) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+    return;
+  }
+
+  Abort();
+}
+
+bool
+IDBFileHandle::CheckState(ErrorResult& aRv)
+{
+  if (!IsOpen()) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
+    return false;
+  }
+
+  return true;
+}
+
+bool
+IDBFileHandle::CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv)
+{
+  // Common state checking
+  if (!CheckState(aRv)) {
+    return false;
+  }
+
+  // Additional state checking for read
+  if (mLocation == UINT64_MAX) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+    return false;
+  }
+
+  // Argument checking for read
+  if (!aSize) {
+    aRv.ThrowTypeError<MSG_INVALID_READ_SIZE>();
+    return false;
+  }
+
+  return true;
+}
+
+bool
+IDBFileHandle::CheckStateForWrite(ErrorResult& aRv)
+{
+  // Common state checking
+  if (!CheckState(aRv)) {
+    return false;
+  }
+
+  // Additional state checking for write
+  if (mMode != FileMode::Readwrite) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
+    return false;
+  }
+
+  return true;
+}
+
+bool
+IDBFileHandle::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;
+}
+
+bool
+IDBFileHandle::CheckWindow()
+{
+  AssertIsOnOwningThread();
+
+  return GetOwner();
+}
+
+already_AddRefed<IDBFileRequest>
+IDBFileHandle::Read(uint64_t aSize, bool aHasEncoding,
+                    const nsAString& aEncoding, ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
+
+  // State and argument checking for read
+  if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
+    return nullptr;
+  }
+
+  // Do nothing if the window is closed
+  if (!CheckWindow()) {
+    return nullptr;
+  }
+
+  FileRequestReadParams params;
+  params.offset() = mLocation;
+  params.size() = aSize;
+
+  RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
+  if (aHasEncoding) {
+    fileRequest->SetEncoding(aEncoding);
+  }
 
   StartRequest(fileRequest, params);
 
-  return fileRequest.forget().downcast<IDBFileRequest>();
+  mLocation += aSize;
+
+  return fileRequest.forget();
+}
+
+already_AddRefed<IDBFileRequest>
+IDBFileHandle::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<IDBFileRequest>
+IDBFileHandle::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<IDBFileRequest>
+IDBFileHandle::WriteOrAppend(const ArrayBuffer& 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<IDBFileRequest>
+IDBFileHandle::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<IDBFileRequest>
+IDBFileHandle::WriteOrAppend(Blob& aValue,
+                             bool aAppend,
+                             ErrorResult& aRv)
+{
+  AssertIsOnOwningThread();
+
+  // State checking for write or append
+  if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
+    return nullptr;
+  }
+
+  ErrorResult error;
+  uint64_t dataLength = aValue.GetSize(error);
+  if (NS_WARN_IF(error.Failed())) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  if (!dataLength) {
+    return nullptr;
+  }
+
+  PBackgroundChild* backgroundActor = BackgroundChild::GetForCurrentThread();
+  MOZ_ASSERT(backgroundActor);
+
+  IPCBlob ipcBlob;
+  nsresult rv =
+    IPCBlobUtils::Serialize(aValue.Impl(), backgroundActor, ipcBlob);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return nullptr;
+  }
+
+  FileRequestBlobData blobData;
+  blobData.blob() = ipcBlob;
+
+  // Do nothing if the window is closed
+  if (!CheckWindow()) {
+    return nullptr;
+  }
+
+  return WriteInternal(blobData, dataLength, aAppend, aRv);
+}
+
+already_AddRefed<IDBFileRequest>
+IDBFileHandle::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(aDataLength);
+  MOZ_ASSERT(CheckWindow());
+
+  FileRequestWriteParams params;
+  params.offset() = aAppend ? UINT64_MAX : mLocation;
+  params.data() = aData;
+  params.dataLength() = aDataLength;
+
+  RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
+  MOZ_ASSERT(fileRequest);
+
+  StartRequest(fileRequest, params);
+
+  if (aAppend) {
+    mLocation = UINT64_MAX;
+  }
+  else {
+    mLocation += aDataLength;
+  }
+
+  return fileRequest.forget();
+}
+
+void
+IDBFileHandle::SendFinish()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mAborted);
+  MOZ_ASSERT(IsFinishingOrDone());
+  MOZ_ASSERT(!mSentFinishOrAbort);
+  MOZ_ASSERT(!mPendingRequestCount);
+
+  MOZ_ASSERT(mBackgroundActor);
+  mBackgroundActor->SendFinish();
+
+#ifdef DEBUG
+  mSentFinishOrAbort = true;
+#endif
+}
+
+void
+IDBFileHandle::SendAbort()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mAborted);
+  MOZ_ASSERT(IsFinishingOrDone());
+  MOZ_ASSERT(!mSentFinishOrAbort);
+
+  MOZ_ASSERT(mBackgroundActor);
+  mBackgroundActor->SendAbort();
+
+#ifdef DEBUG
+  mSentFinishOrAbort = true;
+#endif
 }
 
 NS_IMPL_ADDREF_INHERITED(IDBFileHandle, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(IDBFileHandle, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBFileHandle)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
@@ -118,17 +762,25 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   // Don't unlink mMutableFile!
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMETHODIMP
 IDBFileHandle::Run()
 {
   AssertIsOnOwningThread();
 
-  OnReturnToEventLoop();
+  // We're back at the event loop, no longer newborn.
+  mCreating = false;
+
+  // Maybe finish if there were no requests generated.
+  if (mReadyState == INITIAL) {
+    mReadyState = DONE;
+
+    SendFinish();
+  }
 
   return NS_OK;
 }
 
 nsresult
 IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor)
 {
   AssertIsOnOwningThread();
@@ -142,60 +794,10 @@ IDBFileHandle::GetEventTargetParent(Even
 JSObject*
 IDBFileHandle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   AssertIsOnOwningThread();
 
   return IDBFileHandleBinding::Wrap(aCx, this, aGivenProto);
 }
 
-mozilla::dom::MutableFileBase*
-IDBFileHandle::MutableFile() const
-{
-  AssertIsOnOwningThread();
-
-  return mMutableFile;
-}
-
-void
-IDBFileHandle::HandleCompleteOrAbort(bool aAborted)
-{
-  AssertIsOnOwningThread();
-
-  FileHandleBase::HandleCompleteOrAbort(aAborted);
-
-  nsCOMPtr<nsIDOMEvent> event;
-  if (aAborted) {
-    event = CreateGenericEvent(this, nsDependentString(kAbortEventType),
-                               eDoesBubble, eNotCancelable);
-  } else {
-    event = CreateGenericEvent(this, nsDependentString(kCompleteEventType),
-                               eDoesNotBubble, eNotCancelable);
-  }
-  if (NS_WARN_IF(!event)) {
-    return;
-  }
-
-  bool dummy;
-  if (NS_FAILED(DispatchEvent(event, &dummy))) {
-    NS_WARNING("DispatchEvent failed!");
-  }
-}
-
-bool
-IDBFileHandle::CheckWindow()
-{
-  AssertIsOnOwningThread();
-
-  return GetOwner();
-}
-
-already_AddRefed<mozilla::dom::FileRequestBase>
-IDBFileHandle::GenerateFileRequest()
-{
-  AssertIsOnOwningThread();
-
-  return IDBFileRequest::Create(GetOwner(), this,
-                                /* aWrapAsDOMRequest */ false);
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/IDBFileHandle.h
+++ b/dom/indexedDB/IDBFileHandle.h
@@ -5,44 +5,141 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_idbfilehandle_h__
 #define mozilla_dom_idbfilehandle_h__
 
 #include "IDBFileRequest.h"
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/dom/FileHandleBase.h"
+#include "mozilla/dom/FileModeBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIRunnable.h"
 #include "nsWeakReference.h"
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
+class Blob;
+class FileRequestData;
+class FileRequestParams;
 struct IDBFileMetadataParameters;
 class IDBFileRequest;
 class IDBMutableFile;
+class StringOrArrayBufferOrArrayBufferViewOrBlob;
+
+namespace indexedDB {
+class BackgroundFileHandleChild;
+}
 
 class IDBFileHandle final
   : public DOMEventTargetHelper
   , public nsIRunnable
-  , public FileHandleBase
   , public nsSupportsWeakReference
 {
+public:
+  enum ReadyState
+  {
+    INITIAL = 0,
+    LOADING,
+    FINISHING,
+    DONE
+  };
+
+private:
   RefPtr<IDBMutableFile> mMutableFile;
 
+  indexedDB::BackgroundFileHandleChild* mBackgroundActor;
+
+  uint64_t mLocation;
+
+  uint32_t mPendingRequestCount;
+
+  ReadyState mReadyState;
+  FileMode mMode;
+
+  bool mAborted;
+  bool mCreating;
+
+#ifdef DEBUG
+  bool mSentFinishOrAbort;
+  bool mFiredCompleteOrAbort;
+#endif
+
 public:
   static already_AddRefed<IDBFileHandle>
   Create(IDBMutableFile* aMutableFile,
          FileMode aMode);
 
+  static IDBFileHandle*
+  GetCurrent();
+
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  void
+  SetBackgroundActor(indexedDB::BackgroundFileHandleChild* aActor);
+
+  void
+  ClearBackgroundActor()
+  {
+    AssertIsOnOwningThread();
+
+    mBackgroundActor = nullptr;
+  }
+
+  void
+  StartRequest(IDBFileRequest* aFileRequest, const FileRequestParams& aParams);
+
+  void
+  OnNewRequest();
+
+  void
+  OnRequestFinished(bool aActorDestroyedNormally);
+
+  void
+  FireCompleteOrAbortEvents(bool aAborted);
+
+  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
+  Abort();
+
   // WebIDL
   nsPIDOMWindowInner*
   GetParentObject() const
   {
     AssertIsOnOwningThread();
     return GetOwner();
   }
 
@@ -55,62 +152,96 @@ public:
 
   IDBMutableFile*
   GetFileHandle() const
   {
     AssertIsOnOwningThread();
     return GetMutableFile();
   }
 
+  FileMode
+  Mode() const
+  {
+    AssertIsOnOwningThread();
+    return mMode;
+  }
+
+  bool
+  Active() const
+  {
+    AssertIsOnOwningThread();
+    return IsOpen();
+  }
+
+  Nullable<uint64_t>
+  GetLocation() const
+  {
+    AssertIsOnOwningThread();
+
+    if (mLocation == UINT64_MAX) {
+      return Nullable<uint64_t>();
+    }
+
+    return Nullable<uint64_t>(mLocation);
+  }
+
+  void
+  SetLocation(const Nullable<uint64_t>& aLocation)
+  {
+    AssertIsOnOwningThread();
+
+    // Null means the end-of-file.
+    if (aLocation.IsNull()) {
+      mLocation = UINT64_MAX;
+    } else {
+      mLocation = aLocation.Value();
+    }
+  }
+
   already_AddRefed<IDBFileRequest>
   GetMetadata(const IDBFileMetadataParameters& aParameters, ErrorResult& aRv);
 
   already_AddRefed<IDBFileRequest>
   ReadAsArrayBuffer(uint64_t aSize, ErrorResult& aRv)
   {
     AssertIsOnOwningThread();
-    return Read(aSize, false, NullString(), aRv).downcast<IDBFileRequest>();
+    return Read(aSize, false, NullString(), aRv);
   }
 
   already_AddRefed<IDBFileRequest>
   ReadAsText(uint64_t aSize, const nsAString& aEncoding, ErrorResult& aRv)
   {
     AssertIsOnOwningThread();
-    return Read(aSize, true, aEncoding, aRv).downcast<IDBFileRequest>();
+    return Read(aSize, true, aEncoding, aRv);
   }
 
   already_AddRefed<IDBFileRequest>
   Write(const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue,
         ErrorResult& aRv)
   {
     AssertIsOnOwningThread();
-    return WriteOrAppend(aValue, false, aRv).downcast<IDBFileRequest>();
+    return WriteOrAppend(aValue, false, aRv);
   }
 
   already_AddRefed<IDBFileRequest>
   Append(const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue,
          ErrorResult& aRv)
   {
     AssertIsOnOwningThread();
-    return WriteOrAppend(aValue, true, aRv).downcast<IDBFileRequest>();
+    return WriteOrAppend(aValue, true, aRv);
   }
 
   already_AddRefed<IDBFileRequest>
-  Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv)
-  {
-    AssertIsOnOwningThread();
-    return FileHandleBase::Truncate(aSize, aRv).downcast<IDBFileRequest>();
-  }
+  Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv);
 
   already_AddRefed<IDBFileRequest>
-  Flush(ErrorResult& aRv)
-  {
-    AssertIsOnOwningThread();
-    return FileHandleBase::Flush(aRv).downcast<IDBFileRequest>();
-  }
+  Flush(ErrorResult& aRv);
+
+  void
+  Abort(ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(complete)
   IMPL_EVENT_HANDLER(abort)
   IMPL_EVENT_HANDLER(error)
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIRUNNABLE
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBFileHandle, DOMEventTargetHelper)
@@ -118,32 +249,64 @@ public:
   // nsIDOMEventTarget
   virtual nsresult
   GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
 
   // WrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  // FileHandleBase
-  virtual MutableFileBase*
-  MutableFile() const override;
-
-  virtual void
-  HandleCompleteOrAbort(bool aAborted) override;
-
 private:
-  IDBFileHandle(FileMode aMode,
-                IDBMutableFile* aMutableFile);
+  IDBFileHandle(IDBMutableFile* aMutableFile,
+                FileMode aMode);
   ~IDBFileHandle();
 
-  // FileHandleBase
-  virtual bool
-  CheckWindow() override;
+  bool
+  CheckState(ErrorResult& aRv);
+
+  bool
+  CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv);
+
+  bool
+  CheckStateForWrite(ErrorResult& aRv);
+
+  bool
+  CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv);
+
+  bool
+  CheckWindow();
+
+  already_AddRefed<IDBFileRequest>
+  Read(uint64_t aSize, bool aHasEncoding, const nsAString& aEncoding,
+       ErrorResult& aRv);
 
-  virtual already_AddRefed<FileRequestBase>
-  GenerateFileRequest() override;
+  already_AddRefed<IDBFileRequest>
+  WriteOrAppend(const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue,
+                bool aAppend,
+                ErrorResult& aRv);
+
+  already_AddRefed<IDBFileRequest>
+  WriteOrAppend(const nsAString& aValue, bool aAppend, ErrorResult& aRv);
+
+  already_AddRefed<IDBFileRequest>
+  WriteOrAppend(const ArrayBuffer& aValue, bool aAppend, ErrorResult& aRv);
+
+  already_AddRefed<IDBFileRequest>
+  WriteOrAppend(const ArrayBufferView& aValue, bool aAppend, ErrorResult& aRv);
+
+  already_AddRefed<IDBFileRequest>
+  WriteOrAppend(Blob& aValue, bool aAppend, ErrorResult& aRv);
+
+  already_AddRefed<IDBFileRequest>
+  WriteInternal(const FileRequestData& aData, uint64_t aDataLength,
+                bool aAppend, ErrorResult& aRv);
+
+  void
+  SendFinish();
+
+  void
+  SendAbort();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_idbfilehandle_h__
--- a/dom/indexedDB/IDBFileRequest.cpp
+++ b/dom/indexedDB/IDBFileRequest.cpp
@@ -19,46 +19,104 @@
 #include "nsError.h"
 #include "nsLiteralString.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace mozilla::dom::indexedDB;
 
-IDBFileRequest::IDBFileRequest(nsPIDOMWindowInner* aWindow,
-                               IDBFileHandle* aFileHandle,
+IDBFileRequest::IDBFileRequest(IDBFileHandle* aFileHandle,
                                bool aWrapAsDOMRequest)
-  : DOMRequest(aWindow)
-  , FileRequestBase(DEBUGONLY(aFileHandle->OwningThread()))
+  : DOMRequest(aFileHandle->GetOwner())
   , mFileHandle(aFileHandle)
+#ifdef DEBUG
+  , mOwningThread(PR_GetCurrentThread())
+#endif
   , mWrapAsDOMRequest(aWrapAsDOMRequest)
+  , mHasEncoding(false)
 {
-  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileHandle);
+  aFileHandle->AssertIsOnOwningThread();
 }
 
 IDBFileRequest::~IDBFileRequest()
 {
   AssertIsOnOwningThread();
 }
 
+#ifdef DEBUG
+
+void
+IDBFileRequest::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+  MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+}
+
+#endif // DEBUG
+
 // static
 already_AddRefed<IDBFileRequest>
-IDBFileRequest::Create(nsPIDOMWindowInner* aOwner, IDBFileHandle* aFileHandle,
+IDBFileRequest::Create(IDBFileHandle* aFileHandle,
                        bool aWrapAsDOMRequest)
 {
   MOZ_ASSERT(aFileHandle);
   aFileHandle->AssertIsOnOwningThread();
 
   RefPtr<IDBFileRequest> request =
-    new IDBFileRequest(aOwner, aFileHandle, aWrapAsDOMRequest);
+    new IDBFileRequest(aFileHandle, aWrapAsDOMRequest);
 
   return request.forget();
 }
 
+void
+IDBFileRequest::FireProgressEvent(uint64_t aLoaded, uint64_t aTotal)
+{
+  AssertIsOnOwningThread();
+
+  if (NS_FAILED(CheckInnerWindowCorrectness())) {
+    return;
+  }
+
+  ProgressEventInit init;
+  init.mBubbles = false;
+  init.mCancelable = false;
+  init.mLengthComputable = false;
+  init.mLoaded = aLoaded;
+  init.mTotal = aTotal;
+
+  RefPtr<ProgressEvent> event =
+    ProgressEvent::Constructor(this, NS_LITERAL_STRING("progress"), init);
+  DispatchTrustedEvent(event);
+}
+
+void
+IDBFileRequest::SetResultCallback(ResultCallback* aCallback)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aCallback);
+
+  AutoJSAPI autoJS;
+  if (NS_WARN_IF(!autoJS.Init(GetOwner()))) {
+    FireError(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+    return;
+  }
+
+  JSContext* cx = autoJS.cx();
+
+  JS::Rooted<JS::Value> result(cx);
+  nsresult rv = aCallback->GetResult(cx, &result);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FireError(rv);
+  } else {
+    FireSuccess(result);
+  }
+}
+
 NS_IMPL_ADDREF_INHERITED(IDBFileRequest, DOMRequest)
 NS_IMPL_RELEASE_INHERITED(IDBFileRequest, DOMRequest)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBFileRequest)
 NS_INTERFACE_MAP_END_INHERITING(DOMRequest)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBFileRequest, DOMRequest,
                                    mFileHandle)
@@ -80,78 +138,10 @@ IDBFileRequest::WrapObject(JSContext* aC
   AssertIsOnOwningThread();
 
   if (mWrapAsDOMRequest) {
     return DOMRequest::WrapObject(aCx, aGivenProto);
   }
   return IDBFileRequestBinding::Wrap(aCx, this, aGivenProto);
 }
 
-mozilla::dom::FileHandleBase*
-IDBFileRequest::FileHandle() const
-{
-  AssertIsOnOwningThread();
-
-  return mFileHandle;
-}
-
-void
-IDBFileRequest::OnProgress(uint64_t aProgress, uint64_t aProgressMax)
-{
-  AssertIsOnOwningThread();
-
-  FireProgressEvent(aProgress, aProgressMax);
-}
-
-void
-IDBFileRequest::SetResultCallback(ResultCallback* aCallback)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aCallback);
-
-  AutoJSAPI autoJS;
-  if (NS_WARN_IF(!autoJS.Init(GetOwner()))) {
-    FireError(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
-    return;
-  }
-
-  JSContext* cx = autoJS.cx();
-
-  JS::Rooted<JS::Value> result(cx);
-  nsresult rv = aCallback->GetResult(cx, &result);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    FireError(rv);
-  } else {
-    FireSuccess(result);
-  }
-}
-
-void
-IDBFileRequest::SetError(nsresult aError)
-{
-  AssertIsOnOwningThread();
-
-  FireError(aError);
-}
-
-void
-IDBFileRequest::FireProgressEvent(uint64_t aLoaded, uint64_t aTotal)
-{
-  AssertIsOnOwningThread();
-
-  if (NS_FAILED(CheckInnerWindowCorrectness())) {
-    return;
-  }
-
-  ProgressEventInit init;
-  init.mBubbles = false;
-  init.mCancelable = false;
-  init.mLengthComputable = false;
-  init.mLoaded = aLoaded;
-  init.mTotal = aTotal;
-
-  RefPtr<ProgressEvent> event =
-    ProgressEvent::Constructor(this, NS_LITERAL_STRING("progress"), init);
-  DispatchTrustedEvent(event);
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/IDBFileRequest.h
+++ b/dom/indexedDB/IDBFileRequest.h
@@ -5,42 +5,76 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_idbfilerequest_h__
 #define mozilla_dom_idbfilerequest_h__
 
 #include "DOMRequest.h"
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/dom/FileRequestBase.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsString.h"
 
 template <class> struct already_AddRefed;
-class nsPIDOMWindowInner;
+struct PRThread;
 
 namespace mozilla {
 
 class EventChainPreVisitor;
 
 namespace dom {
 
 class IDBFileHandle;
 
-class IDBFileRequest final : public DOMRequest,
-                             public FileRequestBase
+class IDBFileRequest final
+  : public DOMRequest
 {
   RefPtr<IDBFileHandle> mFileHandle;
 
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
+
+  nsString mEncoding;
+
   bool mWrapAsDOMRequest;
+  bool mHasEncoding;
 
 public:
+  class ResultCallback;
+
   static already_AddRefed<IDBFileRequest>
-  Create(nsPIDOMWindowInner* aOwner, IDBFileHandle* aFileHandle,
+  Create(IDBFileHandle* aFileHandle,
          bool aWrapAsDOMRequest);
 
+  void
+  SetEncoding(const nsAString& aEncoding)
+  {
+    mEncoding = aEncoding;
+    mHasEncoding = true;
+  }
+
+  const nsAString&
+  GetEncoding() const
+  {
+    return mEncoding;
+  }
+
+  bool
+  HasEncoding() const
+  {
+    return mHasEncoding;
+  }
+
+  void
+  FireProgressEvent(uint64_t aLoaded, uint64_t aTotal);
+
+  void
+  SetResultCallback(ResultCallback* aCallback);
+
   // WebIDL
   IDBFileHandle*
   GetFileHandle() const
   {
     AssertIsOnOwningThread();
     return mFileHandle;
   }
 
@@ -48,47 +82,49 @@ public:
   GetLockedFile() const
   {
     AssertIsOnOwningThread();
     return GetFileHandle();
   }
 
   IMPL_EVENT_HANDLER(progress)
 
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBFileRequest, DOMRequest)
 
   // nsIDOMEventTarget
   virtual nsresult
   GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
 
   // nsWrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  // FileRequestBase
-  virtual FileHandleBase*
-  FileHandle() const override;
-
-  virtual void
-  OnProgress(uint64_t aProgress, uint64_t aProgressMax) override;
-
-  virtual void
-  SetResultCallback(ResultCallback* aCallback) override;
-
-  virtual void
-  SetError(nsresult aError) override;
-
 private:
-  IDBFileRequest(nsPIDOMWindowInner* aWindow,
-                 IDBFileHandle* aFileHandle,
+  IDBFileRequest(IDBFileHandle* aFileHandle,
                  bool aWrapAsDOMRequest);
 
   ~IDBFileRequest();
+};
 
-  void
-  FireProgressEvent(uint64_t aLoaded, uint64_t aTotal);
+class NS_NO_VTABLE IDBFileRequest::ResultCallback
+{
+public:
+  virtual nsresult
+  GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) = 0;
+
+protected:
+  ResultCallback()
+  { }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_idbfilerequest_h__
--- a/dom/indexedDB/IDBMutableFile.cpp
+++ b/dom/indexedDB/IDBMutableFile.cpp
@@ -3,28 +3,26 @@
 /* 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 "IDBMutableFile.h"
 
 #include "ActorsChild.h"
 #include "FileInfo.h"
-#include "FileSnapshot.h"
 #include "IDBDatabase.h"
 #include "IDBFactory.h"
 #include "IDBFileHandle.h"
 #include "IDBFileRequest.h"
 #include "IndexedDatabaseManager.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/IDBMutableFileBinding.h"
-#include "mozilla/dom/filehandle/ActorsChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
 #include "mozilla/dom/quota/FileStreams.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
@@ -38,37 +36,52 @@ namespace dom {
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::dom::quota;
 
 IDBMutableFile::IDBMutableFile(IDBDatabase* aDatabase,
                                BackgroundMutableFileChild* aActor,
                                const nsAString& aName,
                                const nsAString& aType)
   : DOMEventTargetHelper(aDatabase)
-  , MutableFileBase(DEBUGONLY(aDatabase->OwningThread(),)
-                    aActor)
   , mDatabase(aDatabase)
+  , mBackgroundActor(aActor)
   , mName(aName)
   , mType(aType)
   , mInvalidated(false)
 {
-  AssertIsOnOwningThread();
   MOZ_ASSERT(aDatabase);
   aDatabase->AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
 
   mDatabase->NoteLiveMutableFile(this);
 }
 
 IDBMutableFile::~IDBMutableFile()
 {
   AssertIsOnOwningThread();
 
   mDatabase->NoteFinishedMutableFile(this);
+
+  if (mBackgroundActor) {
+    mBackgroundActor->SendDeleteMeInternal();
+    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
+  }
 }
 
+#ifdef DEBUG
+
+void
+IDBMutableFile::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mDatabase);
+  mDatabase->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
 int64_t
 IDBMutableFile::GetFileId() const
 {
   AssertIsOnOwningThread();
 
   int64_t fileId;
   if (!mBackgroundActor ||
       NS_WARN_IF(!mBackgroundActor->SendGetFileId(&fileId))) {
@@ -178,19 +191,17 @@ IDBMutableFile::Open(FileMode aMode, Err
 
   RefPtr<IDBFileHandle> fileHandle =
     IDBFileHandle::Create(this, aMode);
   if (NS_WARN_IF(!fileHandle)) {
     aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
     return nullptr;
   }
 
-  BackgroundFileHandleChild* actor =
-    new BackgroundFileHandleChild(DEBUGONLY(mBackgroundActor->OwningThread(),)
-                                  fileHandle);
+  BackgroundFileHandleChild* actor = new BackgroundFileHandleChild(fileHandle);
 
   MOZ_ALWAYS_TRUE(
     mBackgroundActor->SendPBackgroundFileHandleConstructor(actor, aMode));
 
   fileHandle->SetBackgroundActor(actor);
 
   return fileHandle.forget();
 }
@@ -201,19 +212,17 @@ IDBMutableFile::GetFile(ErrorResult& aEr
   RefPtr<IDBFileHandle> fileHandle = Open(FileMode::Readonly, aError);
   if (NS_WARN_IF(aError.Failed())) {
     return nullptr;
   }
 
   FileRequestGetFileParams params;
 
   RefPtr<IDBFileRequest> request =
-    IDBFileRequest::Create(GetOwner(),
-                           fileHandle,
-                           /* aWrapAsDOMRequest */ true);
+    IDBFileRequest::Create(fileHandle, /* aWrapAsDOMRequest */ true);
 
   fileHandle->StartRequest(request, params);
 
   return request.forget();
 }
 
 NS_IMPL_ADDREF_INHERITED(IDBMutableFile, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(IDBMutableFile, DOMEventTargetHelper)
@@ -237,47 +246,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 JSObject*
 IDBMutableFile::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return IDBMutableFileBinding::Wrap(aCx, this, aGivenProto);
 }
 
-const nsString&
-IDBMutableFile::Name() const
-{
-  AssertIsOnOwningThread();
-
-  return mName;
-}
-
-const nsString&
-IDBMutableFile::Type() const
-{
-  AssertIsOnOwningThread();
-
-  return mType;
-}
-
-bool
-IDBMutableFile::IsInvalidated()
-{
-  AssertIsOnOwningThread();
-
-  return mInvalidated;
-}
-
-already_AddRefed<File>
-IDBMutableFile::CreateFileFor(BlobImpl* aBlobImpl,
-                              FileHandleBase* aFileHandle)
-{
-  AssertIsOnOwningThread();
-
-  RefPtr<BlobImpl> blobImplSnapshot =
-    new BlobImplSnapshot(aBlobImpl, static_cast<IDBFileHandle*>(aFileHandle));
-
-  RefPtr<File> file = File::Create(GetOwner(), blobImplSnapshot);
-  return file.forget();
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/IDBMutableFile.h
+++ b/dom/indexedDB/IDBMutableFile.h
@@ -7,17 +7,16 @@
 #ifndef mozilla_dom_idbmutablefile_h__
 #define mozilla_dom_idbmutablefile_h__
 
 #include "js/TypeDecls.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/FileModeBinding.h"
-#include "mozilla/dom/MutableFileBase.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsHashKeys.h"
 #include "nsString.h"
 #include "nsTHashtable.h"
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
@@ -32,47 +31,96 @@ class IDBDatabase;
 class IDBFileHandle;
 
 namespace indexedDB {
 class BackgroundMutableFileChild;
 }
 
 class IDBMutableFile final
   : public DOMEventTargetHelper
-  , public MutableFileBase
 {
   RefPtr<IDBDatabase> mDatabase;
 
+  indexedDB::BackgroundMutableFileChild* mBackgroundActor;
+
   nsTHashtable<nsPtrHashKey<IDBFileHandle>> mFileHandles;
 
   nsString mName;
   nsString mType;
 
   Atomic<bool> mInvalidated;
 
 public:
   IDBMutableFile(IDBDatabase* aDatabase,
                  indexedDB::BackgroundMutableFileChild* aActor,
                  const nsAString& aName,
                  const nsAString& aType);
 
   void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  indexedDB::BackgroundMutableFileChild*
+  GetBackgroundActor() const
+  {
+    AssertIsOnOwningThread();
+
+    return mBackgroundActor;
+  }
+
+  void
+  ClearBackgroundActor()
+  {
+    AssertIsOnOwningThread();
+
+    mBackgroundActor = nullptr;
+  }
+
+  const nsString&
+  Name() const
+  {
+    AssertIsOnOwningThread();
+
+    return mName;
+  }
+
+  const nsString&
+  Type() const
+  {
+    AssertIsOnOwningThread();
+
+    return mType;
+  }
+
+  void
   SetLazyData(const nsAString& aName,
               const nsAString& aType)
   {
     mName = aName;
     mType = aType;
   }
 
   int64_t
   GetFileId() const;
 
   void
   Invalidate();
 
+  bool
+  IsInvalidated() const
+  {
+    AssertIsOnOwningThread();
+
+    return mInvalidated;
+  }
+
   void
   RegisterFileHandle(IDBFileHandle* aFileHandle);
 
   void
   UnregisterFileHandle(IDBFileHandle* aFileHandle);
 
   void
   AbortFileHandles();
@@ -110,30 +158,16 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBMutableFile, DOMEventTargetHelper)
 
   // nsWrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  // MutableFileBase
-  virtual const nsString&
-  Name() const override;
-
-  virtual const nsString&
-  Type() const override;
-
-  virtual bool
-  IsInvalidated() override;
-
-  virtual already_AddRefed<File>
-  CreateFileFor(BlobImpl* aBlobImpl,
-                FileHandleBase* aFileHandle) override;
-
 private:
   ~IDBMutableFile();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_idbmutablefile_h__
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -9,17 +9,17 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 namespace dom {
 
-class FileHandleBase;
+class IDBFileHandle;
 
 namespace indexedDB {
 
 class ThreadLocal;
 
 } // namespace indexedDB
 } // namespace dom
 
@@ -211,17 +211,17 @@ protected:
 };
 
 class BackgroundChildImpl::ThreadLocal final
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
-  mozilla::dom::FileHandleBase* mCurrentFileHandle;
+  mozilla::dom::IDBFileHandle* mCurrentFileHandle;
 
 public:
   ThreadLocal();
 
 private:
   // Only destroyed by nsAutoPtr<ThreadLocal>.
   ~ThreadLocal();
 };