Bug 994190 - 'Modify main-thread IndexedDB to use PBackground', r=khuey.
authorBen Turner <bent.mozilla@gmail.com>
Fri, 26 Sep 2014 16:21:57 -0700
changeset 207571 8892214038df24332f83c99a5203af332e8035c9
parent 207570 627e848b2bf369668371ac47261bd898cdf8979b
child 207572 7b9c4085e11fbb983cc8c2ab2a097b58e3b7714e
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerskhuey
bugs994190
milestone35.0a1
Bug 994190 - 'Modify main-thread IndexedDB to use PBackground', r=khuey.
CLOBBER
content/base/public/nsDOMFile.h
content/base/src/nsDOMBlobBuilder.cpp
content/base/src/nsDOMBlobBuilder.h
content/base/src/nsDOMFile.cpp
content/base/src/nsDocument.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/test/test_ipc_messagemanager_blob.html
dom/archivereader/ArchiveZipFile.cpp
dom/archivereader/ArchiveZipFile.h
dom/base/nsDOMWindowUtils.cpp
dom/base/nsGlobalWindow.cpp
dom/bluetooth/BluetoothService.cpp
dom/bluetooth/BluetoothService.h
dom/bluetooth/bluedroid/BluetoothOppManager.cpp
dom/bluetooth/bluedroid/BluetoothOppManager.h
dom/bluetooth/bluez/BluetoothDBusService.cpp
dom/bluetooth/bluez/BluetoothOppManager.cpp
dom/bluetooth/bluez/BluetoothOppManager.h
dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
dom/bluetooth2/BluetoothService.cpp
dom/bluetooth2/BluetoothService.h
dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
dom/bluetooth2/bluedroid/BluetoothOppManager.h
dom/bluetooth2/bluez/BluetoothOppManager.cpp
dom/bluetooth2/bluez/BluetoothOppManager.h
dom/datastore/DataStoreDB.cpp
dom/datastore/DataStoreRevision.cpp
dom/datastore/DataStoreService.cpp
dom/datastore/tests/test_oop_events.html
dom/devicestorage/DeviceStorageRequestChild.cpp
dom/devicestorage/DeviceStorageRequestParent.cpp
dom/devicestorage/nsDeviceStorage.cpp
dom/filehandle/FileStreamWrappers.cpp
dom/filehandle/FileStreamWrappers.h
dom/filehandle/moz.build
dom/filesystem/CreateFileTask.cpp
dom/filesystem/FileSystemTaskBase.cpp
dom/filesystem/FileSystemTaskBase.h
dom/filesystem/GetFileOrDirectoryTask.cpp
dom/filesystem/RemoveTask.cpp
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/ActorsChild.h
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/ActorsParent.h
dom/indexedDB/AsyncConnectionHelper.cpp
dom/indexedDB/AsyncConnectionHelper.h
dom/indexedDB/CheckPermissionsHelper.cpp
dom/indexedDB/CheckPermissionsHelper.h
dom/indexedDB/Client.cpp
dom/indexedDB/Client.h
dom/indexedDB/DatabaseInfo.cpp
dom/indexedDB/DatabaseInfo.h
dom/indexedDB/FileInfo.cpp
dom/indexedDB/FileInfo.h
dom/indexedDB/FileManager.cpp
dom/indexedDB/FileManager.h
dom/indexedDB/FileSnapshot.cpp
dom/indexedDB/FileSnapshot.h
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBCursor.h
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBDatabase.h
dom/indexedDB/IDBEvents.cpp
dom/indexedDB/IDBEvents.h
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBFactory.h
dom/indexedDB/IDBFileHandle.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBIndex.h
dom/indexedDB/IDBKeyRange.cpp
dom/indexedDB/IDBKeyRange.h
dom/indexedDB/IDBMutableFile.cpp
dom/indexedDB/IDBMutableFile.h
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/indexedDB/IDBRequest.cpp
dom/indexedDB/IDBRequest.h
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IDBTransaction.h
dom/indexedDB/IDBWrapperCache.cpp
dom/indexedDB/IDBWrapperCache.h
dom/indexedDB/IndexedDatabase.h
dom/indexedDB/IndexedDatabaseInlines.h
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/indexedDB/Key.cpp
dom/indexedDB/Key.h
dom/indexedDB/KeyPath.cpp
dom/indexedDB/KeyPath.h
dom/indexedDB/OpenDatabaseHelper.cpp
dom/indexedDB/OpenDatabaseHelper.h
dom/indexedDB/PBackgroundIDBCursor.ipdl
dom/indexedDB/PBackgroundIDBDatabase.ipdl
dom/indexedDB/PBackgroundIDBDatabaseFile.ipdl
dom/indexedDB/PBackgroundIDBFactory.ipdl
dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl
dom/indexedDB/PBackgroundIDBRequest.ipdl
dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
dom/indexedDB/PBackgroundIDBTransaction.ipdl
dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl
dom/indexedDB/PIndexedDBPermissionRequest.ipdl
dom/indexedDB/PermissionRequestBase.cpp
dom/indexedDB/PermissionRequestBase.h
dom/indexedDB/ProfilerHelpers.h
dom/indexedDB/ReportInternalError.cpp
dom/indexedDB/ReportInternalError.h
dom/indexedDB/SerializationHelpers.h
dom/indexedDB/TransactionThreadPool.cpp
dom/indexedDB/TransactionThreadPool.h
dom/indexedDB/ipc/IndexedDBChild.cpp
dom/indexedDB/ipc/IndexedDBChild.h
dom/indexedDB/ipc/IndexedDBParams.ipdlh
dom/indexedDB/ipc/IndexedDBParent.cpp
dom/indexedDB/ipc/IndexedDBParent.h
dom/indexedDB/ipc/Makefile.in
dom/indexedDB/ipc/PIndexedDB.ipdl
dom/indexedDB/ipc/PIndexedDBCursor.ipdl
dom/indexedDB/ipc/PIndexedDBDatabase.ipdl
dom/indexedDB/ipc/PIndexedDBDeleteDatabaseRequest.ipdl
dom/indexedDB/ipc/PIndexedDBIndex.ipdl
dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
dom/indexedDB/ipc/PIndexedDBRequest.ipdl
dom/indexedDB/ipc/PIndexedDBTransaction.ipdl
dom/indexedDB/ipc/SerializationHelpers.h
dom/indexedDB/ipc/mochitest.ini
dom/indexedDB/ipc/moz.build
dom/indexedDB/ipc/test_ipc.html
dom/indexedDB/ipc/unit/head.js
dom/indexedDB/ipc/unit/xpcshell.ini
dom/indexedDB/moz.build
dom/indexedDB/test/browser.ini
dom/indexedDB/test/file.js
dom/indexedDB/test/helpers.js
dom/indexedDB/test/mochitest.ini
dom/indexedDB/test/test_blob_simple.html
dom/indexedDB/test/test_blocked_order.html
dom/indexedDB/test/test_disabled_quota_prompt.html
dom/indexedDB/test/test_file_cross_database_copying.html
dom/indexedDB/test/test_file_os_delete.html
dom/indexedDB/test/test_file_sharing.html
dom/indexedDB/test/test_filehandle_compat.html
dom/indexedDB/test/test_filehandle_getFile.html
dom/indexedDB/test/test_filehandle_lifetimes.html
dom/indexedDB/test/test_filehandle_lifetimes_nested.html
dom/indexedDB/test/test_filehandle_location.html
dom/indexedDB/test/test_filehandle_ordering.html
dom/indexedDB/test/test_filehandle_overlapping.html
dom/indexedDB/test/test_filehandle_readonly_exceptions.html
dom/indexedDB/test/test_filehandle_request_readyState.html
dom/indexedDB/test/test_filehandle_success_events_after_abort.html
dom/indexedDB/test/test_invalidate.html
dom/indexedDB/test/test_message_manager_ipc.html
dom/indexedDB/test/test_persistenceType.html
dom/indexedDB/test/unit/head.js
dom/indexedDB/test/unit/mochitest.ini
dom/indexedDB/test/unit/test_blocked_order.js
dom/indexedDB/test/unit/test_indexes.js
dom/indexedDB/test/unit/test_indexes_funny_things.js
dom/indexedDB/test/unit/test_invalidate.js
dom/indexedDB/test/unit/test_setVersion_events.js
dom/indexedDB/test/unit/test_temporary_storage.js
dom/indexedDB/test/unit/xpcshell-child-process.ini
dom/indexedDB/test/unit/xpcshell-head-child-process.js
dom/indexedDB/test/unit/xpcshell-head-parent-process.js
dom/indexedDB/test/unit/xpcshell-parent-process.ini
dom/indexedDB/test/unit/xpcshell-shared.ini
dom/indexedDB/test/unit/xpcshell.ini
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/ipc/Blob.cpp
dom/ipc/Blob.h
dom/ipc/BlobChild.h
dom/ipc/BlobParent.h
dom/ipc/ContentBridgeChild.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/DOMTypes.ipdlh
dom/ipc/FileDescriptorSetChild.cpp
dom/ipc/FileDescriptorSetChild.h
dom/ipc/FileDescriptorSetParent.cpp
dom/ipc/FileDescriptorSetParent.h
dom/ipc/FilePickerParent.cpp
dom/ipc/PBlob.ipdl
dom/ipc/PBlobStream.ipdl
dom/ipc/PBrowser.ipdl
dom/ipc/PContent.ipdl
dom/ipc/PFileDescriptorSet.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/ipc/moz.build
dom/ipc/nsIContentChild.cpp
dom/ipc/nsIContentChild.h
dom/ipc/nsIContentParent.cpp
dom/ipc/nsIContentParent.h
dom/ipc/nsIRemoteBlob.h
dom/mobilemessage/MmsMessage.cpp
dom/mobilemessage/ipc/SmsIPCService.cpp
dom/mobilemessage/ipc/SmsParent.cpp
dom/quota/QuotaManager.cpp
dom/quota/QuotaManager.h
dom/quota/StoragePrivilege.h
dom/quota/nsIOfflineStorage.h
dom/webidl/IDBDatabase.webidl
dom/webidl/IDBIndex.webidl
dom/webidl/IDBObjectStore.webidl
dom/webidl/IDBTransaction.webidl
ipc/glue/BackgroundChild.h
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundImpl.cpp
ipc/glue/BackgroundParent.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/FileDescriptorSetChild.cpp
ipc/glue/FileDescriptorSetChild.h
ipc/glue/FileDescriptorSetParent.cpp
ipc/glue/FileDescriptorSetParent.h
ipc/glue/InputStreamParams.ipdlh
ipc/glue/InputStreamUtils.cpp
ipc/glue/PBackground.ipdl
ipc/glue/PFileDescriptorSet.ipdl
ipc/glue/moz.build
netwerk/base/public/nsIFileStreams.idl
netwerk/base/src/nsFileStreams.cpp
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
testing/mochitest/b2g_start_script.js
testing/specialpowers/components/SpecialPowersObserver.js
testing/specialpowers/content/SpecialPowersObserverAPI.js
testing/specialpowers/content/specialpowersAPI.js
testing/web-platform/meta/IndexedDB/idbcursor_continue_invalid.htm.ini
testing/web-platform/meta/IndexedDB/idbfactory_deleteDatabase3.htm.ini
testing/web-platform/meta/IndexedDB/idbobjectstore_clear.htm.ini
testing/web-platform/meta/IndexedDB/idbobjectstore_clear2.htm.ini
testing/web-platform/meta/IndexedDB/idbversionchangeevent.htm.ini
widget/android/NativeJSContainer.cpp
widget/xpwidgets/nsFilePickerProxy.cpp
xpcom/glue/nsID.h
xpcom/io/nsILocalFileWin.idl
xpcom/io/nsLocalFileWin.cpp
xpcom/io/nsLocalFileWin.h
xpcom/threads/LazyIdleThread.cpp
xpcom/threads/nsThread.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1065897: Updated moz.build requires CLOBBER
+Bug 1069071: IPDL changes require CLOBBER
--- a/content/base/public/nsDOMFile.h
+++ b/content/base/public/nsDOMFile.h
@@ -28,16 +28,17 @@
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/indexedDB/FileInfo.h"
 #include "mozilla/dom/indexedDB/FileManager.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsWeakReference.h"
 
 class nsDOMMultipartFile;
 class nsIFile;
 class nsIInputStream;
 class nsIClassInfo;
 
 #define PIDOMFILEIMPL_IID \
   { 0x218ee173, 0xf44f, 0x4d30, \
@@ -59,16 +60,17 @@ class FileInfo;
 };
 
 class DOMFileImpl;
 
 class DOMFile MOZ_FINAL : public nsIDOMFile
                         , public nsIXHRSendable
                         , public nsIMutable
                         , public nsIJSNativeInitializer
+                        , public nsSupportsWeakReference
 {
 public:
   NS_DECL_NSIDOMBLOB
   NS_DECL_NSIDOMFILE
   NS_DECL_NSIXHRSENDABLE
   NS_DECL_NSIMUTABLE
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -185,19 +187,19 @@ public:
 
   virtual nsresult GetSize(uint64_t* aSize) = 0;
 
   virtual nsresult GetType(nsAString& aType) = 0;
 
   virtual nsresult GetMozLastModifiedDate(uint64_t* aDate) = 0;
 
   nsresult Slice(int64_t aStart, int64_t aEnd, const nsAString& aContentType,
-                 uint8_t aArgc, nsIDOMBlob **aBlob);
+                 uint8_t aArgc, DOMFileImpl** aBlobImpl);
 
-  virtual already_AddRefed<nsIDOMBlob>
+  virtual already_AddRefed<DOMFileImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType) = 0;
 
   virtual const nsTArray<nsRefPtr<DOMFileImpl>>*
   GetSubBlobImpls() const = 0;
 
   virtual nsresult GetInternalStream(nsIInputStream** aStream) = 0;
 
@@ -316,17 +318,17 @@ public:
   virtual nsresult GetMozFullPathInternal(nsAString& aFileName) MOZ_OVERRIDE;
 
   virtual nsresult GetSize(uint64_t* aSize) MOZ_OVERRIDE;
 
   virtual nsresult GetType(nsAString& aType) MOZ_OVERRIDE;
 
   virtual nsresult GetMozLastModifiedDate(uint64_t* aDate) MOZ_OVERRIDE;
 
-  virtual already_AddRefed<nsIDOMBlob>
+  virtual already_AddRefed<DOMFileImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType) MOZ_OVERRIDE;
 
   virtual const nsTArray<nsRefPtr<DOMFileImpl>>*
   GetSubBlobImpls() const MOZ_OVERRIDE
   {
     return nullptr;
   }
@@ -459,17 +461,17 @@ public:
     : DOMFileImplBase(aContentType, aLength)
     , mDataOwner(new DataOwner(aMemoryBuffer, aLength))
   {
     NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
   }
 
   virtual nsresult GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
 
-  virtual already_AddRefed<nsIDOMBlob>
+  virtual already_AddRefed<DOMFileImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType) MOZ_OVERRIDE;
 
   virtual bool IsMemoryFile() const MOZ_OVERRIDE
   {
     return true;
   }
 
@@ -546,17 +548,17 @@ public:
     , mStartPos(aStartPos)
     , mContentType(aContentType)
   {
     mFileDescOwner = new nsTemporaryFileInputStream::FileDescOwner(aFD);
   }
 
   virtual nsresult GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
 
-  virtual already_AddRefed<nsIDOMBlob>
+  virtual already_AddRefed<DOMFileImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType) MOZ_OVERRIDE;
 
 private:
   DOMFileImplTemporaryFileBlob(const DOMFileImplTemporaryFileBlob* aOther,
                                uint64_t aStart, uint64_t aLength,
                                const nsAString& aContentType)
     : DOMFileImplBase(aContentType, aLength)
@@ -568,17 +570,17 @@ private:
   ~DOMFileImplTemporaryFileBlob() {}
 
   uint64_t mLength;
   uint64_t mStartPos;
   nsRefPtr<nsTemporaryFileInputStream::FileDescOwner> mFileDescOwner;
   nsString mContentType;
 };
 
-class DOMFileImplFile MOZ_FINAL : public DOMFileImplBase
+class DOMFileImplFile : public DOMFileImplBase
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // Create as a file
   explicit DOMFileImplFile(nsIFile* aFile)
     : DOMFileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX)
     , mFile(aFile)
@@ -685,16 +687,19 @@ public:
   virtual nsresult GetLastModifiedDate(JSContext* aCx,
                                JS::MutableHandle<JS::Value> aLastModifiedDate) MOZ_OVERRIDE;
   virtual nsresult GetMozLastModifiedDate(uint64_t* aLastModifiedDate) MOZ_OVERRIDE;
   virtual nsresult GetMozFullPathInternal(nsAString& aFullPath) MOZ_OVERRIDE;
   virtual nsresult GetInternalStream(nsIInputStream**) MOZ_OVERRIDE;
 
   void SetPath(const nsAString& aFullPath);
 
+protected:
+  virtual ~DOMFileImplFile() {}
+
 private:
   // Create slice
   DOMFileImplFile(const DOMFileImplFile* aOther, uint64_t aStart,
                   uint64_t aLength, const nsAString& aContentType)
     : DOMFileImplBase(aContentType, aOther->mStart + aStart, aLength)
     , mFile(aOther->mFile)
     , mWholeFile(false)
     , mStoredFile(aOther->mStoredFile)
@@ -714,19 +719,17 @@ private:
         mozilla::MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
         fileInfo = aOther->GetFileInfo();
       }
 
       mFileInfos.AppendElement(fileInfo);
     }
   }
 
-  ~DOMFileImplFile() {}
-
-  virtual already_AddRefed<nsIDOMBlob>
+  virtual already_AddRefed<DOMFileImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType) MOZ_OVERRIDE;
 
   virtual bool IsStoredFile() const MOZ_OVERRIDE
   {
     return mStoredFile;
   }
 
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -50,17 +50,17 @@ DOMMultipartFileImpl::GetInternalStream(
 
     rv = stream->AppendStream(scratchStream);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return CallQueryInterface(stream, aStream);
 }
 
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
 DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
                                   const nsAString& aContentType)
 {
   // If we clamped to nothing we create an empty blob
   nsTArray<nsRefPtr<DOMFileImpl>> blobImpls;
 
   uint64_t length = aLength;
   uint64_t skipStart = aStart;
@@ -72,60 +72,59 @@ DOMMultipartFileImpl::CreateSlice(uint64
 
     uint64_t l;
     nsresult rv = blobImpl->GetSize(&l);
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     if (skipStart < l) {
       uint64_t upperBound = std::min<uint64_t>(l - skipStart, length);
 
-      nsCOMPtr<nsIDOMBlob> firstBlob;
-      rv = blobImpl->Slice(skipStart, skipStart + upperBound,
-                           aContentType, 3,
-                           getter_AddRefs(firstBlob));
+      nsRefPtr<DOMFileImpl> firstImpl;
+      rv = blobImpl->Slice(skipStart, skipStart + upperBound, aContentType, 3,
+                           getter_AddRefs(firstImpl));
       NS_ENSURE_SUCCESS(rv, nullptr);
 
       // Avoid wrapping a single blob inside an DOMMultipartFileImpl
       if (length == upperBound) {
-        return firstBlob.forget();
+        return firstImpl.forget();
       }
 
-      blobImpls.AppendElement(static_cast<DOMFile*>(firstBlob.get())->Impl());
+      blobImpls.AppendElement(firstImpl);
       length -= upperBound;
       i++;
       break;
     }
     skipStart -= l;
   }
 
   // Now append enough blobs until we're done
   for (; length && i < mBlobImpls.Length(); i++) {
     DOMFileImpl* blobImpl = mBlobImpls[i].get();
 
     uint64_t l;
     nsresult rv = blobImpl->GetSize(&l);
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     if (length < l) {
-      nsCOMPtr<nsIDOMBlob> lastBlob;
+      nsRefPtr<DOMFileImpl> lastBlob;
       rv = blobImpl->Slice(0, length, aContentType, 3,
                            getter_AddRefs(lastBlob));
       NS_ENSURE_SUCCESS(rv, nullptr);
 
-      blobImpls.AppendElement(static_cast<DOMFile*>(lastBlob.get())->Impl());
+      blobImpls.AppendElement(lastBlob);
     } else {
       blobImpls.AppendElement(blobImpl);
     }
     length -= std::min<uint64_t>(l, length);
   }
 
   // we can create our blob now
-  nsCOMPtr<nsIDOMBlob> blob =
-    new DOMFile(new DOMMultipartFileImpl(blobImpls, aContentType));
-  return blob.forget();
+  nsRefPtr<DOMFileImpl> impl =
+    new DOMMultipartFileImpl(blobImpls, aContentType);
+  return impl.forget();
 }
 
 /* static */ nsresult
 DOMMultipartFileImpl::NewFile(const nsAString& aName, nsISupports** aNewObject)
 {
   nsCOMPtr<nsISupports> file =
     do_QueryObject(new DOMFile(new DOMMultipartFileImpl(aName)));
   file.forget(aNewObject);
--- a/content/base/src/nsDOMBlobBuilder.h
+++ b/content/base/src/nsDOMBlobBuilder.h
@@ -73,17 +73,17 @@ public:
                     UnwrapFuncPtr aUnwrapFunc);
   nsresult InitFile(JSContext* aCx,
                     uint32_t aArgc,
                     JS::Value* aArgv);
   nsresult InitChromeFile(JSContext* aCx,
                           uint32_t aArgc,
                           JS::Value* aArgv);
 
-  virtual already_AddRefed<nsIDOMBlob>
+  virtual already_AddRefed<DOMFileImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType) MOZ_OVERRIDE;
 
   virtual nsresult GetSize(uint64_t* aSize) MOZ_OVERRIDE;
 
   virtual nsresult GetInternalStream(nsIInputStream** aInputStream) MOZ_OVERRIDE;
 
   static nsresult NewFile(const nsAString& aName, nsISupports** aNewObject);
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -142,16 +142,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsIRemoteBlob)));
 
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, IsFile())
   NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
   NS_INTERFACE_MAP_ENTRY(nsIMutable)
   NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, IsFile())
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !(IsFile()))
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMFile)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMFile)
 
 /* static */ already_AddRefed<DOMFile>
@@ -293,17 +294,20 @@ DOMFile::SetLazyData(const nsAString& aN
 {
   return mImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
 }
 
 already_AddRefed<nsIDOMBlob>
 DOMFile::CreateSlice(uint64_t aStart, uint64_t aLength,
                      const nsAString& aContentType)
 {
-  return mImpl->CreateSlice(aStart, aLength, aContentType);
+  nsRefPtr<DOMFileImpl> impl =
+    mImpl->CreateSlice(aStart, aLength, aContentType);
+  nsRefPtr<DOMFile> slice = new DOMFile(impl);
+  return slice.forget();
 }
 
 NS_IMETHODIMP
 DOMFile::Initialize(nsISupports* aOwner, JSContext* aCx, JSObject* aObj,
                     const JS::CallArgs& aArgs)
 {
   return mImpl->Initialize(aOwner, aCx, aObj, aArgs);
 }
@@ -395,17 +399,27 @@ ParseSize(int64_t aSize, int64_t& aStart
 }
 
 NS_IMETHODIMP
 DOMFile::Slice(int64_t aStart, int64_t aEnd,
                const nsAString& aContentType, uint8_t aArgc,
                nsIDOMBlob **aBlob)
 {
   MOZ_ASSERT(mImpl);
-  return mImpl->Slice(aStart, aEnd, aContentType, aArgc, aBlob);
+  nsRefPtr<DOMFileImpl> impl;
+  nsresult rv = mImpl->Slice(aStart, aEnd, aContentType, aArgc,
+                             getter_AddRefs(impl));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsRefPtr<DOMFile> blob = new DOMFile(impl);
+  blob.forget(aBlob);
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMFile::GetInternalStream(nsIInputStream** aStream)
 {
  return mImpl->GetInternalStream(aStream);
 }
 
@@ -455,37 +469,40 @@ DOMFile::IsMemoryFile()
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // mozilla::dom::DOMFileImpl implementation
 
 nsresult
 DOMFileImpl::Slice(int64_t aStart, int64_t aEnd,
                    const nsAString& aContentType, uint8_t aArgc,
-                   nsIDOMBlob **aBlob)
+                   DOMFileImpl** aBlobImpl)
 {
-  *aBlob = nullptr;
+  *aBlobImpl = nullptr;
 
   // Truncate aStart and aEnd so that we stay within this file.
   uint64_t thisLength;
   nsresult rv = GetSize(&thisLength);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aArgc < 2) {
     aEnd = (int64_t)thisLength;
   }
 
   ParseSize((int64_t)thisLength, aStart, aEnd);
 
-  // Create the new file
-  nsCOMPtr<nsIDOMBlob> blob =
+  nsRefPtr<DOMFileImpl> impl =
     CreateSlice((uint64_t)aStart, (uint64_t)(aEnd - aStart), aContentType);
 
-  blob.forget(aBlob);
-  return *aBlob ? NS_OK : NS_ERROR_UNEXPECTED;
+  if (!impl) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  impl.forget(aBlobImpl);
+  return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // DOMFileImpl implementation
 
 NS_IMPL_ISUPPORTS(DOMFileImpl, PIDOMFileImpl)
 
 ////////////////////////////////////////////////////////////////////////////
@@ -570,17 +587,17 @@ DOMFileImplBase::GetMozLastModifiedDate(
   NS_ASSERTION(mIsFile, "Should only be called on files");
   if (IsDateUnknown()) {
     mLastModificationDate = PR_Now();
   }
   *aLastModifiedDate = mLastModificationDate;
   return NS_OK;
 }
 
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
 DOMFileImplBase::CreateSlice(uint64_t aStart, uint64_t aLength,
                              const nsAString& aContentType)
 {
   return nullptr;
 }
 
 nsresult
 DOMFileImplBase::GetInternalStream(nsIInputStream** aStream)
@@ -719,23 +736,23 @@ DOMFileImplBase::SetMutable(bool aMutabl
 
   mImmutable = !aMutable;
   return rv;
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // DOMFileImplFile implementation
 
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
 DOMFileImplFile::CreateSlice(uint64_t aStart, uint64_t aLength,
                              const nsAString& aContentType)
 {
-  nsCOMPtr<nsIDOMBlob> blob =
-    new DOMFile(new DOMFileImplFile(this, aStart, aLength, aContentType));
-  return blob.forget();
+  nsRefPtr<DOMFileImpl> impl =
+    new DOMFileImplFile(this, aStart, aLength, aContentType);
+  return impl.forget();
 }
 
 nsresult
 DOMFileImplFile::GetMozFullPathInternal(nsAString& aFilename)
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
   return mFile->GetPath(aFilename);
 }
@@ -827,17 +844,18 @@ DOMFileImplFile::GetMozLastModifiedDate(
   }
   *aLastModifiedDate = mLastModificationDate;
   return NS_OK;
 }
 
 const uint32_t sFileStreamFlags =
   nsIFileInputStream::CLOSE_ON_EOF |
   nsIFileInputStream::REOPEN_ON_REWIND |
-  nsIFileInputStream::DEFER_OPEN;
+  nsIFileInputStream::DEFER_OPEN |
+  nsIFileInputStream::SHARE_DELETE;
 
 nsresult
 DOMFileImplFile::GetInternalStream(nsIInputStream** aStream)
 {
   return mWholeFile ?
     NS_NewLocalFileInputStream(aStream, mFile, -1, -1, sFileStreamFlags) :
     NS_NewPartialLocalFileInputStream(aStream, mFile, mStart, mLength,
                                       -1, -1, sFileStreamFlags);
@@ -852,23 +870,23 @@ DOMFileImplFile::SetPath(const nsAString
   mPath = aPath;
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // DOMFileImplMemory implementation
 
 NS_IMPL_ISUPPORTS_INHERITED0(DOMFileImplMemory, DOMFileImpl)
 
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
 DOMFileImplMemory::CreateSlice(uint64_t aStart, uint64_t aLength,
                                const nsAString& aContentType)
 {
-  nsCOMPtr<nsIDOMBlob> blob =
-    new DOMFile(new DOMFileImplMemory(this, aStart, aLength, aContentType));
-  return blob.forget();
+  nsRefPtr<DOMFileImpl> impl =
+    new DOMFileImplMemory(this, aStart, aLength, aContentType);
+  return impl.forget();
 }
 
 nsresult
 DOMFileImplMemory::GetInternalStream(nsIInputStream** aStream)
 {
   if (mLength > INT32_MAX)
     return NS_ERROR_FAILURE;
 
@@ -977,27 +995,27 @@ DOMFileImplMemory::DataOwner::EnsureMemo
   sMemoryReporterRegistered = true;
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // DOMFileImplTemporaryFileBlob implementation
 
 NS_IMPL_ISUPPORTS_INHERITED0(DOMFileImplTemporaryFileBlob, DOMFileImpl)
 
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<DOMFileImpl>
 DOMFileImplTemporaryFileBlob::CreateSlice(uint64_t aStart, uint64_t aLength,
                                           const nsAString& aContentType)
 {
   if (aStart + aLength > mLength)
     return nullptr;
 
-  nsCOMPtr<nsIDOMBlob> blob =
-    new DOMFile(new DOMFileImplTemporaryFileBlob(this, aStart + mStartPos,
-                                                 aLength, aContentType));
-  return blob.forget();
+  nsRefPtr<DOMFileImpl> impl =
+    new DOMFileImplTemporaryFileBlob(this, aStart + mStartPos, aLength,
+                                     aContentType);
+  return impl.forget();
 }
 
 nsresult
 DOMFileImplTemporaryFileBlob::GetInternalStream(nsIInputStream** aStream)
 {
   nsCOMPtr<nsIInputStream> stream =
     new nsTemporaryFileInputStream(mFileDescOwner, mStartPos, mStartPos + mLength);
   stream.forget(aStream);
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -149,17 +149,16 @@
 #include "nsObjectLoadingContent.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "mozilla/dom/HTMLLinkElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/dom/EncodingUtils.h"
-#include "mozilla/dom/quota/QuotaManager.h"
 #include "nsDOMNavigationTiming.h"
 
 #include "nsSMILAnimationController.h"
 #include "imgIContainer.h"
 #include "nsSVGUtils.h"
 #include "SVGElementFactory.h"
 
 #include "nsRefreshDriver.h"
@@ -8534,23 +8533,16 @@ nsDocument::CanSavePresentation(nsIReque
         printf("document %s has request %s\n",
                docSpec.get(), requestName.get());
 #endif
         return false;
       }
     }
   }
 
-  // Check if we have running offline storage transactions
-  quota::QuotaManager* quotaManager =
-    win ? quota::QuotaManager::Get() : nullptr;
-  if (quotaManager && quotaManager->HasOpenTransactions(win)) {
-   return false;
-  }
-
 #ifdef MOZ_MEDIA_NAVIGATOR
   // Check if we have active GetUserMedia use
   if (MediaManager::Exists() && win &&
       MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
     return false;
   }
 #endif // MOZ_MEDIA_NAVIGATOR
 
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -29,18 +29,18 @@
 #include "nsIDOMClassInfo.h"
 #include "nsIDOMFile.h"
 #include "xpcpublic.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/nsIContentParent.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredCloneUtils.h"
-#include "mozilla/dom/PBlobChild.h"
-#include "mozilla/dom/PBlobParent.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "JavaScriptChild.h"
 #include "JavaScriptParent.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "nsPrintfCString.h"
 #include "nsXULAppAPI.h"
 #include <algorithm>
 
 #ifdef ANDROID
--- a/content/base/test/test_ipc_messagemanager_blob.html
+++ b/content/base/test/test_ipc_messagemanager_blob.html
@@ -35,52 +35,91 @@
                          "error";
           sendAsyncMessage(message.name, response);
         });
         reader.readAsText(message.json);
       });
     }
 
     function runTests() {
+      function done() {
+        SpecialPowers.removePermission("browser", document);
+        SimpleTest.finish();
+      }
+
       ok("Browser prefs set.");
 
       let iframe = document.createElement("iframe");
       SpecialPowers.wrap(iframe).mozbrowser = true;
       iframe.id = "iframe";
       iframe.src = childFrameURL;
 
       iframe.addEventListener("mozbrowserloadend", function() {
         ok(true, "Got iframe load event.");
 
+        const blobString = "this is a great success!";
+
         const messages = [
           "hi!",
           "",
           2,
           -.04,
           3432987324987239872948732982,
           true,
           false,
           null,
           0,
+
+          // Make sure this one is always last.
           new Blob(["this ", "is ", "a ", "great ", "success!"],
                    {"type" : "text\/plain"}),
         ];
         let receivedMessageIndex = 0;
 
         let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
         mm.addMessageListener("test:ipcClonedMessage", function(message) {
-          // We need to wrap to access message.json, and unwrap to do the
-          // identity check.
-          is(SpecialPowers.unwrap(SpecialPowers.wrap(message).json),
+          let data = message.json;
+
+          if (data instanceof Blob) {
+            is(receivedMessageIndex, messages.length - 1, "Blob is last");
+            is (data.size,
+                messages[receivedMessageIndex].size,
+                "Correct blob size");
+            is (data.type,
+                messages[receivedMessageIndex].type,
+                "Correct blob type");
+
+            let result1, result2;
+
+            let reader1 = new FileReader();
+            reader1.onload = function() {
+              result1 = reader1.result == blobString ? reader1.result : "bad1";
+              if (result2) {
+                is(result1, result2, "Same results");
+                done();
+              }
+            };
+
+            let reader2 = new FileReader();
+            reader2.onload = function() {
+              result2 = reader2.result == blobString ? reader2.result : "bad2";
+              if (result1) {
+                is(result1, result2, "Same results");
+                done();
+              }
+            };
+
+            reader1.readAsText(data);
+            reader2.readAsText(messages[receivedMessageIndex]);
+            return;
+          }
+
+          is(message.json,
              messages[receivedMessageIndex++],
              "Got correct round-tripped response");
-          if (receivedMessageIndex == messages.length) {
-            SpecialPowers.removePermission("browser", document);
-            SimpleTest.finish();
-          }
         });
         mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
                            false);
 
         for each (let message in messages) {
           mm.sendAsyncMessage("test:ipcClonedMessage", message);
         }
       });
--- a/dom/archivereader/ArchiveZipFile.cpp
+++ b/dom/archivereader/ArchiveZipFile.cpp
@@ -391,21 +391,20 @@ ArchiveZipFileImpl::Unlink()
 
 void
 ArchiveZipFileImpl::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
   ArchiveZipFileImpl* tmp = this;
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArchiveReader);
 }
 
-already_AddRefed<nsIDOMBlob>
+already_AddRefed<mozilla::dom::DOMFileImpl>
 ArchiveZipFileImpl::CreateSlice(uint64_t aStart,
                                 uint64_t aLength,
                                 const nsAString& aContentType)
 {
-  nsCOMPtr<nsIDOMBlob> t =
-    new DOMFile(new ArchiveZipFileImpl(mFilename, mContentType,
-                                       aStart, mLength, mCentral,
-                                       mArchiveReader));
-  return t.forget();
+  nsRefPtr<DOMFileImpl> impl =
+    new ArchiveZipFileImpl(mFilename, mContentType, aStart, mLength, mCentral,
+                           mArchiveReader);
+  return impl.forget();
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(ArchiveZipFileImpl, DOMFileImpl)
--- a/dom/archivereader/ArchiveZipFile.h
+++ b/dom/archivereader/ArchiveZipFile.h
@@ -66,19 +66,19 @@ public:
   }
 
 protected:
   virtual ~ArchiveZipFileImpl()
   {
     MOZ_COUNT_DTOR(ArchiveZipFileImpl);
   }
 
-  virtual already_AddRefed<nsIDOMBlob> CreateSlice(uint64_t aStart,
-                                                   uint64_t aLength,
-                                                   const nsAString& aContentType) MOZ_OVERRIDE;
+  virtual already_AddRefed<DOMFileImpl> CreateSlice(uint64_t aStart,
+                                                    uint64_t aLength,
+                                                    const nsAString& aContentType) MOZ_OVERRIDE;
 
 private: // Data
   ZipCentral mCentral;
   nsRefPtr<ArchiveReader> mArchiveReader;
 
   nsString mFilename;
 };
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -95,16 +95,17 @@
 #include "nsContentPermissionHelper.h"
 
 #ifdef XP_WIN
 #undef GetClassName
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla::gfx;
 
 class gfxContext;
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
@@ -2999,40 +3000,75 @@ nsDOMWindowUtils::AreDialogsEnabled(bool
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   *aResult = static_cast<nsGlobalWindow*>(window.get())->AreDialogsEnabled();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetFileId(JS::Handle<JS::Value> aFile, JSContext* aCx,
-                            int64_t* aResult)
+                            int64_t* _retval)
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
-  if (!aFile.isPrimitive()) {
-    JSObject* obj = aFile.toObjectOrNull();
-
-    indexedDB::IDBMutableFile* mutableFile = nullptr;
-    if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, obj, mutableFile))) {
-      *aResult = mutableFile->GetFileId();
-      return NS_OK;
+  if (aFile.isPrimitive()) {
+    *_retval = -1;
+    return NS_OK;
+  }
+
+  JSObject* obj = aFile.toObjectOrNull();
+
+  indexedDB::IDBMutableFile* mutableFile = nullptr;
+  if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, obj, mutableFile))) {
+    *_retval = mutableFile->GetFileId();
+    return NS_OK;
+  }
+
+  nsISupports* nativeObj =
+    nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
+
+  nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(nativeObj);
+  if (blob) {
+    *_retval = blob->GetFileId();
+    return NS_OK;
+  }
+
+  *_retval = -1;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::GetFilePath(JS::HandleValue aFile, JSContext* aCx,
+                              nsAString& _retval)
+{
+  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
+
+  if (aFile.isPrimitive()) {
+    _retval.Truncate();
+    return NS_OK;
+  }
+
+  JSObject* obj = aFile.toObjectOrNull();
+
+  nsISupports* nativeObj =
+    nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
+
+  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(nativeObj);
+  if (file) {
+    nsString filePath;
+    nsresult rv = file->GetMozFullPathInternal(filePath);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
 
-    nsISupports* nativeObj =
-      nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
-
-    nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(nativeObj);
-    if (blob) {
-      *aResult = blob->GetFileId();
-      return NS_OK;
-    }
+    _retval = filePath;
+    return NS_OK;
   }
 
-  *aResult = -1;
+  _retval.Truncate();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId,
                                     JS::Handle<JS::Value> aOptions,
                                     int32_t* aRefCnt, int32_t* aDBRefCnt,
                                     int32_t* aSliceRefCnt, JSContext* aCx,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -182,17 +182,16 @@
 #include "prlog.h"
 #include "prenv.h"
 #include "prprf.h"
 
 #include "mozilla/dom/MessageChannel.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/indexedDB/IDBFactory.h"
-#include "mozilla/dom/quota/QuotaManager.h"
 
 #include "mozilla/dom/StructuredCloneTags.h"
 
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadService.h"
 #endif
 
 #include "nsRefreshDriver.h"
@@ -1569,22 +1568,16 @@ nsGlobalWindow::FreeInnerObjects()
   // re-create.
   NotifyDOMWindowDestroyed(this);
 
   mInnerObjectsFreed = true;
 
   // Kill all of the workers for this window.
   mozilla::dom::workers::CancelWorkersForWindow(this);
 
-  // Close all offline storages for this window.
-  quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
-  if (quotaManager) {
-    quotaManager->AbortCloseStoragesForWindow(this);
-  }
-
   ClearAllTimeouts();
 
   if (mIdleTimer) {
     mIdleTimer->Cancel();
     mIdleTimer = nullptr;
   }
 
   mIdleObservers.Clear();
@@ -10588,19 +10581,21 @@ GetIndexedDBEnabledForAboutURI(nsIURI *a
 
   uint32_t flags;
   rv = module->GetURIFlags(aURI, &flags);
   NS_ENSURE_SUCCESS(rv, false);
 
   return flags & nsIAboutModule::ENABLE_INDEXED_DB;
 }
 
-indexedDB::IDBFactory*
+mozilla::dom::indexedDB::IDBFactory*
 nsGlobalWindow::GetIndexedDB(ErrorResult& aError)
 {
+  using mozilla::dom::indexedDB::IDBFactory;
+
   if (!mIndexedDB) {
     // If the document has the sandboxed origin flag set
     // don't allow access to indexedDB.
     if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
       aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
       return nullptr;
     }
 
@@ -10637,18 +10632,17 @@ nsGlobalWindow::GetIndexedDB(ErrorResult
           NS_WARN_IF_FALSE(aError.Failed(),
                            "IndexedDB is not permitted in a third-party window.");
           return nullptr;
         }
       }
     }
 
     // This may be null if being created from a file.
-    aError = indexedDB::IDBFactory::Create(this, nullptr,
-                                           getter_AddRefs(mIndexedDB));
+    aError = IDBFactory::CreateForWindow(this, getter_AddRefs(mIndexedDB));
   }
 
   return mIndexedDB;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetIndexedDB(nsISupports** _retval)
 {
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -21,16 +21,18 @@
 
 #include "jsapi.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsXPCOM.h"
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -4,25 +4,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluetootheventservice_h__
 #define mozilla_dom_bluetooth_bluetootheventservice_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
-#include "mozilla/dom/ipc/Blob.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsIDOMFile.h"
 #include "nsIObserver.h"
 #include "nsTObserverArray.h"
 #include "nsThreadUtils.h"
 
+class nsIDOMBlob;
+
 namespace mozilla {
+namespace dom {
+class BlobChild;
+class BlobParent;
+}
 namespace ipc {
 class UnixSocketConsumer;
 }
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothManager;
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
@@ -9,16 +9,17 @@
 
 #include "BluetoothService.h"
 #include "BluetoothSocket.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "ObexBase.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIDOMFile.h"
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.h
@@ -6,24 +6,30 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
 #define mozilla_dom_bluetooth_bluetoothoppmanager_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
 #include "BluetoothSocketObserver.h"
 #include "DeviceStorage.h"
-#include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "nsCOMArray.h"
 
+class nsIDOMBlob;
 class nsIOutputStream;
 class nsIInputStream;
 class nsIVolumeMountLock;
 
+namespace mozilla {
+namespace dom {
+class BlobParent;
+}
+}
+
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocket;
 class ObexHeaderSet;
 class SendFileBatch;
 
 class BluetoothOppManager : public BluetoothSocketObserver
                           , public BluetoothProfileManagerBase
--- a/dom/bluetooth/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp
@@ -39,16 +39,17 @@
 #include "mozilla/Atomics.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Hal.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "mozilla/ipc/DBusUtils.h"
 #include "mozilla/ipc/RawDBusConnection.h"
 #include "mozilla/LazyIdleThread.h"
+#include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/unused.h"
 
 #if defined(MOZ_WIDGET_GONK)
 #include "cutils/properties.h"
 #include <dlfcn.h>
--- a/dom/bluetooth/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothOppManager.cpp
@@ -9,16 +9,17 @@
 
 #include "BluetoothService.h"
 #include "BluetoothSocket.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "ObexBase.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIDOMFile.h"
--- a/dom/bluetooth/bluez/BluetoothOppManager.h
+++ b/dom/bluetooth/bluez/BluetoothOppManager.h
@@ -6,24 +6,30 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
 #define mozilla_dom_bluetooth_bluetoothoppmanager_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
 #include "BluetoothSocketObserver.h"
 #include "DeviceStorage.h"
-#include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "nsCOMArray.h"
 
+class nsIDOMBlob;
 class nsIOutputStream;
 class nsIInputStream;
 class nsIVolumeMountLock;
 
+namespace mozilla {
+namespace dom {
+class BlobParent;
+}
+}
+
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocket;
 class ObexHeaderSet;
 class SendFileBatch;
 
 class BluetoothOppManager : public BluetoothSocketObserver
                           , public BluetoothProfileManagerBase
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
@@ -5,16 +5,17 @@
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "BluetoothServiceChildProcess.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ipc/BlobChild.h"
 
 #include "BluetoothChild.h"
 #include "MainThreadUtils.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 namespace {
 
--- a/dom/bluetooth2/BluetoothService.cpp
+++ b/dom/bluetooth2/BluetoothService.cpp
@@ -21,16 +21,18 @@
 
 #include "jsapi.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsXPCOM.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
--- a/dom/bluetooth2/BluetoothService.h
+++ b/dom/bluetooth2/BluetoothService.h
@@ -4,25 +4,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluetootheventservice_h__
 #define mozilla_dom_bluetooth_bluetootheventservice_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
-#include "mozilla/dom/ipc/Blob.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsIDOMFile.h"
 #include "nsIObserver.h"
 #include "nsTObserverArray.h"
 #include "nsThreadUtils.h"
 
+class nsIDOMBlob;
+
 namespace mozilla {
+namespace dom {
+class BlobChild;
+class BlobParent;
+}
 namespace ipc {
 class UnixSocketConsumer;
 }
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothManager;
--- a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
@@ -9,16 +9,17 @@
 
 #include "BluetoothService.h"
 #include "BluetoothSocket.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "ObexBase.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIDOMFile.h"
--- a/dom/bluetooth2/bluedroid/BluetoothOppManager.h
+++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.h
@@ -6,24 +6,30 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
 #define mozilla_dom_bluetooth_bluetoothoppmanager_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
 #include "BluetoothSocketObserver.h"
 #include "DeviceStorage.h"
-#include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "nsCOMArray.h"
 
+class nsIDOMBlob;
 class nsIOutputStream;
 class nsIInputStream;
 class nsIVolumeMountLock;
 
+namespace mozilla {
+namespace dom {
+class BlobParent;
+}
+}
+
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocket;
 class ObexHeaderSet;
 class SendFileBatch;
 
 class BluetoothOppManager : public BluetoothSocketObserver
                           , public BluetoothProfileManagerBase
--- a/dom/bluetooth2/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothOppManager.cpp
@@ -9,16 +9,17 @@
 
 #include "BluetoothService.h"
 #include "BluetoothSocket.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "ObexBase.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIDOMFile.h"
--- a/dom/bluetooth2/bluez/BluetoothOppManager.h
+++ b/dom/bluetooth2/bluez/BluetoothOppManager.h
@@ -6,24 +6,30 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
 #define mozilla_dom_bluetooth_bluetoothoppmanager_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
 #include "BluetoothSocketObserver.h"
 #include "DeviceStorage.h"
-#include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "nsCOMArray.h"
 
+class nsIDOMBlob;
 class nsIOutputStream;
 class nsIInputStream;
 class nsIVolumeMountLock;
 
+namespace mozilla {
+namespace dom {
+class BlobParent;
+}
+}
+
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocket;
 class ObexHeaderSet;
 class SendFileBatch;
 
 class BluetoothOppManager : public BluetoothSocketObserver
                           , public BluetoothProfileManagerBase
--- a/dom/datastore/DataStoreDB.cpp
+++ b/dom/datastore/DataStoreDB.cpp
@@ -2,25 +2,32 @@
 /* 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 "DataStoreDB.h"
 
 #include "DataStoreCallbacks.h"
+#include "jsapi.h"
 #include "mozilla/dom/IDBDatabaseBinding.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
+#include "mozilla/dom/IDBObjectStoreBinding.h"
 #include "mozilla/dom/indexedDB/IDBDatabase.h"
 #include "mozilla/dom/indexedDB/IDBEvents.h"
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/indexedDB/IDBIndex.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
 #include "mozilla/dom/indexedDB/IDBRequest.h"
+#include "mozilla/dom/indexedDB/IDBTransaction.h"
+#include "nsComponentManagerUtils.h"
+#include "nsContentUtils.h"
 #include "nsIDOMEvent.h"
+#include "nsIPrincipal.h"
+#include "nsIXPConnect.h"
 
 #define DATASTOREDB_VERSION        1
 #define DATASTOREDB_NAME           "DataStoreDB"
 #define DATASTOREDB_REVISION_INDEX "revisionIndex"
 
 using namespace mozilla::dom::indexedDB;
 
 namespace mozilla {
@@ -58,17 +65,19 @@ public:
 #ifdef DEBUG
     nsCOMPtr<IDBVersionChangeEvent> event = do_QueryInterface(aEvent);
     MOZ_ASSERT(event);
 
     Nullable<uint64_t> version = event->GetNewVersion();
     MOZ_ASSERT(version.IsNull());
 #endif
 
-    return mDatabase->Close();
+    mDatabase->Close();
+
+    return NS_OK;
   }
 
 private:
   IDBDatabase* mDatabase;
 
   ~VersionChangeListener() {}
 };
 
@@ -88,17 +97,46 @@ DataStoreDB::DataStoreDB(const nsAString
 DataStoreDB::~DataStoreDB()
 {
 }
 
 nsresult
 DataStoreDB::CreateFactoryIfNeeded()
 {
   if (!mFactory) {
-    nsresult rv = IDBFactory::Create(nullptr, getter_AddRefs(mFactory));
+    nsresult rv;
+    nsCOMPtr<nsIPrincipal> principal =
+      do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsIXPConnect* xpc = nsContentUtils::XPConnect();
+    MOZ_ASSERT(xpc);
+
+    AutoSafeJSContext cx;
+
+    nsCOMPtr<nsIXPConnectJSObjectHolder> globalHolder;
+    rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    JS::Rooted<JSObject*> global(cx, globalHolder->GetJSObject());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    // The CreateSandbox call returns a proxy to the actual sandbox object. We
+    // don't need a proxy here.
+    global = js::UncheckedUnwrap(global);
+
+    JSAutoCompartment ac(cx, global);
+
+    rv = IDBFactory::CreateForDatastore(cx, global, getter_AddRefs(mFactory));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
@@ -296,21 +334,17 @@ DataStoreDB::Delete()
   nsresult rv = CreateFactoryIfNeeded();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mTransaction = nullptr;
 
   if (mDatabase) {
-    rv = mDatabase->Close();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
+    mDatabase->Close();
     mDatabase = nullptr;
   }
 
   ErrorResult error;
   nsRefPtr<IDBOpenDBRequest> request =
     mFactory->DeleteDatabase(mDatabaseName, IDBOpenDBOptions(), error);
   if (NS_WARN_IF(error.Failed())) {
     return error.ErrorCode();
--- a/dom/datastore/DataStoreRevision.cpp
+++ b/dom/datastore/DataStoreRevision.cpp
@@ -4,18 +4,19 @@
  * 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 "DataStoreRevision.h"
 
 #include "DataStoreCallbacks.h"
 #include "DataStoreService.h"
 #include "mozilla/dom/DataStoreBinding.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
-#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/indexedDB/IDBRequest.h"
 #include "nsIDOMEvent.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace indexedDB;
 
 NS_IMPL_ISUPPORTS(DataStoreRevision, nsIDOMEventListener)
--- a/dom/datastore/DataStoreService.cpp
+++ b/dom/datastore/DataStoreService.cpp
@@ -17,16 +17,18 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/indexedDB/IDBCursor.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "mozilla/dom/indexedDB/IDBRequest.h"
+#include "mozilla/dom/indexedDB/IDBTransaction.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/unused.h"
 
 #include "mozIApplication.h"
 #include "mozIApplicationClearPrivateDataParams.h"
 #include "nsIAppsService.h"
 #include "nsIDOMEvent.h"
--- a/dom/datastore/tests/test_oop_events.html
+++ b/dom/datastore/tests/test_oop_events.html
@@ -1,9 +1,9 @@
-<!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test for DataStore - basic operation on a readonly db</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
@@ -132,17 +132,17 @@
     function() { installApp(gHostedManifestURL1); },
     function() { installApp(gHostedManifestURL2); },
 
     // Run tests in apps
     testApp,
 
     // Uninstall the apps
     function() { uninstallApp(gApps[0]); },
-    function() { uninstallApp(gApps[1]); },
+    function() { uninstallApp(gApps[1]); }
   ];
 
   function runTest() {
     if (!tests.length) {
       finish();
       return;
     }
 
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "DeviceStorageRequestChild.h"
 #include "DeviceStorageFileDescriptor.h"
 #include "nsDeviceStorage.h"
 #include "nsDOMFile.h"
-#include "mozilla/dom/ipc/Blob.h"
+#include "mozilla/dom/ipc/BlobChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace devicestorage {
 
 DeviceStorageRequestChild::DeviceStorageRequestChild()
   : mCallback(nullptr)
 {
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -3,17 +3,17 @@
  * 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 "DeviceStorageRequestParent.h"
 #include "nsDOMFile.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "mozilla/unused.h"
-#include "mozilla/dom/ipc/Blob.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "ContentParent.h"
 #include "nsProxyRelease.h"
 #include "AppProcessChecker.h"
 #include "mozilla/Preferences.h"
 #include "nsNetCID.h"
 
 namespace mozilla {
 namespace dom {
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -11,17 +11,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/DeviceStorageBinding.h"
 #include "mozilla/dom/DeviceStorageChangeEvent.h"
 #include "mozilla/dom/DeviceStorageFileSystem.h"
 #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/FileSystemUtils.h"
-#include "mozilla/dom/ipc/Blob.h"
+#include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Preferences.h"
--- a/dom/filehandle/FileStreamWrappers.cpp
+++ b/dom/filehandle/FileStreamWrappers.cpp
@@ -3,24 +3,30 @@
 /* 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"
 
+#ifdef DEBUG
+#include "nsXULAppAPI.h"
+#endif
+
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 class ProgressRunnable MOZ_FINAL : public nsIRunnable
 {
 public:
@@ -122,17 +128,18 @@ FileInputStreamWrapper::FileInputStreamW
 : FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
 {
   mInputStream = do_QueryInterface(mFileStream);
   NS_ASSERTION(mInputStream, "This should always succeed!");
 }
 
 NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper,
                             FileStreamWrapper,
-                            nsIInputStream)
+                            nsIInputStream,
+                            nsIIPCSerializableInputStream)
 
 NS_IMETHODIMP
 FileInputStreamWrapper::Close()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   if (mFlags & NOTIFY_CLOSE) {
     nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
@@ -225,16 +232,36 @@ FileInputStreamWrapper::ReadSegments(nsW
 
 NS_IMETHODIMP
 FileInputStreamWrapper::IsNonBlocking(bool* _retval)
 {
   *_retval = false;
   return NS_OK;
 }
 
+void
+FileInputStreamWrapper::Serialize(InputStreamParams& aParams,
+                                  FileDescriptorArray& /* aFDs */)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  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)
 #ifdef DEBUG
 , mWriteThread(nullptr)
--- a/dom/filehandle/FileStreamWrappers.h
+++ b/dom/filehandle/FileStreamWrappers.h
@@ -6,18 +6,23 @@
 
 #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
@@ -41,21 +46,25 @@ protected:
   nsRefPtr<FileHelper> mFileHelper;
   uint64_t mOffset;
   uint64_t mLimit;
   uint32_t mFlags;
   bool mFirstTime;
 };
 
 class FileInputStreamWrapper : public FileStreamWrapper,
-                               public nsIInputStream
+                               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:
--- a/dom/filehandle/moz.build
+++ b/dom/filehandle/moz.build
@@ -23,13 +23,15 @@ UNIFIED_SOURCES += [
     'FileStreamWrappers.cpp',
     'MemoryStreams.cpp',
     'MetadataHelper.cpp',
     'MutableFile.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 LOCAL_INCLUDES += [
     '../base',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/dom/filesystem/CreateFileTask.cpp
+++ b/dom/filesystem/CreateFileTask.cpp
@@ -8,16 +8,18 @@
 
 #include <algorithm>
 
 #include "DOMError.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "nsDOMFile.h"
 #include "nsIFile.h"
 #include "nsNetUtil.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -8,16 +8,17 @@
 
 #include "nsNetUtil.h" // Stream transport service.
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PContent.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/unused.h"
 #include "nsDOMFile.h"
 
 namespace mozilla {
 namespace dom {
 
 FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem)
   : mErrorValue(NS_OK)
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -5,23 +5,23 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FileSystemTaskBase_h
 #define mozilla_dom_FileSystemTaskBase_h
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
-#include "mozilla/dom/ipc/Blob.h"
 
 class nsIDOMFile;
 
 namespace mozilla {
 namespace dom {
 
+class BlobParent;
 class FileSystemBase;
 class FileSystemParams;
 class Promise;
 
 /*
  * The base class to implement a Task class.
  * The task is used to handle the OOP (out of process) operations.
  * The file system operations can only be performed in the parent process. When
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -6,16 +6,18 @@
 
 #include "GetFileOrDirectoryTask.h"
 
 #include "js/Value.h"
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "nsDOMFile.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
 GetFileOrDirectoryTask::GetFileOrDirectoryTask(
--- a/dom/filesystem/RemoveTask.cpp
+++ b/dom/filesystem/RemoveTask.cpp
@@ -5,16 +5,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "RemoveTask.h"
 
 #include "DOMError.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
 #include "nsDOMFile.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
 RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -0,0 +1,2352 @@
+/* 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 "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"
+#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/BlobChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsIBFCacheEntry.h"
+#include "nsIDocument.h"
+#include "nsIDOMEvent.h"
+#include "nsIEventTarget.h"
+#include "nsPIDOMWindow.h"
+#include "nsThreadUtils.h"
+#include "nsTraceRefcnt.h"
+#include "PermissionRequestBase.h"
+#include "ProfilerHelpers.h"
+#include "ReportInternalError.h"
+
+#ifdef DEBUG
+#include "IndexedDatabaseManager.h"
+#endif
+
+#define GC_ON_IPC_MESSAGES 0
+
+#if defined(DEBUG) || GC_ON_IPC_MESSAGES
+
+#include "js/GCAPI.h"
+#include "nsJSEnvironment.h"
+
+#define BUILD_GC_ON_IPC_MESSAGES
+
+#endif // DEBUG || GC_ON_IPC_MESSAGES
+
+namespace mozilla {
+namespace dom {
+namespace indexedDB {
+
+/*******************************************************************************
+ * Helpers
+ ******************************************************************************/
+
+namespace {
+
+void
+MaybeCollectGarbageOnIPCMessage()
+{
+#ifdef BUILD_GC_ON_IPC_MESSAGES
+  static const bool kCollectGarbageOnIPCMessages =
+#if GC_ON_IPC_MESSAGES
+    true;
+#else
+    false;
+#endif // GC_ON_IPC_MESSAGES
+
+  if (!kCollectGarbageOnIPCMessages) {
+    return;
+  }
+
+  static bool haveWarnedAboutGC = false;
+  static bool haveWarnedAboutNonMainThread = false;
+
+  if (!haveWarnedAboutGC) {
+    haveWarnedAboutGC = true;
+    NS_WARNING("IndexedDB child actor GC debugging enabled!");
+  }
+
+  if (!NS_IsMainThread()) {
+    if (!haveWarnedAboutNonMainThread)  {
+      haveWarnedAboutNonMainThread = true;
+      NS_WARNING("Don't know how to GC on a non-main thread yet.");
+    }
+    return;
+  }
+
+  nsJSContext::GarbageCollectNow(JS::gcreason::DOM_IPC);
+  nsJSContext::CycleCollectNow();
+#endif // BUILD_GC_ON_IPC_MESSAGES
+}
+
+class MOZ_STACK_CLASS AutoSetCurrentTransaction MOZ_FINAL
+{
+  typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl;
+
+  IDBTransaction* const mTransaction;
+  IDBTransaction* mPreviousTransaction;
+  IDBTransaction** mThreadLocalSlot;
+
+public:
+  explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction)
+    : mTransaction(aTransaction)
+    , mPreviousTransaction(nullptr)
+    , mThreadLocalSlot(nullptr)
+  {
+    if (aTransaction) {
+      BackgroundChildImpl::ThreadLocal* threadLocal =
+        BackgroundChildImpl::GetThreadLocalForCurrentThread();
+      MOZ_ASSERT(threadLocal);
+
+      // Hang onto this location for resetting later.
+      mThreadLocalSlot = &threadLocal->mCurrentTransaction;
+
+      // Save the current value.
+      mPreviousTransaction = *mThreadLocalSlot;
+
+      // Set the new value.
+      *mThreadLocalSlot = aTransaction;
+    }
+  }
+
+  ~AutoSetCurrentTransaction()
+  {
+    MOZ_ASSERT_IF(mThreadLocalSlot, mTransaction);
+
+    if (mThreadLocalSlot) {
+      MOZ_ASSERT(*mThreadLocalSlot == mTransaction);
+
+      // Reset old value.
+      *mThreadLocalSlot = mPreviousTransaction;
+    }
+  }
+
+  IDBTransaction*
+  Transaction() const
+  {
+    return mTransaction;
+  }
+};
+
+class MOZ_STACK_CLASS ResultHelper MOZ_FINAL
+  : public IDBRequest::ResultCallback
+{
+  IDBRequest* mRequest;
+  AutoSetCurrentTransaction mAutoTransaction;
+
+  union
+  {
+    nsISupports* mISupports;
+    StructuredCloneReadInfo* mStructuredClone;
+    const nsTArray<StructuredCloneReadInfo>* mStructuredCloneArray;
+    const Key* mKey;
+    const nsTArray<Key>* mKeyArray;
+    const JS::Value* mJSVal;
+    const JS::Handle<JS::Value>* mJSValHandle;
+  } mResult;
+
+  enum
+  {
+    ResultTypeISupports,
+    ResultTypeStructuredClone,
+    ResultTypeStructuredCloneArray,
+    ResultTypeKey,
+    ResultTypeKeyArray,
+    ResultTypeJSVal,
+    ResultTypeJSValHandle,
+  } mResultType;
+
+public:
+  ResultHelper(IDBRequest* aRequest,
+               IDBTransaction* aTransaction,
+               nsISupports* aResult)
+    : mRequest(aRequest)
+    , mAutoTransaction(aTransaction)
+    , mResultType(ResultTypeISupports)
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!");
+    MOZ_ASSERT(aRequest);
+    MOZ_ASSERT(aResult);
+
+    mResult.mISupports = aResult;
+  }
+
+  ResultHelper(IDBRequest* aRequest,
+               IDBTransaction* aTransaction,
+               StructuredCloneReadInfo* aResult)
+    : mRequest(aRequest)
+    , mAutoTransaction(aTransaction)
+    , mResultType(ResultTypeStructuredClone)
+  {
+    MOZ_ASSERT(aRequest);
+    MOZ_ASSERT(aResult);
+
+    mResult.mStructuredClone = aResult;
+  }
+
+  ResultHelper(IDBRequest* aRequest,
+               IDBTransaction* aTransaction,
+               const nsTArray<StructuredCloneReadInfo>* aResult)
+    : mRequest(aRequest)
+    , mAutoTransaction(aTransaction)
+    , mResultType(ResultTypeStructuredCloneArray)
+  {
+    MOZ_ASSERT(aRequest);
+    MOZ_ASSERT(aResult);
+
+    mResult.mStructuredCloneArray = aResult;
+  }
+
+  ResultHelper(IDBRequest* aRequest,
+               IDBTransaction* aTransaction,
+               const Key* aResult)
+    : mRequest(aRequest)
+    , mAutoTransaction(aTransaction)
+    , mResultType(ResultTypeKey)
+  {
+    MOZ_ASSERT(aRequest);
+    MOZ_ASSERT(aResult);
+
+    mResult.mKey = aResult;
+  }
+
+  ResultHelper(IDBRequest* aRequest,
+               IDBTransaction* aTransaction,
+               const nsTArray<Key>* aResult)
+    : mRequest(aRequest)
+    , mAutoTransaction(aTransaction)
+    , mResultType(ResultTypeKeyArray)
+  {
+    MOZ_ASSERT(aRequest);
+    MOZ_ASSERT(aResult);
+
+    mResult.mKeyArray = aResult;
+  }
+
+  ResultHelper(IDBRequest* aRequest,
+               IDBTransaction* aTransaction,
+               const JS::Value* aResult)
+    : mRequest(aRequest)
+    , mAutoTransaction(aTransaction)
+    , mResultType(ResultTypeJSVal)
+  {
+    MOZ_ASSERT(aRequest);
+    MOZ_ASSERT(!aResult->isGCThing());
+
+    mResult.mJSVal = aResult;
+  }
+
+  ResultHelper(IDBRequest* aRequest,
+               IDBTransaction* aTransaction,
+               const JS::Handle<JS::Value>* aResult)
+    : mRequest(aRequest)
+    , mAutoTransaction(aTransaction)
+    , mResultType(ResultTypeJSValHandle)
+  {
+    MOZ_ASSERT(aRequest);
+
+    mResult.mJSValHandle = aResult;
+  }
+
+  IDBRequest*
+  Request() const
+  {
+    return mRequest;
+  }
+
+  IDBTransaction*
+  Transaction() const
+  {
+    return mAutoTransaction.Transaction();
+  }
+
+  virtual nsresult
+  GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(aCx);
+    MOZ_ASSERT(mRequest);
+
+    switch (mResultType) {
+      case ResultTypeISupports:
+        return GetResult(aCx, mResult.mISupports, aResult);
+
+      case ResultTypeStructuredClone:
+        return GetResult(aCx, mResult.mStructuredClone, aResult);
+
+      case ResultTypeStructuredCloneArray:
+        return GetResult(aCx, mResult.mStructuredCloneArray, aResult);
+
+      case ResultTypeKey:
+        return GetResult(aCx, mResult.mKey, aResult);
+
+      case ResultTypeKeyArray:
+        return GetResult(aCx, mResult.mKeyArray, aResult);
+
+      case ResultTypeJSVal:
+        aResult.set(*mResult.mJSVal);
+        return NS_OK;
+
+      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,
+            nsISupports* aSupports,
+            JS::MutableHandle<JS::Value> aResult)
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!");
+
+    if (!aSupports) {
+      aResult.setNull();
+      return NS_OK;
+    }
+
+    nsresult rv = nsContentUtils::WrapNative(aCx, aSupports, aResult);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      IDB_REPORT_INTERNAL_ERR();
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    return NS_OK;
+  }
+
+  nsresult
+  GetResult(JSContext* aCx,
+            StructuredCloneReadInfo* aCloneInfo,
+            JS::MutableHandle<JS::Value> aResult)
+  {
+    bool ok = IDBObjectStore::DeserializeValue(aCx, *aCloneInfo, aResult);
+
+    aCloneInfo->mCloneBuffer.clear();
+
+    if (NS_WARN_IF(!ok)) {
+      return NS_ERROR_DOM_DATA_CLONE_ERR;
+    }
+
+    return NS_OK;
+  }
+
+  nsresult
+  GetResult(JSContext* aCx,
+            const nsTArray<StructuredCloneReadInfo>* aCloneInfos,
+            JS::MutableHandle<JS::Value> aResult)
+  {
+    JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
+    if (NS_WARN_IF(!array)) {
+      IDB_REPORT_INTERNAL_ERR();
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    if (!aCloneInfos->IsEmpty()) {
+      const uint32_t count = aCloneInfos->Length();
+
+      if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) {
+        IDB_REPORT_INTERNAL_ERR();
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
+
+      for (uint32_t index = 0; index < count; index++) {
+        auto& cloneInfo =
+          const_cast<StructuredCloneReadInfo&>(aCloneInfos->ElementAt(index));
+
+        JS::Rooted<JS::Value> value(aCx);
+
+        nsresult rv = GetResult(aCx, &cloneInfo, &value);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        if (NS_WARN_IF(!JS_SetElement(aCx, array, index, value))) {
+          IDB_REPORT_INTERNAL_ERR();
+          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+        }
+      }
+    }
+
+    aResult.setObject(*array);
+    return NS_OK;
+  }
+
+  nsresult
+  GetResult(JSContext* aCx,
+            const Key* aKey,
+            JS::MutableHandle<JS::Value> aResult)
+  {
+    nsresult rv = aKey->ToJSVal(aCx, aResult);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
+  }
+
+  nsresult
+  GetResult(JSContext* aCx,
+            const nsTArray<Key>* aKeys,
+            JS::MutableHandle<JS::Value> aResult)
+  {
+    JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
+    if (NS_WARN_IF(!array)) {
+      IDB_REPORT_INTERNAL_ERR();
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    if (!aKeys->IsEmpty()) {
+      const uint32_t count = aKeys->Length();
+
+      if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) {
+        IDB_REPORT_INTERNAL_ERR();
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
+
+      for (uint32_t index = 0; index < count; index++) {
+        const Key& key = aKeys->ElementAt(index);
+        MOZ_ASSERT(!key.IsUnset());
+
+        JS::Rooted<JS::Value> value(aCx);
+
+        nsresult rv = GetResult(aCx, &key, &value);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        if (NS_WARN_IF(!JS_SetElement(aCx, array, index, value))) {
+          IDB_REPORT_INTERNAL_ERR();
+          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+        }
+      }
+    }
+
+    aResult.setObject(*array);
+    return NS_OK;
+  }
+};
+
+class PermissionRequestMainProcessHelper MOZ_FINAL
+  : public PermissionRequestBase
+{
+  BackgroundFactoryRequestChild* mActor;
+  nsRefPtr<IDBFactory> mFactory;
+
+public:
+  PermissionRequestMainProcessHelper(BackgroundFactoryRequestChild* aActor,
+                                     IDBFactory* aFactory,
+                                     nsPIDOMWindow* aWindow,
+                                     nsIPrincipal* aPrincipal)
+    : PermissionRequestBase(aWindow, aPrincipal)
+    , mActor(aActor)
+    , mFactory(aFactory)
+  {
+    MOZ_ASSERT(aActor);
+    MOZ_ASSERT(aFactory);
+    aActor->AssertIsOnOwningThread();
+  }
+
+protected:
+  ~PermissionRequestMainProcessHelper()
+  { }
+
+private:
+  virtual void
+  OnPromptComplete(PermissionValue aPermissionValue) MOZ_OVERRIDE;
+};
+
+class PermissionRequestChildProcessActor MOZ_FINAL
+  : public PIndexedDBPermissionRequestChild
+{
+  BackgroundFactoryRequestChild* mActor;
+  nsRefPtr<IDBFactory> mFactory;
+
+public:
+  PermissionRequestChildProcessActor(BackgroundFactoryRequestChild* aActor,
+                                     IDBFactory* aFactory)
+    : mActor(aActor)
+    , mFactory(aFactory)
+  {
+    MOZ_ASSERT(aActor);
+    MOZ_ASSERT(aFactory);
+    aActor->AssertIsOnOwningThread();
+  }
+
+protected:
+  ~PermissionRequestChildProcessActor()
+  { }
+
+  virtual bool
+  Recv__delete__(const uint32_t& aPermission) MOZ_OVERRIDE;
+};
+
+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());
+
+  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]);
+
+      nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob();
+      MOZ_ASSERT(blob);
+
+      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);
+      }
+
+      aDatabase->NoteReceivedBlob(blob);
+
+      StructuredCloneFile* file = aFiles.AppendElement();
+      MOZ_ASSERT(file);
+
+      file->mFile.swap(blob);
+      file->mFileInfo.swap(fileInfo);
+    }
+  }
+}
+
+void
+DispatchSuccessEvent(ResultHelper* aResultHelper,
+                     nsIDOMEvent* aEvent = nullptr)
+{
+  MOZ_ASSERT(aResultHelper);
+
+  PROFILER_LABEL("IndexedDB",
+                 "DispatchSuccessEvent",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsRefPtr<IDBRequest> request = aResultHelper->Request();
+  MOZ_ASSERT(request);
+  request->AssertIsOnOwningThread();
+
+  nsRefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
+
+  nsCOMPtr<nsIDOMEvent> successEvent;
+  if (!aEvent) {
+    successEvent = CreateGenericEvent(request,
+                                      nsDependentString(kSuccessEventType),
+                                      eDoesNotBubble,
+                                      eNotCancelable);
+    if (NS_WARN_IF(!successEvent)) {
+      return;
+    }
+
+    aEvent = successEvent;
+  }
+
+  request->SetResultCallback(aResultHelper);
+
+  MOZ_ASSERT(aEvent);
+  MOZ_ASSERT_IF(transaction, transaction->IsOpen());
+
+  bool dummy;
+  nsresult rv = request->DispatchEvent(aEvent, &dummy);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  MOZ_ASSERT_IF(transaction,
+                transaction->IsOpen() || transaction->IsAborted());
+
+  WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
+  MOZ_ASSERT(internalEvent);
+
+  if (transaction &&
+      transaction->IsOpen() &&
+      internalEvent->mFlags.mExceptionHasBeenRisen) {
+    transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+  }
+}
+
+void
+DispatchErrorEvent(IDBRequest* aRequest,
+                   nsresult aErrorCode,
+                   IDBTransaction* aTransaction = nullptr,
+                   nsIDOMEvent* aEvent = nullptr)
+{
+  MOZ_ASSERT(aRequest);
+  aRequest->AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_FAILED(aErrorCode));
+  MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+
+  PROFILER_LABEL("IndexedDB",
+                 "DispatchErrorEvent",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsRefPtr<IDBRequest> request = aRequest;
+  nsRefPtr<IDBTransaction> transaction = aTransaction;
+
+  request->SetError(aErrorCode);
+
+  nsCOMPtr<nsIDOMEvent> errorEvent;
+  if (!aEvent) {
+    // Make an error event and fire it at the target.
+    errorEvent = CreateGenericEvent(request,
+                                    nsDependentString(kErrorEventType),
+                                    eDoesBubble,
+                                    eCancelable);
+    if (NS_WARN_IF(!errorEvent)) {
+      return;
+    }
+
+    aEvent = errorEvent;
+  }
+
+  Maybe<AutoSetCurrentTransaction> asct;
+  if (aTransaction) {
+    asct.emplace(aTransaction);
+  }
+
+  bool doDefault;
+  nsresult rv = request->DispatchEvent(aEvent, &doDefault);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  MOZ_ASSERT(!transaction || transaction->IsOpen() || transaction->IsAborted());
+
+  if (transaction && transaction->IsOpen()) {
+    WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
+    MOZ_ASSERT(internalEvent);
+
+    if (internalEvent->mFlags.mExceptionHasBeenRisen) {
+      transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+    } else if (doDefault) {
+      transaction->Abort(request);
+    }
+  }
+}
+
+} // anonymous namespace
+
+/*******************************************************************************
+ * Local class implementations
+ ******************************************************************************/
+
+void
+PermissionRequestMainProcessHelper::OnPromptComplete(
+                                               PermissionValue aPermissionValue)
+{
+  MOZ_ASSERT(mActor);
+  mActor->AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  mActor->SendPermissionRetry();
+
+  mActor = nullptr;
+  mFactory = nullptr;
+}
+
+bool
+PermissionRequestChildProcessActor::Recv__delete__(
+                                              const uint32_t& /* aPermission */)
+{
+  MOZ_ASSERT(mActor);
+  mActor->AssertIsOnOwningThread();
+  MOZ_ASSERT(mFactory);
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  nsRefPtr<IDBFactory> factory;
+  mFactory.swap(factory);
+
+  mActor->SendPermissionRetry();
+  mActor = nullptr;
+
+  return true;
+}
+
+/*******************************************************************************
+ * BackgroundRequestChildBase
+ ******************************************************************************/
+
+BackgroundRequestChildBase::BackgroundRequestChildBase(IDBRequest* aRequest)
+  : mRequest(aRequest)
+  , mActorDestroyed(false)
+{
+  MOZ_ASSERT(aRequest);
+  aRequest->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase);
+}
+
+BackgroundRequestChildBase::~BackgroundRequestChildBase()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChildBase);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundRequestChildBase::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mRequest);
+  mRequest->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundRequestChildBase::NoteActorDestroyed()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mActorDestroyed);
+
+  mActorDestroyed = true;
+}
+
+/*******************************************************************************
+ * BackgroundFactoryChild
+ ******************************************************************************/
+
+BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory* aFactory)
+  : mFactory(aFactory)
+#ifdef DEBUG
+  , mOwningThread(NS_GetCurrentThread())
+#endif
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFactory);
+  aFactory->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryChild);
+}
+
+BackgroundFactoryChild::~BackgroundFactoryChild()
+{
+  MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundFactoryChild::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+
+  bool current;
+  MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)));
+  MOZ_ASSERT(current);
+}
+
+#endif // DEBUG
+
+void
+BackgroundFactoryChild::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+
+  if (mFactory) {
+    mFactory->ClearBackgroundActor();
+    mFactory = nullptr;
+
+    MOZ_ALWAYS_TRUE(PBackgroundIDBFactoryChild::SendDeleteMe());
+  }
+}
+
+void
+BackgroundFactoryChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  if (mFactory) {
+    mFactory->ClearBackgroundActor();
+#ifdef DEBUG
+    mFactory = nullptr;
+#endif
+  }
+}
+
+PBackgroundIDBFactoryRequestChild*
+BackgroundFactoryChild::AllocPBackgroundIDBFactoryRequestChild(
+                                            const FactoryRequestParams& aParams)
+{
+  MOZ_CRASH("PBackgroundIDBFactoryRequestChild actors should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundFactoryChild::DeallocPBackgroundIDBFactoryRequestChild(
+                                      PBackgroundIDBFactoryRequestChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundFactoryRequestChild*>(aActor);
+  return true;
+}
+
+PBackgroundIDBDatabaseChild*
+BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild(
+                                    const DatabaseSpec& aSpec,
+                                    PBackgroundIDBFactoryRequestChild* aRequest)
+{
+  AssertIsOnOwningThread();
+
+  auto request = static_cast<BackgroundFactoryRequestChild*>(aRequest);
+  MOZ_ASSERT(request);
+
+  return new BackgroundDatabaseChild(aSpec, request);
+}
+
+bool
+BackgroundFactoryChild::DeallocPBackgroundIDBDatabaseChild(
+                                            PBackgroundIDBDatabaseChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundDatabaseChild*>(aActor);
+  return true;
+}
+
+/*******************************************************************************
+ * BackgroundFactoryRequestChild
+ ******************************************************************************/
+
+BackgroundFactoryRequestChild::BackgroundFactoryRequestChild(
+                                               IDBFactory* aFactory,
+                                               IDBOpenDBRequest* aOpenRequest,
+                                               bool aIsDeleteOp,
+                                               uint64_t aRequestedVersion)
+  : BackgroundRequestChildBase(aOpenRequest)
+  , mFactory(aFactory)
+  , mRequestedVersion(aRequestedVersion)
+  , mIsDeleteOp(aIsDeleteOp)
+{
+  // Can't assert owning thread here because IPDL has not yet set our manager!
+  MOZ_ASSERT(aFactory);
+  aFactory->AssertIsOnOwningThread();
+  MOZ_ASSERT(aOpenRequest);
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryRequestChild);
+}
+
+BackgroundFactoryRequestChild::~BackgroundFactoryRequestChild()
+{
+  MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild);
+}
+
+IDBOpenDBRequest*
+BackgroundFactoryRequestChild::GetOpenDBRequest() const
+{
+  AssertIsOnOwningThread();
+
+  IDBRequest* baseRequest = BackgroundRequestChildBase::GetDOMObject();
+  return static_cast<IDBOpenDBRequest*>(baseRequest);
+}
+
+bool
+BackgroundFactoryRequestChild::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
+BackgroundFactoryRequestChild::HandleResponse(
+                                   const OpenDatabaseRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  mRequest->Reset();
+
+  auto databaseActor =
+    static_cast<BackgroundDatabaseChild*>(aResponse.databaseChild());
+  MOZ_ASSERT(databaseActor);
+
+  databaseActor->EnsureDOMObject();
+
+  IDBDatabase* database = databaseActor->GetDOMObject();
+  MOZ_ASSERT(database);
+
+  ResultHelper helper(mRequest, nullptr,
+                      static_cast<IDBWrapperCache*>(database));
+
+  DispatchSuccessEvent(&helper);
+
+  databaseActor->ReleaseDOMObject();
+
+  return true;
+}
+
+bool
+BackgroundFactoryRequestChild::HandleResponse(
+                                 const DeleteDatabaseRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  ResultHelper helper(mRequest, nullptr, &JS::UndefinedHandleValue);
+
+  nsCOMPtr<nsIDOMEvent> successEvent =
+    IDBVersionChangeEvent::Create(mRequest,
+                                  nsDependentString(kSuccessEventType),
+                                  aResponse.previousVersion());
+  if (NS_WARN_IF(!successEvent)) {
+    return false;
+  }
+
+  DispatchSuccessEvent(&helper, successEvent);
+
+  return true;
+}
+
+void
+BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  NoteActorDestroyed();
+}
+
+bool
+BackgroundFactoryRequestChild::Recv__delete__(
+                                        const FactoryRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  switch (aResponse.type()) {
+    case FactoryRequestResponse::Tnsresult:
+      return HandleResponse(aResponse.get_nsresult());
+
+    case FactoryRequestResponse::TOpenDatabaseRequestResponse:
+      return HandleResponse(aResponse.get_OpenDatabaseRequestResponse());
+
+    case FactoryRequestResponse::TDeleteDatabaseRequestResponse:
+      return HandleResponse(aResponse.get_DeleteDatabaseRequestResponse());
+
+    default:
+      MOZ_CRASH("Unknown response type!");
+  }
+
+  MOZ_CRASH("Should never get here!");
+}
+
+bool
+BackgroundFactoryRequestChild::RecvPermissionChallenge(
+                                            const PrincipalInfo& aPrincipalInfo)
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  if (!NS_IsMainThread()) {
+    MOZ_CRASH("Implement me for workers!");
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIPrincipal> principal =
+    mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    nsCOMPtr<nsPIDOMWindow> window = mFactory->GetParentObject();
+    MOZ_ASSERT(window);
+
+    nsRefPtr<PermissionRequestMainProcessHelper> helper =
+      new PermissionRequestMainProcessHelper(this, mFactory, window, principal);
+
+    PermissionRequestBase::PermissionValue permission;
+    if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) {
+      return false;
+    }
+
+    MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
+               permission == PermissionRequestBase::kPermissionDenied ||
+               permission == PermissionRequestBase::kPermissionPrompt);
+
+    if (permission != PermissionRequestBase::kPermissionPrompt) {
+      SendPermissionRetry();
+    }
+    return true;
+  }
+
+  nsRefPtr<TabChild> tabChild = mFactory->GetTabChild();
+  MOZ_ASSERT(tabChild);
+
+  IPC::Principal ipcPrincipal(principal);
+
+  auto* actor = new PermissionRequestChildProcessActor(this, mFactory);
+
+  tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal);
+
+  return true;
+}
+
+bool
+BackgroundFactoryRequestChild::RecvBlocked(const uint64_t& aCurrentVersion)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  const nsDependentString type(kBlockedEventType);
+
+  nsCOMPtr<nsIDOMEvent> blockedEvent;
+  if (mIsDeleteOp) {
+    blockedEvent =
+      IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion);
+  } else {
+    blockedEvent =
+      IDBVersionChangeEvent::Create(mRequest,
+                                    type,
+                                    aCurrentVersion,
+                                    mRequestedVersion);
+  }
+
+  if (NS_WARN_IF(!blockedEvent)) {
+    return false;
+  }
+
+  nsRefPtr<IDBRequest> kungFuDeathGrip = mRequest;
+
+  bool dummy;
+  if (NS_FAILED(mRequest->DispatchEvent(blockedEvent, &dummy))) {
+    NS_WARNING("Failed to dispatch event!");
+  }
+
+  return true;
+}
+
+/*******************************************************************************
+ * BackgroundDatabaseChild
+ ******************************************************************************/
+
+BackgroundDatabaseChild::BackgroundDatabaseChild(
+                               const DatabaseSpec& aSpec,
+                               BackgroundFactoryRequestChild* aOpenRequestActor)
+  : mSpec(new DatabaseSpec(aSpec))
+  , mOpenRequestActor(aOpenRequestActor)
+  , mDatabase(nullptr)
+{
+  // Can't assert owning thread here because IPDL has not yet set our manager!
+  MOZ_ASSERT(aOpenRequestActor);
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseChild);
+}
+
+BackgroundDatabaseChild::~BackgroundDatabaseChild()
+{
+  MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseChild);
+}
+
+void
+BackgroundDatabaseChild::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mTemporaryStrongDatabase);
+  MOZ_ASSERT(!mOpenRequestActor);
+
+  if (mDatabase) {
+    mDatabase->ClearBackgroundActor();
+    mDatabase = nullptr;
+
+    MOZ_ALWAYS_TRUE(PBackgroundIDBDatabaseChild::SendDeleteMe());
+  }
+}
+
+void
+BackgroundDatabaseChild::EnsureDOMObject()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mOpenRequestActor);
+
+  if (mTemporaryStrongDatabase) {
+    MOZ_ASSERT(!mSpec);
+    return;
+  }
+
+  MOZ_ASSERT(mSpec);
+
+  auto request = mOpenRequestActor->GetDOMObject();
+  MOZ_ASSERT(request);
+
+  auto factory =
+    static_cast<BackgroundFactoryChild*>(Manager())->GetDOMObject();
+  MOZ_ASSERT(factory);
+
+  mTemporaryStrongDatabase =
+    IDBDatabase::Create(request, factory, this, mSpec);
+
+  MOZ_ASSERT(mTemporaryStrongDatabase);
+  mTemporaryStrongDatabase->AssertIsOnOwningThread();
+
+  mDatabase = mTemporaryStrongDatabase;
+  mSpec.forget();
+}
+
+void
+BackgroundDatabaseChild::ReleaseDOMObject()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mTemporaryStrongDatabase);
+  mTemporaryStrongDatabase->AssertIsOnOwningThread();
+  MOZ_ASSERT(mOpenRequestActor);
+  MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase);
+
+  mOpenRequestActor = nullptr;
+
+  // This may be the final reference to the IDBDatabase object so we may end up
+  // calling SendDeleteMeInternal() here. Make sure everything is cleaned up
+  // properly before proceeding.
+  mTemporaryStrongDatabase = nullptr;
+}
+
+void
+BackgroundDatabaseChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  if (mDatabase) {
+    mDatabase->ClearBackgroundActor();
+#ifdef DEBUG
+    mDatabase = nullptr;
+#endif
+  }
+}
+
+PBackgroundIDBDatabaseFileChild*
+BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseFileChild(
+                                                         PBlobChild* aBlobChild)
+{
+  MOZ_CRASH("PBackgroundIDBFileChild actors should be manually constructed!");
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseFileChild(
+                                        PBackgroundIDBDatabaseFileChild* aActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+
+  delete aActor;
+  return true;
+}
+
+PBackgroundIDBTransactionChild*
+BackgroundDatabaseChild::AllocPBackgroundIDBTransactionChild(
+                                    const nsTArray<nsString>& aObjectStoreNames,
+                                    const Mode& aMode)
+{
+  MOZ_CRASH("PBackgroundIDBTransactionChild actors should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBTransactionChild(
+                                         PBackgroundIDBTransactionChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundTransactionChild*>(aActor);
+  return true;
+}
+
+PBackgroundIDBVersionChangeTransactionChild*
+BackgroundDatabaseChild::AllocPBackgroundIDBVersionChangeTransactionChild(
+                                              const uint64_t& aCurrentVersion,
+                                              const uint64_t& aRequestedVersion,
+                                              const int64_t& aNextObjectStoreId,
+                                              const int64_t& aNextIndexId)
+{
+  AssertIsOnOwningThread();
+
+  IDBOpenDBRequest* request = mOpenRequestActor->GetOpenDBRequest();
+  MOZ_ASSERT(request);
+
+  return new BackgroundVersionChangeTransactionChild(request);
+}
+
+bool
+BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor(
+                            PBackgroundIDBVersionChangeTransactionChild* aActor,
+                            const uint64_t& aCurrentVersion,
+                            const uint64_t& aRequestedVersion,
+                            const int64_t& aNextObjectStoreId,
+                            const int64_t& aNextIndexId)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(mOpenRequestActor);
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  EnsureDOMObject();
+
+  auto actor = static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
+
+  nsRefPtr<IDBTransaction> transaction =
+    IDBTransaction::CreateVersionChange(mDatabase, actor, aNextObjectStoreId,
+                                        aNextIndexId);
+  if (NS_WARN_IF(!transaction)) {
+    return false;
+  }
+
+  transaction->AssertIsOnOwningThread();
+
+  actor->SetDOMTransaction(transaction);
+
+  mDatabase->EnterSetVersionTransaction(aRequestedVersion);
+
+  nsRefPtr<IDBOpenDBRequest> request = mOpenRequestActor->GetOpenDBRequest();
+  MOZ_ASSERT(request);
+
+  request->SetTransaction(transaction);
+
+  nsCOMPtr<nsIDOMEvent> upgradeNeededEvent =
+    IDBVersionChangeEvent::Create(request,
+                                  nsDependentString(kUpgradeNeededEventType),
+                                  aCurrentVersion,
+                                  aRequestedVersion);
+  if (NS_WARN_IF(!upgradeNeededEvent)) {
+    return false;
+  }
+
+  ResultHelper helper(request, transaction,
+                      static_cast<IDBWrapperCache*>(mDatabase));
+
+  DispatchSuccessEvent(&helper, upgradeNeededEvent);
+
+  return true;
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBVersionChangeTransactionChild(
+                            PBackgroundIDBVersionChangeTransactionChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
+  return true;
+}
+
+bool
+BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion,
+                                           const NullableVersion& aNewVersion)
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  if (!mDatabase || mDatabase->IsClosed()) {
+    return true;
+  }
+
+  nsRefPtr<IDBDatabase> kungFuDeathGrip = mDatabase;
+
+  // Handle bfcache'd windows.
+  if (nsPIDOMWindow* owner = mDatabase->GetOwner()) {
+    // The database must be closed if the window is already frozen.
+    bool shouldAbortAndClose = owner->IsFrozen();
+
+    // Anything in the bfcache has to be evicted and then we have to close the
+    // database also.
+    if (nsCOMPtr<nsIDocument> doc = owner->GetExtantDoc()) {
+      if (nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry()) {
+        bfCacheEntry->RemoveFromBFCacheSync();
+        shouldAbortAndClose = true;
+      }
+    }
+
+    if (shouldAbortAndClose) {
+      // Invalidate() doesn't close the database in the parent, so we have
+      // to call Close() and AbortTransactions() manually.
+      mDatabase->AbortTransactions();
+      mDatabase->Close();
+      return true;
+    }
+  }
+
+  // Otherwise fire a versionchange event.
+  const nsDependentString type(kVersionChangeEventType);
+
+  nsCOMPtr<nsIDOMEvent> versionChangeEvent;
+
+  switch (aNewVersion.type()) {
+    case NullableVersion::Tnull_t:
+      versionChangeEvent =
+        IDBVersionChangeEvent::Create(mDatabase, type, aOldVersion);
+      break;
+
+    case NullableVersion::Tuint64_t:
+      versionChangeEvent =
+        IDBVersionChangeEvent::Create(mDatabase,
+                                      type,
+                                      aOldVersion,
+                                      aNewVersion.get_uint64_t());
+      break;
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  if (NS_WARN_IF(!versionChangeEvent)) {
+    return false;
+  }
+
+  bool dummy;
+  if (NS_FAILED(mDatabase->DispatchEvent(versionChangeEvent, &dummy))) {
+    NS_WARNING("Failed to dispatch event!");
+  }
+
+  if (!mDatabase->IsClosed()) {
+    SendBlocked();
+  }
+
+  return true;
+}
+
+bool
+BackgroundDatabaseChild::RecvInvalidate()
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  if (mDatabase) {
+    mDatabase->Invalidate();
+  }
+
+  return true;
+}
+
+/*******************************************************************************
+ * BackgroundTransactionBase
+ ******************************************************************************/
+
+BackgroundTransactionBase::BackgroundTransactionBase()
+: mTransaction(nullptr)
+{
+  MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase);
+}
+
+BackgroundTransactionBase::BackgroundTransactionBase(
+                                                   IDBTransaction* aTransaction)
+  : mTemporaryStrongTransaction(aTransaction)
+  , mTransaction(aTransaction)
+{
+  MOZ_ASSERT(aTransaction);
+  aTransaction->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase);
+}
+
+BackgroundTransactionBase::~BackgroundTransactionBase()
+{
+  MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionBase);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundTransactionBase::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mTransaction);
+  mTransaction->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundTransactionBase::NoteActorDestroyed()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT_IF(mTemporaryStrongTransaction, mTransaction);
+
+  if (mTransaction) {
+    mTransaction->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.
+    mTemporaryStrongTransaction = nullptr;
+    mTransaction = nullptr;
+  }
+}
+
+void
+BackgroundTransactionBase::SetDOMTransaction(IDBTransaction* aTransaction)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aTransaction);
+  aTransaction->AssertIsOnOwningThread();
+  MOZ_ASSERT(!mTemporaryStrongTransaction);
+  MOZ_ASSERT(!mTransaction);
+
+  mTemporaryStrongTransaction = aTransaction;
+  mTransaction = aTransaction;
+}
+
+void
+BackgroundTransactionBase::NoteComplete()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT_IF(mTransaction, mTemporaryStrongTransaction);
+
+  mTemporaryStrongTransaction = nullptr;
+}
+
+/*******************************************************************************
+ * BackgroundTransactionChild
+ ******************************************************************************/
+
+BackgroundTransactionChild::BackgroundTransactionChild(
+                                                   IDBTransaction* aTransaction)
+  : BackgroundTransactionBase(aTransaction)
+{
+  MOZ_ASSERT(aTransaction);
+  aTransaction->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionChild);
+}
+
+BackgroundTransactionChild::~BackgroundTransactionChild()
+{
+  MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundTransactionChild::AssertIsOnOwningThread() const
+{
+  static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundTransactionChild::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+
+  if (mTransaction) {
+    NoteActorDestroyed();
+
+    MOZ_ALWAYS_TRUE(PBackgroundIDBTransactionChild::SendDeleteMe());
+  }
+}
+
+void
+BackgroundTransactionChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  NoteActorDestroyed();
+}
+
+bool
+BackgroundTransactionChild::RecvComplete(const nsresult& aResult)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mTransaction);
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  mTransaction->FireCompleteOrAbortEvents(aResult);
+
+  NoteComplete();
+  return true;
+}
+
+PBackgroundIDBRequestChild*
+BackgroundTransactionChild::AllocPBackgroundIDBRequestChild(
+                                                   const RequestParams& aParams)
+{
+  MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundTransactionChild::DeallocPBackgroundIDBRequestChild(
+                                             PBackgroundIDBRequestChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundRequestChild*>(aActor);
+  return true;
+}
+
+PBackgroundIDBCursorChild*
+BackgroundTransactionChild::AllocPBackgroundIDBCursorChild(
+                                                const OpenCursorParams& aParams)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
+}
+
+bool
+BackgroundTransactionChild::DeallocPBackgroundIDBCursorChild(
+                                              PBackgroundIDBCursorChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundCursorChild*>(aActor);
+  return true;
+}
+
+/*******************************************************************************
+ * BackgroundVersionChangeTransactionChild
+ ******************************************************************************/
+
+BackgroundVersionChangeTransactionChild::
+BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest)
+  : mOpenDBRequest(aOpenDBRequest)
+{
+  MOZ_ASSERT(aOpenDBRequest);
+  aOpenDBRequest->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundVersionChangeTransactionChild);
+}
+
+BackgroundVersionChangeTransactionChild::
+~BackgroundVersionChangeTransactionChild()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(indexedDB::BackgroundVersionChangeTransactionChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundVersionChangeTransactionChild::AssertIsOnOwningThread() const
+{
+  static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundVersionChangeTransactionChild::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+
+  if (mTransaction) {
+    NoteActorDestroyed();
+
+    MOZ_ALWAYS_TRUE(PBackgroundIDBVersionChangeTransactionChild::
+                      SendDeleteMe());
+  }
+}
+
+void
+BackgroundVersionChangeTransactionChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  mOpenDBRequest = nullptr;
+
+  NoteActorDestroyed();
+}
+
+bool
+BackgroundVersionChangeTransactionChild::RecvComplete(const nsresult& aResult)
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  if (!mTransaction) {
+    return true;
+  }
+
+  MOZ_ASSERT(mOpenDBRequest);
+
+  IDBDatabase* database = mTransaction->Database();
+  MOZ_ASSERT(database);
+
+  database->ExitSetVersionTransaction();
+
+  if (NS_FAILED(aResult)) {
+    database->Close();
+  }
+
+  mTransaction->FireCompleteOrAbortEvents(aResult);
+
+  mOpenDBRequest->SetTransaction(nullptr);
+  mOpenDBRequest = nullptr;
+
+  NoteComplete();
+  return true;
+}
+
+PBackgroundIDBRequestChild*
+BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBRequestChild(
+                                                   const RequestParams& aParams)
+{
+  MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBRequestChild(
+                                             PBackgroundIDBRequestChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundRequestChild*>(aActor);
+  return true;
+}
+
+PBackgroundIDBCursorChild*
+BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBCursorChild(
+                                                const OpenCursorParams& aParams)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
+}
+
+bool
+BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBCursorChild(
+                                              PBackgroundIDBCursorChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BackgroundCursorChild*>(aActor);
+  return true;
+}
+
+/*******************************************************************************
+ * BackgroundRequestChild
+ ******************************************************************************/
+
+BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest)
+  : BackgroundRequestChildBase(aRequest)
+  , mTransaction(aRequest->GetTransaction())
+{
+  MOZ_ASSERT(mTransaction);
+  mTransaction->AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild);
+
+  mTransaction->OnNewRequest();
+}
+
+BackgroundRequestChild::~BackgroundRequestChild()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT_IF(!IsActorDestroyed(), mTransaction);
+
+  MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild);
+
+  MaybeFinishTransactionEarly();
+}
+
+void
+BackgroundRequestChild::HoldFileInfosUntilComplete(
+                                       nsTArray<nsRefPtr<FileInfo>>& aFileInfos)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mFileInfos.IsEmpty());
+
+  mFileInfos.SwapElements(aFileInfos);
+}
+
+void
+BackgroundRequestChild::MaybeFinishTransactionEarly()
+{
+  AssertIsOnOwningThread();
+
+  if (mTransaction) {
+    mTransaction->AssertIsOnOwningThread();
+
+    mTransaction->OnRequestFinished();
+    mTransaction = nullptr;
+  }
+}
+
+bool
+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);
+  return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(const Key& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  ResultHelper helper(mRequest, mTransaction, &aResponse);
+
+  DispatchSuccessEvent(&helper);
+  return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(const nsTArray<Key>& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  ResultHelper helper(mRequest, mTransaction, &aResponse);
+
+  DispatchSuccessEvent(&helper);
+  return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(
+                             const SerializedStructuredCloneReadInfo& aResponse)
+{
+  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);
+  return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(
+                   const nsTArray<SerializedStructuredCloneReadInfo>& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  nsTArray<StructuredCloneReadInfo> cloneReadInfos;
+
+  if (!aResponse.IsEmpty()) {
+    const uint32_t count = aResponse.Length();
+
+    cloneReadInfos.SetCapacity(count);
+
+    IDBDatabase* database = mTransaction->Database();
+
+    for (uint32_t index = 0; index < count; index++) {
+      // 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);
+
+  DispatchSuccessEvent(&helper);
+  return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse)
+{
+  AssertIsOnOwningThread();
+
+  ResultHelper helper(mRequest, mTransaction, &aResponse);
+
+  DispatchSuccessEvent(&helper);
+  return true;
+}
+
+bool
+BackgroundRequestChild::HandleResponse(uint64_t aResponse)
+{
+  AssertIsOnOwningThread();
+
+  JS::Value response(JS::NumberValue(aResponse));
+
+  ResultHelper helper(mRequest, mTransaction, &response);
+
+  DispatchSuccessEvent(&helper);
+  return true;
+}
+
+void
+BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  MaybeFinishTransactionEarly();
+
+  NoteActorDestroyed();
+}
+
+bool
+BackgroundRequestChild::Recv__delete__(const RequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mTransaction);
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  // Always fire an "error" event with ABORT_ERR if the transaction was aborted,
+  // even if the request succeeded or failed with another error.
+  if (mTransaction->IsAborted()) {
+    return HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+  }
+
+  switch (aResponse.type()) {
+    case RequestResponse::Tnsresult:
+      return HandleResponse(aResponse.get_nsresult());
+
+    case RequestResponse::TObjectStoreAddResponse:
+      return HandleResponse(aResponse.get_ObjectStoreAddResponse().key());
+
+    case RequestResponse::TObjectStorePutResponse:
+      return HandleResponse(aResponse.get_ObjectStorePutResponse().key());
+
+    case RequestResponse::TObjectStoreGetResponse:
+      return HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo());
+
+    case RequestResponse::TObjectStoreGetAllResponse:
+      return HandleResponse(aResponse.get_ObjectStoreGetAllResponse()
+                                     .cloneInfos());
+
+    case RequestResponse::TObjectStoreGetAllKeysResponse:
+      return HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse()
+                                     .keys());
+
+    case RequestResponse::TObjectStoreDeleteResponse:
+      return HandleResponse(JS::UndefinedHandleValue);
+
+    case RequestResponse::TObjectStoreClearResponse:
+      return HandleResponse(JS::UndefinedHandleValue);
+
+    case RequestResponse::TObjectStoreCountResponse:
+      return HandleResponse(aResponse.get_ObjectStoreCountResponse().count());
+
+    case RequestResponse::TIndexGetResponse:
+      return HandleResponse(aResponse.get_IndexGetResponse().cloneInfo());
+
+    case RequestResponse::TIndexGetKeyResponse:
+      return HandleResponse(aResponse.get_IndexGetKeyResponse().key());
+
+    case RequestResponse::TIndexGetAllResponse:
+      return HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos());
+
+    case RequestResponse::TIndexGetAllKeysResponse:
+      return HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys());
+
+    case RequestResponse::TIndexCountResponse:
+      return HandleResponse(aResponse.get_IndexCountResponse().count());
+
+    default:
+      MOZ_CRASH("Unknown response type!");
+  }
+
+  MOZ_CRASH("Should never get here!");
+}
+
+/*******************************************************************************
+ * BackgroundCursorChild
+ ******************************************************************************/
+
+class BackgroundCursorChild::DelayedDeleteRunnable MOZ_FINAL
+  : public nsIRunnable
+{
+  BackgroundCursorChild* mActor;
+  nsRefPtr<IDBRequest> mRequest;
+
+public:
+  explicit DelayedDeleteRunnable(BackgroundCursorChild* aActor)
+    : mActor(aActor)
+    , mRequest(aActor->mRequest)
+  {
+    MOZ_ASSERT(aActor);
+    aActor->AssertIsOnOwningThread();
+    MOZ_ASSERT(mRequest);
+  }
+
+  // Does not need to be threadsafe since this only runs on one thread.
+  NS_DECL_ISUPPORTS
+
+private:
+  ~DelayedDeleteRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest,
+                                             IDBObjectStore* aObjectStore,
+                                             Direction aDirection)
+  : mRequest(aRequest)
+  , mTransaction(aRequest->GetTransaction())
+  , mObjectStore(aObjectStore)
+  , mIndex(nullptr)
+  , mCursor(nullptr)
+  , mStrongRequest(aRequest)
+  , mDirection(aDirection)
+{
+  MOZ_ASSERT(aObjectStore);
+  aObjectStore->AssertIsOnOwningThread();
+  MOZ_ASSERT(mTransaction);
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild);
+
+#ifdef DEBUG
+  mOwningThread = PR_GetCurrentThread();
+  MOZ_ASSERT(mOwningThread);
+#endif
+}
+
+BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest,
+                                             IDBIndex* aIndex,
+                                             Direction aDirection)
+  : mRequest(aRequest)
+  , mTransaction(aRequest->GetTransaction())
+  , mObjectStore(nullptr)
+  , mIndex(aIndex)
+  , mCursor(nullptr)
+  , mStrongRequest(aRequest)
+  , mDirection(aDirection)
+{
+  MOZ_ASSERT(aIndex);
+  aIndex->AssertIsOnOwningThread();
+  MOZ_ASSERT(mTransaction);
+
+  MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild);
+
+#ifdef DEBUG
+  mOwningThread = PR_GetCurrentThread();
+  MOZ_ASSERT(mOwningThread);
+#endif
+}
+
+BackgroundCursorChild::~BackgroundCursorChild()
+{
+  MOZ_COUNT_DTOR(indexedDB::BackgroundCursorChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundCursorChild::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread == PR_GetCurrentThread());
+}
+
+#endif // DEBUG
+
+void
+BackgroundCursorChild::SendContinueInternal(const CursorRequestParams& aParams)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(mCursor);
+  MOZ_ASSERT(!mStrongRequest);
+  MOZ_ASSERT(!mStrongCursor);
+
+  // Make sure all our DOM objects stay alive.
+  mStrongRequest = mRequest;
+  mStrongCursor = mCursor;
+
+  MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done);
+  mRequest->Reset();
+
+  mTransaction->OnNewRequest();
+
+  MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(aParams));
+}
+
+void
+BackgroundCursorChild::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mStrongRequest);
+  MOZ_ASSERT(!mStrongCursor);
+
+  mRequest = nullptr;
+  mTransaction = nullptr;
+  mObjectStore = nullptr;
+  mIndex = nullptr;
+
+  if (mCursor) {
+    mCursor->ClearBackgroundActor();
+    mCursor = nullptr;
+
+    MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendDeleteMe());
+  }
+}
+
+void
+BackgroundCursorChild::HandleResponse(nsresult aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_FAILED(aResponse));
+  MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(!mStrongRequest);
+  MOZ_ASSERT(!mStrongCursor);
+
+  DispatchErrorEvent(mRequest, aResponse, mTransaction);
+}
+
+void
+BackgroundCursorChild::HandleResponse(const void_t& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(!mStrongRequest);
+  MOZ_ASSERT(!mStrongCursor);
+
+  if (mCursor) {
+    mCursor->Reset();
+  }
+
+  ResultHelper helper(mRequest, mTransaction, &JS::NullHandleValue);
+  DispatchSuccessEvent(&helper);
+
+  if (!mCursor) {
+    nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(deleteRunnable)));
+  }
+}
+
+void
+BackgroundCursorChild::HandleResponse(
+                                     const ObjectStoreCursorResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(mObjectStore);
+  MOZ_ASSERT(!mStrongRequest);
+  MOZ_ASSERT(!mStrongCursor);
+
+  // XXX Fix this somehow...
+  auto& response = const_cast<ObjectStoreCursorResponse&>(aResponse);
+
+  StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo()));
+
+  ConvertActorsToBlobs(mTransaction->Database(),
+                       response.cloneInfo(),
+                       cloneReadInfo.mFiles);
+
+  nsRefPtr<IDBCursor> newCursor;
+
+  if (mCursor) {
+    mCursor->Reset(Move(response.key()), Move(cloneReadInfo));
+  } else {
+    newCursor = IDBCursor::Create(mObjectStore,
+                                  this,
+                                  mDirection,
+                                  Move(response.key()),
+                                  Move(cloneReadInfo));
+    mCursor = newCursor;
+  }
+
+  ResultHelper helper(mRequest, mTransaction, mCursor);
+  DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundCursorChild::HandleResponse(
+                                  const ObjectStoreKeyCursorResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(mObjectStore);
+  MOZ_ASSERT(!mStrongRequest);
+  MOZ_ASSERT(!mStrongCursor);
+
+  // XXX Fix this somehow...
+  auto& response = const_cast<ObjectStoreKeyCursorResponse&>(aResponse);
+
+  nsRefPtr<IDBCursor> newCursor;
+
+  if (mCursor) {
+    mCursor->Reset(Move(response.key()));
+  } else {
+    newCursor = IDBCursor::Create(mObjectStore,
+                                  this,
+                                  mDirection,
+                                  Move(response.key()));
+    mCursor = newCursor;
+  }
+
+  ResultHelper helper(mRequest, mTransaction, mCursor);
+  DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundCursorChild::HandleResponse(const IndexCursorResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(mIndex);
+  MOZ_ASSERT(!mStrongRequest);
+  MOZ_ASSERT(!mStrongCursor);
+
+  // XXX Fix this somehow...
+  auto& response = const_cast<IndexCursorResponse&>(aResponse);
+
+  StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo()));
+
+  ConvertActorsToBlobs(mTransaction->Database(),
+                       aResponse.cloneInfo(),
+                       cloneReadInfo.mFiles);
+
+  nsRefPtr<IDBCursor> newCursor;
+
+  if (mCursor) {
+    mCursor->Reset(Move(response.key()),
+                   Move(response.objectKey()),
+                   Move(cloneReadInfo));
+  } else {
+    newCursor = IDBCursor::Create(mIndex,
+                                  this,
+                                  mDirection,
+                                  Move(response.key()),
+                                  Move(response.objectKey()),
+                                  Move(cloneReadInfo));
+    mCursor = newCursor;
+  }
+
+  ResultHelper helper(mRequest, mTransaction, mCursor);
+  DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundCursorChild::HandleResponse(const IndexKeyCursorResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(mIndex);
+  MOZ_ASSERT(!mStrongRequest);
+  MOZ_ASSERT(!mStrongCursor);
+
+  // XXX Fix this somehow...
+  auto& response = const_cast<IndexKeyCursorResponse&>(aResponse);
+
+  nsRefPtr<IDBCursor> newCursor;
+
+  if (mCursor) {
+    mCursor->Reset(Move(response.key()), Move(response.objectKey()));
+  } else {
+    newCursor = IDBCursor::Create(mIndex,
+                                  this,
+                                  mDirection,
+                                  Move(response.key()),
+                                  Move(response.objectKey()));
+    mCursor = newCursor;
+  }
+
+  ResultHelper helper(mRequest, mTransaction, mCursor);
+  DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundCursorChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT_IF(aWhy == Deletion, !mStrongRequest);
+  MOZ_ASSERT_IF(aWhy == Deletion, !mStrongCursor);
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  if (mStrongRequest && !mStrongCursor && mTransaction) {
+    mTransaction->OnRequestFinished();
+  }
+
+  if (mCursor) {
+    mCursor->ClearBackgroundActor();
+#ifdef DEBUG
+    mCursor = nullptr;
+#endif
+  }
+
+#ifdef DEBUG
+  mRequest = nullptr;
+  mTransaction = nullptr;
+  mObjectStore = nullptr;
+  mIndex = nullptr;
+#endif
+}
+
+bool
+BackgroundCursorChild::RecvResponse(const CursorResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aResponse.type() != CursorResponse::T__None);
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(mStrongRequest);
+  MOZ_ASSERT_IF(mCursor, mStrongCursor);
+
+  MaybeCollectGarbageOnIPCMessage();
+
+  nsRefPtr<IDBRequest> request;
+  mStrongRequest.swap(request);
+
+  nsRefPtr<IDBCursor> cursor;
+  mStrongCursor.swap(cursor);
+
+  switch (aResponse.type()) {
+    case CursorResponse::Tnsresult:
+      HandleResponse(aResponse.get_nsresult());
+      break;
+
+    case CursorResponse::Tvoid_t:
+      HandleResponse(aResponse.get_void_t());
+      break;
+
+    case CursorResponse::TObjectStoreCursorResponse:
+      HandleResponse(aResponse.get_ObjectStoreCursorResponse());
+      break;
+
+    case CursorResponse::TObjectStoreKeyCursorResponse:
+      HandleResponse(aResponse.get_ObjectStoreKeyCursorResponse());
+      break;
+
+    case CursorResponse::TIndexCursorResponse:
+      HandleResponse(aResponse.get_IndexCursorResponse());
+      break;
+
+    case CursorResponse::TIndexKeyCursorResponse:
+      HandleResponse(aResponse.get_IndexKeyCursorResponse());
+      break;
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  mTransaction->OnRequestFinished();
+
+  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::DelayedDeleteRunnable,
+                  nsIRunnable)
+
+NS_IMETHODIMP
+BackgroundCursorChild::
+DelayedDeleteRunnable::Run()
+{
+  MOZ_ASSERT(mActor);
+  mActor->AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  mActor->SendDeleteMeInternal();
+
+  mActor = nullptr;
+  mRequest = nullptr;
+
+  return NS_OK;
+}
+
+} // namespace indexedDB
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/ActorsChild.h
@@ -0,0 +1,620 @@
+/* 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_indexeddb_actorschild_h__
+#define mozilla_dom_indexeddb_actorschild_h__
+
+#include "js/RootingAPI.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+
+class nsIEventTarget;
+struct PRThread;
+
+namespace mozilla {
+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 IDBTransaction;
+class Key;
+class PBackgroundIDBFileChild;
+class PermissionRequestChild;
+class PermissionRequestParent;
+class SerializedStructuredCloneReadInfo;
+
+class BackgroundFactoryChild MOZ_FINAL
+  : public PBackgroundIDBFactoryChild
+{
+  friend class mozilla::ipc::BackgroundChildImpl;
+  friend class IDBFactory;
+
+  IDBFactory* mFactory;
+
+#ifdef DEBUG
+  nsCOMPtr<nsIEventTarget> mOwningThread;
+#endif
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  IDBFactory*
+  GetDOMObject() const
+  {
+    AssertIsOnOwningThread();
+    return mFactory;
+  }
+
+private:
+  // Only created by IDBFactory.
+  explicit BackgroundFactoryChild(IDBFactory* aFactory);
+
+  // Only destroyed by mozilla::ipc::BackgroundChildImpl.
+  ~BackgroundFactoryChild();
+
+  void
+  SendDeleteMeInternal();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBFactoryRequestChild*
+  AllocPBackgroundIDBFactoryRequestChild(const FactoryRequestParams& aParams)
+                                         MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBFactoryRequestChild(
+                                      PBackgroundIDBFactoryRequestChild* aActor)
+                                      MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBDatabaseChild*
+  AllocPBackgroundIDBDatabaseChild(const DatabaseSpec& aSpec,
+                                   PBackgroundIDBFactoryRequestChild* aRequest)
+                                   MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBDatabaseChild(PBackgroundIDBDatabaseChild* aActor)
+                                     MOZ_OVERRIDE;
+
+  bool
+  SendDeleteMe() MOZ_DELETE;
+};
+
+class BackgroundDatabaseChild;
+
+class BackgroundRequestChildBase
+{
+protected:
+  nsRefPtr<IDBRequest> mRequest;
+
+private:
+  bool mActorDestroyed;
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  IDBRequest*
+  GetDOMObject() const
+  {
+    AssertIsOnOwningThread();
+    return mRequest;
+  }
+
+  bool
+  IsActorDestroyed() const
+  {
+    AssertIsOnOwningThread();
+    return mActorDestroyed;
+  }
+
+protected:
+  explicit BackgroundRequestChildBase(IDBRequest* aRequest);
+
+  virtual
+  ~BackgroundRequestChildBase();
+
+  void
+  NoteActorDestroyed();
+};
+
+class BackgroundFactoryRequestChild MOZ_FINAL
+  : public BackgroundRequestChildBase
+  , public PBackgroundIDBFactoryRequestChild
+{
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
+
+  friend class IDBFactory;
+  friend class BackgroundFactoryChild;
+  friend class BackgroundDatabaseChild;
+  friend class PermissionRequestChild;
+  friend class PermissionRequestParent;
+
+  nsRefPtr<IDBFactory> mFactory;
+  const uint64_t mRequestedVersion;
+  const bool mIsDeleteOp;
+
+public:
+  IDBOpenDBRequest*
+  GetOpenDBRequest() const;
+
+private:
+  // Only created by IDBFactory.
+  BackgroundFactoryRequestChild(IDBFactory* aFactory,
+                                IDBOpenDBRequest* aOpenRequest,
+                                bool aIsDeleteOp,
+                                uint64_t aRequestedVersion);
+
+  // Only destroyed by BackgroundFactoryChild.
+  ~BackgroundFactoryRequestChild();
+
+  bool
+  HandleResponse(nsresult aResponse);
+
+  bool
+  HandleResponse(const OpenDatabaseRequestResponse& aResponse);
+
+  bool
+  HandleResponse(const DeleteDatabaseRequestResponse& aResponse);
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  Recv__delete__(const FactoryRequestResponse& aResponse) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPermissionChallenge(const PrincipalInfo& aPrincipalInfo) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvBlocked(const uint64_t& aCurrentVersion) MOZ_OVERRIDE;
+};
+
+class BackgroundDatabaseChild MOZ_FINAL
+  : public PBackgroundIDBDatabaseChild
+{
+  friend class BackgroundFactoryChild;
+  friend class BackgroundFactoryRequestChild;
+  friend class IDBDatabase;
+
+  nsAutoPtr<DatabaseSpec> mSpec;
+  nsRefPtr<IDBDatabase> mTemporaryStrongDatabase;
+  BackgroundFactoryRequestChild* mOpenRequestActor;
+  IDBDatabase* mDatabase;
+
+public:
+  void
+  AssertIsOnOwningThread() const
+  {
+    static_cast<BackgroundFactoryChild*>(Manager())->AssertIsOnOwningThread();
+  }
+
+  const DatabaseSpec*
+  Spec() const
+  {
+    AssertIsOnOwningThread();
+    return mSpec;
+  }
+
+  IDBDatabase*
+  GetDOMObject() const
+  {
+    AssertIsOnOwningThread();
+    return mDatabase;
+  }
+
+private:
+  // Only constructed by BackgroundFactoryChild.
+  BackgroundDatabaseChild(const DatabaseSpec& aSpec,
+                          BackgroundFactoryRequestChild* aOpenRequest);
+
+  // Only destroyed by BackgroundFactoryChild.
+  ~BackgroundDatabaseChild();
+
+  void
+  SendDeleteMeInternal();
+
+  void
+  EnsureDOMObject();
+
+  void
+  ReleaseDOMObject();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBDatabaseFileChild*
+  AllocPBackgroundIDBDatabaseFileChild(PBlobChild* aBlobChild)
+                                       MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBDatabaseFileChild(
+                                        PBackgroundIDBDatabaseFileChild* aActor)
+                                        MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBTransactionChild*
+  AllocPBackgroundIDBTransactionChild(
+                                    const nsTArray<nsString>& aObjectStoreNames,
+                                    const Mode& aMode)
+                                    MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBTransactionChild(PBackgroundIDBTransactionChild* aActor)
+                                        MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBVersionChangeTransactionChild*
+  AllocPBackgroundIDBVersionChangeTransactionChild(
+                                              const uint64_t& aCurrentVersion,
+                                              const uint64_t& aRequestedVersion,
+                                              const int64_t& aNextObjectStoreId,
+                                              const int64_t& aNextIndexId)
+                                              MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBackgroundIDBVersionChangeTransactionConstructor(
+                            PBackgroundIDBVersionChangeTransactionChild* aActor,
+                            const uint64_t& aCurrentVersion,
+                            const uint64_t& aRequestedVersion,
+                            const int64_t& aNextObjectStoreId,
+                            const int64_t& aNextIndexId)
+                            MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBVersionChangeTransactionChild(
+                            PBackgroundIDBVersionChangeTransactionChild* aActor)
+                            MOZ_OVERRIDE;
+
+  virtual bool
+  RecvVersionChange(const uint64_t& aOldVersion,
+                    const NullableVersion& aNewVersion)
+                    MOZ_OVERRIDE;
+
+  virtual bool
+  RecvInvalidate() MOZ_OVERRIDE;
+
+  bool
+  SendDeleteMe() MOZ_DELETE;
+};
+
+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
+  // function is called.
+  nsRefPtr<IDBTransaction> mTemporaryStrongTransaction;
+
+protected:
+  // mTransaction is weak and is valid until the NoteActorDestroyed() member
+  // function is called.
+  IDBTransaction* mTransaction;
+
+public:
+#ifdef DEBUG
+  virtual void
+  AssertIsOnOwningThread() const = 0;
+#else
+  void
+  AssertIsOnOwningThread() const
+  { }
+#endif
+
+  IDBTransaction*
+  GetDOMObject() const
+  {
+    AssertIsOnOwningThread();
+    return mTransaction;
+  }
+
+protected:
+  BackgroundTransactionBase();
+  explicit BackgroundTransactionBase(IDBTransaction* aTransaction);
+
+  virtual
+  ~BackgroundTransactionBase();
+
+  void
+  NoteActorDestroyed();
+
+  void
+  NoteComplete();
+
+private:
+  // Only called by BackgroundVersionChangeTransactionChild.
+  void
+  SetDOMTransaction(IDBTransaction* aDOMObject);
+};
+
+class BackgroundTransactionChild MOZ_FINAL
+  : public BackgroundTransactionBase
+  , public PBackgroundIDBTransactionChild
+{
+  friend class BackgroundDatabaseChild;
+  friend class IDBDatabase;
+
+public:
+#ifdef DEBUG
+  virtual void
+  AssertIsOnOwningThread() const MOZ_OVERRIDE;
+#endif
+
+  void
+  SendDeleteMeInternal();
+
+private:
+  // Only created by IDBDatabase.
+  explicit BackgroundTransactionChild(IDBTransaction* aTransaction);
+
+  // Only destroyed by BackgroundDatabaseChild.
+  ~BackgroundTransactionChild();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  bool
+  RecvComplete(const nsresult& aResult) MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBRequestChild*
+  AllocPBackgroundIDBRequestChild(const RequestParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor)
+                                    MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBCursorChild*
+  AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor)
+                                   MOZ_OVERRIDE;
+
+  bool
+  SendDeleteMe() MOZ_DELETE;
+};
+
+class BackgroundVersionChangeTransactionChild MOZ_FINAL
+  : public BackgroundTransactionBase
+  , public PBackgroundIDBVersionChangeTransactionChild
+{
+  friend class BackgroundDatabaseChild;
+
+  IDBOpenDBRequest* mOpenDBRequest;
+
+public:
+#ifdef DEBUG
+  virtual void
+  AssertIsOnOwningThread() const MOZ_OVERRIDE;
+#endif
+
+  void
+  SendDeleteMeInternal();
+
+private:
+  // Only created by BackgroundDatabaseChild.
+  explicit BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest);
+
+  // Only destroyed by BackgroundDatabaseChild.
+  ~BackgroundVersionChangeTransactionChild();
+
+  // Only called by BackgroundDatabaseChild.
+  void
+  SetDOMTransaction(IDBTransaction* aDOMObject)
+  {
+    BackgroundTransactionBase::SetDOMTransaction(aDOMObject);
+  }
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  bool
+  RecvComplete(const nsresult& aResult) MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBRequestChild*
+  AllocPBackgroundIDBRequestChild(const RequestParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor)
+                                    MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBCursorChild*
+  AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor)
+                                   MOZ_OVERRIDE;
+
+  bool
+  SendDeleteMe() MOZ_DELETE;
+};
+
+class BackgroundRequestChild MOZ_FINAL
+  : public BackgroundRequestChildBase
+  , public PBackgroundIDBRequestChild
+{
+  friend class BackgroundTransactionChild;
+  friend class BackgroundVersionChangeTransactionChild;
+
+  nsRefPtr<IDBTransaction> mTransaction;
+  nsTArray<nsRefPtr<FileInfo>> mFileInfos;
+
+public:
+  explicit BackgroundRequestChild(IDBRequest* aRequest);
+
+  void
+  HoldFileInfosUntilComplete(nsTArray<nsRefPtr<FileInfo>>& aFileInfos);
+
+private:
+  // Only destroyed by BackgroundTransactionChild or
+  // BackgroundVersionChangeTransactionChild.
+  ~BackgroundRequestChild();
+
+  void
+  MaybeFinishTransactionEarly();
+
+  bool
+  HandleResponse(nsresult aResponse);
+
+  bool
+  HandleResponse(const Key& aResponse);
+
+  bool
+  HandleResponse(const nsTArray<Key>& aResponse);
+
+  bool
+  HandleResponse(const SerializedStructuredCloneReadInfo& aResponse);
+
+  bool
+  HandleResponse(const nsTArray<SerializedStructuredCloneReadInfo>& aResponse);
+
+  bool
+  HandleResponse(JS::Handle<JS::Value> aResponse);
+
+  bool
+  HandleResponse(uint64_t aResponse);
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  Recv__delete__(const RequestResponse& aResponse) MOZ_OVERRIDE;
+};
+
+class BackgroundCursorChild MOZ_FINAL
+  : public PBackgroundIDBCursorChild
+{
+  friend class BackgroundTransactionChild;
+  friend class BackgroundVersionChangeTransactionChild;
+
+  class DelayedDeleteRunnable;
+
+  IDBRequest* mRequest;
+  IDBTransaction* mTransaction;
+  IDBObjectStore* mObjectStore;
+  IDBIndex* mIndex;
+  IDBCursor* mCursor;
+
+  // These are only set while a request is in progress.
+  nsRefPtr<IDBRequest> mStrongRequest;
+  nsRefPtr<IDBCursor> mStrongCursor;
+
+  Direction mDirection;
+
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
+
+public:
+  BackgroundCursorChild(IDBRequest* aRequest,
+                        IDBObjectStore* aObjectStore,
+                        Direction aDirection);
+
+  BackgroundCursorChild(IDBRequest* aRequest,
+                        IDBIndex* aIndex,
+                        Direction aDirection);
+
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  void
+  SendContinueInternal(const CursorRequestParams& aParams);
+
+  void
+  SendDeleteMeInternal();
+
+private:
+  // Only destroyed by BackgroundTransactionChild or
+  // BackgroundVersionChangeTransactionChild.
+  ~BackgroundCursorChild();
+
+  void
+  HandleResponse(nsresult aResponse);
+
+  void
+  HandleResponse(const void_t& aResponse);
+
+  void
+  HandleResponse(const ObjectStoreCursorResponse& aResponse);
+
+  void
+  HandleResponse(const ObjectStoreKeyCursorResponse& aResponse);
+
+  void
+  HandleResponse(const IndexCursorResponse& aResponse);
+
+  void
+  HandleResponse(const IndexKeyCursorResponse& aResponse);
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvResponse(const CursorResponse& aResponse) MOZ_OVERRIDE;
+
+  // Force callers to use SendContinueInternal.
+  bool
+  SendContinue(const CursorRequestParams& aParams) MOZ_DELETE;
+
+  bool
+  SendDeleteMe() MOZ_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__
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -0,0 +1,16878 @@
+/* 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 <algorithm>
+#include "CheckQuotaHelper.h"
+#include "FileInfo.h"
+#include "FileManager.h"
+#include "IDBObjectStore.h"
+#include "IDBTransaction.h"
+#include "IndexedDatabase.h"
+#include "IndexedDatabaseInlines.h"
+#include "IndexedDatabaseManager.h"
+#include "js/StructuredClone.h"
+#include "js/Value.h"
+#include "jsapi.h"
+#include "KeyPath.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/AppProcessChecker.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/Endian.h"
+#include "mozilla/LazyIdleThread.h"
+#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/StructuredCloneTags.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.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"
+#include "mozilla/dom/quota/FileStreams.h"
+#include "mozilla/dom/quota/OriginOrPatternString.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/quota/StoragePrivilege.h"
+#include "mozilla/dom/quota/UsageInfo.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/InputStreamParams.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/PBackground.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsClassHashtable.h"
+#include "nsCOMPtr.h"
+#include "nsDataHashtable.h"
+#include "nsDOMFile.h"
+#include "nsEscape.h"
+#include "nsHashKeys.h"
+#include "nsNetUtil.h"
+#include "nsIAppsService.h"
+#include "nsIDOMFile.h"
+#include "nsIEventTarget.h"
+#include "nsIFile.h"
+#include "nsIFileURL.h"
+#include "nsIInputStream.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsInterfaceHashtable.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIOfflineStorage.h"
+#include "nsIOutputStream.h"
+#include "nsIPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsISupports.h"
+#include "nsISupportsImpl.h"
+#include "nsISupportsPriority.h"
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+#include "nsPrintfCString.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOMCID.h"
+#include "PermissionRequestBase.h"
+#include "ProfilerHelpers.h"
+#include "ReportInternalError.h"
+#include "snappy/snappy.h"
+#include "TransactionThreadPool.h"
+
+namespace mozilla {
+namespace dom {
+namespace indexedDB {
+
+using namespace mozilla::dom::quota;
+using namespace mozilla::ipc;
+
+#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 {
+
+class Cursor;
+class Database;
+struct DatabaseActorInfo;
+class DatabaseFile;
+class DatabaseOfflineStorage;
+class Factory;
+class OpenDatabaseOp;
+class TransactionBase;
+class VersionChangeTransaction;
+
+/*******************************************************************************
+ * Constants
+ ******************************************************************************/
+
+// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
+// schema version.
+static_assert(JS_STRUCTURED_CLONE_VERSION == 5,
+              "Need to update the major schema version.");
+
+// Major schema version. Bump for almost everything.
+const uint32_t kMajorSchemaVersion = 17;
+
+// Minor schema version. Should almost always be 0 (maybe bump on release
+// branches if we have to).
+const uint32_t kMinorSchemaVersion = 0;
+
+// The schema version we store in the SQLite database is a (signed) 32-bit
+// integer. The major version is left-shifted 4 bits so the max value is
+// 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
+static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
+              "Major version needs to fit in 28 bits.");
+static_assert(kMinorSchemaVersion <= 0xF,
+              "Minor version needs to fit in 4 bits.");
+
+const int32_t kSQLiteSchemaVersion =
+  int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion);
+
+const int32_t kStorageProgressGranularity = 1000;
+
+const char kSavepointClause[] = "SAVEPOINT sp;";
+
+const fallible_t fallible = fallible_t();
+
+const uint32_t kFileCopyBufferSize = 32768;
+
+const char kJournalDirectoryName[] = "journals";
+
+const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled";
+
+#define IDB_PREFIX "indexedDB"
+
+#ifdef MOZ_CHILD_PERMISSIONS
+const char kPermissionString[] = IDB_PREFIX;
+#endif // MOZ_CHILD_PERMISSIONS
+
+const char kPermissionStringChromeBase[] = IDB_PREFIX "-chrome-";
+const char kPermissionStringChromeReadSuffix[] = "-read";
+const char kPermissionStringChromeWriteSuffix[] = "-write";
+
+#undef IDB_PREFIX
+
+enum AppId {
+  kNoAppId = nsIScriptSecurityManager::NO_APP_ID,
+  kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID
+};
+
+#ifdef DEBUG
+
+const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL;
+const uint32_t kDEBUGThreadSleepMS = 0;
+
+#endif
+
+/*******************************************************************************
+ * Metadata classes
+ ******************************************************************************/
+
+struct FullIndexMetadata
+{
+  IndexMetadata mCommonMetadata;
+
+  bool mDeleted;
+
+public:
+  FullIndexMetadata()
+    : mCommonMetadata(0, nsString(), KeyPath(0), false, false)
+    , mDeleted(false)
+  {
+    // This can happen either on the QuotaManager IO thread or on a
+    // versionchange transaction thread. These threads can never race so this is
+    // totally safe.
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata)
+
+private:
+  ~FullIndexMetadata()
+  { }
+};
+
+typedef nsRefPtrHashtable<nsUint64HashKey, FullIndexMetadata> IndexTable;
+
+struct FullObjectStoreMetadata
+{
+  ObjectStoreMetadata mCommonMetadata;
+  IndexTable mIndexes;
+
+  // These two members are only ever touched on a transaction thread!
+  int64_t mNextAutoIncrementId;
+  int64_t mComittedAutoIncrementId;
+
+  bool mDeleted;
+
+public:
+  FullObjectStoreMetadata()
+    : mCommonMetadata(0, nsString(), KeyPath(0), false)
+    , mNextAutoIncrementId(0)
+    , mComittedAutoIncrementId(0)
+    , mDeleted(false)
+  {
+    // This can happen either on the QuotaManager IO thread or on a
+    // versionchange transaction thread. These threads can never race so this is
+    // totally safe.
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata);
+
+private:
+  ~FullObjectStoreMetadata()
+  { }
+};
+
+typedef nsRefPtrHashtable<nsUint64HashKey, FullObjectStoreMetadata>
+  ObjectStoreTable;
+
+struct FullDatabaseMetadata
+{
+  DatabaseMetadata mCommonMetadata;
+  nsCString mDatabaseId;
+  nsString mFilePath;
+  ObjectStoreTable mObjectStores;
+
+  int64_t mNextObjectStoreId;
+  int64_t mNextIndexId;
+
+public:
+  explicit FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata)
+    : mCommonMetadata(aCommonMetadata)
+    , mNextObjectStoreId(0)
+    , mNextIndexId(0)
+  {
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullDatabaseMetadata)
+
+  already_AddRefed<FullDatabaseMetadata>
+  Duplicate() const;
+
+private:
+  ~FullDatabaseMetadata()
+  { }
+};
+
+template <class MetadataType>
+class MOZ_STACK_CLASS MetadataNameOrIdMatcher MOZ_FINAL
+{
+  typedef MetadataNameOrIdMatcher<MetadataType> SelfType;
+
+  const int64_t mId;
+  const nsString mName;
+  nsRefPtr<MetadataType> mMetadata;
+  bool mCheckName;
+
+public:
+  template <class Enumerable>
+  static MetadataType*
+  Match(const Enumerable& aEnumerable, uint64_t aId, const nsAString& aName)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aId);
+
+    SelfType closure(aId, aName);
+    aEnumerable.EnumerateRead(Enumerate, &closure);
+
+    return closure.mMetadata;
+  }
+
+  template <class Enumerable>
+  static MetadataType*
+  Match(const Enumerable& aEnumerable, uint64_t aId)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aId);
+
+    SelfType closure(aId);
+    aEnumerable.EnumerateRead(Enumerate, &closure);
+
+    return closure.mMetadata;
+  }
+
+private:
+  MetadataNameOrIdMatcher(const int64_t& aId, const nsAString& aName)
+    : mId(aId)
+    , mName(PromiseFlatString(aName))
+    , mMetadata(nullptr)
+    , mCheckName(true)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aId);
+  }
+
+  explicit MetadataNameOrIdMatcher(const int64_t& aId)
+    : mId(aId)
+    , mMetadata(nullptr)
+    , mCheckName(false)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aId);
+  }
+
+  static PLDHashOperator
+  Enumerate(const uint64_t& aKey, MetadataType* aValue, void* aClosure)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aKey);
+    MOZ_ASSERT(aValue);
+    MOZ_ASSERT(aClosure);
+
+    auto* closure = static_cast<SelfType*>(aClosure);
+
+    if (!aValue->mDeleted &&
+        (closure->mId == aValue->mCommonMetadata.id() ||
+         (closure->mCheckName &&
+          closure->mName == aValue->mCommonMetadata.name()))) {
+      closure->mMetadata = aValue;
+      return PL_DHASH_STOP;
+    }
+
+    return PL_DHASH_NEXT;
+  }
+};
+
+/*******************************************************************************
+ * SQLite functions
+ ******************************************************************************/
+
+int32_t
+MakeSchemaVersion(uint32_t aMajorSchemaVersion,
+                  uint32_t aMinorSchemaVersion)
+{
+  return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
+}
+
+uint32_t
+HashName(const nsAString& aName)
+{
+  struct Helper
+  {
+    static uint32_t
+    RotateBitsLeft32(uint32_t aValue, uint8_t aBits)
+    {
+      MOZ_ASSERT(aBits < 32);
+      return (aValue << aBits) | (aValue >> (32 - aBits));
+    }
+  };
+
+  static const uint32_t kGoldenRatioU32 = 0x9e3779b9u;
+
+  const char16_t* str = aName.BeginReading();
+  size_t length = aName.Length();
+
+  uint32_t hash = 0;
+  for (size_t i = 0; i < length; i++) {
+    hash = kGoldenRatioU32 * (Helper::RotateBitsLeft32(hash, 5) ^ str[i]);
+  }
+
+  return hash;
+}
+
+nsresult
+ClampResultCode(nsresult aResultCode)
+{
+  if (NS_SUCCEEDED(aResultCode) ||
+      NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) {
+    return aResultCode;
+  }
+
+  switch (aResultCode) {
+    case NS_ERROR_FILE_NO_DEVICE_SPACE:
+      return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+    case NS_ERROR_STORAGE_CONSTRAINT:
+      return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
+    default:
+#ifdef DEBUG
+      nsPrintfCString message("Converting non-IndexedDB error code (0x%X) to "
+                              "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR",
+                              aResultCode);
+      NS_WARNING(message.get());
+#else
+      ;
+#endif
+  }
+
+  IDB_REPORT_INTERNAL_ERR();
+  return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+}
+
+void
+GetDatabaseFilename(const nsAString& aName,
+                    nsAutoString& aDatabaseFilename)
+{
+  MOZ_ASSERT(aDatabaseFilename.IsEmpty());
+
+  aDatabaseFilename.AppendInt(HashName(aName));
+
+  nsCString escapedName;
+  if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
+    MOZ_CRASH("Can't escape database name!");
+  }
+
+  const char* forwardIter = escapedName.BeginReading();
+  const char* backwardIter = escapedName.EndReading() - 1;
+
+  nsAutoCString substring;
+  while (forwardIter <= backwardIter && substring.Length() < 21) {
+    if (substring.Length() % 2) {
+      substring.Append(*backwardIter--);
+    } else {
+      substring.Append(*forwardIter++);
+    }
+  }
+
+  aDatabaseFilename.AppendASCII(substring.get(), substring.Length());
+}
+
+nsresult
+CreateFileTables(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "CreateFileTables",
+                 js::ProfileEntry::Category::STORAGE);
+
+  // Table `file`
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE file ("
+      "id INTEGER PRIMARY KEY, "
+      "refcount INTEGER NOT NULL"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TRIGGER object_data_insert_trigger "
+    "AFTER INSERT ON object_data "
+    "FOR EACH ROW "
+    "WHEN NEW.file_ids IS NOT NULL "
+    "BEGIN "
+      "SELECT update_refcount(NULL, NEW.file_ids); "
+    "END;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TRIGGER object_data_update_trigger "
+    "AFTER UPDATE OF file_ids ON object_data "
+    "FOR EACH ROW "
+    "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
+    "BEGIN "
+      "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
+    "END;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TRIGGER object_data_delete_trigger "
+    "AFTER DELETE ON object_data "
+    "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
+    "BEGIN "
+      "SELECT update_refcount(OLD.file_ids, NULL); "
+    "END;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TRIGGER file_update_trigger "
+    "AFTER UPDATE ON file "
+    "FOR EACH ROW WHEN NEW.refcount = 0 "
+    "BEGIN "
+      "DELETE FROM file WHERE id = OLD.id; "
+    "END;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+CreateTables(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "CreateTables",
+                 js::ProfileEntry::Category::STORAGE);
+
+  // Table `database`
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE database ("
+      "name TEXT NOT NULL, "
+      "version INTEGER NOT NULL DEFAULT 0"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Table `object_store`
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE object_store ("
+      "id INTEGER PRIMARY KEY, "
+      "auto_increment INTEGER NOT NULL DEFAULT 0, "
+      "name TEXT NOT NULL, "
+      "key_path TEXT, "
+      "UNIQUE (name)"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Table `object_data`
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE object_data ("
+      "id INTEGER PRIMARY KEY, "
+      "object_store_id INTEGER NOT NULL, "
+      "key_value BLOB DEFAULT NULL, "
+      "file_ids TEXT, "
+      "data BLOB NOT NULL, "
+      "UNIQUE (object_store_id, key_value), "
+      "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Table `index`
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE object_store_index ("
+      "id INTEGER PRIMARY KEY, "
+      "object_store_id INTEGER NOT NULL, "
+      "name TEXT NOT NULL, "
+      "key_path TEXT NOT NULL, "
+      "unique_index INTEGER NOT NULL, "
+      "multientry INTEGER NOT NULL, "
+      "UNIQUE (object_store_id, name), "
+      "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Table `index_data`
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE index_data ("
+      "index_id INTEGER NOT NULL, "
+      "value BLOB NOT NULL, "
+      "object_data_key BLOB NOT NULL, "
+      "object_data_id INTEGER NOT NULL, "
+      "PRIMARY KEY (index_id, value, object_data_key), "
+      "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+        "CASCADE, "
+      "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Need this to make cascading deletes from object_data and object_store fast.
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE INDEX index_data_object_data_id_index "
+    "ON index_data (object_data_id);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Table `unique_index_data`
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE unique_index_data ("
+      "index_id INTEGER NOT NULL, "
+      "value BLOB NOT NULL, "
+      "object_data_key BLOB NOT NULL, "
+      "object_data_id INTEGER NOT NULL, "
+      "PRIMARY KEY (index_id, value, object_data_key), "
+      "UNIQUE (index_id, value), "
+      "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+        "CASCADE "
+      "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Need this to make cascading deletes from object_data and object_store fast.
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE INDEX unique_index_data_object_data_id_index "
+    "ON unique_index_data (object_data_id);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = CreateFileTables(aConnection);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->SetSchemaVersion(kSQLiteSchemaVersion);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom4To5",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsresult rv;
+
+  // All we changed is the type of the version column, so lets try to
+  // convert that to an integer, and if we fail, set it to 0.
+  nsCOMPtr<mozIStorageStatement> stmt;
+  rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT name, version, dataVersion "
+    "FROM database"
+  ), getter_AddRefs(stmt));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsString name;
+  int32_t intVersion;
+  int64_t dataVersion;
+
+  {
+    mozStorageStatementScoper scoper(stmt);
+
+    bool hasResults;
+    rv = stmt->ExecuteStep(&hasResults);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    if (NS_WARN_IF(!hasResults)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsString version;
+    rv = stmt->GetString(1, version);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    intVersion = version.ToInteger(&rv);
+    if (NS_FAILED(rv)) {
+      intVersion = 0;
+    }
+
+    rv = stmt->GetString(0, name);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = stmt->GetInt64(2, &dataVersion);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE database"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE database ("
+      "name TEXT NOT NULL, "
+      "version INTEGER NOT NULL DEFAULT 0, "
+      "dataVersion INTEGER NOT NULL"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
+    "INSERT INTO database (name, version, dataVersion) "
+    "VALUES (:name, :version, :dataVersion)"
+  ), getter_AddRefs(stmt));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  {
+    mozStorageStatementScoper scoper(stmt);
+
+    rv = stmt->BindStringParameter(0, name);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = stmt->BindInt32Parameter(1, intVersion);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = stmt->BindInt64Parameter(2, dataVersion);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = stmt->Execute();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  rv = aConnection->SetSchemaVersion(5);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom5To6",
+                 js::ProfileEntry::Category::STORAGE);
+
+  // First, drop all the indexes we're no longer going to use.
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP INDEX key_index;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP INDEX ai_key_index;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP INDEX value_index;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP INDEX ai_value_index;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Now, reorder the columns of object_data to put the blob data last. We do
+  // this by copying into a temporary table, dropping the original, then copying
+  // back into a newly created table.
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "id INTEGER PRIMARY KEY, "
+      "object_store_id, "
+      "key_value, "
+      "data "
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT id, object_store_id, key_value, data "
+      "FROM object_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE object_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE object_data ("
+      "id INTEGER PRIMARY KEY, "
+      "object_store_id INTEGER NOT NULL, "
+      "key_value DEFAULT NULL, "
+      "data BLOB NOT NULL, "
+      "UNIQUE (object_store_id, key_value), "
+      "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO object_data "
+      "SELECT id, object_store_id, key_value, data "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // We need to add a unique constraint to our ai_object_data table. Copy all
+  // the data out of it using a temporary table as before.
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "id INTEGER PRIMARY KEY, "
+      "object_store_id, "
+      "data "
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT id, object_store_id, data "
+      "FROM ai_object_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE ai_object_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE ai_object_data ("
+      "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+      "object_store_id INTEGER NOT NULL, "
+      "data BLOB NOT NULL, "
+      "UNIQUE (object_store_id, id), "
+      "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO ai_object_data "
+      "SELECT id, object_store_id, data "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Fix up the index_data table. We're reordering the columns as well as
+  // changing the primary key from being a simple id to being a composite.
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "index_id, "
+      "value, "
+      "object_data_key, "
+      "object_data_id "
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT index_id, value, object_data_key, object_data_id "
+      "FROM index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE index_data ("
+      "index_id INTEGER NOT NULL, "
+      "value NOT NULL, "
+      "object_data_key NOT NULL, "
+      "object_data_id INTEGER NOT NULL, "
+      "PRIMARY KEY (index_id, value, object_data_key), "
+      "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+        "CASCADE, "
+      "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT OR IGNORE INTO index_data "
+      "SELECT index_id, value, object_data_key, object_data_id "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE INDEX index_data_object_data_id_index "
+    "ON index_data (object_data_id);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Fix up the unique_index_data table. We're reordering the columns as well as
+  // changing the primary key from being a simple id to being a composite.
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "index_id, "
+      "value, "
+      "object_data_key, "
+      "object_data_id "
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT index_id, value, object_data_key, object_data_id "
+      "FROM unique_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE unique_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE unique_index_data ("
+      "index_id INTEGER NOT NULL, "
+      "value NOT NULL, "
+      "object_data_key NOT NULL, "
+      "object_data_id INTEGER NOT NULL, "
+      "PRIMARY KEY (index_id, value, object_data_key), "
+      "UNIQUE (index_id, value), "
+      "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+        "CASCADE "
+      "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO unique_index_data "
+      "SELECT index_id, value, object_data_key, object_data_id "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE INDEX unique_index_data_object_data_id_index "
+    "ON unique_index_data (object_data_id);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Fix up the ai_index_data table. We're reordering the columns as well as
+  // changing the primary key from being a simple id to being a composite.
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "index_id, "
+      "value, "
+      "ai_object_data_id "
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT index_id, value, ai_object_data_id "
+      "FROM ai_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE ai_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE ai_index_data ("
+      "index_id INTEGER NOT NULL, "
+      "value NOT NULL, "
+      "ai_object_data_id INTEGER NOT NULL, "
+      "PRIMARY KEY (index_id, value, ai_object_data_id), "
+      "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+        "CASCADE, "
+      "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT OR IGNORE INTO ai_index_data "
+      "SELECT index_id, value, ai_object_data_id "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE INDEX ai_index_data_ai_object_data_id_index "
+    "ON ai_index_data (ai_object_data_id);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Fix up the ai_unique_index_data table. We're reordering the columns as well
+  // as changing the primary key from being a simple id to being a composite.
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "index_id, "
+      "value, "
+      "ai_object_data_id "
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT index_id, value, ai_object_data_id "
+      "FROM ai_unique_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE ai_unique_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE ai_unique_index_data ("
+      "index_id INTEGER NOT NULL, "
+      "value NOT NULL, "
+      "ai_object_data_id INTEGER NOT NULL, "
+      "UNIQUE (index_id, value), "
+      "PRIMARY KEY (index_id, value, ai_object_data_id), "
+      "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+        "CASCADE, "
+      "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO ai_unique_index_data "
+      "SELECT index_id, value, ai_object_data_id "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
+    "ON ai_unique_index_data (ai_object_data_id);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->SetSchemaVersion(6);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom6To7",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "id, "
+      "name, "
+      "key_path, "
+      "auto_increment"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT id, name, key_path, auto_increment "
+      "FROM object_store;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE object_store;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE object_store ("
+      "id INTEGER PRIMARY KEY, "
+      "auto_increment INTEGER NOT NULL DEFAULT 0, "
+      "name TEXT NOT NULL, "
+      "key_path TEXT, "
+      "UNIQUE (name)"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO object_store "
+      "SELECT id, auto_increment, name, nullif(key_path, '') "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->SetSchemaVersion(7);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom7To8",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "id, "
+      "object_store_id, "
+      "name, "
+      "key_path, "
+      "unique_index, "
+      "object_store_autoincrement"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT id, object_store_id, name, key_path, "
+      "unique_index, object_store_autoincrement "
+      "FROM object_store_index;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE object_store_index;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE object_store_index ("
+      "id INTEGER, "
+      "object_store_id INTEGER NOT NULL, "
+      "name TEXT NOT NULL, "
+      "key_path TEXT NOT NULL, "
+      "unique_index INTEGER NOT NULL, "
+      "multientry INTEGER NOT NULL, "
+      "object_store_autoincrement INTERGER NOT NULL, "
+      "PRIMARY KEY (id), "
+      "UNIQUE (object_store_id, name), "
+      "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO object_store_index "
+      "SELECT id, object_store_id, name, key_path, "
+      "unique_index, 0, object_store_autoincrement "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->SetSchemaVersion(8);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+class CompressDataBlobsFunction MOZ_FINAL
+  : public mozIStorageFunction
+{
+public:
+  NS_DECL_ISUPPORTS
+
+private:
+  ~CompressDataBlobsFunction()
+  { }
+
+  NS_IMETHOD
+  OnFunctionCall(mozIStorageValueArray* aArguments,
+                 nsIVariant** aResult) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(aArguments);
+    MOZ_ASSERT(aResult);
+
+    PROFILER_LABEL("IndexedDB",
+                   "CompressDataBlobsFunction::OnFunctionCall",
+                   js::ProfileEntry::Category::STORAGE);
+
+    uint32_t argc;
+    nsresult rv = aArguments->GetNumEntries(&argc);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (argc != 1) {
+      NS_WARNING("Don't call me with the wrong number of arguments!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    int32_t type;
+    rv = aArguments->GetTypeOfIndex(0, &type);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
+      NS_WARNING("Don't call me with the wrong type of arguments!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    const uint8_t* uncompressed;
+    uint32_t uncompressedLength;
+    rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
+    nsAutoArrayPtr<char> compressed(new (fallible) char[compressedLength]);
+    if (NS_WARN_IF(!compressed)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    snappy::RawCompress(reinterpret_cast<const char*>(uncompressed),
+                        uncompressedLength, compressed.get(),
+                        &compressedLength);
+
+    std::pair<const void *, int> data(static_cast<void*>(compressed.get()),
+                                      int(compressedLength));
+
+    // XXX This copies the buffer again... There doesn't appear to be any way to
+    //     preallocate space and write directly to a BlobVariant at the moment.
+    nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
+
+    result.forget(aResult);
+    return NS_OK;
+  }
+};
+
+nsresult
+UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom8To9_0",
+                 js::ProfileEntry::Category::STORAGE);
+
+  // We no longer use the dataVersion column.
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "UPDATE database SET dataVersion = 0;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
+
+  NS_NAMED_LITERAL_CSTRING(compressorName, "compress");
+
+  rv = aConnection->CreateFunction(compressorName, 1, compressor);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Turn off foreign key constraints before we do anything here.
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "UPDATE object_data SET data = compress(data);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "UPDATE ai_object_data SET data = compress(data);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->RemoveFunction(compressorName);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom9_0To10_0",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = CreateFileTables(aConnection);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom10_0To11_0",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "id, "
+      "object_store_id, "
+      "name, "
+      "key_path, "
+      "unique_index, "
+      "multientry"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT id, object_store_id, name, key_path, "
+      "unique_index, multientry "
+      "FROM object_store_index;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE object_store_index;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE object_store_index ("
+      "id INTEGER PRIMARY KEY, "
+      "object_store_id INTEGER NOT NULL, "
+      "name TEXT NOT NULL, "
+      "key_path TEXT NOT NULL, "
+      "unique_index INTEGER NOT NULL, "
+      "multientry INTEGER NOT NULL, "
+      "UNIQUE (object_store_id, name), "
+      "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO object_store_index "
+      "SELECT id, object_store_id, name, key_path, "
+      "unique_index, multientry "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TRIGGER object_data_insert_trigger;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
+      "SELECT object_store_id, id, data, file_ids "
+      "FROM ai_object_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TRIGGER object_data_insert_trigger "
+    "AFTER INSERT ON object_data "
+    "FOR EACH ROW "
+    "WHEN NEW.file_ids IS NOT NULL "
+    "BEGIN "
+      "SELECT update_refcount(NULL, NEW.file_ids); "
+    "END;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) "
+      "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id "
+      "FROM ai_index_data "
+      "INNER JOIN object_store_index ON "
+        "object_store_index.id = ai_index_data.index_id "
+      "INNER JOIN object_data ON "
+        "object_data.object_store_id = object_store_index.object_store_id AND "
+        "object_data.key_value = ai_index_data.ai_object_data_id;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) "
+      "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id "
+      "FROM ai_unique_index_data "
+      "INNER JOIN object_store_index ON "
+        "object_store_index.id = ai_unique_index_data.index_id "
+      "INNER JOIN object_data ON "
+        "object_data.object_store_id = object_store_index.object_store_id AND "
+        "object_data.key_value = ai_unique_index_data.ai_object_data_id;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "UPDATE object_store "
+      "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
+      "WHERE auto_increment;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE ai_unique_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE ai_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE ai_object_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+class EncodeKeysFunction MOZ_FINAL
+  : public mozIStorageFunction
+{
+public:
+  NS_DECL_ISUPPORTS
+
+private:
+  ~EncodeKeysFunction()
+  { }
+
+  NS_IMETHOD
+  OnFunctionCall(mozIStorageValueArray* aArguments,
+                 nsIVariant** aResult) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(aArguments);
+    MOZ_ASSERT(aResult);
+
+    PROFILER_LABEL("IndexedDB",
+                   "EncodeKeysFunction::OnFunctionCall",
+                   js::ProfileEntry::Category::STORAGE);
+
+    uint32_t argc;
+    nsresult rv = aArguments->GetNumEntries(&argc);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (argc != 1) {
+      NS_WARNING("Don't call me with the wrong number of arguments!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    int32_t type;
+    rv = aArguments->GetTypeOfIndex(0, &type);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    Key key;
+    if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) {
+      int64_t intKey;
+      aArguments->GetInt64(0, &intKey);
+      key.SetFromInteger(intKey);
+    } else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) {
+      nsString stringKey;
+      aArguments->GetString(0, stringKey);
+      key.SetFromString(stringKey);
+    } else {
+      NS_WARNING("Don't call me with the wrong type of arguments!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    const nsCString& buffer = key.GetBuffer();
+
+    std::pair<const void *, int> data(static_cast<const void*>(buffer.get()),
+                                      int(buffer.Length()));
+
+    nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
+
+    result.forget(aResult);
+    return NS_OK;
+  }
+};
+
+nsresult
+UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom11_0To12_0",
+                 js::ProfileEntry::Category::STORAGE);
+
+  NS_NAMED_LITERAL_CSTRING(encoderName, "encode");
+
+  nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
+
+  nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "id INTEGER PRIMARY KEY, "
+      "object_store_id, "
+      "key_value, "
+      "data, "
+      "file_ids "
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT id, object_store_id, encode(key_value), data, file_ids "
+      "FROM object_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE object_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE object_data ("
+      "id INTEGER PRIMARY KEY, "
+      "object_store_id INTEGER NOT NULL, "
+      "key_value BLOB DEFAULT NULL, "
+      "file_ids TEXT, "
+      "data BLOB NOT NULL, "
+      "UNIQUE (object_store_id, key_value), "
+      "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO object_data "
+      "SELECT id, object_store_id, key_value, file_ids, data "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TRIGGER object_data_insert_trigger "
+    "AFTER INSERT ON object_data "
+    "FOR EACH ROW "
+    "WHEN NEW.file_ids IS NOT NULL "
+    "BEGIN "
+      "SELECT update_refcount(NULL, NEW.file_ids); "
+    "END;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TRIGGER object_data_update_trigger "
+    "AFTER UPDATE OF file_ids ON object_data "
+    "FOR EACH ROW "
+    "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
+    "BEGIN "
+      "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
+    "END;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TRIGGER object_data_delete_trigger "
+    "AFTER DELETE ON object_data "
+    "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
+    "BEGIN "
+      "SELECT update_refcount(OLD.file_ids, NULL); "
+    "END;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "index_id, "
+      "value, "
+      "object_data_key, "
+      "object_data_id "
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
+      "FROM index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE index_data ("
+      "index_id INTEGER NOT NULL, "
+      "value BLOB NOT NULL, "
+      "object_data_key BLOB NOT NULL, "
+      "object_data_id INTEGER NOT NULL, "
+      "PRIMARY KEY (index_id, value, object_data_key), "
+      "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+        "CASCADE, "
+      "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO index_data "
+      "SELECT index_id, value, object_data_key, object_data_id "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE INDEX index_data_object_data_id_index "
+    "ON index_data (object_data_id);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TEMPORARY TABLE temp_upgrade ("
+      "index_id, "
+      "value, "
+      "object_data_key, "
+      "object_data_id "
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO temp_upgrade "
+      "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
+      "FROM unique_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE unique_index_data;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE TABLE unique_index_data ("
+      "index_id INTEGER NOT NULL, "
+      "value BLOB NOT NULL, "
+      "object_data_key BLOB NOT NULL, "
+      "object_data_id INTEGER NOT NULL, "
+      "PRIMARY KEY (index_id, value, object_data_key), "
+      "UNIQUE (index_id, value), "
+      "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
+        "CASCADE "
+      "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
+        "CASCADE"
+    ");"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "INSERT INTO unique_index_data "
+      "SELECT index_id, value, object_data_key, object_data_id "
+      "FROM temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "DROP TABLE temp_upgrade;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "CREATE INDEX unique_index_data_object_data_id_index "
+    "ON unique_index_data (object_data_id);"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->RemoveFunction(encoderName);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection,
+                            bool* aVacuumNeeded)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom12_0To13_0",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsresult rv;
+
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+  int32_t defaultPageSize;
+  rv = aConnection->GetDefaultPageSize(&defaultPageSize);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Enable auto_vacuum mode and update the page size to the platform default.
+  nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = ");
+  upgradeQuery.AppendInt(defaultPageSize);
+
+  rv = aConnection->ExecuteSimpleSQL(upgradeQuery);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  *aVacuumNeeded = true;
+#endif
+
+  rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  // The only change between 13 and 14 was a different structured
+  // clone format, but it's backwards-compatible.
+  nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection)
+{
+  // The only change between 14 and 15 was a different structured
+  // clone format, but it's backwards-compatible.
+  nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection)
+{
+  // The only change between 15 and 16 was a different structured
+  // clone format, but it's backwards-compatible.
+  nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection)
+{
+  // The only change between 16 and 17 was a different structured
+  // clone format, but it's backwards-compatible.
+  nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+GetDatabaseFileURL(nsIFile* aDatabaseFile,
+                   PersistenceType aPersistenceType,
+                   const nsACString& aGroup,
+                   const nsACString& aOrigin,
+                   nsIFileURL** aResult)
+{
+  MOZ_ASSERT(aDatabaseFile);
+  MOZ_ASSERT(aResult);
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
+  MOZ_ASSERT(fileUrl);
+
+  nsAutoCString type;
+  PersistenceTypeToText(aPersistenceType, type);
+
+  rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type +
+                         NS_LITERAL_CSTRING("&group=") + aGroup +
+                         NS_LITERAL_CSTRING("&origin=") + aOrigin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  fileUrl.forget(aResult);
+  return NS_OK;
+}
+
+nsresult
+SetDefaultPragmas(mozIStorageConnection* aConnection)
+{
+  MOZ_ASSERT(aConnection);
+
+  static const char query[] =
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+    // Switch the journaling mode to TRUNCATE to avoid changing the directory
+    // structure at the conclusion of every transaction for devices with slower
+    // file systems.
+    "PRAGMA journal_mode = TRUNCATE; "
+#endif
+    // We use foreign keys in lots of places.
+    "PRAGMA foreign_keys = ON; "
+    // The "INSERT OR REPLACE" statement doesn't fire the update trigger,
+    // instead it fires only the insert trigger. This confuses the update
+    // refcount function. This behavior changes with enabled recursive triggers,
+    // so the statement fires the delete trigger first and then the insert
+    // trigger.
+    "PRAGMA recursive_triggers = ON;";
+
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+CreateDatabaseConnection(nsIFile* aDBFile,
+                         nsIFile* aFMDirectory,
+                         const nsAString& aName,
+                         PersistenceType aPersistenceType,
+                         const nsACString& aGroup,
+                         const nsACString& aOrigin,
+                         mozIStorageConnection** aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDBFile);
+  MOZ_ASSERT(aFMDirectory);
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "CreateDatabaseConnection",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsresult rv;
+  bool exists;
+
+  if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
+    rv = aDBFile->Exists(&exists);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!exists) {
+      NS_WARNING("Refusing to create database because disk space is low!");
+      return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+    }
+  }
+
+  nsCOMPtr<nsIFileURL> dbFileUrl;
+  rv = GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin,
+                          getter_AddRefs(dbFileUrl));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<mozIStorageService> ss =
+    do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<mozIStorageConnection> connection;
+  rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
+  if (rv == NS_ERROR_FILE_CORRUPTED) {
+    // If we're just opening the database during origin initialization, then
+    // we don't want to erase any files. The failure here will fail origin
+    // initialization too.
+    if (aName.IsVoid()) {
+      return rv;
+    }
+
+    // Nuke the database file.
+    rv = aDBFile->Remove(false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = aFMDirectory->Exists(&exists);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (exists) {
+      bool isDirectory;
+      rv = aFMDirectory->IsDirectory(&isDirectory);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      if (NS_WARN_IF(!isDirectory)) {
+        IDB_REPORT_INTERNAL_ERR();
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
+
+      rv = aFMDirectory->Remove(true);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+
+    rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = SetDefaultPragmas(connection);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem"));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Check to make sure that the database schema is correct.
+  int32_t schemaVersion;
+  rv = connection->GetSchemaVersion(&schemaVersion);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Unknown schema will fail origin initialization too.
+  if (!schemaVersion && aName.IsVoid()) {
+    IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  if (schemaVersion > kSQLiteSchemaVersion) {
+    IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  bool vacuumNeeded = false;
+
+  if (schemaVersion != kSQLiteSchemaVersion) {
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+    if (!schemaVersion) {
+      // Have to do this before opening a transaction.
+      rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+        // Turn on auto_vacuum mode to reclaim disk space on mobile devices.
+        "PRAGMA auto_vacuum = FULL; "
+      ));
+      if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
+        // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
+        // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
+        rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+      }
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+#endif
+
+    mozStorageTransaction transaction(connection, false,
+                                  mozIStorageConnection::TRANSACTION_IMMEDIATE);
+
+    if (!schemaVersion) {
+      // Brand new file, initialize our tables.
+      rv = CreateTables(connection);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)));
+      MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
+
+      nsCOMPtr<mozIStorageStatement> stmt;
+      nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
+        "INSERT INTO database (name) "
+        "VALUES (:name)"
+      ), getter_AddRefs(stmt));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      rv = stmt->Execute();
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    } else  {
+      // This logic needs to change next time we change the schema!
+      static_assert(kSQLiteSchemaVersion == int32_t((17 << 4) + 0),
+                    "Upgrade function needed due to schema version increase.");
+
+      while (schemaVersion != kSQLiteSchemaVersion) {
+        if (schemaVersion == 4) {
+          rv = UpgradeSchemaFrom4To5(connection);
+        } else if (schemaVersion == 5) {
+          rv = UpgradeSchemaFrom5To6(connection);
+        } else if (schemaVersion == 6) {
+          rv = UpgradeSchemaFrom6To7(connection);
+        } else if (schemaVersion == 7) {
+          rv = UpgradeSchemaFrom7To8(connection);
+        } else if (schemaVersion == 8) {
+          rv = UpgradeSchemaFrom8To9_0(connection);
+          vacuumNeeded = true;
+        } else if (schemaVersion == MakeSchemaVersion(9, 0)) {
+          rv = UpgradeSchemaFrom9_0To10_0(connection);
+        } else if (schemaVersion == MakeSchemaVersion(10, 0)) {
+          rv = UpgradeSchemaFrom10_0To11_0(connection);
+        } else if (schemaVersion == MakeSchemaVersion(11, 0)) {
+          rv = UpgradeSchemaFrom11_0To12_0(connection);
+        } else if (schemaVersion == MakeSchemaVersion(12, 0)) {
+          rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded);
+        } else if (schemaVersion == MakeSchemaVersion(13, 0)) {
+          rv = UpgradeSchemaFrom13_0To14_0(connection);
+        } else if (schemaVersion == MakeSchemaVersion(14, 0)) {
+          rv = UpgradeSchemaFrom14_0To15_0(connection);
+        } else if (schemaVersion == MakeSchemaVersion(15, 0)) {
+          rv = UpgradeSchemaFrom15_0To16_0(connection);
+        } else if (schemaVersion == MakeSchemaVersion(16, 0)) {
+          rv = UpgradeSchemaFrom16_0To17_0(connection);
+        } else {
+          IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
+                      "available!");
+          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+        }
+
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        rv = connection->GetSchemaVersion(&schemaVersion);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+      }
+
+      MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
+    }
+
+    rv = transaction.Commit();
+    if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
+      // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
+      // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
+      rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  if (vacuumNeeded) {
+    rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  connection.forget(aConnection);
+  return NS_OK;
+}
+
+already_AddRefed<nsIFile>
+GetFileForPath(const nsAString& aPath)
+{
+  MOZ_ASSERT(!aPath.IsEmpty());
+
+  nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+  if (NS_WARN_IF(!file)) {
+    return nullptr;
+  }
+
+  if (NS_WARN_IF(NS_FAILED(file->InitWithPath(aPath)))) {
+    return nullptr;
+  }
+
+  return file.forget();
+}
+
+nsresult
+GetDatabaseConnection(const nsAString& aDatabaseFilePath,
+                      PersistenceType aPersistenceType,
+                      const nsACString& aGroup,
+                      const nsACString& aOrigin,
+                      mozIStorageConnection** aConnection)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!IsOnBackgroundThread());
+  MOZ_ASSERT(!aDatabaseFilePath.IsEmpty());
+  MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")));
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "GetDatabaseConnection",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsCOMPtr<nsIFile> dbFile = GetFileForPath(aDatabaseFilePath);
+  if (NS_WARN_IF(!dbFile)) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  bool exists;
+  nsresult rv = dbFile->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (NS_WARN_IF(!exists)) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  nsCOMPtr<nsIFileURL> dbFileUrl;
+  rv = GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin,
+                          getter_AddRefs(dbFileUrl));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<mozIStorageService> ss =
+    do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<mozIStorageConnection> connection;
+  rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = SetDefaultPragmas(connection);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  connection.forget(aConnection);
+  return NS_OK;
+}
+
+/*******************************************************************************
+ * Actor class declarations
+ ******************************************************************************/
+
+class DatabaseOperationBase
+  : public nsRunnable
+  , public mozIStorageProgressHandler
+{
+  // Uniquely tracks each operation for logging purposes. Only modified on the
+  // PBackground thread.
+  static uint64_t sNextSerialNumber;
+
+protected:
+  class AutoSetProgressHandler;
+
+  typedef nsDataHashtable<nsUint64HashKey, bool> UniqueIndexTable;
+
+  nsCOMPtr<nsIEventTarget> mOwningThread;
+  const uint64_t mSerialNumber;
+  nsresult mResultCode;
+
+private:
+  Atomic<bool> mOperationMayProceed;
+  bool mActorDestroyed;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  void
+  AssertIsOnOwningThread() const
+  {
+    AssertIsOnBackgroundThread();
+
+#ifdef DEBUG
+    MOZ_ASSERT(mOwningThread);
+    bool current;
+    MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)));
+    MOZ_ASSERT(current);
+#endif
+  }
+
+  void
+  NoteActorDestroyed()
+  {
+    AssertIsOnOwningThread();
+
+    mActorDestroyed = true;
+    mOperationMayProceed = false;
+  }
+
+  bool
+  IsActorDestroyed() const
+  {
+    AssertIsOnOwningThread();
+
+    return mActorDestroyed;
+  }
+
+  // May be called on any thread.
+  bool
+  OperationMayProceed() const
+  {
+    return mOperationMayProceed;
+  }
+
+  uint64_t
+  SerialNumber() const
+  {
+    return mSerialNumber;
+  }
+
+  nsresult
+  ResultCode() const
+  {
+    return mResultCode;
+  }
+
+  void
+  SetFailureCode(nsresult aErrorCode)
+  {
+    MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
+    MOZ_ASSERT(NS_FAILED(aErrorCode));
+
+    mResultCode = aErrorCode;
+  }
+
+protected:
+  DatabaseOperationBase()
+    : mOwningThread(NS_GetCurrentThread())
+    , mSerialNumber(++sNextSerialNumber)
+    , mResultCode(NS_OK)
+    , mOperationMayProceed(true)
+    , mActorDestroyed(false)
+  {
+    AssertIsOnOwningThread();
+  }
+
+  virtual
+  ~DatabaseOperationBase()
+  {
+    MOZ_ASSERT(mActorDestroyed);
+  }
+
+  static void
+  GetBindingClauseForKeyRange(const SerializedKeyRange& aKeyRange,
+                              const nsACString& aKeyColumnName,
+                              nsAutoCString& aBindingClause);
+
+  static uint64_t
+  ReinterpretDoubleAsUInt64(double aDouble);
+
+  static nsresult
+  GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
+                                          uint32_t aDataIndex,
+                                          uint32_t aFileIdsIndex,
+                                          FileManager* aFileManager,
+                                          StructuredCloneReadInfo* aInfo);
+
+  static nsresult
+  BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
+                          mozIStorageStatement* aStatement);
+
+  static void
+  AppendConditionClause(const nsACString& aColumnName,
+                        const nsACString& aArgName,
+                        bool aLessThan,
+                        bool aEquals,
+                        nsAutoCString& aResult);
+
+  static nsresult
+  UpdateIndexes(TransactionBase* aTransaction,
+                const UniqueIndexTable& aUniqueIndexTable,
+                const Key& aObjectStoreKey,
+                bool aOverwrite,
+                int64_t aObjectDataId,
+                const nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
+
+private:
+  // Not to be overridden by subclasses.
+  NS_DECL_MOZISTORAGEPROGRESSHANDLER
+};
+
+class MOZ_STACK_CLASS DatabaseOperationBase::AutoSetProgressHandler MOZ_FINAL
+{
+  mozIStorageConnection* mConnection;
+  DebugOnly<DatabaseOperationBase*> mDEBUGDatabaseOp;
+
+public:
+  AutoSetProgressHandler()
+    : mConnection(nullptr)
+    , mDEBUGDatabaseOp(nullptr)
+  { }
+
+  ~AutoSetProgressHandler();
+
+  nsresult
+  Register(DatabaseOperationBase* aDatabaseOp,
+           const nsCOMPtr<mozIStorageConnection>& aConnection);
+};
+
+class TransactionDatabaseOperationBase
+  : public DatabaseOperationBase
+{
+  nsRefPtr<TransactionBase> mTransaction;
+  const bool mTransactionIsAborted;
+
+public:
+  void
+  AssertIsOnTransactionThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  void
+  DispatchToTransactionThreadPool();
+
+  // May be overridden by subclasses if they need to perform work on the
+  // background thread before being dispatched. Returning false will kill the
+  // child actors and prevent dispatch.
+  virtual bool
+  Init(TransactionBase* aTransaction);
+
+  // 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:
+  explicit TransactionDatabaseOperationBase(TransactionBase* aTransaction);
+
+  virtual
+  ~TransactionDatabaseOperationBase();
+
+  // Must be overridden in subclasses. Called on the target thread to allow the
+  // subclass to perform necessary database or 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
+  DoDatabaseWork(TransactionBase* aTransaction) = 0;
+
+  // Must be overridden in subclasses. Called on the background thread to allow
+  // the subclass to serialize its results and send them to the child actor. A
+  // failed return value will trigger a SendFailureResult callback.
+  virtual nsresult
+  SendSuccessResult() = 0;
+
+  // Must be overridden in subclasses. Called on the background thread to allow
+  // the subclass to send its failure code. Returning false will cause the
+  // transaction to be aborted with aResultCode. Returning true will not cause
+  // the transaction to be aborted.
+  virtual bool
+  SendFailureResult(nsresult aResultCode) = 0;
+
+private:
+  void
+  RunOnTransactionThread();
+
+  void
+  RunOnOwningThread();
+
+  // Not to be overridden by subclasses.
+  NS_DECL_NSIRUNNABLE
+};
+
+class Factory MOZ_FINAL
+  : public PBackgroundIDBFactoryParent
+{
+  // Counts the number of "live" Factory instances that have not yet had
+  // ActorDestroy called.
+  static uint64_t sFactoryInstanceCount;
+
+  const OptionalWindowId mOptionalWindowId;
+
+  DebugOnly<bool> mActorDestroyed;
+
+public:
+  static already_AddRefed<Factory>
+  Create(const OptionalWindowId& aOptionalWindowId);
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Factory)
+
+private:
+  // Only constructed in Create().
+  explicit Factory(const OptionalWindowId& aOptionalWindowId);
+
+  // Reference counted.
+  ~Factory();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvDeleteMe() MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBFactoryRequestParent*
+  AllocPBackgroundIDBFactoryRequestParent(const FactoryRequestParams& aParams)
+                                          MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBackgroundIDBFactoryRequestConstructor(
+                                     PBackgroundIDBFactoryRequestParent* aActor,
+                                     const FactoryRequestParams& aParams)
+                                     MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBFactoryRequestParent(
+                                     PBackgroundIDBFactoryRequestParent* aActor)
+                                     MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBDatabaseParent*
+  AllocPBackgroundIDBDatabaseParent(
+                                   const DatabaseSpec& aSpec,
+                                   PBackgroundIDBFactoryRequestParent* aRequest)
+                                   MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBDatabaseParent(PBackgroundIDBDatabaseParent* aActor)
+                                      MOZ_OVERRIDE;
+};
+
+class Database MOZ_FINAL
+  : public PBackgroundIDBDatabaseParent
+{
+  friend class VersionChangeTransaction;
+
+  nsRefPtr<Factory> mFactory;
+  nsRefPtr<FullDatabaseMetadata> mMetadata;
+  nsRefPtr<FileManager> mFileManager;
+  nsRefPtr<DatabaseOfflineStorage> mOfflineStorage;
+  nsTHashtable<nsPtrHashKey<TransactionBase>> mTransactions;
+  const PrincipalInfo mPrincipalInfo;
+  const nsCString mGroup;
+  const nsCString mOrigin;
+  const nsCString mId;
+  const nsString mFilePath;
+  Atomic<bool> mInvalidatedOnAnyThread;
+  const PersistenceType mPersistenceType;
+  const bool mChromeWriteAccessAllowed;
+  bool mClosed;
+  bool mInvalidated;
+  bool mActorWasAlive;
+  bool mActorDestroyed;
+  bool mMetadataCleanedUp;
+
+public:
+  // Created by OpenDatabaseOp.
+  Database(Factory* aFactory,
+           const PrincipalInfo& aPrincipalInfo,
+           const nsACString& aGroup,
+           const nsACString& aOrigin,
+           FullDatabaseMetadata* aMetadata,
+           FileManager* aFileManager,
+           already_AddRefed<DatabaseOfflineStorage> aOfflineStorage,
+           bool aChromeWriteAccessAllowed);
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Database)
+
+  void
+  Invalidate();
+
+  const PrincipalInfo&
+  GetPrincipalInfo() const
+  {
+    return mPrincipalInfo;
+  }
+
+  const nsCString&
+  Group() const
+  {
+    return mGroup;
+  }
+
+  const nsCString&
+  Origin() const
+  {
+    return mOrigin;
+  }
+
+  const nsCString&
+  Id() const
+  {
+    return mId;
+  }
+
+  PersistenceType
+  Type() const
+  {
+    return mPersistenceType;
+  }
+
+  const nsString&
+  FilePath() const
+  {
+    return mFilePath;
+  }
+
+  FileManager*
+  GetFileManager() const
+  {
+    return mFileManager;
+  }
+
+  FullDatabaseMetadata*
+  Metadata() const
+  {
+    MOZ_ASSERT(mMetadata);
+    return mMetadata;
+  }
+
+  PBackgroundParent*
+  GetBackgroundParent() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(!IsActorDestroyed());
+
+    return Manager()->Manager();
+  }
+
+  bool
+  RegisterTransaction(TransactionBase* aTransaction);
+
+  void
+  UnregisterTransaction(TransactionBase* aTransaction);
+
+  void
+  SetActorAlive();
+
+  bool
+  IsActorAlive() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mActorWasAlive && !mActorDestroyed;
+  }
+
+  bool
+  IsActorDestroyed() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mActorWasAlive && mActorDestroyed;
+  }
+
+  bool
+  IsClosed() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mClosed;
+  }
+
+  bool
+  IsInvalidated() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mInvalidated;
+  }
+
+private:
+  // Reference counted.
+  ~Database()
+  {
+    MOZ_ASSERT(mClosed);
+    MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
+  }
+
+  bool
+  CloseInternal();
+
+  void
+  CleanupMetadata();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBDatabaseFileParent*
+  AllocPBackgroundIDBDatabaseFileParent(PBlobParent* aBlobParent)
+                                        MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBDatabaseFileParent(
+                                       PBackgroundIDBDatabaseFileParent* aActor)
+                                       MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBTransactionParent*
+  AllocPBackgroundIDBTransactionParent(
+                                    const nsTArray<nsString>& aObjectStoreNames,
+                                    const Mode& aMode)
+                                    MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBackgroundIDBTransactionConstructor(
+                                    PBackgroundIDBTransactionParent* aActor,
+                                    const nsTArray<nsString>& aObjectStoreNames,
+                                    const Mode& aMode)
+                                    MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBTransactionParent(
+                                        PBackgroundIDBTransactionParent* aActor)
+                                        MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBVersionChangeTransactionParent*
+  AllocPBackgroundIDBVersionChangeTransactionParent(
+                                              const uint64_t& aCurrentVersion,
+                                              const uint64_t& aRequestedVersion,
+                                              const int64_t& aNextObjectStoreId,
+                                              const int64_t& aNextIndexId)
+                                              MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBVersionChangeTransactionParent(
+                           PBackgroundIDBVersionChangeTransactionParent* aActor)
+                           MOZ_OVERRIDE;
+
+  virtual bool
+  RecvDeleteMe() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvBlocked() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvClose() MOZ_OVERRIDE;
+};
+
+class DatabaseFile MOZ_FINAL
+  : public PBackgroundIDBDatabaseFileParent
+{
+  friend class Database;
+
+  nsRefPtr<DOMFileImpl> mBlobImpl;
+  nsRefPtr<FileInfo> mFileInfo;
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile);
+
+  FileInfo*
+  GetFileInfo() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mFileInfo;
+  }
+
+  already_AddRefed<nsIInputStream>
+  GetInputStream() const
+  {
+    AssertIsOnBackgroundThread();
+
+    nsCOMPtr<nsIInputStream> inputStream;
+    if (mBlobImpl) {
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+        mBlobImpl->GetInternalStream(getter_AddRefs(inputStream))));
+    }
+
+    return inputStream.forget();
+  }
+
+  void
+  ClearInputStream()
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mBlobImpl);
+
+    mBlobImpl = nullptr;
+  }
+
+private:
+  // Called when sending to the child.
+  explicit DatabaseFile(FileInfo* aFileInfo)
+    : mFileInfo(aFileInfo)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aFileInfo);
+  }
+
+  // Called when receiving from the child.
+  DatabaseFile(DOMFileImpl* aBlobImpl, FileInfo* aFileInfo)
+    : mBlobImpl(aBlobImpl)
+    , mFileInfo(aFileInfo)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aBlobImpl);
+    MOZ_ASSERT(aFileInfo);
+  }
+
+  ~DatabaseFile()
+  { }
+
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
+  {
+    AssertIsOnBackgroundThread();
+
+    mBlobImpl = nullptr;
+    mFileInfo = nullptr;
+  }
+};
+
+class TransactionBase
+{
+  friend class Cursor;
+
+  class CommitOp;
+  class UpdateRefcountFunction;
+
+public:
+  class AutoSavepoint;
+  class CachedStatement;
+
+protected:
+  typedef IDBTransaction::Mode Mode;
+
+private:
+  nsRefPtr<Database> mDatabase;
+  nsCOMPtr<mozIStorageConnection> mConnection;
+  nsRefPtr<UpdateRefcountFunction> mUpdateFileRefcountFunction;
+  nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
+    mCachedStatements;
+  nsTArray<nsRefPtr<FullObjectStoreMetadata>>
+    mModifiedAutoIncrementObjectStoreMetadataArray;
+  const uint64_t mTransactionId;
+  const nsCString mDatabaseId;
+  uint64_t mActiveRequestCount;
+  Atomic<bool> mInvalidatedOnAnyThread;
+  Mode mMode;
+  bool mHasBeenActive;
+  bool mActorDestroyed;
+  bool mInvalidated;
+
+protected:
+  nsresult mResultCode;
+  bool mCommitOrAbortReceived;
+  bool mCommittedOrAborted;
+  bool mForceAborted;
+
+private:
+  DebugOnly<PRThread*> mTransactionThread;
+  DebugOnly<uint32_t> mSavepointCount;
+
+public:
+  void
+  AssertIsOnTransactionThread() const
+  {
+    MOZ_ASSERT(mTransactionThread);
+    MOZ_ASSERT(PR_GetCurrentThread() == mTransactionThread);
+  }
+
+  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, NS_FAILED(mResultCode));
+
+    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::indexedDB::TransactionBase)
+
+  nsresult
+  GetCachedStatement(const nsACString& aQuery,
+                     CachedStatement* aCachedStatement);
+
+  template<int N>
+  nsresult
+  GetCachedStatement(const char (&aQuery)[N],
+                     CachedStatement* aCachedStatement)
+  {
+    AssertIsOnTransactionThread();
+    MOZ_ASSERT(aCachedStatement);
+
+    return GetCachedStatement(NS_LITERAL_CSTRING(aQuery), aCachedStatement);
+  }
+
+  nsresult
+  EnsureConnection();
+
+  void
+  Abort(nsresult aResultCode, bool aForce);
+
+  mozIStorageConnection*
+  Connection() const
+  {
+    AssertIsOnTransactionThread();
+    MOZ_ASSERT(mConnection);
+
+    return mConnection;
+  }
+
+  uint64_t
+  TransactionId() const
+  {
+    return mTransactionId;
+  }
+
+  const nsCString&
+  DatabaseId() const
+  {
+    return mDatabaseId;
+  }
+
+  Mode
+  GetMode() const
+  {
+    return mMode;
+  }
+
+  Database*
+  GetDatabase() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mDatabase);
+
+    return mDatabase;
+  }
+
+  bool
+  IsAborted() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return NS_FAILED(mResultCode);
+  }
+
+  already_AddRefed<FullObjectStoreMetadata>
+  GetMetadataForObjectStoreId(int64_t aObjectStoreId) const;
+
+  already_AddRefed<FullIndexMetadata>
+  GetMetadataForIndexId(FullObjectStoreMetadata* const aObjectStoreMetadata,
+                        int64_t aIndexId) const;
+
+  PBackgroundParent*
+  GetBackgroundParent() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(!IsActorDestroyed());
+
+    return GetDatabase()->GetBackgroundParent();
+  }
+
+  void
+  NoteModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
+
+  void
+  ForgetModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
+
+  nsresult
+  StartSavepoint();
+
+  nsresult
+  ReleaseSavepoint();
+
+  nsresult
+  RollbackSavepoint();
+
+  void
+  NoteActiveRequest();
+
+  void
+  NoteFinishedRequest();
+
+  void
+  Invalidate();
+
+protected:
+  TransactionBase(Database* aDatabase,
+                  Mode aMode);
+
+  virtual
+  ~TransactionBase();
+
+  void
+  NoteActorDestroyed()
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(!mActorDestroyed);
+
+    mActorDestroyed = true;
+  }
+
+#ifdef DEBUG
+  // Only called by VersionChangeTransaction.
+  void
+  FakeActorDestroyed()
+  {
+    mActorDestroyed = true;
+  }
+#endif
+
+  bool
+  RecvCommit();
+
+  bool
+  RecvAbort(nsresult aResultCode);
+
+  void
+  MaybeCommitOrAbort()
+  {
+    AssertIsOnBackgroundThread();
+
+    // If we've already committed or aborted then there's nothing else to do.
+    if (mCommittedOrAborted) {
+      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 commit or abort message then there could be
+    // additional requests coming so we should wait unless we're being forced to
+    // abort.
+    if (!mCommitOrAbortReceived && !mForceAborted) {
+      return;
+    }
+
+    CommitOrAbort();
+  }
+
+  PBackgroundIDBRequestParent*
+  AllocRequest(const RequestParams& aParams, bool aTrustParams);
+
+  bool
+  StartRequest(PBackgroundIDBRequestParent* aActor);
+
+  bool
+  DeallocRequest(PBackgroundIDBRequestParent* aActor);
+
+  PBackgroundIDBCursorParent*
+  AllocCursor(const OpenCursorParams& aParams, bool aTrustParams);
+
+  bool
+  StartCursor(PBackgroundIDBCursorParent* aActor,
+              const OpenCursorParams& aParams);
+
+  bool
+  DeallocCursor(PBackgroundIDBCursorParent* aActor);
+
+  virtual void
+  UpdateMetadata(nsresult aResult)
+  { }
+
+  virtual bool
+  SendCompleteNotification(nsresult aResult) = 0;
+
+private:
+  // Only called by CommitOp.
+  void
+  ReleaseTransactionThreadObjects();
+
+  // Only called by CommitOp.
+  void
+  ReleaseBackgroundThreadObjects();
+
+  bool
+  VerifyRequestParams(const RequestParams& aParams) const;
+
+  bool
+  VerifyRequestParams(const OpenCursorParams& aParams) const;
+
+  bool
+  VerifyRequestParams(const CursorRequestParams& aParams) const;
+
+  bool
+  VerifyRequestParams(const SerializedKeyRange& aKeyRange) const;
+
+  bool
+  VerifyRequestParams(const ObjectStoreAddPutParams& aParams) const;
+
+  bool
+  VerifyRequestParams(const OptionalKeyRange& aKeyRange) const;
+
+  void
+  CommitOrAbort();
+};
+
+class TransactionBase::CommitOp MOZ_FINAL
+  : public DatabaseOperationBase
+  , public TransactionThreadPool::FinishCallback
+{
+  friend class TransactionBase;
+
+  nsRefPtr<TransactionBase> mTransaction;
+  nsresult mResultCode;
+
+private:
+  CommitOp(TransactionBase* aTransaction,
+           nsresult aResultCode)
+    : mTransaction(aTransaction)
+    , mResultCode(aResultCode)
+  {
+    MOZ_ASSERT(aTransaction);
+  }
+
+  ~CommitOp()
+  { }
+
+  // Writes new autoIncrement counts to database.
+  nsresult
+  WriteAutoIncrementCounts();
+
+  // Updates counts after a database activity has finished.
+  void
+  CommitOrRollbackAutoIncrementCounts();
+
+  NS_DECL_NSIRUNNABLE
+
+  virtual void
+  TransactionFinishedBeforeUnblock() MOZ_OVERRIDE;
+
+  virtual void
+  TransactionFinishedAfterUnblock() MOZ_OVERRIDE;
+
+public:
+  void
+  AssertIsOnTransactionThread() const
+  {
+    MOZ_ASSERT(mTransaction);
+    mTransaction->AssertIsOnTransactionThread();
+  }
+
+  NS_DECL_ISUPPORTS_INHERITED
+};
+
+class TransactionBase::UpdateRefcountFunction MOZ_FINAL
+  : public mozIStorageFunction
+{
+  class FileInfoEntry
+  {
+    friend class UpdateRefcountFunction;
+
+    nsRefPtr<FileInfo> mFileInfo;
+    int32_t mDelta;
+    int32_t mSavepointDelta;
+
+  public:
+    explicit FileInfoEntry(FileInfo* aFileInfo)
+      : mFileInfo(aFileInfo)
+      , mDelta(0)
+      , mSavepointDelta(0)
+    { }
+  };
+
+  enum UpdateType
+  {
+    eIncrement,
+    eDecrement
+  };
+
+  class DatabaseUpdateFunction
+  {
+    nsCOMPtr<mozIStorageConnection> mConnection;
+    nsCOMPtr<mozIStorageStatement> mUpdateStatement;
+    nsCOMPtr<mozIStorageStatement> mSelectStatement;
+    nsCOMPtr<mozIStorageStatement> mInsertStatement;
+
+    UpdateRefcountFunction* mFunction;
+
+    nsresult mErrorCode;
+
+  public:
+    DatabaseUpdateFunction(mozIStorageConnection* aConnection,
+                           UpdateRefcountFunction* aFunction)
+      : mConnection(aConnection)
+      , mFunction(aFunction)
+      , mErrorCode(NS_OK)
+    { }
+
+    bool
+    Update(int64_t aId, int32_t aDelta);
+
+    nsresult
+    ErrorCode() const
+    {
+      return mErrorCode;
+    }
+
+  private:
+    nsresult
+    UpdateInternal(int64_t aId, int32_t aDelta);
+  };
+
+  FileManager* mFileManager;
+  nsClassHashtable<nsUint64HashKey, FileInfoEntry> mFileInfoEntries;
+  nsDataHashtable<nsUint64HashKey, FileInfoEntry*> mSavepointEntriesIndex;
+
+  nsTArray<int64_t> mJournalsToCreateBeforeCommit;
+  nsTArray<int64_t> mJournalsToRemoveAfterCommit;
+  nsTArray<int64_t> mJournalsToRemoveAfterAbort;
+
+  bool mInSavepoint;
+
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_MOZISTORAGEFUNCTION
+
+  explicit UpdateRefcountFunction(FileManager* aFileManager)
+    : mFileManager(aFileManager)
+    , mInSavepoint(false)
+  { }
+
+  void
+  ClearFileInfoEntries()
+  {
+    mFileInfoEntries.Clear();
+  }
+
+  nsresult
+  WillCommit(mozIStorageConnection* aConnection);
+
+  void
+  DidCommit();
+
+  void
+  DidAbort();
+
+  void
+  StartSavepoint();
+
+  void
+  ReleaseSavepoint();
+
+  void
+  RollbackSavepoint();
+
+private:
+  ~UpdateRefcountFunction()
+  { }
+
+  nsresult
+  ProcessValue(mozIStorageValueArray* aValues,
+               int32_t aIndex,
+               UpdateType aUpdateType);
+
+  nsresult
+  CreateJournals();
+
+  nsresult
+  RemoveJournals(const nsTArray<int64_t>& aJournals);
+
+  static PLDHashOperator
+  DatabaseUpdateCallback(const uint64_t& aKey,
+                         FileInfoEntry* aValue,
+                         void* aUserArg);
+
+  static PLDHashOperator
+  FileInfoUpdateCallback(const uint64_t& aKey,
+                         FileInfoEntry* aValue,
+                         void* aUserArg);
+};
+
+class MOZ_STACK_CLASS TransactionBase::AutoSavepoint MOZ_FINAL
+{
+  TransactionBase* mTransaction;
+
+public:
+  AutoSavepoint()
+    : mTransaction(nullptr)
+  { }
+
+  ~AutoSavepoint();
+
+  nsresult
+  Start(TransactionBase* aTransaction);
+
+  nsresult
+  Commit();
+};
+
+class TransactionBase::CachedStatement MOZ_FINAL
+{
+  friend class TransactionBase;
+
+  nsCOMPtr<mozIStorageStatement> mStatement;
+  Maybe<mozStorageStatementScoper> mScoper;
+
+public:
+  CachedStatement()
+  { }
+
+  ~CachedStatement()
+  { }
+
+  operator mozIStorageStatement*()
+  {
+    return mStatement;
+  }
+
+  mozIStorageStatement*
+  operator->()
+  {
+    MOZ_ASSERT(mStatement);
+    return mStatement;
+  }
+
+  void
+  Reset()
+  {
+    MOZ_ASSERT_IF(mStatement, mScoper);
+
+    if (mStatement) {
+      mScoper.reset();
+      mScoper.emplace(mStatement);
+    }
+  }
+
+private:
+  // Only called by TransactionBase.
+  void
+  Assign(already_AddRefed<mozIStorageStatement> aStatement)
+  {
+    mScoper.reset();
+
+    mStatement = aStatement;
+
+    if (mStatement) {
+      mScoper.emplace(mStatement);
+    }
+  }
+
+  // No funny business allowed.
+  CachedStatement(const CachedStatement&) MOZ_DELETE;
+  CachedStatement& operator=(const CachedStatement&) MOZ_DELETE;
+};
+
+class NormalTransaction MOZ_FINAL
+  : public TransactionBase
+  , public PBackgroundIDBTransactionParent
+{
+  friend class Database;
+
+  nsTArray<nsRefPtr<FullObjectStoreMetadata>> mObjectStores;
+
+private:
+  // This constructor is only called by Database.
+  NormalTransaction(Database* aDatabase,
+                    nsTArray<nsRefPtr<FullObjectStoreMetadata>>& aObjectStores,
+                    TransactionBase::Mode aMode);
+
+  // Reference counted.
+  ~NormalTransaction()
+  { }
+
+  bool
+  IsSameProcessActor();
+
+  // Only called by TransactionBase.
+  virtual bool
+  SendCompleteNotification(nsresult aResult) MOZ_OVERRIDE;
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvDeleteMe() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvCommit() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvAbort(const nsresult& aResultCode) MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBRequestParent*
+  AllocPBackgroundIDBRequestParent(const RequestParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor,
+                                       const RequestParams& aParams)
+                                       MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor)
+                                     MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBCursorParent*
+  AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor,
+                                      const OpenCursorParams& aParams)
+                                      MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor)
+                                    MOZ_OVERRIDE;
+};
+
+class VersionChangeTransaction MOZ_FINAL
+  : public TransactionBase
+  , public PBackgroundIDBVersionChangeTransactionParent
+{
+  friend class OpenDatabaseOp;
+
+  nsRefPtr<OpenDatabaseOp> mOpenDatabaseOp;
+  nsRefPtr<FullDatabaseMetadata> mOldMetadata;
+
+  bool mActorWasAlive;
+
+private:
+  // Only called by OpenDatabaseOp.
+  explicit VersionChangeTransaction(OpenDatabaseOp* aOpenDatabaseOp);
+
+  // Reference counted.
+  ~VersionChangeTransaction();
+
+  bool
+  IsSameProcessActor();
+
+  // Only called by OpenDatabaseOp.
+  bool
+  CopyDatabaseMetadata();
+
+  void
+  SetActorAlive();
+
+  // Only called by TransactionBase.
+  virtual void
+  UpdateMetadata(nsresult aResult) MOZ_OVERRIDE;
+
+  // Only called by TransactionBase.
+  virtual bool
+  SendCompleteNotification(nsresult aResult) MOZ_OVERRIDE;
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvDeleteMe() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvCommit() MOZ_OVERRIDE;
+
+  virtual bool
+  RecvAbort(const nsresult& aResultCode) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvCreateObjectStore(const ObjectStoreMetadata& aMetadata) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvDeleteObjectStore(const int64_t& aObjectStoreId) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvCreateIndex(const int64_t& aObjectStoreId,
+                  const IndexMetadata& aMetadata) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvDeleteIndex(const int64_t& aObjectStoreId,
+                  const int64_t& aIndexId) MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBRequestParent*
+  AllocPBackgroundIDBRequestParent(const RequestParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor,
+                                       const RequestParams& aParams)
+                                       MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor)
+                                     MOZ_OVERRIDE;
+
+  virtual PBackgroundIDBCursorParent*
+  AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor,
+                                      const OpenCursorParams& aParams)
+                                      MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor)
+                                    MOZ_OVERRIDE;
+};
+
+class FactoryOp
+  : public DatabaseOperationBase
+  , public PBackgroundIDBFactoryRequestParent
+{
+public:
+  struct MaybeBlockedDatabaseInfo;
+
+protected:
+  enum State
+  {
+    // Just created on the PBackground thread, dispatched to the main thread.
+    // Next step is State_OpenPending.
+    State_Initial,
+
+    // Waiting for open allowed on the main thread. The next step is either
+    // State_SendingResults if permission is denied,
+    // State_PermissionChallenge if the permission is unknown, or
+    // State_DatabaseWorkOpen if permission is granted.
+    State_OpenPending,
+
+    // Sending a permission challenge message to the child on the PBackground
+    // thread. Next step is State_PermissionRetryReady.
+    State_PermissionChallenge,
+
+    // Retrying permission check after a challenge on the main thread. Next step
+    // is either State_SendingResults if permission is denied or
+    // State_DatabaseWorkOpen if permission is granted.
+    State_PermissionRetry,
+
+    // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
+    // either State_BeginVersionChange if the requested version doesn't match
+    // the existing database version or State_SendingResults if the versions
+    // match.
+    State_DatabaseWorkOpen,
+
+    // Starting a version change transaction or deleting a database on the
+    // PBackground thread. We need to notify other databases that a version
+    // change is about to happen, and maybe tell the request that a version
+    // change has been blocked. If databases are notified then the next step is
+    // State_WaitingForOtherDatabasesToClose. Otherwise the next step is
+    // State_DispatchToWorkThread.
+    State_BeginVersionChange,
+
+    // Waiting for other databases to close on the PBackground thread. This
+    // state may persist until all databases are closed. The next state is
+    // State_WaitingForTransactionsToComplete.
+    State_WaitingForOtherDatabasesToClose,
+
+    // Waiting for all transactions that could interfere with this operation to
+    // complete on the PBackground thread. Next state is
+    // State_DatabaseWorkVersionChange.
+    State_WaitingForTransactionsToComplete,
+
+    // Waiting to do/doing work on the "work thread". This involves waiting for
+    // the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a
+    // different implementation) to do its work. Eventually the state will
+    // transition to State_SendingResults.
+    State_DatabaseWorkVersionChange,