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 id27562
push usercbook@mozilla.com
push dateMon, 29 Sep 2014 13:45:04 +0000
treeherdermozilla-central@f57209ddc739 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs994190
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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: