Backed out 5 changesets (bug 866846, bug 1131776, bug 1131766, bug 1144806, bug 1112702) on suspicion of causing Windows debug devtools-4 storage crashes
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 29 Mar 2015 12:55:11 -0700
changeset 266747 beb016173f6f1ac953c3c4998f232e2f7d5adccc
parent 266746 1d5bdeca48eb73d0ffc4d36a6662e219d66357a0
child 266748 dfe60814eda70948c866d548498cc6dbe5f1de5c
child 266751 54a080c7d385b34ee320e4035275b44a0702ff1d
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs866846, 1131776, 1131766, 1144806, 1112702
milestone39.0a1
backs out142d9ae5826c9bddf3ce111895c6a4ff883b30c8
0b4de21b759fa23cb87931efe83458103b682787
1b4ead852ae03055b1f984b8fd42c33d5b91d3fa
cbd862dd036f9190dda9ee8ed535dd5f726c5360
178412a2fe8be1c71354385094901374b0ace38c
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
Backed out 5 changesets (bug 866846, bug 1131776, bug 1131766, bug 1144806, bug 1112702) on suspicion of causing Windows debug devtools-4 storage crashes CLOSED TREE Backed out changeset 142d9ae5826c (bug 1131776) Backed out changeset 0b4de21b759f (bug 1112702) Backed out changeset 1b4ead852ae0 (bug 1131766) Backed out changeset cbd862dd036f (bug 866846) Backed out changeset 178412a2fe8b (bug 1144806)
dom/asmjscache/AsmJSCache.cpp
dom/datastore/DataStoreDB.cpp
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBDatabase.h
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IDBTransaction.h
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/indexedDB/Key.cpp
dom/indexedDB/Key.h
dom/indexedDB/ProfilerHelpers.h
dom/indexedDB/TransactionThreadPool.cpp
dom/indexedDB/TransactionThreadPool.h
dom/indexedDB/moz.build
dom/indexedDB/test/mochitest.ini
dom/indexedDB/test/test_readwriteflush_disabled.html
dom/indexedDB/test/unit/schema18upgrade_profile.zip
dom/indexedDB/test/unit/test_clear.js
dom/indexedDB/test/unit/test_readwriteflush_disabled.js
dom/indexedDB/test/unit/test_schema18upgrade.js
dom/indexedDB/test/unit/test_temporary_storage.js
dom/indexedDB/test/unit/test_transaction_abort.js
dom/indexedDB/test/unit/xpcshell-parent-process.ini
dom/quota/FileStreams.cpp
dom/quota/QuotaObject.cpp
dom/quota/QuotaObject.h
dom/webidl/IDBDatabase.webidl
dom/webidl/IDBTransaction.webidl
storage/src/TelemetryVFS.cpp
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/IndexedDB/key_valid.html
--- a/dom/asmjscache/AsmJSCache.cpp
+++ b/dom/asmjscache/AsmJSCache.cpp
@@ -828,25 +828,23 @@ MainProcessRunnable::OpenCacheFileForWri
 
   if (mEnforcingQuota) {
     // Create the QuotaObject before all file IO and keep it alive until caching
     // completes to get maximum assertion coverage in QuotaManager against
     // concurrent removal, etc.
     mQuotaObject = qm->GetQuotaObject(mPersistence, mGroup, mOrigin, file);
     NS_ENSURE_STATE(mQuotaObject);
 
-    if (!mQuotaObject->MaybeUpdateSize(mWriteParams.mSize,
-                                       /* aTruncate */ false)) {
+    if (!mQuotaObject->MaybeAllocateMoreSpace(0, mWriteParams.mSize)) {
       // If the request fails, it might be because mOrigin is using too much
-      // space (MaybeUpdateSize will not evict our own origin since it is
+      // space (MaybeAllocateMoreSpace will not evict our own origin since it is
       // active). Try to make some space by evicting LRU entries until there is
       // enough space.
       EvictEntries(mDirectory, mGroup, mOrigin, mWriteParams.mSize, mMetadata);
-      if (!mQuotaObject->MaybeUpdateSize(mWriteParams.mSize,
-                                         /* aTruncate */ false)) {
+      if (!mQuotaObject->MaybeAllocateMoreSpace(0, mWriteParams.mSize)) {
         mResult = JS::AsmJSCache_QuotaExceeded;
         return NS_ERROR_FAILURE;
       }
     }
   }
 
   int32_t openFlags = PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE;
   rv = file->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc);
--- a/dom/datastore/DataStoreDB.cpp
+++ b/dom/datastore/DataStoreDB.cpp
@@ -310,23 +310,19 @@ DataStoreDB::DatabaseOpened()
   nsRefPtr<VersionChangeListener> listener =
     new VersionChangeListener(mDatabase);
   rv = mDatabase->EventTarget::AddEventListener(
     NS_LITERAL_STRING("versionchange"), listener, false);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  StringOrStringSequence objectStores;
-  objectStores.RawSetAsStringSequence().AppendElements(mObjectStores);
-
-  nsRefPtr<IDBTransaction> txn;
-  error = mDatabase->Transaction(objectStores,
-                                 mTransactionMode,
-                                 getter_AddRefs(txn));
+  nsRefPtr<IDBTransaction> txn = mDatabase->Transaction(mObjectStores,
+                                                        mTransactionMode,
+                                                        error);
   if (NS_WARN_IF(error.Failed())) {
     return error.ErrorCode();
   }
 
   mTransaction = txn.forget();
   return NS_OK;
 }
 
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -46,17 +46,16 @@
 #include "mozilla/dom/quota/OriginOrPatternString.h"
 #include "mozilla/dom/quota/QuotaManager.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 "mozilla/storage/Variant.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsClassHashtable.h"
 #include "nsCOMPtr.h"
 #include "nsDataHashtable.h"
 #include "nsEscape.h"
 #include "nsHashKeys.h"
 #include "nsNetUtil.h"
 #include "nsIAppsService.h"
@@ -70,80 +69,68 @@
 #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 "nsIThread.h"
-#include "nsITimer.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
 
-#define IDB_DEBUG_LOG(_args)                                                   \
-  PR_LOG(IndexedDatabaseManager::GetLoggingModule(),                           \
-         PR_LOG_DEBUG,                                                         \
-         _args )
-
-#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
-#define IDB_MOBILE
-#endif
-
-namespace mozilla {
-namespace dom {
-namespace indexedDB {
-
-using namespace mozilla::dom::quota;
-using namespace mozilla::ipc;
-
 namespace {
 
-class ConnectionPool;
 class Cursor;
 class Database;
 struct DatabaseActorInfo;
 class DatabaseLoggingInfo;
 class DatabaseFile;
 class DatabaseOfflineStorage;
 class Factory;
 class OpenDatabaseOp;
 class TransactionBase;
-class TransactionDatabaseOperationBase;
 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 = 18;
+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.
@@ -152,78 +139,23 @@ static_assert(kMajorSchemaVersion <= 0xF
 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;
 
-// Changing the value here will override the page size of new databases only.
-// A journal mode change and VACUUM are needed to change existing databases, so
-// the best way to do that is to use the schema version upgrade mechanism.
-const uint32_t kSQLitePageSizeOverride =
-#ifdef IDB_MOBILE
-  2048;
-#else
-  4096;
-#endif
-
-static_assert(kSQLitePageSizeOverride == /* mozStorage default */ 0 ||
-              (kSQLitePageSizeOverride % 2 == 0 &&
-               kSQLitePageSizeOverride >= 512  &&
-               kSQLitePageSizeOverride <= 65536),
-              "Must be 0 (disabled) or a power of 2 between 512 and 65536!");
-
-// Set to -1 to use SQLite's default, 0 to disable, or some positive number to
-// enforce a custom limit.
-const int32_t kMaxWALPages = 5000; // 20MB on desktop, 10MB on mobile.
-
-// Set to some multiple of the page size to grow the database in larger chunks.
-const uint32_t kSQLiteGrowthIncrement = kSQLitePageSizeOverride * 2;
-
-static_assert(kSQLiteGrowthIncrement >= 0 &&
-              kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 &&
-              kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
-              "Must be 0 (disabled) or a positive multiple of the page size!");
-
-// The maximum number of threads that can be used for database activity at a
-// single time.
-const uint32_t kMaxConnectionThreadCount = 20;
-
-static_assert(kMaxConnectionThreadCount, "Must have at least one thread!");
-
-// The maximum number of threads to keep when idle. Threads that become idle in
-// excess of this number will be shut down immediately.
-const uint32_t kMaxIdleConnectionThreadCount = 2;
-
-static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount,
-              "Idle thread limit must be less than total thread limit!");
-
-// The length of time that database connections will be held open after all
-// transactions have completed.
-const uint32_t kConnectionIdleCheckpointsMS = 2 * 1000; // 2 seconds
-
-// The length of time that database connections will be held open after all
-// transactions and checkpointing  have completed.
-const uint32_t kConnectionIdleCloseMS = 10 * 1000; // 10 seconds
-
-// The length of time that idle threads will stay alive before being shut down.
-const uint32_t kConnectionThreadIdleMS = 30 * 1000; // 30 seconds
-
 const char kSavepointClause[] = "SAVEPOINT sp;";
 
 const uint32_t kFileCopyBufferSize = 32768;
 
 const char kJournalDirectoryName[] = "journals";
 
 const char kFileManagerDirectoryNameSuffix[] = ".files";
-const char kSQLiteJournalSuffix[] = ".sqlite-journal";
-const char kSQLiteSHMSuffix[] = ".sqlite-shm";
-const char kSQLiteWALSuffix[] = ".sqlite-wal";
 
 const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled";
 
 #define IDB_PREFIX "indexedDB"
 
 #ifdef MOZ_CHILD_PERMISSIONS
 const char kPermissionString[] = IDB_PREFIX;
 #endif // MOZ_CHILD_PERMISSIONS
@@ -239,44 +171,18 @@ enum AppId {
   kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID
 };
 
 #ifdef DEBUG
 
 const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL;
 const uint32_t kDEBUGThreadSleepMS = 0;
 
-const int32_t kDEBUGTransactionThreadPriority =
-  nsISupportsPriority::PRIORITY_NORMAL;
-const uint32_t kDEBUGTransactionThreadSleepMS = 0;
-
 #endif
 
-struct MozFreeDeleter
-{
-  void
-  operator()(void* aPtr) const
-  {
-    moz_free(aPtr);
-  }
-};
-
-template <typename T>
-using UniqueMozFreePtr = UniquePtr<T, MozFreeDeleter>;
-
-template <size_t N>
-MOZ_CONSTEXPR size_t
-LiteralStringLength(const char (&aArr)[N])
-{
-  static_assert(N, "Zero-length string literal?!");
-
-  // Don't include the null terminator.
-  return N - 1;
-}
-
 /*******************************************************************************
  * Metadata classes
  ******************************************************************************/
 
 struct FullIndexMetadata
 {
   IndexMetadata mCommonMetadata;
 
@@ -321,19 +227,16 @@ public:
   {
     // 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);
 
-  bool
-  HasLiveIndexes() const;
-
 private:
   ~FullObjectStoreMetadata()
   { }
 };
 
 typedef nsRefPtrHashtable<nsUint64HashKey, FullObjectStoreMetadata>
   ObjectStoreTable;
 
@@ -440,73 +343,16 @@ private:
       closure->mMetadata = aValue;
       return PL_DHASH_STOP;
     }
 
     return PL_DHASH_NEXT;
   }
 };
 
-struct IndexDataValue final
-{
-  int64_t mIndexId;
-  Key mKey;
-  bool mUnique;
-
-  IndexDataValue()
-    : mIndexId(0)
-    , mUnique(false)
-  {
-    MOZ_COUNT_CTOR(IndexDataValue);
-  }
-
-  explicit
-  IndexDataValue(const IndexDataValue& aOther)
-    : mIndexId(aOther.mIndexId)
-    , mKey(aOther.mKey)
-    , mUnique(aOther.mUnique)
-  {
-    MOZ_ASSERT(!aOther.mKey.IsUnset());
-
-    MOZ_COUNT_CTOR(IndexDataValue);
-  }
-
-  IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aKey)
-    : mIndexId(aIndexId)
-    , mKey(aKey)
-    , mUnique(aUnique)
-  {
-    MOZ_ASSERT(!aKey.IsUnset());
-
-    MOZ_COUNT_CTOR(IndexDataValue);
-  }
-
-  ~IndexDataValue()
-  {
-    MOZ_COUNT_DTOR(IndexDataValue);
-  }
-
-  bool
-  operator==(const IndexDataValue& aOther) const
-  {
-    return mIndexId == aOther.mIndexId &&
-           mKey == aOther.mKey;
-  }
-
-  bool
-  operator<(const IndexDataValue& aOther) const
-  {
-    if (mIndexId == aOther.mIndexId) {
-      return mKey < aOther.mKey;
-    }
-
-    return mIndexId < aOther.mIndexId;
-  }
-};
-
 /*******************************************************************************
  * SQLite functions
  ******************************************************************************/
 
 int32_t
 MakeSchemaVersion(uint32_t aMajorSchemaVersion,
                   uint32_t aMinorSchemaVersion)
 {
@@ -590,345 +436,16 @@ GetDatabaseFilename(const nsAString& aNa
     } else {
       substring.Append(*forwardIter++);
     }
   }
 
   aDatabaseFilename.AppendASCII(substring.get(), substring.Length());
 }
 
-uint32_t
-CompressedByteCountForNumber(uint64_t aNumber)
-{
-  MOZ_ASSERT(aNumber);
-
-  // All bytes have 7 bits available.
-  uint32_t count = 1;
-  while ((aNumber >>= 7)) {
-    count++;
-  }
-
-  return count;
-}
-
-uint32_t
-CompressedByteCountForIndexId(int64_t aIndexId)
-{
-  MOZ_ASSERT(aIndexId);
-  MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
-              "Overflow!");
-
-  return CompressedByteCountForNumber(uint64_t(aIndexId * 2));
-}
-
-void
-WriteCompressedNumber(uint64_t aNumber, uint8_t** aIterator)
-{
-  MOZ_ASSERT(aIterator);
-  MOZ_ASSERT(*aIterator);
-
-  uint8_t*& buffer = *aIterator;
-
-#ifdef DEBUG
-  const uint8_t* bufferStart = buffer;
-  const uint64_t originalNumber = aNumber;
-#endif
-
-  while (true) {
-    uint64_t shiftedNumber = aNumber >> 7;
-    if (shiftedNumber) {
-      *buffer++ = uint8_t(0x80 | (aNumber & 0x7f));
-      aNumber = shiftedNumber;
-    } else {
-      *buffer++ = uint8_t(aNumber);
-      break;
-    }
-  }
-
-  MOZ_ASSERT(buffer > bufferStart);
-  MOZ_ASSERT(uint32_t(buffer - bufferStart) ==
-               CompressedByteCountForNumber(originalNumber));
-}
-
-uint64_t
-ReadCompressedNumber(const uint8_t** aIterator, const uint8_t* aEnd)
-{
-  MOZ_ASSERT(aIterator);
-  MOZ_ASSERT(*aIterator);
-  MOZ_ASSERT(aEnd);
-  MOZ_ASSERT(*aIterator < aEnd);
-
-  const uint8_t*& buffer = *aIterator;
-
-  uint8_t shiftCounter = 0;
-  uint64_t result = 0;
-
-  while (true) {
-    MOZ_ASSERT(shiftCounter <= 56, "Shifted too many bits!");
-
-    result += (uint64_t(*buffer & 0x7f) << shiftCounter);
-    shiftCounter += 7;
-
-    if (!(*buffer++ & 0x80)) {
-      break;
-    }
-
-    if (NS_WARN_IF(buffer == aEnd)) {
-      MOZ_ASSERT(false);
-      break;
-    }
-  }
-
-  return result;
-}
-
-void
-WriteCompressedIndexId(int64_t aIndexId, bool aUnique, uint8_t** aIterator)
-{
-  MOZ_ASSERT(aIndexId);
-  MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
-             "Overflow!");
-  MOZ_ASSERT(aIterator);
-  MOZ_ASSERT(*aIterator);
-
-  const uint64_t indexId = (uint64_t(aIndexId * 2) | (aUnique ? 1 : 0));
-  WriteCompressedNumber(indexId, aIterator);
-}
-
-void
-ReadCompressedIndexId(const uint8_t** aIterator,
-                      const uint8_t* aEnd,
-                      int64_t* aIndexId,
-                      bool* aUnique)
-{
-  MOZ_ASSERT(aIterator);
-  MOZ_ASSERT(*aIterator);
-  MOZ_ASSERT(aIndexId);
-  MOZ_ASSERT(aUnique);
-
-  uint64_t indexId = ReadCompressedNumber(aIterator, aEnd);
-
-  if (indexId % 2) {
-    *aUnique = true;
-    indexId--;
-  } else {
-    *aUnique = false;
-  }
-
-  MOZ_ASSERT(UINT64_MAX / 2 >= uint64_t(indexId), "Bad index id!");
-
-  *aIndexId = int64_t(indexId / 2);
-}
-
-// static
-nsresult
-MakeCompressedIndexDataValues(
-                          const FallibleTArray<IndexDataValue>& aIndexValues,
-                          UniqueMozFreePtr<uint8_t>& aCompressedIndexDataValues,
-                          uint32_t* aCompressedIndexDataValuesLength)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(!aCompressedIndexDataValues);
-  MOZ_ASSERT(aCompressedIndexDataValuesLength);
-
-  PROFILER_LABEL("IndexedDB",
-                 "MakeCompressedIndexDataValues",
-                 js::ProfileEntry::Category::STORAGE);
-
-  const uint32_t arrayLength = aIndexValues.Length();
-  if (!arrayLength) {
-    *aCompressedIndexDataValuesLength = 0;
-    return NS_OK;
-  }
-
-  // First calculate the size of the final buffer.
-  uint32_t blobDataLength = 0;
-
-  for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
-    const IndexDataValue& info = aIndexValues[arrayIndex];
-    const nsCString& keyBuffer = info.mKey.GetBuffer();
-    const uint32_t keyBufferLength = keyBuffer.Length();
-
-    MOZ_ASSERT(!keyBuffer.IsEmpty());
-
-    // Don't let |infoLength| overflow.
-    if (NS_WARN_IF(UINT32_MAX - keyBuffer.Length() <
-                   CompressedByteCountForIndexId(info.mIndexId) +
-                   CompressedByteCountForNumber(keyBufferLength))) {
-      IDB_REPORT_INTERNAL_ERR();
-      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-    }
-
-    const uint32_t infoLength =
-      CompressedByteCountForIndexId(info.mIndexId) +
-      CompressedByteCountForNumber(keyBufferLength) +
-      keyBufferLength;
-
-    // Don't let |blobDataLength| overflow.
-    if (NS_WARN_IF(UINT32_MAX - infoLength < blobDataLength)) {
-      IDB_REPORT_INTERNAL_ERR();
-      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-    }
-
-    blobDataLength += infoLength;
-  }
-
-  UniqueMozFreePtr<uint8_t> blobData(
-    static_cast<uint8_t*>(moz_malloc(blobDataLength)));
-  if (NS_WARN_IF(!blobData)) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  uint8_t* blobDataIter = blobData.get();
-
-  for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
-    const IndexDataValue& info = aIndexValues[arrayIndex];
-    const nsCString& keyBuffer = info.mKey.GetBuffer();
-    const uint32_t keyBufferLength = keyBuffer.Length();
-
-    WriteCompressedIndexId(info.mIndexId, info.mUnique, &blobDataIter);
-    WriteCompressedNumber(keyBuffer.Length(), &blobDataIter);
-
-    memcpy(blobDataIter, keyBuffer.get(), keyBufferLength);
-    blobDataIter += keyBufferLength;
-  }
-
-  MOZ_ASSERT(blobDataIter == blobData.get() + blobDataLength);
-
-  aCompressedIndexDataValues.swap(blobData);
-  *aCompressedIndexDataValuesLength = uint32_t(blobDataLength);
-
-  return NS_OK;
-}
-
-nsresult
-ReadCompressedIndexDataValuesFromBlob(
-                                   const uint8_t* aBlobData,
-                                   uint32_t aBlobDataLength,
-                                   FallibleTArray<IndexDataValue>& aIndexValues)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(aBlobData);
-  MOZ_ASSERT(aBlobDataLength);
-  MOZ_ASSERT(aIndexValues.IsEmpty());
-
-  PROFILER_LABEL("IndexedDB",
-                 "ReadCompressedIndexDataValuesFromBlob",
-                 js::ProfileEntry::Category::STORAGE);
-
-  const uint8_t* blobDataIter = aBlobData;
-  const uint8_t* blobDataEnd = aBlobData + aBlobDataLength;
-
-  while (blobDataIter < blobDataEnd) {
-    int64_t indexId;
-    bool unique;
-    ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique);
-
-    if (NS_WARN_IF(blobDataIter == blobDataEnd)) {
-      IDB_REPORT_INTERNAL_ERR();
-      return NS_ERROR_FILE_CORRUPTED;
-    }
-
-    // Read key buffer length.
-    const uint64_t keyBufferLength =
-      ReadCompressedNumber(&blobDataIter, blobDataEnd);
-
-    if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
-        NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
-        NS_WARN_IF(blobDataIter + keyBufferLength > blobDataEnd)) {
-      IDB_REPORT_INTERNAL_ERR();
-      return NS_ERROR_FILE_CORRUPTED;
-    }
-
-    nsCString keyBuffer(reinterpret_cast<const char*>(blobDataIter),
-                        uint32_t(keyBufferLength));
-    blobDataIter += keyBufferLength;
-
-    if (NS_WARN_IF(!aIndexValues.InsertElementSorted(
-                      IndexDataValue(indexId, unique, Key(keyBuffer))))) {
-      IDB_REPORT_INTERNAL_ERR();
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  MOZ_ASSERT(blobDataIter == blobDataEnd);
-
-  return NS_OK;
-}
-
-// static
-template <typename T>
-nsresult
-ReadCompressedIndexDataValuesFromSource(
-                                   T* aSource,
-                                   uint32_t aColumnIndex,
-                                   FallibleTArray<IndexDataValue>& aIndexValues)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(aSource);
-  MOZ_ASSERT(aIndexValues.IsEmpty());
-
-  int32_t columnType;
-  nsresult rv = aSource->GetTypeOfIndex(aColumnIndex, &columnType);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
-    return NS_OK;
-  }
-
-  MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_BLOB);
-
-  const uint8_t* blobData;
-  uint32_t blobDataLength;
-  rv = aSource->GetSharedBlob(aColumnIndex, &blobDataLength, &blobData);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (NS_WARN_IF(!blobDataLength)) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_FILE_CORRUPTED;
-  }
-
-  rv = ReadCompressedIndexDataValuesFromBlob(blobData,
-                                             blobDataLength,
-                                             aIndexValues);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-nsresult
-ReadCompressedIndexDataValues(mozIStorageStatement* aStatement,
-                              uint32_t aColumnIndex,
-                              FallibleTArray<IndexDataValue>& aIndexValues)
-{
-  return ReadCompressedIndexDataValuesFromSource(aStatement,
-                                                 aColumnIndex,
-                                                 aIndexValues);
-}
-
-nsresult
-ReadCompressedIndexDataValues(mozIStorageValueArray* aValues,
-                              uint32_t aColumnIndex,
-                              FallibleTArray<IndexDataValue>& aIndexValues)
-{
-  return ReadCompressedIndexDataValuesFromSource(aValues,
-                                                 aColumnIndex,
-                                                 aIndexValues);
-}
-
 nsresult
 CreateFileTables(mozIStorageConnection* aConnection)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aConnection);
 
   PROFILER_LABEL("IndexedDB",
                  "CreateFileTables",
@@ -1005,107 +522,124 @@ CreateTables(mozIStorageConnection* aCon
   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 PRIMARY KEY"
-      ", origin TEXT NOT NULL"
-      ", version INTEGER NOT NULL DEFAULT 0"
-      ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
-      ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
-      ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
-      ") WITHOUT ROWID;"
+    "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"
-      ");"
-  ));
-  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"
-      ", FOREIGN KEY (object_store_id) "
-          "REFERENCES object_store(id) "
-      ");"
+    "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"
-      "( object_store_id INTEGER NOT NULL"
-      ", key BLOB NOT NULL"
-      ", index_data_values BLOB DEFAULT NULL"
-      ", file_ids TEXT"
-      ", data BLOB NOT NULL"
-      ", PRIMARY KEY (object_store_id, key)"
-      ", FOREIGN KEY (object_store_id) "
-          "REFERENCES object_store(id) "
-      ") WITHOUT ROWID;"
+    "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_store_id INTEGER NOT NULL"
-      ", PRIMARY KEY (index_id, value, object_data_key)"
-      ", FOREIGN KEY (index_id) "
-          "REFERENCES object_store_index(id) "
-      ", FOREIGN KEY (object_store_id, object_data_key) "
-          "REFERENCES object_data(object_store_id, key) "
-      ") WITHOUT ROWID;"
+    "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_store_id INTEGER NOT NULL"
-      ", object_data_key BLOB NOT NULL"
-      ", PRIMARY KEY (index_id, value)"
-      ", FOREIGN KEY (index_id) "
-          "REFERENCES object_store_index(id) "
-      ", FOREIGN KEY (object_store_id, object_data_key) "
-          "REFERENCES object_data(object_store_id, key) "
-      ") WITHOUT ROWID;"
+    "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;
@@ -2524,17 +2058,17 @@ UpgradeSchemaFrom12_0To13_0(mozIStorageC
   MOZ_ASSERT(aConnection);
 
   PROFILER_LABEL("IndexedDB",
                  "UpgradeSchemaFrom12_0To13_0",
                  js::ProfileEntry::Category::STORAGE);
 
   nsresult rv;
 
-#ifdef IDB_MOBILE
+#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 = ");
@@ -2606,935 +2140,16 @@ UpgradeSchemaFrom16_0To17_0(mozIStorageC
   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 0));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-class UpgradeSchemaFrom17_0To18_0Helper final
-{
-  class InsertIndexDataValuesFunction;
-  class UpgradeKeyFunction;
-
-public:
-  static nsresult
-  DoUpgrade(mozIStorageConnection* aConnection, const nsACString& aOrigin);
-
-private:
-  static nsresult
-  DoUpgradeInternal(mozIStorageConnection* aConnection,
-                    const nsACString& aOrigin);
-
-  UpgradeSchemaFrom17_0To18_0Helper()
-  {
-    MOZ_ASSERT_UNREACHABLE("Don't create instances of this class!");
-  }
-
-  ~UpgradeSchemaFrom17_0To18_0Helper()
-  {
-    MOZ_ASSERT_UNREACHABLE("Don't create instances of this class!");
-  }
-};
-
-class UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction final
-  : public mozIStorageFunction
-{
-public:
-  InsertIndexDataValuesFunction()
-  { }
-
-  NS_DECL_ISUPPORTS
-
-private:
-  ~InsertIndexDataValuesFunction()
-  { }
-
-  NS_DECL_MOZISTORAGEFUNCTION
-};
-
-NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::
-                    InsertIndexDataValuesFunction,
-                  mozIStorageFunction);
-
-NS_IMETHODIMP
-UpgradeSchemaFrom17_0To18_0Helper::
-InsertIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues,
-                                              nsIVariant** _retval)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(aValues);
-  MOZ_ASSERT(_retval);
-
-#ifdef DEBUG
-  {
-    uint32_t argCount;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aValues->GetNumEntries(&argCount)));
-    MOZ_ASSERT(argCount == 4);
-
-    int32_t valueType;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aValues->GetTypeOfIndex(0, &valueType)));
-    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
-               valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aValues->GetTypeOfIndex(1, &valueType)));
-    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aValues->GetTypeOfIndex(2, &valueType)));
-    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aValues->GetTypeOfIndex(3, &valueType)));
-    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
-  }
-#endif
-
-  // Read out the previous value. It may be NULL, in which case we'll just end
-  // up with an empty array.
-  AutoFallibleTArray<IndexDataValue, 32> indexValues;
-  nsresult rv = ReadCompressedIndexDataValues(aValues, 0, indexValues);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  int64_t indexId;
-  rv = aValues->GetInt64(1, &indexId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  int32_t unique;
-  rv = aValues->GetInt32(2, &unique);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  Key value;
-  rv = value.SetFromValueArray(aValues, 3);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Update the array with the new addition.
-  if (NS_WARN_IF(!indexValues.SetCapacity(indexValues.Length() + 1))) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  MOZ_ALWAYS_TRUE(
-    indexValues.InsertElementSorted(IndexDataValue(indexId, !!unique, value)));
-
-  // Compress the array.
-  UniqueMozFreePtr<uint8_t> indexValuesBlob;
-  uint32_t indexValuesBlobLength;
-  rv = MakeCompressedIndexDataValues(indexValues,
-                                     indexValuesBlob,
-                                     &indexValuesBlobLength);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // The compressed blob is the result of this function.
-  std::pair<uint8_t *, int> indexValuesBlobPair(indexValuesBlob.release(),
-                                                indexValuesBlobLength);
-
-  nsCOMPtr<nsIVariant> result =
-    new storage::AdoptedBlobVariant(indexValuesBlobPair);
-
-  result.forget(_retval);
-  return NS_OK;
-}
-
-class UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction final
-  : public mozIStorageFunction
-{
-public:
-  UpgradeKeyFunction()
-  { }
-
-  static nsresult
-  CopyAndUpgradeKeyBuffer(const uint8_t* aSource,
-                          const uint8_t* aSourceEnd,
-                          uint8_t* aDestination)
-  {
-    return CopyAndUpgradeKeyBufferInternal(aSource,
-                                           aSourceEnd,
-                                           aDestination,
-                                           0 /* aTagOffset */,
-                                           0 /* aRecursionDepth */);
-  }
-
-  NS_DECL_ISUPPORTS
-
-private:
-  ~UpgradeKeyFunction()
-  { }
-
-  static nsresult
-  CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
-                                  const uint8_t* aSourceEnd,
-                                  uint8_t*& aDestination,
-                                  uint8_t aTagOffset,
-                                  uint8_t aRecursionDepth);
-
-  static uint32_t
-  AdjustedSize(uint32_t aMaxSize,
-               const uint8_t* aSource,
-               const uint8_t* aSourceEnd)
-  {
-    MOZ_ASSERT(aMaxSize);
-    MOZ_ASSERT(aSource);
-    MOZ_ASSERT(aSourceEnd);
-    MOZ_ASSERT(aSource <= aSourceEnd);
-
-    return std::min(aMaxSize, uint32_t(aSourceEnd - aSource));
-  }
-
-  NS_DECL_MOZISTORAGEFUNCTION
-};
-
-// static
-nsresult
-UpgradeSchemaFrom17_0To18_0Helper::
-UpgradeKeyFunction::CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
-                                                    const uint8_t* aSourceEnd,
-                                                    uint8_t*& aDestination,
-                                                    uint8_t aTagOffset,
-                                                    uint8_t aRecursionDepth)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(aSource);
-  MOZ_ASSERT(*aSource);
-  MOZ_ASSERT(aSourceEnd);
-  MOZ_ASSERT(aSource < aSourceEnd);
-  MOZ_ASSERT(aDestination);
-  MOZ_ASSERT(aTagOffset <=  Key::kMaxArrayCollapse);
-
-  static MOZ_CONSTEXPR_VAR uint8_t kOldNumberTag = 0x1;
-  static MOZ_CONSTEXPR_VAR uint8_t kOldDateTag = 0x2;
-  static MOZ_CONSTEXPR_VAR uint8_t kOldStringTag = 0x3;
-  static MOZ_CONSTEXPR_VAR uint8_t kOldArrayTag = 0x4;
-  static MOZ_CONSTEXPR_VAR uint8_t kOldMaxType = kOldArrayTag;
-
-  if (NS_WARN_IF(aRecursionDepth > Key::kMaxRecursionDepth)) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_FILE_CORRUPTED;
-  }
-
-  const uint8_t sourceTag = *aSource - (aTagOffset * kOldMaxType);
-  MOZ_ASSERT(sourceTag);
-
-  if (NS_WARN_IF(sourceTag > kOldMaxType * Key::kMaxArrayCollapse)) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_FILE_CORRUPTED;
-  }
-
-  if (sourceTag == kOldNumberTag || sourceTag == kOldDateTag) {
-    // Write the new tag.
-    *aDestination++ =
-      (sourceTag == kOldNumberTag ? Key::eFloat : Key::eDate) +
-      (aTagOffset * Key::eMaxType);
-    aSource++;
-
-    // Numbers and Dates are encoded as 64-bit integers, but trailing 0
-    // bytes have been removed.
-    const uint32_t byteCount =
-      AdjustedSize(sizeof(uint64_t), aSource, aSourceEnd);
-
-    for (uint32_t count = 0; count < byteCount; count++) {
-      *aDestination++ = *aSource++;
-    }
-
-    return NS_OK;
-  }
-
-  if (sourceTag == kOldStringTag) {
-    // Write the new tag.
-    *aDestination++ = Key::eString + (aTagOffset * Key::eMaxType);
-    aSource++;
-
-    while (aSource < aSourceEnd) {
-      const uint8_t byte = *aSource++;
-      *aDestination++ = byte;
-
-      if (!byte) {
-        // Just copied the terminator.
-        break;
-      }
-
-      // Maybe copy one or two extra bytes if the byte is tagged and we have
-      // enough source space.
-      if (byte & 0x80) {
-        const uint32_t byteCount =
-          AdjustedSize((byte & 0x40) ? 2 : 1, aSource, aSourceEnd);
-
-        for (uint32_t count = 0; count < byteCount; count++) {
-          *aDestination++ = *aSource++;
-        }
-      }
-    }
-
-    return NS_OK;
-  }
-
-  if (NS_WARN_IF(sourceTag < kOldArrayTag)) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_FILE_CORRUPTED;
-  }
-
-  aTagOffset++;
-
-  if (aTagOffset == Key::kMaxArrayCollapse) {
-    MOZ_ASSERT(sourceTag == kOldArrayTag);
-
-    *aDestination++ = (aTagOffset * Key::eMaxType);
-    aSource++;
-
-    aTagOffset = 0;
-  }
-
-  while (aSource < aSourceEnd &&
-         (*aSource - (aTagOffset * kOldMaxType)) != Key::eTerminator) {
-    nsresult rv = CopyAndUpgradeKeyBufferInternal(aSource,
-                                                  aSourceEnd,
-                                                  aDestination,
-                                                  aTagOffset,
-                                                  aRecursionDepth + 1);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    aTagOffset = 0;
-  }
-
-  if (aSource < aSourceEnd) {
-    MOZ_ASSERT((*aSource - (aTagOffset * kOldMaxType)) == Key::eTerminator);
-    *aDestination++ = Key::eTerminator + (aTagOffset * Key::eMaxType);
-    aSource++;
-  }
-
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction,
-                  mozIStorageFunction);
-
-NS_IMETHODIMP
-UpgradeSchemaFrom17_0To18_0Helper::
-UpgradeKeyFunction::OnFunctionCall(mozIStorageValueArray* aValues,
-                                   nsIVariant** _retval)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(aValues);
-  MOZ_ASSERT(_retval);
-
-#ifdef DEBUG
-  {
-    uint32_t argCount;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aValues->GetNumEntries(&argCount)));
-    MOZ_ASSERT(argCount == 1);
-
-    int32_t valueType;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aValues->GetTypeOfIndex(0, &valueType)));
-    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
-  }
-#endif
-
-  // Dig the old key out of the values.
-  const uint8_t* blobData;
-  uint32_t blobDataLength;
-  nsresult rv = aValues->GetSharedBlob(0, &blobDataLength, &blobData);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Upgrading the key doesn't change the amount of space needed to hold it.
-  UniqueMozFreePtr<uint8_t> upgradedBlobData(
-    static_cast<uint8_t*>(moz_malloc(blobDataLength)));
-  if (NS_WARN_IF(!upgradedBlobData)) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  rv = CopyAndUpgradeKeyBuffer(blobData,
-                               blobData + blobDataLength,
-                               upgradedBlobData.get());
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // The upgraded key is the result of this function.
-  std::pair<uint8_t*, int> data(upgradedBlobData.release(),
-                                int(blobDataLength));
-
-  nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
-
-  upgradedBlobData.release();
-
-  result.forget(_retval);
-  return NS_OK;
-}
-
-// static
-nsresult
-UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(mozIStorageConnection* aConnection,
-                                             const nsACString& aOrigin)
-{
-  MOZ_ASSERT(aConnection);
-  MOZ_ASSERT(!aOrigin.IsEmpty());
-
-  // Register the |upgrade_key| function.
-  nsRefPtr<UpgradeKeyFunction> updateFunction = new UpgradeKeyFunction();
-
-  NS_NAMED_LITERAL_CSTRING(upgradeKeyFunctionName, "upgrade_key");
-
-  nsresult rv =
-    aConnection->CreateFunction(upgradeKeyFunctionName, 1, updateFunction);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Register the |insert_idv| function.
-  nsRefPtr<InsertIndexDataValuesFunction> insertIDVFunction =
-    new InsertIndexDataValuesFunction();
-
-  NS_NAMED_LITERAL_CSTRING(insertIDVFunctionName, "insert_idv");
-
-  rv = aConnection->CreateFunction(insertIDVFunctionName, 4, insertIDVFunction);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      aConnection->RemoveFunction(upgradeKeyFunctionName)));
-    return rv;
-  }
-
-  rv = DoUpgradeInternal(aConnection, aOrigin);
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-    aConnection->RemoveFunction(upgradeKeyFunctionName)));
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-    aConnection->RemoveFunction(insertIDVFunctionName)));
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-// static
-nsresult
-UpgradeSchemaFrom17_0To18_0Helper::DoUpgradeInternal(
-                                             mozIStorageConnection* aConnection,
-                                             const nsACString& aOrigin)
-{
-  MOZ_ASSERT(aConnection);
-  MOZ_ASSERT(!aOrigin.IsEmpty());
-
-  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // Drop these triggers to avoid unnecessary work during the upgrade process.
-    "DROP TRIGGER object_data_insert_trigger;"
-    "DROP TRIGGER object_data_update_trigger;"
-    "DROP TRIGGER object_data_delete_trigger;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // Drop these indexes before we do anything else to free disk space.
-    "DROP INDEX index_data_object_data_id_index;"
-    "DROP INDEX unique_index_data_object_data_id_index;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Create the new tables and triggers first.
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // This will eventually become the |database| table.
-    "CREATE TABLE database_upgrade "
-      "( name TEXT PRIMARY KEY"
-      ", origin TEXT NOT NULL"
-      ", version INTEGER NOT NULL DEFAULT 0"
-      ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
-      ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
-      ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
-      ") WITHOUT ROWID;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-     // This will eventually become the |object_store| table.
-    "CREATE TABLE object_store_upgrade"
-      "( id INTEGER PRIMARY KEY"
-      ", auto_increment INTEGER NOT NULL DEFAULT 0"
-      ", name TEXT NOT NULL"
-      ", key_path TEXT"
-      ");"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // This will eventually become the |object_store_index| table.
-    "CREATE TABLE object_store_index_upgrade"
-      "( 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"
-      ", FOREIGN KEY (object_store_id) "
-          "REFERENCES object_store(id) "
-      ");"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // This will eventually become the |object_data| table.
-    "CREATE TABLE object_data_upgrade"
-      "( object_store_id INTEGER NOT NULL"
-      ", key BLOB NOT NULL"
-      ", index_data_values BLOB DEFAULT NULL"
-      ", file_ids TEXT"
-      ", data BLOB NOT NULL"
-      ", PRIMARY KEY (object_store_id, key)"
-      ", FOREIGN KEY (object_store_id) "
-          "REFERENCES object_store(id) "
-      ") WITHOUT ROWID;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // This will eventually become the |index_data| table.
-    "CREATE TABLE index_data_upgrade"
-      "( index_id INTEGER NOT NULL"
-      ", value BLOB NOT NULL"
-      ", object_data_key BLOB NOT NULL"
-      ", object_store_id INTEGER NOT NULL"
-      ", PRIMARY KEY (index_id, value, object_data_key)"
-      ", FOREIGN KEY (index_id) "
-          "REFERENCES object_store_index(id) "
-      ", FOREIGN KEY (object_store_id, object_data_key) "
-          "REFERENCES object_data(object_store_id, key) "
-      ") WITHOUT ROWID;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // This will eventually become the |unique_index_data| table.
-    "CREATE TABLE unique_index_data_upgrade"
-      "( index_id INTEGER NOT NULL"
-      ", value BLOB NOT NULL"
-      ", object_store_id INTEGER NOT NULL"
-      ", object_data_key BLOB NOT NULL"
-      ", PRIMARY KEY (index_id, value)"
-      ", FOREIGN KEY (index_id) "
-          "REFERENCES object_store_index(id) "
-      ", FOREIGN KEY (object_store_id, object_data_key) "
-          "REFERENCES object_data(object_store_id, key) "
-      ") WITHOUT ROWID;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // Temporarily store |index_data_values| that we build during the upgrade of
-    // the index tables. We will later move this to the |object_data| table.
-    "CREATE TEMPORARY TABLE temp_index_data_values "
-      "( object_store_id INTEGER NOT NULL"
-      ", key BLOB NOT NULL"
-      ", index_data_values BLOB DEFAULT NULL"
-      ", PRIMARY KEY (object_store_id, key)"
-      ") WITHOUT ROWID;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // These two triggers help build the |index_data_values| blobs. The nested
-    // SELECT statements help us achieve an "INSERT OR UPDATE"-like behavior.
-    "CREATE TEMPORARY TRIGGER unique_index_data_upgrade_insert_trigger "
-      "AFTER INSERT ON unique_index_data_upgrade "
-      "BEGIN "
-        "INSERT OR REPLACE INTO temp_index_data_values "
-          "VALUES "
-          "( NEW.object_store_id"
-          ", NEW.object_data_key"
-          ", insert_idv("
-              "( SELECT index_data_values "
-                  "FROM temp_index_data_values "
-                  "WHERE object_store_id = NEW.object_store_id "
-                  "AND key = NEW.object_data_key "
-              "), NEW.index_id"
-               ", 1" /* unique */
-               ", NEW.value"
-            ")"
-          ");"
-      "END;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "CREATE TEMPORARY TRIGGER index_data_upgrade_insert_trigger "
-      "AFTER INSERT ON index_data_upgrade "
-      "BEGIN "
-        "INSERT OR REPLACE INTO temp_index_data_values "
-          "VALUES "
-          "( NEW.object_store_id"
-          ", NEW.object_data_key"
-          ", insert_idv("
-              "("
-                "SELECT index_data_values "
-                  "FROM temp_index_data_values "
-                  "WHERE object_store_id = NEW.object_store_id "
-                  "AND key = NEW.object_data_key "
-              "), NEW.index_id"
-               ", 0" /* not unique */
-               ", NEW.value"
-            ")"
-          ");"
-      "END;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Update the |unique_index_data| table to change the column order, remove the
-  // ON DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // Insert all the data.
-    "INSERT INTO unique_index_data_upgrade "
-      "SELECT "
-        "unique_index_data.index_id, "
-        "upgrade_key(unique_index_data.value), "
-        "object_data.object_store_id, "
-        "upgrade_key(unique_index_data.object_data_key) "
-        "FROM unique_index_data "
-        "JOIN object_data "
-        "ON unique_index_data.object_data_id = object_data.id;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // The trigger is no longer needed.
-    "DROP TRIGGER unique_index_data_upgrade_insert_trigger;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // The old table is no longer needed.
-    "DROP TABLE unique_index_data;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // Rename the table.
-    "ALTER TABLE unique_index_data_upgrade "
-      "RENAME TO unique_index_data;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Update the |index_data| table to change the column order, remove the ON
-  // DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // Insert all the data.
-    "INSERT INTO index_data_upgrade "
-      "SELECT "
-        "index_data.index_id, "
-        "upgrade_key(index_data.value), "
-        "upgrade_key(index_data.object_data_key), "
-        "object_data.object_store_id "
-        "FROM index_data "
-        "JOIN object_data "
-        "ON index_data.object_data_id = object_data.id;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // The trigger is no longer needed.
-    "DROP TRIGGER index_data_upgrade_insert_trigger;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // The old table is no longer needed.
-    "DROP TABLE index_data;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // Rename the table.
-    "ALTER TABLE index_data_upgrade "
-      "RENAME TO index_data;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Update the |object_data| table to add the |index_data_values| column,
-  // remove the ON DELETE CASCADE clause, and apply the WITHOUT ROWID
-  // optimization.
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // Insert all the data.
-    "INSERT INTO object_data_upgrade "
-      "SELECT "
-        "object_data.object_store_id, "
-        "upgrade_key(object_data.key_value), "
-        "temp_index_data_values.index_data_values, "
-        "object_data.file_ids, "
-        "object_data.data "
-        "FROM object_data "
-        "LEFT JOIN temp_index_data_values "
-        "ON object_data.object_store_id = "
-          "temp_index_data_values.object_store_id "
-        "AND upgrade_key(object_data.key_value) = "
-          "temp_index_data_values.key;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // The temporary table is no longer needed.
-    "DROP TABLE temp_index_data_values;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // The old table is no longer needed.
-    "DROP TABLE object_data;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    // Rename the table.
-    "ALTER TABLE object_data_upgrade "
-      "RENAME TO object_data;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Update the |object_store_index| table to remove the UNIQUE constraint and
-  // the ON DELETE CASCADE clause.
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "INSERT INTO object_store_index_upgrade "
-      "SELECT * "
-        "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(
-    "ALTER TABLE object_store_index_upgrade "
-      "RENAME TO object_store_index;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Update the |object_store| table to remove the UNIQUE constraint.
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "INSERT INTO object_store_upgrade "
-      "SELECT * "
-        "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(
-    "ALTER TABLE object_store_upgrade "
-      "RENAME TO object_store;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Update the |database| table to include the origin, vacuum information, and
-  // apply the WITHOUT ROWID optimization.
-  nsCOMPtr<mozIStorageStatement> stmt;
-  rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
-    "INSERT INTO database_upgrade "
-      "SELECT name, :origin, version, 0, 0, 0 "
-        "FROM database;"
-  ), getter_AddRefs(stmt));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = stmt->Execute();
-  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(
-    "ALTER TABLE database_upgrade "
-      "RENAME TO database;"
-  ));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-#ifdef DEBUG
-  {
-    // Make sure there's only one entry in the |database| table.
-    nsCOMPtr<mozIStorageStatement> stmt;
-    MOZ_ASSERT(NS_SUCCEEDED(
-      aConnection->CreateStatement(
-        NS_LITERAL_CSTRING("SELECT COUNT(*) "
-                             "FROM database;"),
-        getter_AddRefs(stmt))));
-
-    bool hasResult;
-    MOZ_ASSERT(NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)));
-
-    int64_t count;
-    MOZ_ASSERT(NS_SUCCEEDED(stmt->GetInt64(0, &count)));
-
-    MOZ_ASSERT(count == 1);
-  }
-#endif
-
-  // Recreate file table triggers.
-  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-    "CREATE TRIGGER object_data_insert_trigger "
-      "AFTER INSERT ON object_data "
-      "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 "
-      "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 "
-      "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;
-  }
-
-  // Finally, turn on auto_vacuum mode. We use full auto_vacuum mode to reclaim
-  // disk space on mobile devices (at the cost of some COMMIT speed), and
-  // incremental auto_vacuum mode on desktop builds.
-  rv = aConnection->ExecuteSimpleSQL(
-#ifdef IDB_MOBILE
-    NS_LITERAL_CSTRING("PRAGMA auto_vacuum = FULL;")
-#else
-    NS_LITERAL_CSTRING("PRAGMA auto_vacuum = INCREMENTAL;")
-#endif
-  );
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aConnection->SetSchemaVersion(MakeSchemaVersion(18, 0));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-nsresult
-UpgradeSchemaFrom17_0To18_0(mozIStorageConnection* aConnection,
-                            const nsACString& aOrigin)
-{
-  MOZ_ASSERT(aConnection);
-  MOZ_ASSERT(!aOrigin.IsEmpty());
-
-  PROFILER_LABEL("IndexedDB",
-                 "UpgradeSchemaFrom17_0To18_0",
-                 js::ProfileEntry::Category::STORAGE);
-
-  return UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(aConnection, aOrigin);
-}
-
 nsresult
 GetDatabaseFileURL(nsIFile* aDatabaseFile,
                    PersistenceType aPersistenceType,
                    const nsACString& aGroup,
                    const nsACString& aOrigin,
                    nsIFileURL** aResult)
 {
   MOZ_ASSERT(aDatabaseFile);
@@ -3549,274 +2164,84 @@ GetDatabaseFileURL(nsIFile* aDatabaseFil
   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 +
-                         NS_LITERAL_CSTRING("&cache=private"));
+                         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(!NS_IsMainThread());
   MOZ_ASSERT(aConnection);
 
-  static const char kBuiltInPragmas[] =
-    // We use foreign keys in DEBUG builds only because there is a performance
-    // cost to using them.
-   "PRAGMA foreign_keys = "
-#ifdef DEBUG
-     "ON"
-#else
-     "OFF"
+  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;"
-
-    // We aggressively truncate the database file when idle so don't bother
-    // overwriting the WAL with 0 during active periods.
-    "PRAGMA secure_delete = OFF;"
-  ;
-
-  nsresult rv =
-    aConnection->ExecuteSimpleSQL(
-      nsDependentCString(kBuiltInPragmas,
-                         LiteralStringLength(kBuiltInPragmas)));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsAutoCString pragmaStmt;
-  pragmaStmt.AssignLiteral("PRAGMA synchronous = ");
+    // We don't need SQLite's table locks because we manage transaction ordering
+    // ourselves and we know we will never allow a write transaction to modify
+    // an object store that a read transaction is in the process of using.
+    "PRAGMA read_uncommitted = TRUE;"
+    // No more PRAGMAs.
+    ;
+
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   if (IndexedDatabaseManager::FullSynchronous()) {
-    pragmaStmt.AppendLiteral("FULL");
-  } else {
-    pragmaStmt.AppendLiteral("NORMAL");
-  }
-  pragmaStmt.Append(';');
-
-  rv = aConnection->ExecuteSimpleSQL(pragmaStmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-#ifndef IDB_MOBILE
-  if (kSQLiteGrowthIncrement) {
-    rv = aConnection->SetGrowthIncrement(kSQLiteGrowthIncrement,
-                                         EmptyCString());
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-#endif // IDB_MOBILE
-
-  return NS_OK;
-}
-
-nsresult
-SetJournalMode(mozIStorageConnection* aConnection)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConnection);
-
-  // Try enabling WAL mode. This can fail in various circumstances so we have to
-  // check the results here.
-  NS_NAMED_LITERAL_CSTRING(journalModeQueryStart, "PRAGMA journal_mode = ");
-  NS_NAMED_LITERAL_CSTRING(journalModeWAL, "wal");
-
-  nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv =
-    aConnection->CreateStatement(journalModeQueryStart + journalModeWAL,
-                                 getter_AddRefs(stmt));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  bool hasResult;
-  rv = stmt->ExecuteStep(&hasResult);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  MOZ_ASSERT(hasResult);
-
-  nsCString journalMode;
-  rv = stmt->GetUTF8String(0, journalMode);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (journalMode.Equals(journalModeWAL)) {
-    // WAL mode successfully enabled. Maybe set limits on its size here.
-    if (kMaxWALPages >= 0) {
-      nsAutoCString pageCount;
-      pageCount.AppendInt(kMaxWALPages);
-
-      rv = aConnection->ExecuteSimpleSQL(
-        NS_LITERAL_CSTRING("PRAGMA wal_autocheckpoint = ") + pageCount);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-  } else {
-    NS_WARNING("Failed to set WAL mode, falling back to normal journal mode.");
-#ifdef IDB_MOBILE
-    rv = aConnection->ExecuteSimpleSQL(journalModeQueryStart +
-                                       NS_LITERAL_CSTRING("truncate"));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-#endif
-  }
-
-  return NS_OK;
-}
-
-template <class FileOrURLType>
-struct StorageOpenTraits;
-
-template <>
-struct StorageOpenTraits<nsIFileURL*>
-{
-  static nsresult
-  Open(mozIStorageService* aStorageService,
-       nsIFileURL* aFileURL,
-       mozIStorageConnection** aConnection)
-  {
-    return aStorageService->OpenDatabaseWithFileURL(aFileURL, aConnection);
-  }
-
-#ifdef DEBUG
-  static void
-  GetPath(nsIFileURL* aFileURL, nsCString& aPath)
-  {
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aFileURL->GetFileName(aPath)));
-  }
-#endif
-};
-
-template <>
-struct StorageOpenTraits<nsIFile*>
-{
-  static nsresult
-  Open(mozIStorageService* aStorageService,
-       nsIFile* aFile,
-       mozIStorageConnection** aConnection)
-  {
-    return aStorageService->OpenUnsharedDatabase(aFile, aConnection);
-  }
-
-#ifdef DEBUG
-  static void
-  GetPath(nsIFile* aFile, nsCString& aPath)
-  {
-    nsString path;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aFile->GetPath(path)));
-
-    aPath.AssignWithConversion(path);
-  }
-#endif
-};
-
-template <template <class> class SmartPtr, class FileOrURLType>
-struct StorageOpenTraits<SmartPtr<FileOrURLType>>
-  : public StorageOpenTraits<FileOrURLType*>
-{ };
-
-template <class FileOrURLType>
-nsresult
-OpenDatabaseAndHandleBusy(mozIStorageService* aStorageService,
-                          FileOrURLType aFileOrURL,
-                          mozIStorageConnection** aConnection)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(aStorageService);
-  MOZ_ASSERT(aFileOrURL);
-  MOZ_ASSERT(aConnection);
-
-  nsCOMPtr<mozIStorageConnection> connection;
-  nsresult rv =
-    StorageOpenTraits<FileOrURLType>::Open(aStorageService,
-                                           aFileOrURL,
-                                           getter_AddRefs(connection));
-
-  if (rv == NS_ERROR_STORAGE_BUSY) {
-#ifdef DEBUG
-    {
-      nsCString path;
-      StorageOpenTraits<FileOrURLType>::GetPath(aFileOrURL, path);
-
-      nsPrintfCString message("Received NS_ERROR_STORAGE_BUSY when attempting "
-                              "to open database '%s', retrying for up to 10 "
-                              "seconds",
-                              path.get());
-      NS_WARNING(message.get());
-    }
-#endif
-
-    // Another thread must be checkpointing the WAL. Wait up to 10 seconds for
-    // that to complete.
-    TimeStamp start = TimeStamp::NowLoRes();
-
-    while (true) {
-      PR_Sleep(PR_MillisecondsToInterval(100));
-
-      rv = StorageOpenTraits<FileOrURLType>::Open(aStorageService,
-                                                  aFileOrURL,
-                                                  getter_AddRefs(connection));
-      if (rv != NS_ERROR_STORAGE_BUSY ||
-          TimeStamp::NowLoRes() - start > TimeDuration::FromSeconds(10)) {
-        break;
-      }
-    }
-  }
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  connection.forget(aConnection);
-  return NS_OK;
-}
-
-nsresult
-CreateStorageConnection(nsIFile* aDBFile,
-                        nsIFile* aFMDirectory,
-                        const nsAString& aName,
-                        PersistenceType aPersistenceType,
-                        const nsACString& aGroup,
-                        const nsACString& aOrigin,
-                        mozIStorageConnection** aConnection)
+    rv = aConnection->ExecuteSimpleSQL(
+                             NS_LITERAL_CSTRING("PRAGMA synchronous = FULL;"));
+    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",
-                 "CreateStorageConnection",
+                 "CreateDatabaseConnection",
                  js::ProfileEntry::Category::STORAGE);
 
   nsresult rv;
   bool exists;
 
   if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
     rv = aDBFile->Exists(&exists);
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -3838,17 +2263,17 @@ CreateStorageConnection(nsIFile* aDBFile
 
   nsCOMPtr<mozIStorageService> ss =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<mozIStorageConnection> connection;
-  rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(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;
     }
 
@@ -3875,19 +2300,18 @@ CreateStorageConnection(nsIFile* aDBFile
       }
 
       rv = aFMDirectory->Remove(true);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
-    rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(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;
   }
@@ -3910,108 +2334,71 @@ CreateStorageConnection(nsIFile* aDBFile
     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 journalModeSet = false;
+  bool vacuumNeeded = false;
 
   if (schemaVersion != kSQLiteSchemaVersion) {
-    const bool newDatabase = !schemaVersion;
-
-    if (newDatabase) {
-      // Set the page size first.
-      if (kSQLitePageSizeOverride) {
-        rv = connection->ExecuteSimpleSQL(
-          nsPrintfCString("PRAGMA page_size = %lu;", kSQLitePageSizeOverride)
-        );
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-      }
-
-      // We have to set the auto_vacuum mode before opening a transaction.
-      rv = connection->ExecuteSimpleSQL(
-#ifdef IDB_MOBILE
-        // Turn on full auto_vacuum mode to reclaim disk space on mobile
-        // devices (at the cost of some COMMIT speed).
-        NS_LITERAL_CSTRING("PRAGMA auto_vacuum = FULL;")
-#else
-        // Turn on incremental auto_vacuum mode on desktop builds.
-        NS_LITERAL_CSTRING("PRAGMA auto_vacuum = INCREMENTAL;")
-#endif
-      );
+#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;
       }
-
-      rv = SetJournalMode(connection);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      journalModeSet = true;
-    } else {
-#ifdef DEBUG
-    // Disable foreign key support while upgrading. This has to be done before
-    // starting a transaction.
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      connection->ExecuteSimpleSQL(
-        NS_LITERAL_CSTRING("PRAGMA foreign_keys = OFF;"))));
+    }
 #endif
-    }
-
-    bool vacuumNeeded = false;
 
     mozStorageTransaction transaction(connection, false,
                                   mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
-    if (newDatabase) {
+    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, origin) "
-        "VALUES (:name, :origin)"
+        "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->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
-      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((18 << 4) + 0),
+      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) {
@@ -4032,19 +2419,16 @@ CreateStorageConnection(nsIFile* aDBFile
         } 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 if (schemaVersion == MakeSchemaVersion(17, 0)) {
-          rv = UpgradeSchemaFrom17_0To18_0(connection, aOrigin);
-          vacuumNeeded = true;
         } 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;
@@ -4063,169 +2447,20 @@ CreateStorageConnection(nsIFile* aDBFile
     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;
     }
-
-#ifdef DEBUG
-    if (!newDatabase) {
-      // Re-enable foreign key support after doing a foreign key check.
-      nsCOMPtr<mozIStorageStatement> checkStmt;
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-        connection->CreateStatement(
-          NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"),
-          getter_AddRefs(checkStmt))));
-
-      bool hasResult;
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(checkStmt->ExecuteStep(&hasResult)));
-      MOZ_ASSERT(!hasResult, "Database has inconsisistent foreign keys!");
-
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-        connection->ExecuteSimpleSQL(
-          NS_LITERAL_CSTRING("PRAGMA foreign_keys = OFF;"))));
-    }
-#endif
-
-    if (kSQLitePageSizeOverride && !newDatabase) {
-      nsCOMPtr<mozIStorageStatement> stmt;
-      rv = connection->CreateStatement(NS_LITERAL_CSTRING(
-        "PRAGMA page_size;"
-      ), getter_AddRefs(stmt));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      bool hasResult;
-      rv = stmt->ExecuteStep(&hasResult);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      MOZ_ASSERT(hasResult);
-
-      int32_t pageSize;
-      rv = stmt->GetInt32(0, &pageSize);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      MOZ_ASSERT(pageSize >= 512 && pageSize <= 65536);
-
-      if (kSQLitePageSizeOverride != uint32_t(pageSize)) {
-        // We must not be in WAL journal mode to change the page size.
-        rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-          "PRAGMA journal_mode = DELETE;"
-        ));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-
-        rv = connection->CreateStatement(NS_LITERAL_CSTRING(
-          "PRAGMA journal_mode;"
-        ), getter_AddRefs(stmt));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-
-        rv = stmt->ExecuteStep(&hasResult);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-
-        MOZ_ASSERT(hasResult);
-
-        nsCString journalMode;
-        rv = stmt->GetUTF8String(0, journalMode);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-
-        if (journalMode.EqualsLiteral("delete")) {
-          // Successfully set to rollback journal mode so changing the page size
-          // is possible with a VACUUM.
-          rv = connection->ExecuteSimpleSQL(
-            nsPrintfCString("PRAGMA page_size = %lu;", kSQLitePageSizeOverride)
-          );
-          if (NS_WARN_IF(NS_FAILED(rv))) {
-            return rv;
-          }
-
-          // We will need to VACUUM in order to change the page size.
-          vacuumNeeded = true;
-        } else {
-          NS_WARNING("Failed to set journal_mode for database, unable to "
-                     "change the page size!");
-        }
-      }
-    }
-
-    if (vacuumNeeded) {
-      rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
-    if (newDatabase || vacuumNeeded) {
-      if (journalModeSet) {
-        // Make sure we checkpoint to get an accurate file size.
-        rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-          "PRAGMA wal_checkpoint(FULL);"
-        ));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-      }
-
-      int64_t fileSize;
-      rv = aDBFile->GetFileSize(&fileSize);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      MOZ_ASSERT(fileSize > 0);
-
-      PRTime vacuumTime = PR_Now();
-      MOZ_ASSERT(vacuumTime);
-
-      nsCOMPtr<mozIStorageStatement> vacuumTimeStmt;
-      rv = connection->CreateStatement(NS_LITERAL_CSTRING(
-        "UPDATE database "
-          "SET last_vacuum_time = :time"
-            ", last_vacuum_size = :size;"
-      ), getter_AddRefs(vacuumTimeStmt));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      rv = vacuumTimeStmt->BindInt64ByName(NS_LITERAL_CSTRING("time"),
-                                           vacuumTime);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      rv = vacuumTimeStmt->BindInt64ByName(NS_LITERAL_CSTRING("size"),
-                                           fileSize);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      rv = vacuumTimeStmt->Execute();
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-  }
-
-  if (!journalModeSet) {
-    rv = SetJournalMode(connection);
+  }
+
+  if (vacuumNeeded) {
+    rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   connection.forget(aConnection);
   return NS_OK;
 }
@@ -4243,30 +2478,30 @@ GetFileForPath(const nsAString& aPath)
   if (NS_WARN_IF(NS_FAILED(file->InitWithPath(aPath)))) {
     return nullptr;
   }
 
   return file.forget();
 }
 
 nsresult
-GetStorageConnection(const nsAString& aDatabaseFilePath,
-                     PersistenceType aPersistenceType,
-                     const nsACString& aGroup,
-                     const nsACString& aOrigin,
-                     mozIStorageConnection** aConnection)
+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",
-                 "GetStorageConnection",
+                 "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;
   }
 
@@ -4290,794 +2525,31 @@ GetStorageConnection(const nsAString& aD
 
   nsCOMPtr<mozIStorageService> ss =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<mozIStorageConnection> connection;
-  rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(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;
   }
 
-  rv = SetJournalMode(connection);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
   connection.forget(aConnection);
   return NS_OK;
 }
 
 /*******************************************************************************
- * ConnectionPool declarations
- ******************************************************************************/
-
-class DatabaseConnection final
-{
-  friend class ConnectionPool;
-
-public:
-  class AutoSavepoint;
-  class CachedStatement;
-  class UpdateRefcountFunction;
-
-private:
-  nsCOMPtr<mozIStorageConnection> mStorageConnection;
-  nsRefPtr<FileManager> mFileManager;
-  nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
-    mCachedStatements;
-  nsRefPtr<UpdateRefcountFunction> mUpdateRefcountFunction;
-
-#ifdef DEBUG
-  uint32_t mDEBUGSavepointCount;
-  PRThread* mDEBUGThread;
-  bool mDEBUGInWriteTransaction;
-#endif
-
-public:
-  void
-  AssertIsOnConnectionThread() const
-  {
-    MOZ_ASSERT(mDEBUGThread);
-    MOZ_ASSERT(PR_GetCurrentThread() == mDEBUGThread);
-  }
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DatabaseConnection)
-
-  mozIStorageConnection*
-  GetStorageConnection() const
-  {
-    if (mStorageConnection) {
-      AssertIsOnConnectionThread();
-      return mStorageConnection;
-    }
-
-    return nullptr;
-  }
-
-  UpdateRefcountFunction*
-  GetUpdateRefcountFunction() const
-  {
-    AssertIsOnConnectionThread();
-
-    return mUpdateRefcountFunction;
-  }
-
-  nsresult
-  GetCachedStatement(const nsACString& aQuery,
-                     CachedStatement* aCachedStatement);
-
-  template <size_t N>
-  nsresult
-  GetCachedStatement(const char (&aQuery)[N],
-                     CachedStatement* aCachedStatement)
-  {
-    static_assert(N > 1, "Must have a non-empty string!");
-    AssertIsOnConnectionThread();
-    MOZ_ASSERT(aCachedStatement);
-
-    return GetCachedStatement(NS_LITERAL_CSTRING(aQuery), aCachedStatement);
-  }
-
-  nsresult
-  BeginWriteTransaction();
-
-  void
-  FinishWriteTransaction();
-
-  nsresult
-  StartSavepoint();
-
-  nsresult
-  ReleaseSavepoint();
-
-  nsresult
-  RollbackSavepoint();
-
-  nsresult
-  Checkpoint(bool aIdle);
-
-  void
-  Close();
-
-private:
-  DatabaseConnection(mozIStorageConnection* aStorageConnection,
-                     FileManager* aFileManager);
-
-  ~DatabaseConnection();
-
-  nsresult
-  Init();
-};
-
-class MOZ_STACK_CLASS DatabaseConnection::AutoSavepoint final
-{
-  DatabaseConnection* mConnection;
-#ifdef DEBUG
-  const TransactionBase* mDEBUGTransaction;
-#endif
-
-public:
-  AutoSavepoint();
-  ~AutoSavepoint();
-
-  nsresult
-  Start(const TransactionBase* aConnection);
-
-  nsresult
-  Commit();
-};
-
-class DatabaseConnection::CachedStatement final
-{
-  friend class DatabaseConnection;
-
-  nsCOMPtr<mozIStorageStatement> mStatement;
-  Maybe<mozStorageStatementScoper> mScoper;
-
-#ifdef DEBUG
-  DatabaseConnection* mDEBUGConnection;
-#endif
-
-public:
-  CachedStatement();
-  ~CachedStatement();
-
-  void
-  AssertIsOnConnectionThread() const
-  {
-#ifdef DEBUG
-    if (mDEBUGConnection) {
-      mDEBUGConnection->AssertIsOnConnectionThread();
-    }
-    MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(!IsOnBackgroundThread());
-#endif
-  }
-
-  operator mozIStorageStatement*() const;
-
-  mozIStorageStatement*
-  operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN;
-
-  void
-  Reset();
-
-private:
-  // Only called by DatabaseConnection.
-  void
-  Assign(DatabaseConnection* aConnection,
-         already_AddRefed<mozIStorageStatement> aStatement);
-
-  // No funny business allowed.
-  CachedStatement(const CachedStatement&) = delete;
-  CachedStatement& operator=(const CachedStatement&) = delete;
-};
-
-class DatabaseConnection::UpdateRefcountFunction final
-  : public mozIStorageFunction
-{
-  class DatabaseUpdateFunction;
-  class FileInfoEntry;
-
-  enum UpdateType
-  {
-    eIncrement,
-    eDecrement
-  };
-
-  DatabaseConnection* mConnection;
-  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_ISUPPORTS
-  NS_DECL_MOZISTORAGEFUNCTION
-
-  UpdateRefcountFunction(DatabaseConnection* aConnection,
-                         FileManager* aFileManager);
-
-  nsresult
-  WillCommit();
-
-  void
-  DidCommit();
-
-  void
-  DidAbort();
-
-  void
-  StartSavepoint();
-
-  void
-  ReleaseSavepoint();
-
-  void
-  RollbackSavepoint();
-
-  void
-  Reset();
-
-private:
-  ~UpdateRefcountFunction()
-  { }
-
-  nsresult
-  ProcessValue(mozIStorageValueArray* aValues,
-               int32_t aIndex,
-               UpdateType aUpdateType);
-
-  nsresult
-  CreateJournals();
-
-  nsresult
-  RemoveJournals(const nsTArray<int64_t>& aJournals);
-};
-
-class DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction final
-{
-  CachedStatement mUpdateStatement;
-  CachedStatement mSelectStatement;
-  CachedStatement mInsertStatement;
-
-  UpdateRefcountFunction* mFunction;
-
-  nsresult mErrorCode;
-
-public:
-  explicit
-  DatabaseUpdateFunction(UpdateRefcountFunction* aFunction)
-    : mFunction(aFunction)
-    , mErrorCode(NS_OK)
-  {
-    MOZ_COUNT_CTOR(
-      DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction);
-  }
-
-  ~DatabaseUpdateFunction()
-  {
-    MOZ_COUNT_DTOR(
-      DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction);
-  }
-
-  bool
-  Update(int64_t aId, int32_t aDelta);
-
-  nsresult
-  ErrorCode() const
-  {
-    return mErrorCode;
-  }
-
-private:
-  nsresult
-  UpdateInternal(int64_t aId, int32_t aDelta);
-};
-
-class DatabaseConnection::UpdateRefcountFunction::FileInfoEntry final
-{
-  friend class UpdateRefcountFunction;
-
-  nsRefPtr<FileInfo> mFileInfo;
-  int32_t mDelta;
-  int32_t mSavepointDelta;
-
-public:
-  explicit
-  FileInfoEntry(FileInfo* aFileInfo)
-    : mFileInfo(aFileInfo)
-    , mDelta(0)
-    , mSavepointDelta(0)
-  {
-    MOZ_COUNT_CTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
-  }
-
-  ~FileInfoEntry()
-  {
-    MOZ_COUNT_DTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
-  }
-};
-
-class ConnectionPool final
-{
-public:
-  class FinishCallback;
-
-private:
-  class ConnectionRunnable;
-  class CheckpointConnectionRunnable;
-  class CloseConnectionRunnable;
-  struct DatabaseInfo;
-  struct DatabasesCompleteCallback;
-  class FinishCallbackWrapper;
-  struct IdleDatabaseInfo;
-  struct IdleResource;
-  struct IdleThreadInfo;
-  struct ThreadInfo;
-  class ThreadRunnable;
-  struct TransactionInfo;
-  struct TransactionInfoPair;
-
-  // This mutex guards mDatabases, see below.
-  Mutex mDatabasesMutex;
-
-  nsTArray<IdleThreadInfo> mIdleThreads;
-  nsTArray<IdleDatabaseInfo> mIdleDatabases;
-  nsCOMPtr<nsITimer> mIdleTimer;
-  TimeStamp mTargetIdleTime;
-
-  // Only modifed on the owning thread, but read on multiple threads. Therefore
-  // all modifications and all reads off the owning thread must be protected by
-  // mDatabasesMutex.
-  nsClassHashtable<nsCStringHashKey, DatabaseInfo> mDatabases;
-
-  nsClassHashtable<nsUint64HashKey, TransactionInfo> mTransactions;
-  nsTArray<TransactionInfo*> mQueuedTransactions;
-
-  nsTArray<nsAutoPtr<DatabasesCompleteCallback>> mCompleteCallbacks;
-
-  uint64_t mNextTransactionId;
-  uint32_t mTotalThreadCount;
-  bool mShutdownRequested;
-  bool mShutdownComplete;
-
-#ifdef DEBUG
-  PRThread* mDEBUGOwningThread;
-#endif
-
-public:
-  ConnectionPool();
-
-  void
-  AssertIsOnOwningThread() const
-#ifdef DEBUG
-  ;
-#else
-  { }
-#endif
-
-  nsresult
-  GetOrCreateConnection(const Database* aDatabase,
-                        DatabaseConnection** aConnection);
-
-  uint64_t
-  Start(const nsID& aBackgroundChildLoggingId,
-        const nsACString& aDatabaseId,
-        int64_t aLoggingSerialNumber,
-        const nsTArray<nsString>& aObjectStoreNames,
-        bool aIsWriteTransaction,
-        TransactionDatabaseOperationBase* aTransactionOp);
-
-  void
-  Dispatch(uint64_t aTransactionId, nsIRunnable* aRunnable);
-
-  void
-  Finish(uint64_t aTransactionId, FinishCallback* aCallback);
-
-  void
-  CloseDatabaseWhenIdle(const nsACString& aDatabaseId)
-  {
-    unused << CloseDatabaseWhenIdleInternal(aDatabaseId);
-  }
-
-  void
-  WaitForDatabasesToComplete(nsTArray<nsCString>&& aDatabaseIds,
-                             nsIRunnable* aCallback);
-
-  void
-  Shutdown();
-
-  NS_INLINE_DECL_REFCOUNTING(ConnectionPool)
-
-private:
-  ~ConnectionPool();
-
-  static void
-  IdleTimerCallback(nsITimer* aTimer, void* aClosure);
-
-  void
-  Cleanup();
-
-  void
-  AdjustIdleTimer();
-
-  void
-  CancelIdleTimer();
-
-  void
-  ShutdownThread(ThreadInfo& aThreadInfo);
-
-  void
-  CloseIdleDatabases();
-
-  void
-  ShutdownIdleThreads();
-
-  bool
-  ScheduleTransaction(TransactionInfo* aTransactionInfo,
-                      bool aFromQueuedTransactions);
-
-  void
-  NoteFinishedTransaction(uint64_t aTransactionId);
-
-  void
-  ScheduleQueuedTransactions(ThreadInfo& aThreadInfo);
-
-  void
-  NoteIdleDatabase(DatabaseInfo* aDatabaseInfo);
-
-  void
-  NoteClosedDatabase(DatabaseInfo* aDatabaseInfo);
-
-  bool
-  MaybeFireCallback(DatabasesCompleteCallback* aCallback);
-
-  void
-  CheckpointDatabase(DatabaseInfo* aDatabaseInfo);
-
-  void
-  CloseDatabase(DatabaseInfo* aDatabaseInfo);
-
-  bool
-  CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId);
-};
-
-class ConnectionPool::ConnectionRunnable
-  : public nsRunnable
-{
-protected:
-  DatabaseInfo* mDatabaseInfo;
-  nsCOMPtr<nsIEventTarget> mOwningThread;
-
-  explicit
-  ConnectionRunnable(DatabaseInfo* aDatabaseInfo);
-
-  virtual
-  ~ConnectionRunnable()
-  { }
-};
-
-class ConnectionPool::CheckpointConnectionRunnable final
-  : public ConnectionRunnable
-{
-public:
-  explicit
-  CheckpointConnectionRunnable(DatabaseInfo* aDatabaseInfo)
-    : ConnectionRunnable(aDatabaseInfo)
-  { }
-
-  NS_DECL_ISUPPORTS_INHERITED
-
-private:
-  ~CheckpointConnectionRunnable()
-  { }
-
-  NS_DECL_NSIRUNNABLE
-};
-
-class ConnectionPool::CloseConnectionRunnable final
-  : public ConnectionRunnable
-{
-public:
-  explicit
-  CloseConnectionRunnable(DatabaseInfo* aDatabaseInfo)
-    : ConnectionRunnable(aDatabaseInfo)
-  { }
-
-  NS_DECL_ISUPPORTS_INHERITED
-
-private:
-  ~CloseConnectionRunnable()
-  { }
-
-  NS_DECL_NSIRUNNABLE
-};
-
-struct ConnectionPool::ThreadInfo
-{
-  nsCOMPtr<nsIThread> mThread;
-  nsRefPtr<ThreadRunnable> mRunnable;
-
-  ThreadInfo();
-
-  explicit
-  ThreadInfo(const ThreadInfo& aOther);
-
-  ~ThreadInfo();
-};
-
-struct ConnectionPool::DatabaseInfo final
-{
-  friend class nsAutoPtr<DatabaseInfo>;
-
-  nsRefPtr<ConnectionPool> mConnectionPool;
-  const nsCString mDatabaseId;
-  nsRefPtr<DatabaseConnection> mConnection;
-  nsClassHashtable<nsStringHashKey, TransactionInfoPair> mBlockingTransactions;
-  nsTArray<TransactionInfo*> mTransactionsScheduledDuringClose;
-  nsTArray<TransactionInfo*> mScheduledWriteTransactions;
-  TransactionInfo* mRunningWriteTransaction;
-  ThreadInfo mThreadInfo;
-  uint32_t mReadTransactionCount;
-  uint32_t mWriteTransactionCount;
-  bool mNeedsCheckpoint;
-  bool mCloseOnIdle;
-  bool mClosing;
-
-#ifdef DEBUG
-  PRThread* mDEBUGConnectionThread;
-#endif
-
-  DatabaseInfo(ConnectionPool* aConnectionPool,
-               const nsACString& aDatabaseId);
-
-  void
-  AssertIsOnConnectionThread() const
-  {
-    MOZ_ASSERT(mDEBUGConnectionThread);
-    MOZ_ASSERT(PR_GetCurrentThread() == mDEBUGConnectionThread);
-  }
-
-  uint64_t
-  TotalTransactionCount() const
-  {
-    return mReadTransactionCount + mWriteTransactionCount;
-  }
-
-private:
-  ~DatabaseInfo();
-
-  DatabaseInfo(const DatabaseInfo&) = delete;
-  DatabaseInfo& operator=(const DatabaseInfo&) = delete;
-};
-
-struct ConnectionPool::DatabasesCompleteCallback final
-{
-  friend class nsAutoPtr<DatabasesCompleteCallback>;
-
-  nsTArray<nsCString> mDatabaseIds;
-  nsCOMPtr<nsIRunnable> mCallback;
-
-  DatabasesCompleteCallback(nsTArray<nsCString>&& aDatabaseIds,
-                            nsIRunnable* aCallback);
-
-private:
-  ~DatabasesCompleteCallback();
-};
-
-class NS_NO_VTABLE ConnectionPool::FinishCallback
-  : public nsIRunnable
-{
-public:
-  // Called on the owning thread before any additional transactions are
-  // unblocked.
-  virtual void
-  TransactionFinishedBeforeUnblock() = 0;
-
-  // Called on the owning thread after additional transactions may have been
-  // unblocked.
-  virtual void
-  TransactionFinishedAfterUnblock() = 0;
-
-protected:
-  FinishCallback()
-  { }
-
-  virtual ~FinishCallback()
-  { }
-};
-
-class ConnectionPool::FinishCallbackWrapper final
-  : public nsRunnable
-{
-  nsRefPtr<ConnectionPool> mConnectionPool;
-  nsRefPtr<FinishCallback> mCallback;
-  nsCOMPtr<nsIEventTarget> mOwningThread;
-  uint64_t mTransactionId;
-  bool mHasRunOnce;
-
-public:
-  FinishCallbackWrapper(ConnectionPool* aConnectionPool,
-                        uint64_t aTransactionId,
-                        FinishCallback* aCallback);
-
-  NS_DECL_ISUPPORTS_INHERITED
-
-private:
-  ~FinishCallbackWrapper();
-
-  NS_DECL_NSIRUNNABLE
-};
-
-struct ConnectionPool::IdleResource
-{
-  TimeStamp mIdleTime;
-
-protected:
-  explicit
-  IdleResource(const TimeStamp& aIdleTime);
-
-  explicit
-  IdleResource(const IdleResource& aOther);
-
-  ~IdleResource();
-};
-
-struct ConnectionPool::IdleDatabaseInfo final
-  : public IdleResource
-{
-  DatabaseInfo* mDatabaseInfo;
-
-public:
-  MOZ_IMPLICIT
-  IdleDatabaseInfo(DatabaseInfo* aDatabaseInfo);
-
-  explicit
-  IdleDatabaseInfo(const IdleDatabaseInfo& aOther);
-
-  ~IdleDatabaseInfo();
-
-  bool
-  operator==(const IdleDatabaseInfo& aOther) const
-  {
-    return mDatabaseInfo == aOther.mDatabaseInfo;
-  }
-
-  bool
-  operator<(const IdleDatabaseInfo& aOther) const
-  {
-    return mIdleTime < aOther.mIdleTime;
-  }
-};
-
-struct ConnectionPool::IdleThreadInfo final
-  : public IdleResource
-{
-  ThreadInfo mThreadInfo;
-
-public:
-  // Boo, this is needed because nsTArray::InsertElementSorted() doesn't yet
-  // work with rvalue references.
-  MOZ_IMPLICIT
-  IdleThreadInfo(const ThreadInfo& aThreadInfo);
-
-  explicit
-  IdleThreadInfo(const IdleThreadInfo& aOther);
-
-  ~IdleThreadInfo();
-
-  bool
-  operator==(const IdleThreadInfo& aOther) const
-  {
-    return mThreadInfo.mRunnable == aOther.mThreadInfo.mRunnable &&
-           mThreadInfo.mThread == aOther.mThreadInfo.mThread;
-  }
-
-  bool
-  operator<(const IdleThreadInfo& aOther) const
-  {
-    return mIdleTime < aOther.mIdleTime;
-  }
-};
-
-class ConnectionPool::ThreadRunnable final
-  : public nsRunnable
-{
-  // Only touched on the background thread.
-  static uint32_t sNextSerialNumber;
-
-  // Set at construction for logging.
-  const uint32_t mSerialNumber;
-
-  // These two values are only modified on the connection thread.
-  bool mFirstRun;
-  bool mContinueRunning;
-
-public:
-  ThreadRunnable();
-
-  NS_DECL_ISUPPORTS_INHERITED
-
-  uint32_t
-  SerialNumber() const
-  {
-    return mSerialNumber;
-  }
-
-private:
-  ~ThreadRunnable();
-
-  NS_DECL_NSIRUNNABLE
-};
-
-struct ConnectionPool::TransactionInfo final
-{
-  friend class nsAutoPtr<TransactionInfo>;
-
-  DatabaseInfo* mDatabaseInfo;
-  const nsID mBackgroundChildLoggingId;
-  const nsCString mDatabaseId;
-  const uint64_t mTransactionId;
-  const int64_t mLoggingSerialNumber;
-  const nsTArray<nsString> mObjectStoreNames;
-  nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlockedOn;
-  nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlocking;
-  nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
-  const bool mIsWriteTransaction;
-  bool mRunning;
-
-  DebugOnly<bool> mFinished;
-
-  TransactionInfo(DatabaseInfo* aDatabaseInfo,
-                  const nsID& aBackgroundChildLoggingId,
-                  const nsACString& aDatabaseId,
-                  uint64_t aTransactionId,
-                  int64_t aLoggingSerialNumber,
-                  const nsTArray<nsString>& aObjectStoreNames,
-                  bool aIsWriteTransaction,
-                  TransactionDatabaseOperationBase* aTransactionOp);
-
-  void
-  Schedule();
-
-private:
-  ~TransactionInfo();
-};
-
-struct ConnectionPool::TransactionInfoPair final
-{
-  friend class nsAutoPtr<TransactionInfoPair>;
-
-  // Multiple reading transactions can block future writes.
-  nsTArray<TransactionInfo*> mLastBlockingWrites;
-  // But only a single writing transaction can block future reads.
-  TransactionInfo* mLastBlockingReads;
-
-  TransactionInfoPair();
-
-private:
-  ~TransactionInfoPair();
-};
-
-/*******************************************************************************
  * Actor class declarations
  ******************************************************************************/
 
 class DatabaseOperationBase
   : public nsRunnable
   , public mozIStorageProgressHandler
 {
 protected:
@@ -5188,151 +2660,78 @@ protected:
   static uint64_t
   ReinterpretDoubleAsUInt64(double aDouble);
 
   static nsresult
   GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
                                           uint32_t aDataIndex,
                                           uint32_t aFileIdsIndex,
                                           FileManager* aFileManager,
-                                          StructuredCloneReadInfo* aInfo)
-  {
-    return GetStructuredCloneReadInfoFromSource(aStatement,
-                                                aDataIndex,
-                                                aFileIdsIndex,
-                                                aFileManager,
-                                                aInfo);
-  }
-
-  static nsresult
-  GetStructuredCloneReadInfoFromValueArray(mozIStorageValueArray* aValues,
-                                           uint32_t aDataIndex,
-                                           uint32_t aFileIdsIndex,
-                                           FileManager* aFileManager,
-                                           StructuredCloneReadInfo* aInfo)
-  {
-    return GetStructuredCloneReadInfoFromSource(aValues,
-                                                aDataIndex,
-                                                aFileIdsIndex,
-                                                aFileManager,
-                                                aInfo);
-  }
+                                          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
-  GetUniqueIndexTableForObjectStore(
-                               TransactionBase* aTransaction,
-                               int64_t aObjectStoreId,
-                               Maybe<UniqueIndexTable>& aMaybeUniqueIndexTable);
-
-  static nsresult
-  IndexDataValuesFromUpdateInfos(
-                              const nsTArray<IndexUpdateInfo>& aUpdateInfos,
-                              const UniqueIndexTable& aUniqueIndexTable,
-                              FallibleTArray<IndexDataValue>& aIndexValues);
-
-  static nsresult
-  InsertIndexTableRows(DatabaseConnection* aConnection,
-                       const int64_t aObjectStoreId,
-                       const Key& aObjectStoreKey,
-                       const FallibleTArray<IndexDataValue>& aIndexValues);
-
-  static nsresult
-  DeleteIndexDataTableRows(DatabaseConnection* aConnection,
-                           const Key& aObjectStoreKey,
-                           const FallibleTArray<IndexDataValue>& aIndexValues);
-
-  static nsresult
-  DeleteObjectStoreDataTableRowsWithIndexes(DatabaseConnection* aConnection,
-                                            const int64_t aObjectStoreId,
-                                            const OptionalKeyRange& aKeyRange);
-
-  static nsresult
-  UpdateIndexValues(DatabaseConnection* aConnection,
-                    const int64_t aObjectStoreId,
-                    const Key& aObjectStoreKey,
-                    const FallibleTArray<IndexDataValue>& aIndexValues);
-
-#ifdef DEBUG
-  static bool
-  ObjectStoreHasIndexes(DatabaseConnection* aConnection,
-                        const int64_t aObjectStoreId);
-#endif
+  UpdateIndexes(TransactionBase* aTransaction,
+                const UniqueIndexTable& aUniqueIndexTable,
+                const Key& aObjectStoreKey,
+                bool aOverwrite,
+                int64_t aObjectDataId,
+                const nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
 
 private:
-  template <typename T>
-  static nsresult
-  GetStructuredCloneReadInfoFromSource(T* aSource,
-                                       uint32_t aDataIndex,
-                                       uint32_t aFileIdsIndex,
-                                       FileManager* aFileManager,
-                                       StructuredCloneReadInfo* aInfo);
-
-  static nsresult
-  GetStructuredCloneReadInfoFromBlob(const uint8_t* aBlobData,
-                                     uint32_t aBlobDataLength,
-                                     const nsAString& aFileIds,
-                                     FileManager* aFileManager,
-                                     StructuredCloneReadInfo* aInfo);
-
   // Not to be overridden by subclasses.
   NS_DECL_MOZISTORAGEPROGRESSHANDLER
 };
 
 class MOZ_STACK_CLASS DatabaseOperationBase::AutoSetProgressHandler final
 {
   mozIStorageConnection* mConnection;
   DebugOnly<DatabaseOperationBase*> mDEBUGDatabaseOp;
 
 public:
-  AutoSetProgressHandler();
+  AutoSetProgressHandler()
+    : mConnection(nullptr)
+    , mDEBUGDatabaseOp(nullptr)
+  { }
 
   ~AutoSetProgressHandler();
 
   nsresult
-  Register(mozIStorageConnection* aConnection,
-           DatabaseOperationBase* aDatabaseOp);
+  Register(DatabaseOperationBase* aDatabaseOp,
+           const nsCOMPtr<mozIStorageConnection>& aConnection);
 };
 
 class TransactionDatabaseOperationBase
   : public DatabaseOperationBase
 {
   nsRefPtr<TransactionBase> mTransaction;
   const int64_t mTransactionLoggingSerialNumber;
   const bool mTransactionIsAborted;
 
 public:
   void
-  AssertIsOnConnectionThread() const
+  AssertIsOnTransactionThread() const
 #ifdef DEBUG
   ;
 #else
   { }
 #endif
 
   void
-  DispatchToConnectionPool();
-
-  TransactionBase*
-  Transaction() const
-  {
-    MOZ_ASSERT(mTransaction);
-
-    return mTransaction;
-  }
+  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
@@ -5346,41 +2745,41 @@ protected:
   TransactionDatabaseOperationBase(TransactionBase* aTransaction);
 
   TransactionDatabaseOperationBase(TransactionBase* aTransaction,
                                    uint64_t aLoggingSerialNumber);
 
   virtual
   ~TransactionDatabaseOperationBase();
 
-  virtual void
-  RunOnConnectionThread();
-
   // 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(DatabaseConnection* aConnection) = 0;
+  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 final
   : public PBackgroundIDBFactoryParent
@@ -5452,25 +2851,21 @@ private:
                                       override;
 };
 
 class Database final
   : public PBackgroundIDBDatabaseParent
 {
   friend class VersionChangeTransaction;
 
-  class StartTransactionOp;
-
-private:
   nsRefPtr<Factory> mFactory;
   nsRefPtr<FullDatabaseMetadata> mMetadata;
   nsRefPtr<FileManager> mFileManager;
   nsRefPtr<DatabaseOfflineStorage> mOfflineStorage;
   nsTHashtable<nsPtrHashKey<TransactionBase>> mTransactions;
-  nsRefPtr<DatabaseConnection> mConnection;
   const PrincipalInfo mPrincipalInfo;
   const nsCString mGroup;
   const nsCString mOrigin;
   const nsCString mId;
   const nsString mFilePath;
   const PersistenceType mPersistenceType;
   const bool mChromeWriteAccessAllowed;
   bool mClosed;
@@ -5485,31 +2880,16 @@ public:
            const PrincipalInfo& aPrincipalInfo,
            const nsACString& aGroup,
            const nsACString& aOrigin,
            FullDatabaseMetadata* aMetadata,
            FileManager* aFileManager,
            already_AddRefed<DatabaseOfflineStorage> aOfflineStorage,
            bool aChromeWriteAccessAllowed);
 
-  void
-  AssertIsOnConnectionThread() const
-  {
-#ifdef DEBUG
-    if (mConnection) {
-      MOZ_ASSERT(mConnection);
-      mConnection->AssertIsOnConnectionThread();
-    } else {
-      MOZ_ASSERT(!NS_IsMainThread());
-      MOZ_ASSERT(!IsOnBackgroundThread());
-      MOZ_ASSERT(mInvalidated);
-    }
-#endif
-  }
-
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Database)
 
   void
   Invalidate();
 
   const PrincipalInfo&
   GetPrincipalInfo() const
   {
@@ -5572,22 +2952,16 @@ public:
   GetLoggingInfo() const
   {
     AssertIsOnBackgroundThread();
     MOZ_ASSERT(mFactory);
 
     return mFactory->GetLoggingInfo();
   }
 
-  void
-  ReleaseTransactionThreadObjects();
-
-  void
-  ReleaseBackgroundThreadObjects();
-
   bool
   RegisterTransaction(TransactionBase* aTransaction);
 
   void
   UnregisterTransaction(TransactionBase* aTransaction);
 
   void
   SetActorAlive();
@@ -5619,49 +2993,28 @@ public:
   bool
   IsInvalidated() const
   {
     AssertIsOnBackgroundThread();
 
     return mInvalidated;
   }
 
-  nsresult
-  EnsureConnection();
-
-  DatabaseConnection*
-  GetConnection() const
-  {
-#ifdef DEBUG
-    if (mConnection) {
-      mConnection->AssertIsOnConnectionThread();
-    }
-#endif
-
-    return mConnection;
-  }
-
 private:
   // Reference counted.
   ~Database()
   {
     MOZ_ASSERT(mClosed);
     MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
   }
 
   bool
   CloseInternal();
 
   void
-  CloseConnection();
-
-  void
-  ConnectionClosedCallback();
-
-  void
   CleanupMetadata();
 
   // IPDL methods are only called by IPDL.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual PBackgroundIDBDatabaseFileParent*
   AllocPBackgroundIDBDatabaseFileParent(PBlobParent* aBlobParent)
@@ -5708,47 +3061,16 @@ private:
 
   virtual bool
   RecvBlocked() override;
 
   virtual bool
   RecvClose() override;
 };
 
-class Database::StartTransactionOp final
-  : public TransactionDatabaseOperationBase
-{
-  friend class Database;
-
-private:
-  explicit
-  StartTransactionOp(TransactionBase* aTransaction)
-    : TransactionDatabaseOperationBase(aTransaction,
-                                       /* aLoggingSerialNumber */ 0)
-  { }
-
-  ~StartTransactionOp()
-  { }
-
-  virtual void
-  RunOnConnectionThread() override;
-
-  virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
-
-  virtual nsresult
-  SendSuccessResult() override;
-
-  virtual bool
-  SendFailureResult(nsresult aResultCode) override;
-
-  virtual void
-  Cleanup() override;
-};
-
 class DatabaseFile final
   : public PBackgroundIDBDatabaseFileParent
 {
   friend class Database;
 
   nsRefPtr<FileImpl> mBlobImpl;
   nsRefPtr<FileInfo> mFileInfo;
 
@@ -5817,46 +3139,59 @@ private:
   }
 };
 
 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;
-  uint64_t mTransactionId;
+  const uint64_t mTransactionId;
   const nsCString mDatabaseId;
   const int64_t mLoggingSerialNumber;
   uint64_t mActiveRequestCount;
   Atomic<bool> mInvalidatedOnAnyThread;
-  const Mode mMode;
+  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
-  AssertIsOnConnectionThread() const
-  {
-    MOZ_ASSERT(mDatabase);
-    mDatabase->AssertIsOnConnectionThread();
+  AssertIsOnTransactionThread() const
+  {
+    MOZ_ASSERT(mTransactionThread);
+    MOZ_ASSERT(PR_GetCurrentThread() == mTransactionThread);
   }
 
   bool
   IsActorDestroyed() const
   {
     AssertIsOnBackgroundThread();
 
     return mActorDestroyed;
@@ -5875,31 +3210,56 @@ public:
   // May be called on any thread, but is more expensive than IsInvalidated().
   bool
   IsInvalidatedOnAnyThread() const
   {
     return mInvalidatedOnAnyThread;
   }
 
   void
-  SetActive(uint64_t aTransactionId)
+  SetActive()
   {
     AssertIsOnBackgroundThread();
-    MOZ_ASSERT(aTransactionId);
-
-    mTransactionId = aTransactionId;
+
     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
@@ -5911,16 +3271,17 @@ public:
   GetMode() const
   {
     return mMode;
   }
 
   Database*
   GetDatabase() const
   {
+    AssertIsOnBackgroundThread();
     MOZ_ASSERT(mDatabase);
 
     return mDatabase;
   }
 
   DatabaseLoggingInfo*
   GetLoggingInfo() const
   {
@@ -5961,16 +3322,25 @@ public:
   }
 
   void
   NoteModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
 
   void
   ForgetModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata);
 
+  nsresult
+  StartSavepoint();
+
+  nsresult
+  ReleaseSavepoint();
+
+  nsresult
+  RollbackSavepoint();
+
   void
   NoteActiveRequest();
 
   void
   NoteFinishedRequest();
 
   void
   Invalidate();
@@ -6049,20 +3419,28 @@ protected:
 
   bool
   DeallocCursor(PBackgroundIDBCursorParent* aActor);
 
   virtual void
   UpdateMetadata(nsresult aResult)
   { }
 
-  virtual void
+  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;
@@ -6077,17 +3455,17 @@ private:
   VerifyRequestParams(const OptionalKeyRange& aKeyRange) const;
 
   void
   CommitOrAbort();
 };
 
 class TransactionBase::CommitOp final
   : public DatabaseOperationBase
-  , public ConnectionPool::FinishCallback
+  , public TransactionThreadPool::FinishCallback
 {
   friend class TransactionBase;
 
   nsRefPtr<TransactionBase> mTransaction;
   nsresult mResultCode;
 
 private:
   CommitOp(TransactionBase* aTransaction, nsresult aResultCode);
@@ -6098,36 +3476,236 @@ private:
   // Writes new autoIncrement counts to database.
   nsresult
   WriteAutoIncrementCounts();
 
   // Updates counts after a database activity has finished.
   void
   CommitOrRollbackAutoIncrementCounts();
 
-  void
-  AssertForeignKeyConsistency(DatabaseConnection* aConnection)
-#ifdef DEBUG
-  ;
-#else
-  { }
-#endif
-
   NS_DECL_NSIRUNNABLE
 
   virtual void
   TransactionFinishedBeforeUnblock() override;
 
   virtual void
   TransactionFinishedAfterUnblock() override;
 
 public:
+  void
+  AssertIsOnTransactionThread() const
+  {
+    MOZ_ASSERT(mTransaction);
+    mTransaction->AssertIsOnTransactionThread();
+  }
+
   NS_DECL_ISUPPORTS_INHERITED
 };
 
+class TransactionBase::UpdateRefcountFunction 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 final
+{
+  TransactionBase* mTransaction;
+
+public:
+  AutoSavepoint()
+    : mTransaction(nullptr)
+  { }
+
+  ~AutoSavepoint();
+
+  nsresult
+  Start(TransactionBase* aTransaction);
+
+  nsresult
+  Commit();
+};
+
+class TransactionBase::CachedStatement final
+{
+  friend class TransactionBase;
+
+  nsCOMPtr<mozIStorageStatement> mStatement;
+  Maybe<mozStorageStatementScoper> mScoper;
+
+public:
+  CachedStatement()
+  { }
+
+  ~CachedStatement()
+  { }
+
+  operator mozIStorageStatement*()
+  {
+    return mStatement;
+  }
+
+  mozIStorageStatement*
+  operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN
+  {
+    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&) = delete;
+  CachedStatement& operator=(const CachedStatement&) = delete;
+};
+
 class NormalTransaction final
   : public TransactionBase
   , public PBackgroundIDBTransactionParent
 {
   friend class Database;
 
   nsTArray<nsRefPtr<FullObjectStoreMetadata>> mObjectStores;
 
@@ -6140,17 +3718,17 @@ private:
   // Reference counted.
   ~NormalTransaction()
   { }
 
   bool
   IsSameProcessActor();
 
   // Only called by TransactionBase.
-  virtual void
+  virtual bool
   SendCompleteNotification(nsresult aResult) override;
 
   // IPDL methods are only called by IPDL.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual bool
   RecvDeleteMe() override;
@@ -6214,17 +3792,17 @@ private:
   void
   SetActorAlive();
 
   // Only called by TransactionBase.
   virtual void
   UpdateMetadata(nsresult aResult) override;
 
   // Only called by TransactionBase.
-  virtual void
+  virtual bool
   SendCompleteNotification(nsresult aResult) override;
 
   // IPDL methods are only called by IPDL.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual bool
   RecvDeleteMe() override;
@@ -6559,19 +4137,16 @@ private:
   void
   AssertMetadataConsistency(const FullDatabaseMetadata* aMetadata)
 #ifdef DEBUG
   ;
 #else
   { }
 #endif
 
-  void
-  ConnectionClosedCallback();
-
   virtual nsresult
   QuotaManagerOpen() override;
 
   virtual nsresult
   DoDatabaseWork() override;
 
   virtual nsresult
   BeginVersionChange() override;
@@ -6611,17 +4186,17 @@ private:
     MOZ_ASSERT(aOpenDatabaseOp);
     MOZ_ASSERT(mRequestedVersion);
   }
 
   ~VersionChangeOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual nsresult
   SendSuccessResult() override;
 
   virtual bool
   SendFailureResult(nsresult aResultCode) override;
 
   virtual void
@@ -6701,21 +4276,16 @@ private:
   RunOnMainThread();
 
   nsresult
   RunOnIOThread();
 
   void
   RunOnOwningThread();
 
-  nsresult
-  DeleteFile(nsIFile* aDirectory,
-             const nsAString& aFilename,
-             QuotaManager* aQuotaManager);
-
   NS_DECL_NSIRUNNABLE
 };
 
 class VersionChangeTransactionOp
   : public TransactionDatabaseOperationBase
 {
 public:
   virtual void
@@ -6754,55 +4324,50 @@ private:
   {
     MOZ_ASSERT(aMetadata.id());
   }
 
   ~CreateObjectStoreOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 };
 
 class DeleteObjectStoreOp final
   : public VersionChangeTransactionOp
 {
   friend class VersionChangeTransaction;
 
   const nsRefPtr<FullObjectStoreMetadata> mMetadata;
-  const bool mIsLastObjectStore;
-  const bool mObjectStoreHasIndexes;
 
 private:
   // Only created by VersionChangeTransaction.
   DeleteObjectStoreOp(VersionChangeTransaction* aTransaction,
-                      FullObjectStoreMetadata* const aMetadata,
-                      const bool aIsLastObjectStore)
+                      FullObjectStoreMetadata* const aMetadata)
     : VersionChangeTransactionOp(aTransaction)
     , mMetadata(aMetadata)
-    , mIsLastObjectStore(aIsLastObjectStore)
-    , mObjectStoreHasIndexes(aMetadata->HasLiveIndexes())
   {
     MOZ_ASSERT(aMetadata->mCommonMetadata.id());
   }
 
   ~DeleteObjectStoreOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 };
 
 class CreateIndexOp final
   : public VersionChangeTransactionOp
 {
   friend class VersionChangeTransaction;
 
   class ThreadLocalJSRuntime;
-  class UpdateIndexDataValuesFunction;
+  friend class ThreadLocalJSRuntime;
 
   static const unsigned int kBadThreadLocalIndex =
     static_cast<unsigned int>(-1);
 
   static unsigned int sThreadLocalIndex;
 
   const IndexMetadata mMetadata;
   Maybe<UniqueIndexTable> mMaybeUniqueIndexTable;
@@ -6814,27 +4379,27 @@ private:
   // Only created by VersionChangeTransaction.
   CreateIndexOp(VersionChangeTransaction* aTransaction,
                 const int64_t aObjectStoreId,
                 const IndexMetadata& aMetadata);
 
   ~CreateIndexOp()
   { }
 
-  nsresult
-  InsertDataFromObjectStore(DatabaseConnection* aConnection);
+  static void
+  InitThreadLocals();
 
   nsresult
-  InsertDataFromObjectStoreInternal(DatabaseConnection* aConnection);
+  InsertDataFromObjectStore(TransactionBase* aTransaction);
 
   virtual bool
   Init(TransactionBase* aTransaction) override;
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 };
 
 class CreateIndexOp::ThreadLocalJSRuntime final
 {
   friend class CreateIndexOp;
   friend class nsAutoPtr<ThreadLocalJSRuntime>;
 
   static const JSClass kGlobalClass;
@@ -6881,74 +4446,38 @@ private:
       JS_DestroyRuntime(mRuntime);
     }
   }
 
   bool
   Init();
 };
 
-class CreateIndexOp::UpdateIndexDataValuesFunction final
-  : public mozIStorageFunction
-{
-  nsRefPtr<CreateIndexOp> mOp;
-  nsRefPtr<DatabaseConnection> mConnection;
-  JSContext* mCx;
-
-public:
-  UpdateIndexDataValuesFunction(CreateIndexOp* aOp,
-                                DatabaseConnection* aConnection,
-                                JSContext* aCx)
-    : mOp(aOp)
-    , mConnection(aConnection)
-    , mCx(aCx)
-  {
-    MOZ_ASSERT(aOp);
-    MOZ_ASSERT(aConnection);
-    aConnection->AssertIsOnConnectionThread();
-    MOZ_ASSERT(aCx);
-  }
-
-  NS_DECL_ISUPPORTS
-
-private:
-  ~UpdateIndexDataValuesFunction()
-  { }
-
-  NS_DECL_MOZISTORAGEFUNCTION
-};
-
 class DeleteIndexOp final
   : public VersionChangeTransactionOp
 {
   friend class VersionChangeTransaction;
 
-  const int64_t mObjectStoreId;
   const int64_t mIndexId;
-  const bool mUnique;
-  const bool mIsLastIndex;
 
 private:
   // Only created by VersionChangeTransaction.
   DeleteIndexOp(VersionChangeTransaction* aTransaction,
-                const int64_t aObjectStoreId,
-                const int64_t aIndexId,
-                const bool aUnique,
-                const bool aIsLastIndex);
+                const int64_t aIndexId)
+    : VersionChangeTransactionOp(aTransaction)
+    , mIndexId(aIndexId)
+  {
+    MOZ_ASSERT(aIndexId);
+  }
 
   ~DeleteIndexOp()
   { }
 
-  nsresult
-  RemoveReferencesToIndex(DatabaseConnection* aConnection,
-                          const Key& aObjectDataKey,
-                          FallibleTArray<IndexDataValue>& aIndexValues);
-
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 };
 
 class NormalTransactionOp
   : public TransactionDatabaseOperationBase
   , public PBackgroundIDBRequestParent
 {
   DebugOnly<bool> mResponseSent;
 
@@ -7002,37 +4531,33 @@ class ObjectStoreAddOrPutRequestOp final
 
   nsRefPtr<FileManager> mFileManager;
 
   Key mResponse;
   const nsCString mGroup;
   const nsCString mOrigin;
   const PersistenceType mPersistenceType;
   const bool mOverwrite;
-  const bool mObjectStoreHasIndexes;
 
 private:
   // Only created by TransactionBase.
   ObjectStoreAddOrPutRequestOp(TransactionBase* aTransaction,
                                const RequestParams& aParams);
 
   ~ObjectStoreAddOrPutRequestOp()
   { }
 
   nsresult
-  RemoveOldIndexDataValues(DatabaseConnection* aConnection);
-
-  nsresult
   CopyFileData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream);
 
   virtual bool
   Init(TransactionBase* aTransaction) override;
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override;
 
   virtual void
   Cleanup() override;
 };
 
@@ -7081,17 +4606,17 @@ private:
   ~ObjectStoreGetRequestOp()
   { }
 
   nsresult
   ConvertResponse(uint32_t aIndex,
                   SerializedStructuredCloneReadInfo& aSerializedInfo);
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override;
 };
 
 class ObjectStoreGetAllKeysRequestOp final
   : public NormalTransactionOp
 {
@@ -7107,66 +4632,70 @@ private:
     : NormalTransactionOp(aTransaction)
     , mParams(aParams)
   { }
 
   ~ObjectStoreGetAllKeysRequestOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override;
 };
 
 class ObjectStoreDeleteRequestOp final
   : public NormalTransactionOp
 {
   friend class TransactionBase;
 
   const ObjectStoreDeleteParams mParams;
   ObjectStoreDeleteResponse mResponse;
-  const bool mObjectStoreHasIndexes;
 
 private:
   ObjectStoreDeleteRequestOp(TransactionBase* aTransaction,
-                             const ObjectStoreDeleteParams& aParams);
+                             const ObjectStoreDeleteParams& aParams)
+    : NormalTransactionOp(aTransaction)
+    , mParams(aParams)
+  { }
 
   ~ObjectStoreDeleteRequestOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override
   {
     aResponse = Move(mResponse);
   }
 };
 
 class ObjectStoreClearRequestOp final
   : public NormalTransactionOp
 {
   friend class TransactionBase;
 
   const ObjectStoreClearParams mParams;
   ObjectStoreClearResponse mResponse;
-  const bool mObjectStoreHasIndexes;
 
 private:
   ObjectStoreClearRequestOp(TransactionBase* aTransaction,
-                            const ObjectStoreClearParams& aParams);
+                            const ObjectStoreClearParams& aParams)
+    : NormalTransactionOp(aTransaction)
+    , mParams(aParams)
+  { }
 
   ~ObjectStoreClearRequestOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override
   {
     aResponse = Move(mResponse);
   }
 };
 
@@ -7184,17 +4713,17 @@ private:
     : NormalTransactionOp(aTransaction)
     , mParams(aParams)
   { }
 
   ~ObjectStoreCountRequestOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override
   {
     aResponse = Move(mResponse);
   }
 };
 
@@ -7238,17 +4767,17 @@ private:
   IndexGetRequestOp(TransactionBase* aTransaction,
                     const RequestParams& aParams,
                     bool aGetAll);
 
   ~IndexGetRequestOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override;
 };
 
 class IndexGetKeyRequestOp final
   : public IndexRequestOpBase
 {
@@ -7264,17 +4793,17 @@ private:
   IndexGetKeyRequestOp(TransactionBase* aTransaction,
                        const RequestParams& aParams,
                        bool aGetAll);
 
   ~IndexGetKeyRequestOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override;
 };
 
 class IndexCountRequestOp final
   : public IndexRequestOpBase
 {
@@ -7290,17 +4819,17 @@ private:
     : IndexRequestOpBase(aTransaction, aParams)
     , mParams(aParams.get_IndexCountParams())
   { }
 
   ~IndexCountRequestOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override
   {
     aResponse = Move(mResponse);
   }
 };
 
@@ -7429,29 +4958,29 @@ private:
   // Reference counted.
   ~OpenOp()
   { }
 
   void
   GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen);
 
   nsresult
-  DoObjectStoreDatabaseWork(DatabaseConnection* aConnection);
+  DoObjectStoreDatabaseWork(TransactionBase* aTransaction);
 
   nsresult
-  DoObjectStoreKeyDatabaseWork(DatabaseConnection* aConnection);
+  DoObjectStoreKeyDatabaseWork(TransactionBase* aTransaction);
 
   nsresult
-  DoIndexDatabaseWork(DatabaseConnection* aConnection);
+  DoIndexDatabaseWork(TransactionBase* aTransaction);
 
   nsresult
-  DoIndexKeyDatabaseWork(DatabaseConnection* aConnection);
+  DoIndexKeyDatabaseWork(TransactionBase* aTransaction);
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual nsresult
   SendSuccessResult() override;
 };
 
 class Cursor::ContinueOp final
   : public Cursor::CursorOpBase
 {
@@ -7468,17 +4997,17 @@ private:
     MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
   }
 
   // Reference counted.
   ~ContinueOp()
   { }
 
   virtual nsresult
-  DoDatabaseWork(DatabaseConnection* aConnection) override;
+  DoDatabaseWork(TransactionBase* aTransaction) override;
 
   virtual nsresult
   SendSuccessResult() override;
 };
 
 class PermissionRequestHelper final
   : public PermissionRequestBase
   , public PIndexedDBPermissionRequestParent
@@ -7543,24 +5072,20 @@ class DatabaseLoggingInfo final
 #ifdef DEBUG
   // Just for potential warnings.
   friend class Factory;
 #endif
 
   LoggingInfo mLoggingInfo;
 
 public:
-  explicit
-  DatabaseLoggingInfo(const LoggingInfo& aLoggingInfo)
+  explicit DatabaseLoggingInfo(const LoggingInfo& aLoggingInfo)
     : mLoggingInfo(aLoggingInfo)
   {
     AssertIsOnBackgroundThread();
-    MOZ_ASSERT(aLoggingInfo.nextTransactionSerialNumber());
-    MOZ_ASSERT(aLoggingInfo.nextVersionChangeTransactionSerialNumber());
-    MOZ_ASSERT(aLoggingInfo.nextRequestSerialNumber());
   }
 
   const nsID&
   Id() const
   {
     AssertIsOnBackgroundThread();
 
     return mLoggingInfo.backgroundChildLoggingId();
@@ -8090,17 +5615,19 @@ ConvertBlobsToActors(PBackgroundParent* 
  ******************************************************************************/
 
 // Maps a database id to information about live database actors.
 typedef nsClassHashtable<nsCStringHashKey, DatabaseActorInfo>
         DatabaseActorHashtable;
 
 StaticAutoPtr<DatabaseActorHashtable> gLiveDatabaseHashtable;
 
-StaticRefPtr<ConnectionPool> gConnectionPool;
+StaticRefPtr<nsRunnable> gStartTransactionRunnable;
+
+StaticRefPtr<TransactionThreadPool> gTransactionThreadPool;
 
 typedef nsDataHashtable<nsIDHashKey, DatabaseLoggingInfo*>
         DatabaseLoggingInfoHashtable;
 
 StaticAutoPtr<DatabaseLoggingInfoHashtable> gLoggingInfoHashtable;
 
 #ifdef DEBUG
 
@@ -8118,23 +5645,16 @@ PBackgroundIDBFactoryParent*
 AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo)
 {
   AssertIsOnBackgroundThread();
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) {
     return nullptr;
   }
 
-  if (NS_WARN_IF(!aLoggingInfo.nextTransactionSerialNumber()) ||
-      NS_WARN_IF(!aLoggingInfo.nextVersionChangeTransactionSerialNumber()) ||
-      NS_WARN_IF(!aLoggingInfo.nextRequestSerialNumber())) {
-    ASSERT_UNLESS_FUZZING();
-    return nullptr;
-  }
-
   nsRefPtr<Factory> actor = Factory::Create(aLoggingInfo);
   MOZ_ASSERT(actor);
 
   return actor.forget().take();
 }
 
 bool
 RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor,
@@ -8208,2654 +5728,19 @@ CreateQuotaClient()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsRefPtr<QuotaClient> client = new QuotaClient();
   return client.forget();
 }
 
 /*******************************************************************************
- * DatabaseConnection implementation
- ******************************************************************************/
-
-DatabaseConnection::DatabaseConnection(
-                                      mozIStorageConnection* aStorageConnection,
-                                      FileManager* aFileManager)
-  : mStorageConnection(aStorageConnection)
-  , mFileManager(aFileManager)
-#ifdef DEBUG
-  , mDEBUGSavepointCount(0)
-  , mDEBUGThread(PR_GetCurrentThread())
-  , mDEBUGInWriteTransaction(false)
-#endif
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(aStorageConnection);
-  MOZ_ASSERT(aFileManager);
-}
-
-DatabaseConnection::~DatabaseConnection()
-{
-  MOZ_ASSERT(!mStorageConnection);
-  MOZ_ASSERT(!mFileManager);
-  MOZ_ASSERT(!mCachedStatements.Count());
-  MOZ_ASSERT(!mUpdateRefcountFunction);
-  MOZ_ASSERT(!mDEBUGSavepointCount);
-  MOZ_ASSERT(!mDEBUGInWriteTransaction);
-}
-
-nsresult
-DatabaseConnection::Init()
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(!mDEBUGInWriteTransaction);
-
-  CachedStatement stmt;
-  nsresult rv = GetCachedStatement("BEGIN", &stmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = stmt->Execute();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-nsresult
-DatabaseConnection::GetCachedStatement(const nsACString& aQuery,
-                                       CachedStatement* aCachedStatement)
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(!aQuery.IsEmpty());
-  MOZ_ASSERT(aCachedStatement);
-  MOZ_ASSERT(mStorageConnection);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::GetCachedStatement",
-                 js::ProfileEntry::Category::STORAGE);
-
-  nsCOMPtr<mozIStorageStatement> stmt;
-
-  if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) {
-    nsresult rv =
-      mStorageConnection->CreateStatement(aQuery, getter_AddRefs(stmt));
-    if (NS_FAILED(rv)) {
-#ifdef DEBUG
-      nsCString msg;
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-        mStorageConnection->GetLastErrorString(msg)));
-
-      nsAutoCString error =
-        NS_LITERAL_CSTRING("The statement '") + aQuery +
-        NS_LITERAL_CSTRING("' failed to compile with the error message '") +
-        msg + NS_LITERAL_CSTRING("'.");
-
-      NS_WARNING(error.get());
-#endif
-      return rv;
-    }
-
-    mCachedStatements.Put(aQuery, stmt);
-  }
-
-  aCachedStatement->Assign(this, stmt.forget());
-  return NS_OK;
-}
-
-nsresult
-DatabaseConnection::BeginWriteTransaction()
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(mStorageConnection);
-  MOZ_ASSERT(!mDEBUGInWriteTransaction);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::BeginWriteTransaction",
-                 js::ProfileEntry::Category::STORAGE);
-
-  // Release our read locks.
-  CachedStatement commitStmt;
-  nsresult rv = GetCachedStatement("ROLLBACK", &commitStmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = commitStmt->Execute();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (!mUpdateRefcountFunction) {
-    MOZ_ASSERT(mFileManager);
-
-    nsRefPtr<UpdateRefcountFunction> function =
-      new UpdateRefcountFunction(this, mFileManager);
-
-    rv =
-      mStorageConnection->CreateFunction(NS_LITERAL_CSTRING("update_refcount"),
-                                         /* aNumArguments */ 2,
-                                         function);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    mUpdateRefcountFunction.swap(function);
-  }
-
-  CachedStatement beginStmt;
-  rv = GetCachedStatement("BEGIN IMMEDIATE", &beginStmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = beginStmt->Execute();
-  if (rv == NS_ERROR_STORAGE_BUSY) {
-    NS_WARNING("Received NS_ERROR_STORAGE_BUSY when attempting to start write "
-               "transaction, retrying for up to 10 seconds");
-
-    // Another thread must be using the database. Wait up to 10 seconds for
-    // that to complete.
-    TimeStamp start = TimeStamp::NowLoRes();
-
-    while (true) {
-      PR_Sleep(PR_MillisecondsToInterval(100));
-
-      rv = beginStmt->Execute();
-      if (rv != NS_ERROR_STORAGE_BUSY ||
-          TimeStamp::NowLoRes() - start > TimeDuration::FromSeconds(10)) {
-        break;
-      }
-    }
-  }
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-#ifdef DEBUG
-  mDEBUGInWriteTransaction = true;
-#endif
-
-  return NS_OK;
-}
-
-void
-DatabaseConnection::FinishWriteTransaction()
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(mStorageConnection);
-  MOZ_ASSERT(mUpdateRefcountFunction);
-  MOZ_ASSERT(mDEBUGInWriteTransaction);
-
-  mUpdateRefcountFunction->Reset();
-
-#ifdef DEBUG
-  mDEBUGInWriteTransaction = false;
-#endif
-
-  CachedStatement stmt;
-  nsresult rv = GetCachedStatement("BEGIN", &stmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  rv = stmt->Execute();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-}
-
-nsresult
-DatabaseConnection::StartSavepoint()
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(mStorageConnection);
-  MOZ_ASSERT(mUpdateRefcountFunction);
-  MOZ_ASSERT(mDEBUGInWriteTransaction);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::StartSavepoint",
-                 js::ProfileEntry::Category::STORAGE);
-
-  CachedStatement stmt;
-  nsresult rv = GetCachedStatement(kSavepointClause, &stmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = stmt->Execute();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  mUpdateRefcountFunction->StartSavepoint();
-
-#ifdef DEBUG
-  MOZ_ASSERT(mDEBUGSavepointCount < UINT32_MAX);
-  mDEBUGSavepointCount++;
-#endif
-
-  return NS_OK;
-}
-
-nsresult
-DatabaseConnection::ReleaseSavepoint()
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(mStorageConnection);
-  MOZ_ASSERT(mUpdateRefcountFunction);
-  MOZ_ASSERT(mDEBUGInWriteTransaction);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::ReleaseSavepoint",
-                 js::ProfileEntry::Category::STORAGE);
-
-  CachedStatement stmt;
-  nsresult rv = GetCachedStatement(
-    NS_LITERAL_CSTRING("RELEASE ") + NS_LITERAL_CSTRING(kSavepointClause),
-    &stmt);
-  if (NS_SUCCEEDED(rv)) {
-    rv = stmt->Execute();
-    if (NS_SUCCEEDED(rv)) {
-      mUpdateRefcountFunction->ReleaseSavepoint();
-
-#ifdef DEBUG
-      MOZ_ASSERT(mDEBUGSavepointCount);
-      mDEBUGSavepointCount--;
-#endif
-    }
-  }
-
-  return rv;
-}
-
-nsresult
-DatabaseConnection::RollbackSavepoint()
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(mStorageConnection);
-  MOZ_ASSERT(mUpdateRefcountFunction);
-  MOZ_ASSERT(mDEBUGInWriteTransaction);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::RollbackSavepoint",
-                 js::ProfileEntry::Category::STORAGE);
-
-#ifdef DEBUG
-  MOZ_ASSERT(mDEBUGSavepointCount);
-  mDEBUGSavepointCount--;
-#endif
-
-  mUpdateRefcountFunction->RollbackSavepoint();
-
-  CachedStatement stmt;
-  nsresult rv = GetCachedStatement(
-    NS_LITERAL_CSTRING("ROLLBACK TO ") + NS_LITERAL_CSTRING(kSavepointClause),
-    &stmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // This may fail if SQLite already rolled back the savepoint so ignore any
-  // errors.
-  unused << stmt->Execute();
-
-  return NS_OK;
-}
-
-nsresult
-DatabaseConnection::Checkpoint(bool aIdle)
-{
-  AssertIsOnConnectionThread();
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::Checkpoint",
-                 js::ProfileEntry::Category::STORAGE);
-
-  nsAutoCString checkpointMode;
-  if (aIdle) {
-    // When idle we want to reclaim disk space.
-    checkpointMode.AssignLiteral("TRUNCATE");
-  } else {
-    // We're being called at the end of a READ_WRITE_FLUSH transaction so make
-    // sure that the database is completely checkpointed and flushed to disk.
-    checkpointMode.AssignLiteral("FULL");
-  }
-
-  CachedStatement stmt;
-  nsresult rv =
-    GetCachedStatement(
-      NS_LITERAL_CSTRING("PRAGMA wal_checkpoint(") +
-      checkpointMode +
-      NS_LITERAL_CSTRING(")"),
-      &stmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = stmt->Execute();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-void
-DatabaseConnection::Close()
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(mStorageConnection);
-  MOZ_ASSERT(!mDEBUGSavepointCount);
-  MOZ_ASSERT(!mDEBUGInWriteTransaction);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::Close",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (mUpdateRefcountFunction) {
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      mStorageConnection->RemoveFunction(
-        NS_LITERAL_CSTRING("update_refcount"))));
-    mUpdateRefcountFunction = nullptr;
-  }
-
-  mCachedStatements.Clear();
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mStorageConnection->Close()));
-  mStorageConnection = nullptr;
-
-  mFileManager = nullptr;
-}
-
-DatabaseConnection::
-CachedStatement::CachedStatement()
-#ifdef DEBUG
-  : mDEBUGConnection(nullptr)
-#endif
-{
-  AssertIsOnConnectionThread();
-
-  MOZ_COUNT_CTOR(DatabaseConnection::CachedStatement);
-}
-
-DatabaseConnection::
-CachedStatement::~CachedStatement()
-{
-  AssertIsOnConnectionThread();
-
-  MOZ_COUNT_DTOR(DatabaseConnection::CachedStatement);
-}
-
-DatabaseConnection::
-CachedStatement::operator mozIStorageStatement*() const
-{
-  AssertIsOnConnectionThread();
-
-  return mStatement;
-}
-
-mozIStorageStatement*
-DatabaseConnection::
-CachedStatement::operator->() const
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT(mStatement);
-
-  return mStatement;
-}
-
-void
-DatabaseConnection::
-CachedStatement::Reset()
-{
-  AssertIsOnConnectionThread();
-  MOZ_ASSERT_IF(mStatement, mScoper);
-
-  if (mStatement) {
-    mScoper.reset();
-    mScoper.emplace(mStatement);
-  }
-}
-
-void
-DatabaseConnection::
-CachedStatement::Assign(DatabaseConnection* aConnection,
-                        already_AddRefed<mozIStorageStatement> aStatement)
-{
-#ifdef DEBUG
-    MOZ_ASSERT(aConnection);
-    aConnection->AssertIsOnConnectionThread();
-    MOZ_ASSERT_IF(mDEBUGConnection, mDEBUGConnection == aConnection);
-
-    mDEBUGConnection = aConnection;
-#endif
-  AssertIsOnConnectionThread();
-
-  mScoper.reset();
-
-  mStatement = aStatement;
-
-  if (mStatement) {
-    mScoper.emplace(mStatement);
-  }
-}
-
-DatabaseConnection::
-AutoSavepoint::AutoSavepoint()
-  : mConnection(nullptr)
-#ifdef DEBUG
-  , mDEBUGTransaction(nullptr)
-#endif
-{
-  MOZ_COUNT_CTOR(DatabaseConnection::AutoSavepoint);
-}
-
-DatabaseConnection::
-AutoSavepoint::~AutoSavepoint()
-{
-  MOZ_COUNT_DTOR(DatabaseConnection::AutoSavepoint);
-
-  if (mConnection) {
-    mConnection->AssertIsOnConnectionThread();
-    MOZ_ASSERT(mDEBUGTransaction);
-    MOZ_ASSERT(mDEBUGTransaction->GetMode() == IDBTransaction::READ_WRITE ||
-               mDEBUGTransaction->GetMode() ==
-                 IDBTransaction::READ_WRITE_FLUSH ||
-               mDEBUGTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
-
-    if (NS_FAILED(mConnection->RollbackSavepoint())) {
-      NS_WARNING("Failed to rollback savepoint!");
-    }
-  }
-}
-
-nsresult
-DatabaseConnection::
-AutoSavepoint::Start(const TransactionBase* aTransaction)
-{
-  MOZ_ASSERT(aTransaction);
-  MOZ_ASSERT(aTransaction->GetMode() == IDBTransaction::READ_WRITE ||
-             aTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH ||
-             aTransaction->GetMode() == IDBTransaction::VERSION_CHANGE);
-
-  DatabaseConnection* connection = aTransaction->GetDatabase()->GetConnection();
-  MOZ_ASSERT(connection);
-  connection->AssertIsOnConnectionThread();
-
-  MOZ_ASSERT(!mConnection);
-  MOZ_ASSERT(!mDEBUGTransaction);
-
-  nsresult rv = connection->StartSavepoint();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  mConnection = connection;
-#ifdef DEBUG
-  mDEBUGTransaction = aTransaction;
-#endif
-
-  return NS_OK;
-}
-
-nsresult
-DatabaseConnection::
-AutoSavepoint::Commit()
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(mDEBUGTransaction);
-
-  nsresult rv = mConnection->ReleaseSavepoint();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  mConnection = nullptr;
-#ifdef DEBUG
-  mDEBUGTransaction = nullptr;
-#endif
-
-  return NS_OK;
-}
-
-DatabaseConnection::
-UpdateRefcountFunction::UpdateRefcountFunction(DatabaseConnection* aConnection,
-                                               FileManager* aFileManager)
-  : mConnection(aConnection)
-  , mFileManager(aFileManager)
-  , mInSavepoint(false)
-{
-  MOZ_ASSERT(aConnection);
-  aConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(aFileManager);
-}
-
-nsresult
-DatabaseConnection::
-UpdateRefcountFunction::WillCommit()
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::UpdateRefcountFunction::WillCommit",
-                 js::ProfileEntry::Category::STORAGE);
-
-  struct Helper final
-  {
-    static PLDHashOperator
-    Update(const uint64_t& aKey, FileInfoEntry* aValue, void* aUserArg)
-    {
-      MOZ_ASSERT(aValue);
-
-      auto* function = static_cast<DatabaseUpdateFunction*>(aUserArg);
-      MOZ_ASSERT(function);
-
-      if (aValue->mDelta && !function->Update(aKey, aValue->mDelta)) {
-        return PL_DHASH_STOP;
-      }
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
-  DatabaseUpdateFunction function(this);
-
-  mFileInfoEntries.EnumerateRead(Helper::Update, &function);
-
-  nsresult rv = function.ErrorCode();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = CreateJournals();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-void
-DatabaseConnection::
-UpdateRefcountFunction::DidCommit()
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::UpdateRefcountFunction::DidCommit",
-                 js::ProfileEntry::Category::STORAGE);
-
-  struct Helper final
-  {
-    static PLDHashOperator
-    Update(const uint64_t& aKey, FileInfoEntry* aValue, void* /* aUserArg */)
-    {
-      MOZ_ASSERT(aValue);
-
-      if (aValue->mDelta) {
-        aValue->mFileInfo->UpdateDBRefs(aValue->mDelta);
-      }
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
-  mFileInfoEntries.EnumerateRead(Helper::Update, nullptr);
-
-  if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterCommit))) {
-    NS_WARNING("RemoveJournals failed!");
-  }
-}
-
-void
-DatabaseConnection::
-UpdateRefcountFunction::DidAbort()
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::UpdateRefcountFunction::DidAbort",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterAbort))) {
-    NS_WARNING("RemoveJournals failed!");
-  }
-}
-
-void
-DatabaseConnection::
-UpdateRefcountFunction::StartSavepoint()
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(!mInSavepoint);
-  MOZ_ASSERT(!mSavepointEntriesIndex.Count());
-
-  mInSavepoint = true;
-}
-
-void
-DatabaseConnection::
-UpdateRefcountFunction::ReleaseSavepoint()
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(mInSavepoint);
-
-  mSavepointEntriesIndex.Clear();
-  mInSavepoint = false;
-}
-
-void
-DatabaseConnection::
-UpdateRefcountFunction::RollbackSavepoint()
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(mInSavepoint);
-
-  struct Helper
-  {
-    static PLDHashOperator
-    Rollback(const uint64_t& aKey, FileInfoEntry* aValue, void* /* aUserArg */)
-    {
-      MOZ_ASSERT(!IsOnBackgroundThread());
-      MOZ_ASSERT(aValue);
-
-      aValue->mDelta -= aValue->mSavepointDelta;
-      return PL_DHASH_NEXT;
-    }
-  };
-
-  mSavepointEntriesIndex.EnumerateRead(Helper::Rollback, nullptr);
-
-  mInSavepoint = false;
-  mSavepointEntriesIndex.Clear();
-}
-
-void
-DatabaseConnection::
-UpdateRefcountFunction::Reset()
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(!mSavepointEntriesIndex.Count());
-  MOZ_ASSERT(!mInSavepoint);
-
-  mJournalsToCreateBeforeCommit.Clear();
-  mJournalsToRemoveAfterCommit.Clear();
-  mJournalsToRemoveAfterAbort.Clear();
-  mFileInfoEntries.Clear();
-}
-
-nsresult
-DatabaseConnection::
-UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues,
-                                     int32_t aIndex,
-                                     UpdateType aUpdateType)
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(aValues);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::UpdateRefcountFunction::ProcessValue",
-                 js::ProfileEntry::Category::STORAGE);
-
-  int32_t type;
-  nsresult rv = aValues->GetTypeOfIndex(aIndex, &type);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (type == mozIStorageValueArray::VALUE_TYPE_NULL) {
-    return NS_OK;
-  }
-
-  nsString ids;
-  rv = aValues->GetString(aIndex, ids);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsTArray<int64_t> fileIds;
-  rv = ConvertFileIdsToArray(ids, fileIds);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  for (uint32_t i = 0; i < fileIds.Length(); i++) {
-    int64_t id = fileIds.ElementAt(i);
-
-    FileInfoEntry* entry;
-    if (!mFileInfoEntries.Get(id, &entry)) {
-      nsRefPtr<FileInfo> fileInfo = mFileManager->GetFileInfo(id);
-      MOZ_ASSERT(fileInfo);
-
-      entry = new FileInfoEntry(fileInfo);
-      mFileInfoEntries.Put(id, entry);
-    }
-
-    if (mInSavepoint) {
-      mSavepointEntriesIndex.Put(id, entry);
-    }
-
-    switch (aUpdateType) {
-      case eIncrement:
-        entry->mDelta++;
-        if (mInSavepoint) {
-          entry->mSavepointDelta++;
-        }
-        break;
-      case eDecrement:
-        entry->mDelta--;
-        if (mInSavepoint) {
-          entry->mSavepointDelta--;
-        }
-        break;
-      default:
-        MOZ_CRASH("Unknown update type!");
-    }
-  }
-
-  return NS_OK;
-}
-
-nsresult
-DatabaseConnection::
-UpdateRefcountFunction::CreateJournals()
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::UpdateRefcountFunction::CreateJournals",
-                 js::ProfileEntry::Category::STORAGE);
-
-  nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory();
-  if (NS_WARN_IF(!journalDirectory)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  for (uint32_t i = 0; i < mJournalsToCreateBeforeCommit.Length(); i++) {
-    int64_t id = mJournalsToCreateBeforeCommit[i];
-
-    nsCOMPtr<nsIFile> file =
-      mFileManager->GetFileForId(journalDirectory, id);
-    if (NS_WARN_IF(!file)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    mJournalsToRemoveAfterAbort.AppendElement(id);
-  }
-
-  return NS_OK;
-}
-
-nsresult
-DatabaseConnection::
-UpdateRefcountFunction::RemoveJournals(const nsTArray<int64_t>& aJournals)
-{
-  MOZ_ASSERT(mConnection);
-  mConnection->AssertIsOnConnectionThread();
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::UpdateRefcountFunction::RemoveJournals",
-                 js::ProfileEntry::Category::STORAGE);
-
-  nsCOMPtr<nsIFile> journalDirectory = mFileManager->GetJournalDirectory();
-  if (NS_WARN_IF(!journalDirectory)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  for (uint32_t index = 0; index < aJournals.Length(); index++) {
-    nsCOMPtr<nsIFile> file =
-      mFileManager->GetFileForId(journalDirectory, aJournals[index]);
-    if (NS_WARN_IF(!file)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    if (NS_FAILED(file->Remove(false))) {
-      NS_WARNING("Failed to removed journal!");
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(DatabaseConnection::UpdateRefcountFunction,
-                  mozIStorageFunction)
-
-NS_IMETHODIMP
-DatabaseConnection::
-UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues,
-                                       nsIVariant** _retval)
-{
-  MOZ_ASSERT(aValues);
-  MOZ_ASSERT(_retval);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::UpdateRefcountFunction::OnFunctionCall",
-                 js::ProfileEntry::Category::STORAGE);
-
-  uint32_t numEntries;
-  nsresult rv = aValues->GetNumEntries(&numEntries);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  MOZ_ASSERT(numEntries == 2);
-
-#ifdef DEBUG
-  {
-    int32_t type1 = mozIStorageValueArray::VALUE_TYPE_NULL;
-    MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(0, &type1)));
-
-    int32_t type2 = mozIStorageValueArray::VALUE_TYPE_NULL;
-    MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(1, &type2)));
-
-    MOZ_ASSERT(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL &&
-                 type2 == mozIStorageValueArray::VALUE_TYPE_NULL));
-  }
-#endif
-
-  rv = ProcessValue(aValues, 0, eDecrement);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = ProcessValue(aValues, 1, eIncrement);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-bool
-DatabaseConnection::UpdateRefcountFunction::
-DatabaseUpdateFunction::Update(int64_t aId,
-                               int32_t aDelta)
-{
-  nsresult rv = UpdateInternal(aId, aDelta);
-  if (NS_FAILED(rv)) {
-    mErrorCode = rv;
-    return false;
-  }
-
-  return true;
-}
-
-nsresult
-DatabaseConnection::UpdateRefcountFunction::
-DatabaseUpdateFunction::UpdateInternal(int64_t aId,
-                                       int32_t aDelta)
-{
-  MOZ_ASSERT(mFunction);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseConnection::UpdateRefcountFunction::"
-                 "DatabaseUpdateFunction::UpdateInternal",
-                 js::ProfileEntry::Category::STORAGE);
-
-  DatabaseConnection* connection = mFunction->mConnection;
-  MOZ_ASSERT(connection);
-  connection->AssertIsOnConnectionThread();
-
-  MOZ_ASSERT(connection->GetStorageConnection());
-
-  nsresult rv;
-  if (!mUpdateStatement) {
-    rv = connection->GetCachedStatement(
-      "UPDATE file "
-      "SET refcount = refcount + :delta "
-      "WHERE id = :id",
-      &mUpdateStatement);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  mozStorageStatementScoper updateScoper(mUpdateStatement);
-
-  rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mUpdateStatement->Execute();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  int32_t rows;
-  rv = connection->GetStorageConnection()->GetAffectedRows(&rows);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (rows > 0) {
-    if (!mSelectStatement) {
-      rv = connection->GetCachedStatement(
-        "SELECT id "
-        "FROM file "
-        "WHERE id = :id",
-        &mSelectStatement);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
-    mozStorageStatementScoper selectScoper(mSelectStatement);
-
-    rv = mSelectStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    bool hasResult;
-    rv = mSelectStatement->ExecuteStep(&hasResult);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    if (!hasResult) {
-      // Don't have to create the journal here, we can create all at once,
-      // just before commit
-      mFunction->mJournalsToCreateBeforeCommit.AppendElement(aId);
-    }
-
-    return NS_OK;
-  }
-
-  if (!mInsertStatement) {
-    rv = connection->GetCachedStatement(
-      "INSERT INTO file (id, refcount) "
-      "VALUES(:id, :delta)",
-      &mInsertStatement);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  mozStorageStatementScoper insertScoper(mInsertStatement);
-
-  rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mInsertStatement->Execute();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  mFunction->mJournalsToRemoveAfterCommit.AppendElement(aId);
-  return NS_OK;
-}
-
-/*******************************************************************************
- * ConnectionPool implementation
- ******************************************************************************/
-
-ConnectionPool::ConnectionPool()
-  : mDatabasesMutex("ConnectionPool::mDatabasesMutex")
-  , mIdleTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
-  , mNextTransactionId(0)
-  , mTotalThreadCount(0)
-  , mShutdownRequested(false)
-  , mShutdownComplete(false)
-#ifdef DEBUG
-  , mDEBUGOwningThread(PR_GetCurrentThread())
-#endif
-{
-  AssertIsOnOwningThread();
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mIdleTimer);
-}
-
-ConnectionPool::~ConnectionPool()
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(mIdleThreads.IsEmpty());
-  MOZ_ASSERT(mIdleDatabases.IsEmpty());
-  MOZ_ASSERT(!mIdleTimer);
-  MOZ_ASSERT(mTargetIdleTime.IsNull());
-  MOZ_ASSERT(!mDatabases.Count());
-  MOZ_ASSERT(!mTransactions.Count());
-  MOZ_ASSERT(mQueuedTransactions.IsEmpty());
-  MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
-  MOZ_ASSERT(!mTotalThreadCount);
-  MOZ_ASSERT(mShutdownRequested);
-  MOZ_ASSERT(mShutdownComplete);
-}
-
-#ifdef DEBUG
-
-void
-ConnectionPool::AssertIsOnOwningThread() const
-{
-  MOZ_ASSERT(mDEBUGOwningThread);
-  MOZ_ASSERT(PR_GetCurrentThread() == mDEBUGOwningThread);
-}
-
-#endif // DEBUG
-
-// static
-void
-ConnectionPool::IdleTimerCallback(nsITimer* aTimer, void* aClosure)
-{
-  MOZ_ASSERT(aTimer);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::IdleTimerCallback",
-                 js::ProfileEntry::Category::STORAGE);
-
-  auto* self = static_cast<ConnectionPool*>(aClosure);
-  MOZ_ASSERT(self);
-  MOZ_ASSERT(self->mIdleTimer);
-  MOZ_ASSERT(SameCOMIdentity(self->mIdleTimer, aTimer));
-  MOZ_ASSERT(!self->mTargetIdleTime.IsNull());
-  MOZ_ASSERT_IF(self->mIdleDatabases.IsEmpty(), !self->mIdleThreads.IsEmpty());
-  MOZ_ASSERT_IF(self->mIdleThreads.IsEmpty(), !self->mIdleDatabases.IsEmpty());
-
-  self->mTargetIdleTime = TimeStamp();
-
-  // Cheat a little.
-  TimeStamp now = TimeStamp::NowLoRes() + TimeDuration::FromMilliseconds(500);
-
-  uint32_t index = 0;
-
-  for (uint32_t count = self->mIdleDatabases.Length(); index < count; index++) {
-    IdleDatabaseInfo& info = self->mIdleDatabases[index];
-
-    if (now >= info.mIdleTime) {
-      if (info.mDatabaseInfo->mNeedsCheckpoint) {
-        self->CheckpointDatabase(info.mDatabaseInfo);
-      } else {
-        self->CloseDatabase(info.mDatabaseInfo);
-      }
-    } else {
-      break;
-    }
-  }
-
-  if (index) {
-    self->mIdleDatabases.RemoveElementsAt(0, index);
-
-    index = 0;
-  }
-
-  for (uint32_t count = self->mIdleThreads.Length(); index < count; index++) {
-    IdleThreadInfo& info = self->mIdleThreads[index];
-    MOZ_ASSERT(info.mThreadInfo.mThread);
-    MOZ_ASSERT(info.mThreadInfo.mRunnable);
-
-    if (now >= info.mIdleTime) {
-      self->ShutdownThread(info.mThreadInfo);
-    } else {
-      break;
-    }
-  }
-
-  if (index) {
-    self->mIdleThreads.RemoveElementsAt(0, index);
-  }
-
-  self->AdjustIdleTimer();
-}
-
-nsresult
-ConnectionPool::GetOrCreateConnection(const Database* aDatabase,
-                                      DatabaseConnection** aConnection)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(aDatabase);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::GetOrCreateConnection",
-                 js::ProfileEntry::Category::STORAGE);
-
-  DatabaseInfo* dbInfo;
-  {
-    MutexAutoLock lock(mDatabasesMutex);
-
-    dbInfo = mDatabases.Get(aDatabase->Id());
-  }
-
-  MOZ_ASSERT(dbInfo);
-
-  nsRefPtr<DatabaseConnection> connection = dbInfo->mConnection;
-  if (!connection) {
-    MOZ_ASSERT(!dbInfo->mDEBUGConnectionThread);
-
-    nsCOMPtr<mozIStorageConnection> storageConnection;
-    nsresult rv =
-      GetStorageConnection(aDatabase->FilePath(),
-                           aDatabase->Type(),
-                           aDatabase->Group(),
-                           aDatabase->Origin(),
-                           getter_AddRefs(storageConnection));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    connection =
-      new DatabaseConnection(storageConnection, aDatabase->GetFileManager());
-
-    rv = connection->Init();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    dbInfo->mConnection = connection;
-
-    IDB_DEBUG_LOG(("ConnectionPool created connection 0x%p for '%s'",
-                   dbInfo->mConnection.get(),
-                   NS_ConvertUTF16toUTF8(aDatabase->FilePath()).get()));
-
-#ifdef DEBUG
-    dbInfo->mDEBUGConnectionThread = PR_GetCurrentThread();
-#endif
-  }
-
-  dbInfo->AssertIsOnConnectionThread();
-
-  connection.forget(aConnection);
-  return NS_OK;
-}
-
-uint64_t
-ConnectionPool::Start(const nsID& aBackgroundChildLoggingId,
-                      const nsACString& aDatabaseId,
-                      int64_t aLoggingSerialNumber,
-                      const nsTArray<nsString>& aObjectStoreNames,
-                      bool aIsWriteTransaction,
-                      TransactionDatabaseOperationBase* aTransactionOp)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(!aDatabaseId.IsEmpty());
-  MOZ_ASSERT(mNextTransactionId < UINT64_MAX);
-  MOZ_ASSERT(!mShutdownRequested);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::Start",
-                 js::ProfileEntry::Category::STORAGE);
-
-  const uint64_t transactionId = ++mNextTransactionId;
-
-  DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId);
-
-  const bool databaseInfoIsNew = !dbInfo;
-
-  if (databaseInfoIsNew) {
-    dbInfo = new DatabaseInfo(this, aDatabaseId);
-
-    MutexAutoLock lock(mDatabasesMutex);
-
-    mDatabases.Put(aDatabaseId, dbInfo);
-  }
-
-  TransactionInfo* transactionInfo =
-    new TransactionInfo(dbInfo,
-                        aBackgroundChildLoggingId,
-                        aDatabaseId,
-                        transactionId,
-                        aLoggingSerialNumber,
-                        aObjectStoreNames,
-                        aIsWriteTransaction,
-                        aTransactionOp);
-
-  MOZ_ASSERT(!mTransactions.Get(transactionId));
-  mTransactions.Put(transactionId, transactionInfo);
-
-  if (aIsWriteTransaction) {
-    MOZ_ASSERT(dbInfo->mWriteTransactionCount < UINT32_MAX);
-    dbInfo->mWriteTransactionCount++;
-  } else {
-    MOZ_ASSERT(dbInfo->mReadTransactionCount < UINT32_MAX);
-    dbInfo->mReadTransactionCount++;
-  }
-
-  auto& blockingTransactions = dbInfo->mBlockingTransactions;
-
-  for (uint32_t nameIndex = 0, nameCount = aObjectStoreNames.Length();
-       nameIndex < nameCount;
-       nameIndex++) {
-    const nsString& objectStoreName = aObjectStoreNames[nameIndex];
-
-    TransactionInfoPair* blockInfo = blockingTransactions.Get(objectStoreName);
-    if (!blockInfo) {
-      blockInfo = new TransactionInfoPair();
-      blockingTransactions.Put(objectStoreName, blockInfo);
-    }
-
-    // Mark what we are blocking on.
-    if (TransactionInfo* blockingRead = blockInfo->mLastBlockingReads) {
-      transactionInfo->mBlockedOn.PutEntry(blockingRead);
-      blockingRead->mBlocking.PutEntry(transactionInfo);
-    }
-
-    if (aIsWriteTransaction) {
-      if (const uint32_t writeCount = blockInfo->mLastBlockingWrites.Length()) {
-        for (uint32_t writeIndex = 0; writeIndex < writeCount; writeIndex++) {
-          TransactionInfo* blockingWrite =
-            blockInfo->mLastBlockingWrites[writeIndex];
-          MOZ_ASSERT(blockingWrite);
-
-          transactionInfo->mBlockedOn.PutEntry(blockingWrite);
-          blockingWrite->mBlocking.PutEntry(transactionInfo);
-        }
-      }
-
-      blockInfo->mLastBlockingReads = transactionInfo;
-      blockInfo->mLastBlockingWrites.Clear();
-    } else {
-      blockInfo->mLastBlockingWrites.AppendElement(transactionInfo);
-    }
-  }
-
-  if (!transactionInfo->mBlockedOn.Count()) {
-    unused << ScheduleTransaction(transactionInfo,
-                                  /* aFromQueuedTransactions */ false);
-  }
-
-  if (!databaseInfoIsNew && mIdleDatabases.RemoveElement(dbInfo)) {
-    AdjustIdleTimer();
-  }
-
-  return transactionId;
-}
-
-void
-ConnectionPool::Dispatch(uint64_t aTransactionId, nsIRunnable* aRunnable)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aRunnable);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::Dispatch",
-                 js::ProfileEntry::Category::STORAGE);
-
-  TransactionInfo* transactionInfo = mTransactions.Get(aTransactionId);
-  MOZ_ASSERT(transactionInfo);
-  MOZ_ASSERT(!transactionInfo->mFinished);
-
-  if (transactionInfo->mRunning) {
-    DatabaseInfo* dbInfo = transactionInfo->mDatabaseInfo;
-    MOZ_ASSERT(dbInfo);
-    MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
-    MOZ_ASSERT(dbInfo->mThreadInfo.mRunnable);
-    MOZ_ASSERT(!dbInfo->mClosing);
-    MOZ_ASSERT_IF(transactionInfo->mIsWriteTransaction,
-                  dbInfo->mRunningWriteTransaction == transactionInfo);
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      dbInfo->mThreadInfo.mThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL)));
-  } else {
-    transactionInfo->mQueuedRunnables.AppendElement(aRunnable);
-  }
-}
-
-void
-ConnectionPool::Finish(uint64_t aTransactionId, FinishCallback* aCallback)
-{
-  AssertIsOnOwningThread();
-
-#ifdef DEBUG
-  TransactionInfo* transactionInfo = mTransactions.Get(aTransactionId);
-  MOZ_ASSERT(transactionInfo);
-  MOZ_ASSERT(!transactionInfo->mFinished);
-#endif
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::Finish",
-                 js::ProfileEntry::Category::STORAGE);
-
-  nsRefPtr<FinishCallbackWrapper> wrapper =
-    new FinishCallbackWrapper(this, aTransactionId, aCallback);
-
-  Dispatch(aTransactionId, wrapper);
-
-#ifdef DEBUG
-  MOZ_ASSERT(!transactionInfo->mFinished);
-  transactionInfo->mFinished = true;
-#endif
-}
-
-void
-ConnectionPool::WaitForDatabasesToComplete(nsTArray<nsCString>&& aDatabaseIds,
-                                           nsIRunnable* aCallback)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(!aDatabaseIds.IsEmpty());
-  MOZ_ASSERT(aCallback);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::WaitForDatabasesToComplete",
-                 js::ProfileEntry::Category::STORAGE);
-
-  bool mayRunCallbackImmediately = true;
-
-  for (uint32_t index = 0, count = aDatabaseIds.Length();
-       index < count;
-       index++) {
-    const nsCString& databaseId = aDatabaseIds[index];
-    MOZ_ASSERT(!databaseId.IsEmpty());
-
-    if (CloseDatabaseWhenIdleInternal(databaseId)) {
-      mayRunCallbackImmediately = false;
-    }
-  }
-
-  if (mayRunCallbackImmediately) {
-    unused << aCallback->Run();
-    return;
-  }
-
-  nsAutoPtr<DatabasesCompleteCallback> callback(
-    new DatabasesCompleteCallback(Move(aDatabaseIds), aCallback));
-  mCompleteCallbacks.AppendElement(callback.forget());
-}
-
-void
-ConnectionPool::Shutdown()
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(!mShutdownRequested);
-  MOZ_ASSERT(!mShutdownComplete);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::Shutdown",
-                 js::ProfileEntry::Category::STORAGE);
-
-  mShutdownRequested = true;
-
-  CancelIdleTimer();
-  MOZ_ASSERT(mTargetIdleTime.IsNull());
-
-  mIdleTimer = nullptr;
-
-  CloseIdleDatabases();
-
-  ShutdownIdleThreads();
-
-  if (!mDatabases.Count()) {
-    MOZ_ASSERT(!mTransactions.Count());
-
-    Cleanup();
-
-    MOZ_ASSERT(mShutdownComplete);
-    return;
-  }
-
-  nsIThread* currentThread = NS_GetCurrentThread();
-  MOZ_ASSERT(currentThread);
-
-  while (!mShutdownComplete) {
-    MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
-  }
-}
-
-void
-ConnectionPool::Cleanup()
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(mShutdownRequested);
-  MOZ_ASSERT(!mShutdownComplete);
-  MOZ_ASSERT(!mDatabases.Count());
-  MOZ_ASSERT(!mTransactions.Count());
-  MOZ_ASSERT(mIdleThreads.IsEmpty());
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::Cleanup",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (!mCompleteCallbacks.IsEmpty()) {
-    // Run all callbacks manually now.
-    for (uint32_t count = mCompleteCallbacks.Length(), index = 0;
-         index < count;
-         index++) {
-      nsAutoPtr<DatabasesCompleteCallback> completeCallback(
-        mCompleteCallbacks[index].forget());
-      MOZ_ASSERT(completeCallback);
-      MOZ_ASSERT(completeCallback->mCallback);
-
-      unused << completeCallback->mCallback->Run();
-    }
-
-    mCompleteCallbacks.Clear();
-
-    // And make sure they get processed.
-    nsIThread* currentThread = NS_GetCurrentThread();
-    MOZ_ASSERT(currentThread);
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProcessPendingEvents(currentThread)));
-  }
-
-  mShutdownComplete = true;
-}
-
-void
-ConnectionPool::AdjustIdleTimer()
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(mIdleTimer);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::AdjustIdleTimer",
-                 js::ProfileEntry::Category::STORAGE);
-
-  // Figure out the next time at which we should release idle resources. This
-  // includes both databases and threads.
-  TimeStamp newTargetIdleTime;
-  MOZ_ASSERT(newTargetIdleTime.IsNull());
-
-  if (!mIdleDatabases.IsEmpty()) {
-    newTargetIdleTime = mIdleDatabases[0].mIdleTime;
-  }
-
-  if (!mIdleThreads.IsEmpty()) {
-    const TimeStamp& idleTime = mIdleThreads[0].mIdleTime;
-
-    if (newTargetIdleTime.IsNull() || idleTime < newTargetIdleTime) {
-      newTargetIdleTime = idleTime;
-    }
-  }
-
-  MOZ_ASSERT_IF(newTargetIdleTime.IsNull(), mIdleDatabases.IsEmpty());
-  MOZ_ASSERT_IF(newTargetIdleTime.IsNull(), mIdleThreads.IsEmpty());
-
-  // Cancel the timer if it was running and the new target time is different.
-  if (!mTargetIdleTime.IsNull() &&
-      (newTargetIdleTime.IsNull() || mTargetIdleTime != newTargetIdleTime)) {
-    CancelIdleTimer();
-
-    MOZ_ASSERT(mTargetIdleTime.IsNull());
-  }
-
-  // Schedule the timer if we have a target time different than before.
-  if (!newTargetIdleTime.IsNull() &&
-      (mTargetIdleTime.IsNull() || mTargetIdleTime != newTargetIdleTime)) {
-    double delta = (newTargetIdleTime - TimeStamp::NowLoRes()).ToMilliseconds();
-
-    uint32_t delay;
-    if (delta > 0) {
-      delay = uint32_t(std::min(delta, double(UINT32_MAX)));
-    } else {
-      delay = 0;
-    }
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      mIdleTimer->InitWithFuncCallback(IdleTimerCallback,
-                                       this,
-                                       delay,
-                                       nsITimer::TYPE_ONE_SHOT)));
-
-    mTargetIdleTime = newTargetIdleTime;
-  }
-}
-
-void
-ConnectionPool::CancelIdleTimer()
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(mIdleTimer);
-
-  if (!mTargetIdleTime.IsNull()) {
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mIdleTimer->Cancel()));
-
-    mTargetIdleTime = TimeStamp();
-    MOZ_ASSERT(mTargetIdleTime.IsNull());
-  }
-}
-
-void
-ConnectionPool::ShutdownThread(ThreadInfo& aThreadInfo)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aThreadInfo.mThread);
-  MOZ_ASSERT(aThreadInfo.mRunnable);
-  MOZ_ASSERT(mTotalThreadCount);
-
-  nsRefPtr<ThreadRunnable> runnable;
-  aThreadInfo.mRunnable.swap(runnable);
-
-  nsCOMPtr<nsIThread> thread;
-  aThreadInfo.mThread.swap(thread);
-
-  IDB_DEBUG_LOG(("ConnectionPool shutting down thread %lu",
-                 runnable->SerialNumber()));
-
-  // This should clean up the thread with the profiler.
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Dispatch(runnable, NS_DISPATCH_NORMAL)));
-
-  nsCOMPtr<nsIRunnable> shutdownRunnable =
-    NS_NewRunnableMethod(thread, &nsIThread::Shutdown);
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(shutdownRunnable)));
-
-  mTotalThreadCount--;
-}
-
-void
-ConnectionPool::CloseIdleDatabases()
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(mShutdownRequested);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::CloseIdleDatabases",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (!mIdleDatabases.IsEmpty()) {
-    for (uint32_t count = mIdleDatabases.Length(), index = 0;
-         index < count;
-         index++) {
-      CloseDatabase(mIdleDatabases[index].mDatabaseInfo);
-    }
-    mIdleDatabases.Clear();
-  }
-}
-
-void
-ConnectionPool::ShutdownIdleThreads()
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(mShutdownRequested);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::ShutdownIdleThreads",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (!mIdleThreads.IsEmpty()) {
-    for (uint32_t threadCount = mIdleThreads.Length(), threadIndex = 0;
-         threadIndex < threadCount;
-         threadIndex++) {
-      ShutdownThread(mIdleThreads[threadIndex].mThreadInfo);
-    }
-    mIdleThreads.Clear();
-  }
-}
-
-bool
-ConnectionPool::ScheduleTransaction(TransactionInfo* aTransactionInfo,
-                                    bool aFromQueuedTransactions)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aTransactionInfo);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::ScheduleTransaction",
-                 js::ProfileEntry::Category::STORAGE);
-
-  DatabaseInfo* dbInfo = aTransactionInfo->mDatabaseInfo;
-  MOZ_ASSERT(dbInfo);
-
-  if (dbInfo->mClosing) {
-    MOZ_ASSERT(!mIdleDatabases.Contains(dbInfo));
-    MOZ_ASSERT(
-      !dbInfo->mTransactionsScheduledDuringClose.Contains(aTransactionInfo));
-
-    dbInfo->mTransactionsScheduledDuringClose.AppendElement(aTransactionInfo);
-    return true;
-  }
-
-  if (!dbInfo->mThreadInfo.mThread) {
-    MOZ_ASSERT(!dbInfo->mThreadInfo.mRunnable);
-
-    if (mIdleThreads.IsEmpty()) {
-      bool created = false;
-
-      if (mTotalThreadCount < kMaxConnectionThreadCount) {
-        // This will set the thread up with the profiler.
-        nsRefPtr<ThreadRunnable> runnable = new ThreadRunnable();
-
-        nsCOMPtr<nsIThread> newThread;
-        if (NS_SUCCEEDED(NS_NewThread(getter_AddRefs(newThread), runnable))) {
-          MOZ_ASSERT(newThread);
-
-          IDB_DEBUG_LOG(("ConnectionPool created thread %lu",
-                         runnable->SerialNumber()));
-
-          dbInfo->mThreadInfo.mThread.swap(newThread);
-          dbInfo->mThreadInfo.mRunnable.swap(runnable);
-
-          mTotalThreadCount++;
-          created = true;
-        } else {
-          NS_WARNING("Failed to make new thread!");
-        }
-      }
-
-      if (!created) {
-        if (!aFromQueuedTransactions) {
-          MOZ_ASSERT(!mQueuedTransactions.Contains(aTransactionInfo));
-          mQueuedTransactions.AppendElement(aTransactionInfo);
-        }
-        return false;
-      }
-    } else {
-      const uint32_t lastIndex = mIdleThreads.Length() - 1;
-
-      ThreadInfo& threadInfo = mIdleThreads[lastIndex].mThreadInfo;
-
-      dbInfo->mThreadInfo.mRunnable.swap(threadInfo.mRunnable);
-      dbInfo->mThreadInfo.mThread.swap(threadInfo.mThread);
-
-      mIdleThreads.RemoveElementAt(lastIndex);
-
-      AdjustIdleTimer();
-    }
-  }
-
-  MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
-  MOZ_ASSERT(dbInfo->mThreadInfo.mRunnable);
-
-  if (aTransactionInfo->mIsWriteTransaction) {
-    if (dbInfo->mRunningWriteTransaction) {
-      // SQLite only allows one write transaction at a time so queue this
-      // transaction for later.
-      MOZ_ASSERT(
-        !dbInfo->mScheduledWriteTransactions.Contains(aTransactionInfo));
-
-      dbInfo->mScheduledWriteTransactions.AppendElement(aTransactionInfo);
-      return true;
-    }
-
-    dbInfo->mRunningWriteTransaction = aTransactionInfo;
-    dbInfo->mNeedsCheckpoint = true;
-  }
-
-  MOZ_ASSERT(!aTransactionInfo->mRunning);
-  aTransactionInfo->mRunning = true;
-
-  nsTArray<nsCOMPtr<nsIRunnable>>& queuedRunnables =
-    aTransactionInfo->mQueuedRunnables;
-
-  if (!queuedRunnables.IsEmpty()) {
-    for (uint32_t index = 0, count = queuedRunnables.Length();
-         index < count;
-         index++) {
-      nsCOMPtr<nsIRunnable> runnable;
-      queuedRunnables[index].swap(runnable);
-
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-        dbInfo->mThreadInfo.mThread->Dispatch(runnable, NS_DISPATCH_NORMAL)));
-    }
-
-    queuedRunnables.Clear();
-  }
-
-  return true;
-}
-
-void
-ConnectionPool::NoteFinishedTransaction(uint64_t aTransactionId)
-{
-  AssertIsOnOwningThread();
-
-  struct Helper
-  {
-    static PLDHashOperator
-    MaybeScheduleTransaction(nsPtrHashKey<TransactionInfo>* aKey,
-                             void* aClosure)
-    {
-      AssertIsOnBackgroundThread();
-
-      TransactionInfo* transactionInfo = aKey->GetKey();
-      MOZ_ASSERT(transactionInfo);
-
-      TransactionInfo* finishedInfo = static_cast<TransactionInfo*>(aClosure);
-      MOZ_ASSERT(finishedInfo);
-
-      MOZ_ASSERT(transactionInfo->mBlockedOn.Contains(finishedInfo));
-
-      transactionInfo->mBlockedOn.RemoveEntry(finishedInfo);
-      if (!transactionInfo->mBlockedOn.Count()) {
-        transactionInfo->Schedule();
-      }
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::NoteFinishedTransaction",
-                 js::ProfileEntry::Category::STORAGE);
-
-  TransactionInfo* transactionInfo = mTransactions.Get(aTransactionId);
-  MOZ_ASSERT(transactionInfo);
-  MOZ_ASSERT(transactionInfo->mRunning);
-  MOZ_ASSERT(transactionInfo->mFinished);
-
-  transactionInfo->mRunning = false;
-
-  DatabaseInfo* dbInfo = transactionInfo->mDatabaseInfo;
-  MOZ_ASSERT(dbInfo);
-  MOZ_ASSERT(mDatabases.Get(transactionInfo->mDatabaseId) == dbInfo);
-  MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
-  MOZ_ASSERT(dbInfo->mThreadInfo.mRunnable);
-
-  // Schedule the next write transaction if there are any queued.
-  if (dbInfo->mRunningWriteTransaction == transactionInfo) {
-    MOZ_ASSERT(transactionInfo->mIsWriteTransaction);
-    MOZ_ASSERT(dbInfo->mNeedsCheckpoint);
-
-    dbInfo->mRunningWriteTransaction = nullptr;
-
-    if (!dbInfo->mScheduledWriteTransactions.IsEmpty()) {
-      TransactionInfo* nextWriteTransaction =
-        dbInfo->mScheduledWriteTransactions[0];
-      MOZ_ASSERT(nextWriteTransaction);
-
-      dbInfo->mScheduledWriteTransactions.RemoveElementAt(0);
-
-      MOZ_ALWAYS_TRUE(ScheduleTransaction(nextWriteTransaction,
-                                          /* aFromQueuedTransactions */ false));
-    }
-  }
-
-  const nsTArray<nsString>& objectStoreNames =
-    transactionInfo->mObjectStoreNames;
-
-  for (uint32_t index = 0, count = objectStoreNames.Length();
-       index < count;
-       index++) {
-    TransactionInfoPair* blockInfo =
-      dbInfo->mBlockingTransactions.Get(objectStoreNames[index]);
-    MOZ_ASSERT(blockInfo);
-
-    if (transactionInfo->mIsWriteTransaction &&
-        blockInfo->mLastBlockingReads == transactionInfo) {
-      blockInfo->mLastBlockingReads = nullptr;
-    }
-
-    blockInfo->mLastBlockingWrites.RemoveElement(transactionInfo);
-  }
-
-  transactionInfo->mBlocking.EnumerateEntries(Helper::MaybeScheduleTransaction,
-                                              transactionInfo);
-
-  if (transactionInfo->mIsWriteTransaction) {
-    MOZ_ASSERT(dbInfo->mWriteTransactionCount);
-    dbInfo->mWriteTransactionCount--;
-  } else {
-    MOZ_ASSERT(dbInfo->mReadTransactionCount);
-    dbInfo->mReadTransactionCount--;
-  }
-
-  mTransactions.Remove(aTransactionId);
-
-#ifdef DEBUG
-  // That just deleted |transactionInfo|.
-  transactionInfo = nullptr;
-#endif
-
-  if (!dbInfo->TotalTransactionCount()) {
-    NoteIdleDatabase(dbInfo);
-  }
-}
-
-void
-ConnectionPool::ScheduleQueuedTransactions(ThreadInfo& aThreadInfo)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aThreadInfo.mThread);
-  MOZ_ASSERT(aThreadInfo.mRunnable);
-  MOZ_ASSERT(!mQueuedTransactions.IsEmpty());
-  MOZ_ASSERT(!mIdleThreads.Contains(aThreadInfo));
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::ScheduleQueuedTransactions",
-                 js::ProfileEntry::Category::STORAGE);
-
-  mIdleThreads.InsertElementSorted(aThreadInfo);
-
-  aThreadInfo.mRunnable = nullptr;
-  aThreadInfo.mThread = nullptr;
-
-  uint32_t index = 0;
-  for (uint32_t count = mQueuedTransactions.Length(); index < count; index++) {
-    if (!ScheduleTransaction(mQueuedTransactions[index],
-                             /* aFromQueuedTransactions */ true)) {
-      break;
-    }
-  }
-
-  if (index) {
-    mQueuedTransactions.RemoveElementsAt(0, index);
-  }
-
-  AdjustIdleTimer();
-}
-
-void
-ConnectionPool::NoteIdleDatabase(DatabaseInfo* aDatabaseInfo)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aDatabaseInfo);
-  MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
-  MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
-  MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
-  MOZ_ASSERT(!mIdleDatabases.Contains(aDatabaseInfo));
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::NoteIdleDatabase",
-                 js::ProfileEntry::Category::STORAGE);
-
-  const bool otherDatabasesWaiting = !mQueuedTransactions.IsEmpty();
-
-  if (mShutdownRequested ||
-      otherDatabasesWaiting ||
-      aDatabaseInfo->mCloseOnIdle) {
-    // Make sure we close the connection if we're shutting down or giving the
-    // thread to another database.
-    CloseDatabase(aDatabaseInfo);
-
-    if (otherDatabasesWaiting) {
-      // Let another database use this thread.
-      ScheduleQueuedTransactions(aDatabaseInfo->mThreadInfo);
-    } else if (mShutdownRequested) {
-      // If there are no other databases that need to run then we can shut this
-      // thread down immediately instead of going through the idle thread
-      // mechanism.
-      ShutdownThread(aDatabaseInfo->mThreadInfo);
-    }
-
-    return;
-  }
-
-  mIdleDatabases.InsertElementSorted(aDatabaseInfo);
-
-  AdjustIdleTimer();
-}
-
-void
-ConnectionPool::NoteClosedDatabase(DatabaseInfo* aDatabaseInfo)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aDatabaseInfo);
-  MOZ_ASSERT(aDatabaseInfo->mClosing);
-  MOZ_ASSERT(!mIdleDatabases.Contains(aDatabaseInfo));
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::NoteClosedDatabase",
-                 js::ProfileEntry::Category::STORAGE);
-
-  aDatabaseInfo->mClosing = false;
-
-  // Figure out what to do with this database's thread. It may have already been
-  // given to another database, in which case there's nothing to do here.
-  // Otherwise we prioritize the thread as follows:
-  //   1. Databases that haven't had an opportunity to run at all are highest
-  //      priority. Those live in the |mQueuedTransactions| list.
-  //   2. If this database has additional transactions that were started after
-  //      we began closing the connection then the thread can be reused for
-  //      those transactions.
-  //   3. If we're shutting down then we can get rid of the thread.
-  //   4. Finally, if nothing above took the thread then we can add it to our
-  //      list of idle threads. It may be reused or it may time out. If we have
-  //      too many idle threads then we will shut down the oldest.
-  if (aDatabaseInfo->mThreadInfo.mThread) {
-    MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
-
-    if (!mQueuedTransactions.IsEmpty()) {
-      // Give the thread to another database.
-      ScheduleQueuedTransactions(aDatabaseInfo->mThreadInfo);
-    } else if (!aDatabaseInfo->TotalTransactionCount()) {
-      if (mShutdownRequested) {
-        ShutdownThread(aDatabaseInfo->mThreadInfo);
-      } else {
-        MOZ_ASSERT(!mIdleThreads.Contains(aDatabaseInfo->mThreadInfo));
-
-        mIdleThreads.InsertElementSorted(aDatabaseInfo->mThreadInfo);
-
-        aDatabaseInfo->mThreadInfo.mRunnable = nullptr;
-        aDatabaseInfo->mThreadInfo.mThread = nullptr;
-
-        if (mIdleThreads.Length() > kMaxIdleConnectionThreadCount) {
-          ShutdownThread(mIdleThreads[0].mThreadInfo);
-          mIdleThreads.RemoveElementAt(0);
-        }
-
-        AdjustIdleTimer();
-      }
-    }
-  }
-
-  // Schedule any transactions that were started while we were closing the
-  // connection.
-  if (aDatabaseInfo->TotalTransactionCount()) {
-    nsTArray<TransactionInfo*>& scheduledTransactions =
-      aDatabaseInfo->mTransactionsScheduledDuringClose;
-
-    MOZ_ASSERT(!scheduledTransactions.IsEmpty());
-
-    for (uint32_t index = 0, count = scheduledTransactions.Length();
-         index < count;
-         index++) {
-      unused << ScheduleTransaction(scheduledTransactions[index],
-                                    /* aFromQueuedTransactions */ false);
-    }
-
-    scheduledTransactions.Clear();
-
-    return;
-  }
-
-  // There are no more transactions and the connection has been closed. We're
-  // done with this database.
-  {
-    MutexAutoLock lock(mDatabasesMutex);
-
-    mDatabases.Remove(aDatabaseInfo->mDatabaseId);
-  }
-
-#ifdef DEBUG
-  // That just deleted |aDatabaseInfo|.
-  aDatabaseInfo = nullptr;
-#endif
-
-  // See if we need to fire any complete callbacks now that the database is
-  // finished.
-  for (uint32_t index = 0;
-       index < mCompleteCallbacks.Length();
-       /* conditionally incremented */) {
-    if (MaybeFireCallback(mCompleteCallbacks[index])) {
-      mCompleteCallbacks.RemoveElementAt(index);
-    } else {
-      index++;
-    }
-  }
-
-  // If that was the last database and we're supposed to be shutting down then
-  // we are finished.
-  if (mShutdownRequested && !mDatabases.Count()) {
-    MOZ_ASSERT(!mTransactions.Count());
-    Cleanup();
-  }
-}
-
-bool
-ConnectionPool::MaybeFireCallback(DatabasesCompleteCallback* aCallback)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aCallback);
-  MOZ_ASSERT(!aCallback->mDatabaseIds.IsEmpty());
-  MOZ_ASSERT(aCallback->mCallback);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::MaybeFireCallback",
-                 js::ProfileEntry::Category::STORAGE);
-
-  for (uint32_t count = aCallback->mDatabaseIds.Length(), index = 0;
-       index < count;
-       index++) {
-    const nsCString& databaseId = aCallback->mDatabaseIds[index];
-    MOZ_ASSERT(!databaseId.IsEmpty());
-
-    if (mDatabases.Get(databaseId)) {
-      return false;
-    }
-  }
-
-  unused << aCallback->mCallback->Run();
-  return true;
-}
-
-void
-ConnectionPool::CheckpointDatabase(DatabaseInfo* aDatabaseInfo)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aDatabaseInfo);
-  MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
-  MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
-  MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
-  MOZ_ASSERT(aDatabaseInfo->mNeedsCheckpoint);
-  MOZ_ASSERT(!aDatabaseInfo->mCloseOnIdle);
-  MOZ_ASSERT(!aDatabaseInfo->mClosing);
-
-  aDatabaseInfo->mNeedsCheckpoint = false;
-
-  nsCOMPtr<nsIRunnable> runnable =
-    new CheckpointConnectionRunnable(aDatabaseInfo);
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-    aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable,
-                                                 NS_DISPATCH_NORMAL)));
-}
-
-void
-ConnectionPool::CloseDatabase(DatabaseInfo* aDatabaseInfo)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aDatabaseInfo);
-  MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
-  MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
-  MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
-  MOZ_ASSERT(!aDatabaseInfo->mClosing);
-
-  aDatabaseInfo->mNeedsCheckpoint = false;
-  aDatabaseInfo->mClosing = true;
-
-  nsCOMPtr<nsIRunnable> runnable = new CloseConnectionRunnable(aDatabaseInfo);
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-    aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable,
-                                                 NS_DISPATCH_NORMAL)));
-}
-
-bool
-ConnectionPool::CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(!aDatabaseId.IsEmpty());
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::CloseDatabaseWhenIdleInternal",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId)) {
-    if (mIdleDatabases.RemoveElement(dbInfo)) {
-      CloseDatabase(dbInfo);
-      AdjustIdleTimer();
-    } else {
-      dbInfo->mCloseOnIdle = true;
-    }
-
-    return true;
-  }
-
-  return false;
-}
-
-ConnectionPool::
-ConnectionRunnable::ConnectionRunnable(DatabaseInfo* aDatabaseInfo)
-  : mDatabaseInfo(aDatabaseInfo)
-  , mOwningThread(do_GetCurrentThread())
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aDatabaseInfo);
-  MOZ_ASSERT(aDatabaseInfo->mConnectionPool);
-  aDatabaseInfo->mConnectionPool->AssertIsOnOwningThread();
-  MOZ_ASSERT(mOwningThread);
-}
-
-NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::CheckpointConnectionRunnable,
-                             ConnectionPool::ConnectionRunnable)
-
-NS_IMETHODIMP
-ConnectionPool::
-CheckpointConnectionRunnable::Run()
-{
-  MOZ_ASSERT(mDatabaseInfo);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::CheckpointConnectionRunnable::Run",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (mOwningThread) {
-    mDatabaseInfo->AssertIsOnConnectionThread();
-    MOZ_ASSERT(mDatabaseInfo->mConnection);
-
-    nsCOMPtr<nsIEventTarget> owningThread;
-    mOwningThread.swap(owningThread);
-
-    mDatabaseInfo->mConnection->Checkpoint(/* aIdle */ true);
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      owningThread->Dispatch(this, NS_DISPATCH_NORMAL)));
-    return NS_OK;
-  }
-
-  if (!mDatabaseInfo->TotalTransactionCount()) {
-    nsRefPtr<ConnectionPool> connectionPool = mDatabaseInfo->mConnectionPool;
-    MOZ_ASSERT(connectionPool);
-
-    connectionPool->NoteIdleDatabase(mDatabaseInfo);
-  }
-
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::CloseConnectionRunnable,
-                             ConnectionPool::ConnectionRunnable)
-
-NS_IMETHODIMP
-ConnectionPool::
-CloseConnectionRunnable::Run()
-{
-  MOZ_ASSERT(mDatabaseInfo);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::CloseConnectionRunnable::Run",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (mOwningThread) {
-    MOZ_ASSERT(mDatabaseInfo->mClosing);
-
-    nsCOMPtr<nsIEventTarget> owningThread;
-    mOwningThread.swap(owningThread);
-
-    if (mDatabaseInfo->mConnection) {
-      mDatabaseInfo->AssertIsOnConnectionThread();
-
-      mDatabaseInfo->mConnection->Close();
-
-      IDB_DEBUG_LOG(("ConnectionPool closed connection 0x%p",
-                     mDatabaseInfo->mConnection.get()));
-
-      mDatabaseInfo->mConnection = nullptr;
-
-#ifdef DEBUG
-      mDatabaseInfo->mDEBUGConnectionThread = nullptr;
-#endif
-    }
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      owningThread->Dispatch(this, NS_DISPATCH_NORMAL)));
-    return NS_OK;
-  }
-
-  nsRefPtr<ConnectionPool> connectionPool = mDatabaseInfo->mConnectionPool;
-  MOZ_ASSERT(connectionPool);
-
-  connectionPool->NoteClosedDatabase(mDatabaseInfo);
-  return NS_OK;
-}
-
-ConnectionPool::
-DatabaseInfo::DatabaseInfo(ConnectionPool* aConnectionPool,
-                           const nsACString& aDatabaseId)
-  : mConnectionPool(aConnectionPool)
-  , mDatabaseId(aDatabaseId)
-  , mRunningWriteTransaction(nullptr)
-  , mReadTransactionCount(0)
-  , mWriteTransactionCount(0)
-  , mNeedsCheckpoint(false)
-  , mCloseOnIdle(false)
-  , mClosing(false)
-#ifdef DEBUG
-  , mDEBUGConnectionThread(nullptr)
-#endif
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aConnectionPool);
-  aConnectionPool->AssertIsOnOwningThread();
-  MOZ_ASSERT(!aDatabaseId.IsEmpty());
-
-  MOZ_COUNT_CTOR(ConnectionPool::DatabaseInfo);
-}
-
-ConnectionPool::
-DatabaseInfo::~DatabaseInfo()
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(!mConnection);
-  MOZ_ASSERT(mScheduledWriteTransactions.IsEmpty());
-  MOZ_ASSERT(!mRunningWriteTransaction);
-  MOZ_ASSERT(!mThreadInfo.mThread);
-  MOZ_ASSERT(!mThreadInfo.mRunnable);
-  MOZ_ASSERT(!TotalTransactionCount());
-
-  MOZ_COUNT_DTOR(ConnectionPool::DatabaseInfo);
-}
-
-ConnectionPool::
-DatabasesCompleteCallback::DatabasesCompleteCallback(
-                                             nsTArray<nsCString>&& aDatabaseIds,
-                                             nsIRunnable* aCallback)
-  : mDatabaseIds(Move(aDatabaseIds))
-  , mCallback(aCallback)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(!mDatabaseIds.IsEmpty());
-  MOZ_ASSERT(aCallback);
-
-  MOZ_COUNT_CTOR(ConnectionPool::DatabasesCompleteCallback);
-}
-
-ConnectionPool::
-DatabasesCompleteCallback::~DatabasesCompleteCallback()
-{
-  AssertIsOnBackgroundThread();
-
-  MOZ_COUNT_DTOR(ConnectionPool::DatabasesCompleteCallback);
-}
-
-ConnectionPool::
-FinishCallbackWrapper::FinishCallbackWrapper(ConnectionPool* aConnectionPool,
-                                             uint64_t aTransactionId,
-                                             FinishCallback* aCallback)
-  : mConnectionPool(aConnectionPool)
-  , mCallback(aCallback)
-  , mOwningThread(do_GetCurrentThread())
-  , mTransactionId(aTransactionId)
-  , mHasRunOnce(false)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aConnectionPool);
-  MOZ_ASSERT(aCallback);
-  MOZ_ASSERT(mOwningThread);
-}
-
-ConnectionPool::
-FinishCallbackWrapper::~FinishCallbackWrapper()
-{
-  MOZ_ASSERT(!mConnectionPool);
-  MOZ_ASSERT(!mCallback);
-}
-
-NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::FinishCallbackWrapper, nsRunnable)
-
-nsresult
-ConnectionPool::
-FinishCallbackWrapper::Run()
-{
-  MOZ_ASSERT(mConnectionPool);
-  MOZ_ASSERT(mCallback);
-  MOZ_ASSERT(mOwningThread);
-
-  PROFILER_LABEL("IndexedDB",
-                 "ConnectionPool::FinishCallbackWrapper::Run",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (!mHasRunOnce) {
-    MOZ_ASSERT(!IsOnBackgroundThread());
-
-    mHasRunOnce = true;
-
-    unused << mCallback->Run();
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL)));
-
-    return NS_OK;
-  }
-
-  mConnectionPool->AssertIsOnOwningThread();
-  MOZ_ASSERT(mHasRunOnce);
-
-  nsRefPtr<ConnectionPool> connectionPool = Move(mConnectionPool);
-  nsRefPtr<FinishCallback> callback = Move(mCallback);
-
-  callback->TransactionFinishedBeforeUnblock();
-
-  connectionPool->NoteFinishedTransaction(mTransactionId);
-
-  callback->TransactionFinishedAfterUnblock();
-
-  return NS_OK;
-}
-
-uint32_t ConnectionPool::ThreadRunnable::sNextSerialNumber = 0;
-
-ConnectionPool::
-ThreadRunnable::ThreadRunnable()
-  : mSerialNumber(++sNextSerialNumber)
-  , mFirstRun(true)
-  , mContinueRunning(true)
-{
-  AssertIsOnBackgroundThread();
-}
-
-ConnectionPool::
-ThreadRunnable::~ThreadRunnable()
-{
-  MOZ_ASSERT(!mFirstRun);
-  MOZ_ASSERT(!mContinueRunning);
-}
-
-NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::ThreadRunnable, nsRunnable)
-
-nsresult
-ConnectionPool::
-ThreadRunnable::Run()
-{
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  char stackTopGuess;
-#endif // MOZ_ENABLE_PROFILER_SPS
-
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(mContinueRunning);
-
-  if (!mFirstRun) {
-    mContinueRunning = false;
-    return NS_OK;
-  }
-
-  mFirstRun = false;
-
-  {
-    // Scope for the thread name. Both PR_SetCurrentThreadName() and
-    // profiler_register_thread() copy the string so we don't need to keep it.
-    const nsPrintfCString threadName("IndexedDB #%lu", mSerialNumber);
-
-    PR_SetCurrentThreadName(threadName.get());
-
-#ifdef MOZ_ENABLE_PROFILER_SPS
-    profiler_register_thread(threadName.get(), &stackTopGuess);
-#endif // MOZ_ENABLE_PROFILER_SPS
-  }
-
-  {
-    // Scope for the profiler label.
-    PROFILER_LABEL("IndexedDB",
-                   "ConnectionPool::ThreadRunnable::Run",
-                   js::ProfileEntry::Category::STORAGE);
-
-    nsIThread* currentThread = NS_GetCurrentThread();
-    MOZ_ASSERT(currentThread);
-
-#ifdef DEBUG
-    if (kDEBUGTransactionThreadPriority !=
-          nsISupportsPriority::PRIORITY_NORMAL) {
-      NS_WARNING("ConnectionPool thread debugging enabled, priority has been "
-                 "modified!");
-
-      nsCOMPtr<nsISupportsPriority> thread = do_QueryInterface(currentThread);
-      MOZ_ASSERT(thread);
-
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-        thread->SetPriority(kDEBUGTransactionThreadPriority)));
-    }
-
-    if (kDEBUGTransactionThreadSleepMS) {
-      NS_WARNING("TransactionThreadPool thread debugging enabled, sleeping "
-                 "after every event!");
-    }
-#endif // DEBUG
-
-    while (mContinueRunning) {
-      MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
-
-#ifdef DEBUG
-      if (kDEBUGTransactionThreadSleepMS) {
-        MOZ_ALWAYS_TRUE(
-          PR_Sleep(PR_MillisecondsToInterval(kDEBUGTransactionThreadSleepMS)) ==
-            PR_SUCCESS);
-      }
-#endif // DEBUG
-    }
-  }
-
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  profiler_unregister_thread();
-#endif // MOZ_ENABLE_PROFILER_SPS
-
-  return NS_OK;
-}
-
-ConnectionPool::
-ThreadInfo::ThreadInfo()
-{
-  AssertIsOnBackgroundThread();
-
-  MOZ_COUNT_CTOR(ConnectionPool::ThreadInfo);
-}
-
-ConnectionPool::
-ThreadInfo::ThreadInfo(const ThreadInfo& aOther)
-  : mThread(aOther.mThread)
-  , mRunnable(aOther.mRunnable)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aOther.mThread);
-  MOZ_ASSERT(aOther.mRunnable);
-
-  MOZ_COUNT_CTOR(ConnectionPool::ThreadInfo);
-}
-
-ConnectionPool::
-ThreadInfo::~ThreadInfo()
-{
-  AssertIsOnBackgroundThread();
-
-  MOZ_COUNT_DTOR(ConnectionPool::ThreadInfo);
-}
-
-ConnectionPool::
-IdleResource::IdleResource(const TimeStamp& aIdleTime)
-  : mIdleTime(aIdleTime)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(!aIdleTime.IsNull());
-
-  MOZ_COUNT_CTOR(ConnectionPool::IdleResource);
-}
-
-ConnectionPool::
-IdleResource::IdleResource(const IdleResource& aOther)
-  : mIdleTime(aOther.mIdleTime)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(!aOther.mIdleTime.IsNull());
-
-  MOZ_COUNT_CTOR(ConnectionPool::IdleResource);
-}
-
-ConnectionPool::
-IdleResource::~IdleResource()
-{
-  AssertIsOnBackgroundThread();
-
-  MOZ_COUNT_DTOR(ConnectionPool::IdleResource);
-}
-
-ConnectionPool::
-IdleDatabaseInfo::IdleDatabaseInfo(DatabaseInfo* aDatabaseInfo)
-  : IdleResource(TimeStamp::NowLoRes() +
-                 (aDatabaseInfo->mNeedsCheckpoint ?
-                 TimeDuration::FromMilliseconds(kConnectionIdleCheckpointsMS) :
-                 TimeDuration::FromMilliseconds(kConnectionIdleCloseMS)))
-  , mDatabaseInfo(aDatabaseInfo)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aDatabaseInfo);
-
-  MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo);
-}
-
-ConnectionPool::
-IdleDatabaseInfo::IdleDatabaseInfo(const IdleDatabaseInfo& aOther)
-  : IdleResource(aOther)
-  , mDatabaseInfo(aOther.mDatabaseInfo)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mDatabaseInfo);
-
-  MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo);
-}
-
-ConnectionPool::
-IdleDatabaseInfo::~IdleDatabaseInfo()
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mDatabaseInfo);
-
-  MOZ_COUNT_DTOR(ConnectionPool::IdleDatabaseInfo);
-}
-
-ConnectionPool::
-IdleThreadInfo::IdleThreadInfo(const ThreadInfo& aThreadInfo)
-  : IdleResource(TimeStamp::NowLoRes() +
-                 TimeDuration::FromMilliseconds(kConnectionThreadIdleMS))
-  , mThreadInfo(aThreadInfo)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aThreadInfo.mRunnable);
-  MOZ_ASSERT(aThreadInfo.mThread);
-
-  MOZ_COUNT_CTOR(ConnectionPool::IdleThreadInfo);
-}
-
-ConnectionPool::
-IdleThreadInfo::IdleThreadInfo(const IdleThreadInfo& aOther)
-  : IdleResource(aOther)
-  , mThreadInfo(aOther.mThreadInfo)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mThreadInfo.mRunnable);
-  MOZ_ASSERT(mThreadInfo.mThread);
-
-  MOZ_COUNT_CTOR(ConnectionPool::IdleThreadInfo);
-}
-
-ConnectionPool::
-IdleThreadInfo::~IdleThreadInfo()
-{
-  AssertIsOnBackgroundThread();
-
-  MOZ_COUNT_DTOR(ConnectionPool::IdleThreadInfo);
-}
-
-ConnectionPool::
-TransactionInfo::TransactionInfo(
-                               DatabaseInfo* aDatabaseInfo,
-                               const nsID& aBackgroundChildLoggingId,
-                               const nsACString& aDatabaseId,
-                               uint64_t aTransactionId,
-                               int64_t aLoggingSerialNumber,
-                               const nsTArray<nsString>& aObjectStoreNames,
-                               bool aIsWriteTransaction,
-                               TransactionDatabaseOperationBase* aTransactionOp)
-  : mDatabaseInfo(aDatabaseInfo)
-  , mBackgroundChildLoggingId(aBackgroundChildLoggingId)
-  , mDatabaseId(aDatabaseId)
-  , mTransactionId(aTransactionId)
-  , mLoggingSerialNumber(aLoggingSerialNumber)
-  , mObjectStoreNames(aObjectStoreNames)
-  , mIsWriteTransaction(aIsWriteTransaction)
-  , mRunning(false)
-  , mFinished(false)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aDatabaseInfo);
-  aDatabaseInfo->mConnectionPool->AssertIsOnOwningThread();
-
-  MOZ_COUNT_CTOR(ConnectionPool::TransactionInfo);
-
-  if (aTransactionOp) {
-    mQueuedRunnables.AppendElement(aTransactionOp);
-  }
-}
-
-ConnectionPool::
-TransactionInfo::~TransactionInfo()
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(!mBlockedOn.Count());
-  MOZ_ASSERT(mQueuedRunnables.IsEmpty());
-  MOZ_ASSERT(!mRunning);
-  MOZ_ASSERT(mFinished);
-
-  MOZ_COUNT_DTOR(ConnectionPool::TransactionInfo);
-}
-
-void
-ConnectionPool::
-TransactionInfo::Schedule()
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mDatabaseInfo);
-
-  ConnectionPool* connectionPool = mDatabaseInfo->mConnectionPool;
-  MOZ_ASSERT(connectionPool);
-  connectionPool->AssertIsOnOwningThread();
-
-  unused <<
-    connectionPool->ScheduleTransaction(this,
-                                        /* aFromQueuedTransactions */ false);
-}
-
-ConnectionPool::
-TransactionInfoPair::TransactionInfoPair()
-  : mLastBlockingReads(nullptr)
-{
-  AssertIsOnBackgroundThread();
-
-  MOZ_COUNT_CTOR(ConnectionPool::TransactionInfoPair);
-}
-
-ConnectionPool::
-TransactionInfoPair::~TransactionInfoPair()
-{
-  AssertIsOnBackgroundThread();
-
-  MOZ_COUNT_DTOR(ConnectionPool::TransactionInfoPair);
-}
-
-/*******************************************************************************
  * Metadata classes
  ******************************************************************************/
 
-bool
-FullObjectStoreMetadata::HasLiveIndexes() const
-{
-  AssertIsOnBackgroundThread();
-
-  class MOZ_STACK_CLASS Helper final
-  {
-  public:
-    static bool
-    HasLiveIndexes(const FullObjectStoreMetadata* aMetadata)
-    {
-      AssertIsOnBackgroundThread();
-      MOZ_ASSERT(aMetadata);
-
-      bool hasLiveIndexes = false;
-      aMetadata->mIndexes.EnumerateRead(&Enumerate, &hasLiveIndexes);
-
-      return hasLiveIndexes;
-    }
-
-  private:
-    static PLDHashOperator
-    Enumerate(const uint64_t& aKey, FullIndexMetadata* aValue, void* aClosure)
-    {
-      auto* result = static_cast<bool*>(aClosure);
-      MOZ_ASSERT(result);
-
-      if (!aValue->mDeleted) {
-        *result = true;
-        return PL_DHASH_STOP;
-      }
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
-  return Helper::HasLiveIndexes(this);
-}
-
 already_AddRefed<FullDatabaseMetadata>
 FullDatabaseMetadata::Duplicate() const
 {
   AssertIsOnBackgroundThread();
 
   class MOZ_STACK_CLASS IndexClosure final
   {
     FullObjectStoreMetadata& mNew;
@@ -10987,23 +5872,32 @@ Factory::~Factory()
 already_AddRefed<Factory>
 Factory::Create(const LoggingInfo& aLoggingInfo)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
 
   // If this is the first instance then we need to do some initialization.
   if (!sFactoryInstanceCount) {
-    if (!gConnectionPool) {
-      gConnectionPool = new ConnectionPool();
+    if (!gTransactionThreadPool) {
+      nsRefPtr<TransactionThreadPool> threadPool =
+        TransactionThreadPool::Create();
+      if (NS_WARN_IF(!threadPool)) {
+        return nullptr;
+      }
+
+      gTransactionThreadPool = threadPool;
     }
 
     MOZ_ASSERT(!gLiveDatabaseHashtable);
     gLiveDatabaseHashtable = new DatabaseActorHashtable();
 
+    MOZ_ASSERT(!gStartTransactionRunnable);
+    gStartTransactionRunnable = new nsRunnable();
+
     MOZ_ASSERT(!gLoggingInfoHashtable);
     gLoggingInfoHashtable = new DatabaseLoggingInfoHashtable();
 
 #ifdef DEBUG
     if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) {
       NS_WARNING("PBackground thread debugging enabled, priority has been "
                  "modified!");
       nsCOMPtr<nsISupportsPriority> thread =
@@ -11064,16 +5958,19 @@ Factory::ActorDestroy(ActorDestroyReason
 
   mActorDestroyed = true;
 
   // Clean up if there are no more instances.
   if (!(--sFactoryInstanceCount)) {
     MOZ_ASSERT(gLoggingInfoHashtable);
     gLoggingInfoHashtable = nullptr;
 
+    MOZ_ASSERT(gStartTransactionRunnable);
+    gStartTransactionRunnable = nullptr;
+
     MOZ_ASSERT(gLiveDatabaseHashtable);
     MOZ_ASSERT(!gLiveDatabaseHashtable->Count());
     gLiveDatabaseHashtable = nullptr;
 
 #ifdef DEBUG
     if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) {
       nsCOMPtr<nsISupportsPriority> thread =
         do_QueryInterface(NS_GetCurrentThread());
@@ -11349,39 +6246,16 @@ Database::Invalidate()
     NS_WARNING("Failed to abort all transactions!");
   }
 
   MOZ_ALWAYS_TRUE(CloseInternal());
 
   CleanupMetadata();
 }
 
-nsresult
-Database::EnsureConnection()
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-
-  PROFILER_LABEL("IndexedDB",
-                 "Database::EnsureConnection",
-                 js::ProfileEntry::Category::STORAGE);
-
-  if (!mConnection || !mConnection->GetStorageConnection()) {
-    nsresult rv =
-      gConnectionPool->GetOrCreateConnection(this, getter_AddRefs(mConnection));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  AssertIsOnConnectionThread();
-
-  return NS_OK;
-}
-
 bool
 Database::RegisterTransaction(TransactionBase* aTransaction)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aTransaction);
   MOZ_ASSERT(!mTransactions.GetEntry(aTransaction));
   MOZ_ASSERT(mOfflineStorage);
 
@@ -11396,18 +6270,20 @@ void
 Database::UnregisterTransaction(TransactionBase* aTransaction)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aTransaction);
   MOZ_ASSERT(mTransactions.GetEntry(aTransaction));
 
   mTransactions.RemoveEntry(aTransaction);
 
-  if (!mTransactions.Count() && IsClosed() && mOfflineStorage) {
-    CloseConnection();
+  if (mOfflineStorage && !mTransactions.Count() && IsClosed()) {
+    DatabaseOfflineStorage::UnregisterOnOwningThread(
+      mOfflineStorage.forget());
+    CleanupMetadata();
   }
 }
 
 void
 Database::SetActorAlive()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mActorWasAlive);
@@ -11432,74 +6308,39 @@ Database::CloseInternal()
     }
 
     // Ignore harmless race when we just invalidated the database.
     return true;
   }
 
   mClosed = true;
 
-  if (gConnectionPool) {
-    gConnectionPool->CloseDatabaseWhenIdle(Id());
-  }
-
   DatabaseActorInfo* info;
   MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
 
   MOZ_ASSERT(info->mLiveDatabases.Contains(this));
 
   if (info->mWaitingFactoryOp) {
     info->mWaitingFactoryOp->NoteDatabaseClosed(this);
   }
 
   if (mOfflineStorage) {
     mOfflineStorage->CloseOnOwningThread();
 
     if (!mTransactions.Count()) {
-      CloseConnection();
+      DatabaseOfflineStorage::UnregisterOnOwningThread(
+        mOfflineStorage.forget());
+      CleanupMetadata();
     }
   }
 
   return true;
 }
 
 void
-Database::CloseConnection()
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mClosed);
-  MOZ_ASSERT(!mTransactions.Count());
-
-  if (gConnectionPool) {
-    nsTArray<nsCString> ids(1);
-    ids.AppendElement(Id());
-
-    nsCOMPtr<nsIRunnable> callback =
-      NS_NewRunnableMethod(this, &Database::ConnectionClosedCallback);
-    gConnectionPool->WaitForDatabasesToComplete(Move(ids), callback);
-  } else {
-    ConnectionClosedCallback();
-  }
-}
-
-void
-Database::ConnectionClosedCallback()
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mClosed);
-  MOZ_ASSERT(!mTransactions.Count());
-
-  if (mOfflineStorage) {
-    DatabaseOfflineStorage::UnregisterOnOwningThread(mOfflineStorage.forget());
-  }
-
-  CleanupMetadata();
-}
-
-void
 Database::CleanupMetadata()
 {
   AssertIsOnBackgroundThread();
 
   if (!mMetadataCleanedUp) {
     mMetadataCleanedUp = true;
 
     DatabaseActorInfo* info;
@@ -11616,26 +6457,24 @@ Database::AllocPBackgroundIDBTransaction
   }
 
   if (NS_WARN_IF(aObjectStoreNames.IsEmpty())) {
     ASSERT_UNLESS_FUZZING();
     return nullptr;
   }
 
   if (NS_WARN_IF(aMode != IDBTransaction::READ_ONLY &&
-                 aMode != IDBTransaction::READ_WRITE &&
-                 aMode != IDBTransaction::READ_WRITE_FLUSH)) {
+                 aMode != IDBTransaction::READ_WRITE)) {
     ASSERT_UNLESS_FUZZING();
     return nullptr;
   }
 
   // If this is a readwrite transaction to a chrome database make sure the child
   // has write access.
-  if (NS_WARN_IF((aMode == IDBTransaction::READ_WRITE ||
-                  aMode == IDBTransaction::READ_WRITE_FLUSH) &&
+  if (NS_WARN_IF(aMode == IDBTransaction::READ_WRITE &&
                  mPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo &&
                  !mChromeWriteAccessAllowed)) {
     return nullptr;
   }
 
   const ObjectStoreTable& objectStores = mMetadata->mObjectStores;
   const uint32_t nameCount = aObjectStoreNames.Length();
 
@@ -11686,39 +6525,37 @@ Database::RecvPBackgroundIDBTransactionC
                                     PBackgroundIDBTransactionParent* aActor,
                                     InfallibleTArray<nsString>&& aObjectStoreNames,
                                     const Mode& aMode)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
   MOZ_ASSERT(aMode == IDBTransaction::READ_ONLY ||
-             aMode == IDBTransaction::READ_WRITE ||
-             aMode == IDBTransaction::READ_WRITE_FLUSH);
+             aMode == IDBTransaction::READ_WRITE);
   MOZ_ASSERT(!mClosed);
 
   if (IsInvalidated()) {
     // This is an expected race. We don't want the child to die here, just don't
     // actually do any work.
     return true;
   }
 
   auto* transaction = static_cast<NormalTransaction*>(aActor);
 
-  nsRefPtr<StartTransactionOp> startOp = new StartTransactionOp(transaction);
-
-  uint64_t transactionId =
-    gConnectionPool->Start(GetLoggingInfo()->Id(),
-                           mMetadata->mDatabaseId,
-                           transaction->LoggingSerialNumber(),
-                           aObjectStoreNames,
-                           aMode != IDBTransaction::READ_ONLY,
-                           startOp);
-
-  transaction->SetActive(transactionId);
+  // Add a placeholder for this transaction immediately.
+  gTransactionThreadPool->Start(transaction->TransactionId(),
+                                mMetadata->mDatabaseId,
+                                aObjectStoreNames,
+                                aMode,
+                                GetLoggingInfo()->Id(),
+                                transaction->LoggingSerialNumber(),
+                                gStartTransactionRunnable);
+
+  transaction->SetActive();
 
   if (NS_WARN_IF(!RegisterTransaction(transaction))) {
     IDB_REPORT_INTERNAL_ERR();
     transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ false);
     return true;
   }
 
   return true;
@@ -11796,113 +6633,113 @@ Database::RecvClose()
   if (NS_WARN_IF(!CloseInternal())) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
   return true;
 }
 
-void
-Database::
-StartTransactionOp::RunOnConnectionThread()
-{
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(Transaction());
-  MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
-
-  IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
-                 "Beginning database work",
-               "IndexedDB %s: P T[%lld]: DB Start",
-               IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
-               mLoggingSerialNumber);
-
-  TransactionDatabaseOperationBase::RunOnConnectionThread();
-}
-
-nsresult
-Database::
-StartTransactionOp::DoDatabaseWork(DatabaseConnection* aConnection)
-{
-  MOZ_ASSERT(aConnection);
-  aConnection->AssertIsOnConnectionThread();
-
-  if (Transaction()->GetMode() != IDBTransaction::READ_ONLY) {
-    nsresult rv = aConnection->BeginWriteTransaction();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  return NS_OK;
-}
-
-nsresult
-Database::
-StartTransactionOp::SendSuccessResult()
-{
-  // We don't need to do anything here.
-  return NS_OK;
-}
-
-bool
-Database::
-StartTransactionOp::SendFailureResult(nsresult /* aResultCode */)
-{
-  IDB_REPORT_INTERNAL_ERR();
-
-  // Abort the transaction.
-  return false;
-}
-
-void
-Database::
-StartTransactionOp::Cleanup()
-{
-#ifdef DEBUG
-  // StartTransactionOp is not a normal database operation that is tied to an
-  // actor. Do this to make our assertions happy.
-  NoteActorDestroyed();
-#endif
-
-  TransactionDatabaseOperationBase::Cleanup();
-}
-
 /*******************************************************************************
  * TransactionBase
  ******************************************************************************/
 
 TransactionBase::TransactionBase(Database* aDatabase, Mode aMode)
   : mDatabase(aDatabase)
-  , mTransactionId(0)
+  , mTransactionId(gTransactionThreadPool->NextTransactionId())
   , mDatabaseId(aDatabase->Id())
   , mLoggingSerialNumber(aDatabase->GetLoggingInfo()->NextTransactionSN(aMode))
   , mActiveRequestCount(0)
   , mInvalidatedOnAnyThread(false)
   , mMode(aMode)
   , mHasBeenActive(false)
   , mActorDestroyed(false)
   , mInvalidated(false)
   , mResultCode(NS_OK)
   , mCommitOrAbortReceived(false)
   , mCommittedOrAborted(false)
   , mForceAborted(false)
+  , mTransactionThread(nullptr)
+  , mSavepointCount(0)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aDatabase);
+  MOZ_ASSERT(mTransactionId);
   MOZ_ASSERT(mLoggingSerialNumber);
 }
 
 TransactionBase::~TransactionBase()
 {
+  MOZ_ASSERT(!mSavepointCount);
   MOZ_ASSERT(!mActiveRequestCount);
   MOZ_ASSERT(mActorDestroyed);
   MOZ_ASSERT_IF(mHasBeenActive, mCommittedOrAborted);
 }
 
+nsresult
+TransactionBase::EnsureConnection()
+{
+#ifdef DEBUG
+  MOZ_ASSERT(!IsOnBackgroundThread());
+  if (!mTransactionThread) {
+    mTransactionThread = PR_GetCurrentThread();
+  }
+#endif
+
+  AssertIsOnTransactionThread();
+
+  PROFILER_LABEL("IndexedDB",
+                 "TransactionBase::EnsureConnection",
+                 js::ProfileEntry::Category::STORAGE);
+
+  if (!mConnection) {
+    nsCOMPtr<mozIStorageConnection> connection;
+    nsresult rv =
+      GetDatabaseConnection(mDatabase->FilePath(), mDatabase->Type(),
+                            mDatabase->Group(), mDatabase->Origin(),
+                            getter_AddRefs(connection));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsRefPtr<UpdateRefcountFunction> function;
+    nsCString beginTransaction;
+
+    if (mMode == IDBTransaction::READ_ONLY) {
+      beginTransaction.AssignLiteral("BEGIN TRANSACTION;");
+    } else {
+      function = new UpdateRefcountFunction(mDatabase->GetFileManager());
+
+      rv = connection->CreateFunction(NS_LITERAL_CSTRING("update_refcount"), 2,
+                                      function);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;");
+    }
+
+    nsCOMPtr<mozIStorageStatement> stmt;
+    rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = stmt->Execute();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    function.swap(mUpdateFileRefcountFunction);
+    connection.swap(mConnection);
+  }
+
+  return NS_OK;
+}
+
 void
 TransactionBase::Abort(nsresult aResultCode, bool aForce)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(NS_FAILED(aResultCode));
 
   if (NS_SUCCEEDED(mResultCode)) {
     mResultCode = aResultCode;
@@ -11968,17 +6805,21 @@ TransactionBase::CommitOrAbort()
 
   if (!mHasBeenActive) {
     return;
   }
 
   nsRefPtr<CommitOp> commitOp =
     new CommitOp(this, ClampResultCode(mResultCode));
 
-  gConnectionPool->Finish(TransactionId(), commitOp);
+  gTransactionThreadPool->Dispatch(TransactionId(),
+                                   DatabaseId(),
+                                   commitOp,
+                                   /* aFinish */ true,
+                                   /* aFinishCallback */ commitOp);
 }
 
 already_AddRefed<FullObjectStoreMetadata>
 TransactionBase::GetMetadataForObjectStoreId(int64_t aObjectStoreId) const
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aObjectStoreId);
 
@@ -12020,29 +6861,29 @@ TransactionBase::GetMetadataForIndexId(
 
   return metadata.forget();
 }
 
 void
 TransactionBase::NoteModifiedAutoIncrementObjectStore(
                                              FullObjectStoreMetadata* aMetadata)
 {
-  AssertIsOnConnectionThread();
+  AssertIsOnTransactionThread();
   MOZ_ASSERT(aMetadata);
 
   if (!mModifiedAutoIncrementObjectStoreMetadataArray.Contains(aMetadata)) {
     mModifiedAutoIncrementObjectStoreMetadataArray.AppendElement(aMetadata);
   }
 }
 
 void
 TransactionBase::ForgetModifiedAutoIncrementObjectStore(
                                              FullObjectStoreMetadata* aMetadata)
 {
-  AssertIsOnConnectionThread();
+  AssertIsOnTransactionThread();
   MOZ_ASSERT(aMetadata);
 
   mModifiedAutoIncrementObjectStoreMetadataArray.RemoveElement(aMetadata);
 }
 
 bool
 TransactionBase::VerifyRequestParams(const RequestParams& aParams) const
 {
@@ -12114,17 +6955,16 @@ TransactionBase::VerifyRequestParams(con
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     case RequestParams::TObjectStoreDeleteParams: {
       if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
-                     mMode != IDBTransaction::READ_WRITE_FLUSH &&
                      mMode != IDBTransaction::VERSION_CHANGE)) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
 
       const ObjectStoreDeleteParams& params =
         aParams.get_ObjectStoreDeleteParams();
       const nsRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
@@ -12137,17 +6977,16 @@ TransactionBase::VerifyRequestParams(con
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     case RequestParams::TObjectStoreClearParams: {
       if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
-                     mMode != IDBTransaction::READ_WRITE_FLUSH &&
                      mMode != IDBTransaction::VERSION_CHANGE)) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
 
       const ObjectStoreClearParams& params =
         aParams.get_ObjectStoreClearParams();
       const nsRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
@@ -12432,17 +7271,16 @@ TransactionBase::VerifyRequestParams(con
 
 bool
 TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams)
                                      const
 {
   AssertIsOnBackgroundThread();
 
   if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
-                 mMode != IDBTransaction::READ_WRITE_FLUSH &&
                  mMode != IDBTransaction::VERSION_CHANGE)) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
   nsRefPtr<FullObjectStoreMetadata> objMetadata =
     GetMetadataForObjectStoreId(aParams.objectStoreId());
   if (NS_WARN_IF(!objMetadata)) {
@@ -12489,18 +7327,16 @@ TransactionBase::VerifyRequestParams(con
       ASSERT_UNLESS_FUZZING();
       return false;
     }
 
     if (NS_WARN_IF(updates[index].value().IsUnset())) {
       ASSERT_UNLESS_FUZZING();
       return false;
     }
-
-    MOZ_ASSERT(!updates[index].value().GetBuffer().IsEmpty());
   }
 
   const nsTArray<DatabaseFileOrMutableFileId>& files = aParams.files();
 
   for (uint32_t index = 0; index < files.Length(); index++) {
     const DatabaseFileOrMutableFileId& fileOrFileId = files[index];
 
     MOZ_ASSERT(fileOrFileId.type() != DatabaseFileOrMutableFileId::T__None);
@@ -12551,16 +7387,99 @@ TransactionBase::VerifyRequestParams(con
 
     default:
       MOZ_CRASH("Should never get here!");
   }
 
   return true;
 }
 
+nsresult
+TransactionBase::StartSavepoint()
+{
+  AssertIsOnTransactionThread();
+  MOZ_ASSERT(mConnection);
+  MOZ_ASSERT(IDBTransaction::READ_WRITE == mMode ||
+             IDBTransaction::VERSION_CHANGE == mMode);
+
+  CachedStatement stmt;
+  nsresult rv = GetCachedStatement(kSavepointClause, &stmt);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stmt->Execute();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mUpdateFileRefcountFunction->StartSavepoint();
+
+  mSavepointCount++;
+
+  return NS_OK;
+}
+
+nsresult
+TransactionBase::ReleaseSavepoint()
+{
+  AssertIsOnTransactionThread();
+  MOZ_ASSERT(mConnection);
+  MOZ_ASSERT(IDBTransaction::READ_WRITE == mMode ||
+             IDBTransaction::VERSION_CHANGE == mMode);
+  MOZ_ASSERT(mSavepointCount);
+
+  mSavepointCount--;
+
+  CachedStatement stmt;
+  nsresult rv = GetCachedStatement(
+    NS_LITERAL_CSTRING("RELEASE ") + NS_LITERAL_CSTRING(kSavepointClause),
+    &stmt);
+  if (NS_SUCCEEDED(rv)) {
+    rv = stmt->Execute();
+    if (NS_SUCCEEDED(rv)) {
+      mUpdateFileRefcountFunction->ReleaseSavepoint();
+    }
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mUpdateFileRefcountFunction->RollbackSavepoint();
+  }
+
+  return rv;
+}
+
+nsresult
+TransactionBase::RollbackSavepoint()
+{
+  AssertIsOnTransactionThread();
+  MOZ_ASSERT(mConnection);
+  MOZ_ASSERT(IDBTransaction::READ_WRITE == mMode ||
+             IDBTransaction::VERSION_CHANGE == mMode);
+  MOZ_ASSERT(mSavepointCount);
+
+  mSavepointCount--;
+
+  mUpdateFileRefcountFunction->RollbackSavepoint();
+
+  CachedStatement stmt;
+  nsresult rv = GetCachedStatement(
+    NS_LITERAL_CSTRING("ROLLBACK TO ") + NS_LITERAL_CSTRING(kSavepointClause),
+    &stmt);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // This may fail if SQLite already rolled back the savepoint so ignore any
+  // errors.
+  unused << stmt->Execute();
+
+  return NS_OK;
+}
+
 void
 TransactionBase::NoteActiveRequest()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(mActiveRequestCount < UINT64_MAX);
 
   mActiveRequestCount++;
 }
@@ -12691,17 +7610,17 @@ TransactionBase::StartRequest(PBackgroun
 
   auto* op = static_cast<NormalTransactionOp*>(aActor);
 
   if (NS_WARN_IF(!op->Init(this))) {
     op->Cleanup();
     return false;
   }
 
-  op->DispatchToConnectionPool();
+  op->DispatchToTransactionThreadPool();
   return true;
 }
 
 bool
 TransactionBase::DeallocRequest(PBackgroundIDBRequestParent* aActor)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
@@ -12806,16 +7725,73 @@ TransactionBase::DeallocCursor(PBackgrou
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   // Transfer ownership back from IPDL.
   nsRefPtr<Cursor> actor = dont_AddRef(static_cast<Cursor*>(aActor));
   return true;
 }
 
+nsresult
+TransactionBase::GetCachedStatement(const nsACString& aQuery,
+                                    CachedStatement* aCachedStatement)
+{
+  AssertIsOnTransactionThread();
+  MOZ_ASSERT(!aQuery.IsEmpty());
+  MOZ_ASSERT(aCachedStatement);
+  MOZ_ASSERT(mConnection);
+
+  nsCOMPtr<mozIStorageStatement> stmt;
+
+  if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) {
+    nsresult rv = mConnection->CreateStatement(aQuery, getter_AddRefs(stmt));
+    if (NS_FAILED(rv)) {
+#ifdef DEBUG
+      nsCString msg;
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mConnection->GetLastErrorString(msg)));
+
+      nsAutoCString error =
+        NS_LITERAL_CSTRING("The statement '") + aQuery +
+        NS_LITERAL_CSTRING("' failed to compile with the error message '") +
+        msg + NS_LITERAL_CSTRING("'.");
+
+      NS_WARNING(error.get());
+#endif
+      return rv;
+    }
+
+    mCachedStatements.Put(aQuery, stmt);
+  }
+
+  aCachedStatement->Assign(stmt.forget());
+  return NS_OK;
+}
+
+void
+TransactionBase::ReleaseTransactionThreadObjects()
+{
+  AssertIsOnTransactionThread();
+
+  mCachedStatements.Clear();
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mConnection->Close()));
+  mConnection = nullptr;
+}
+
+void
+TransactionBase::ReleaseBackgroundThreadObjects()
+{
+  AssertIsOnBackgroundThread();
+
+  if (mUpdateFileRefcountFunction) {
+    mUpdateFileRefcountFunction->ClearFileInfoEntries();
+    mUpdateFileRefcountFunction = nullptr;
+  }
+}
+
 /*******************************************************************************
  * NormalTransaction
  ******************************************************************************/
 
 NormalTransaction::NormalTransaction(
                      Database* aDatabase,
                      TransactionBase::Mode aMode,
                      nsTArray<nsRefPtr<FullObjectStoreMetadata>>& aObjectStores)
@@ -12833,43 +7809,41 @@ NormalTransaction::IsSameProcessActor()
   AssertIsOnBackgroundThread();
 
   PBackgroundParent* actor = Manager()->Manager()->Manager();
   MOZ_ASSERT(actor);
 
   return !BackgroundParent::IsOtherProcessActor(actor);
 }
 
-void
+bool
 NormalTransaction::SendCompleteNotification(nsresult aResult)
 {
   AssertIsOnBackgroundThread();
 
-  if (!IsActorDestroyed()) {
-    unused << SendComplete(aResult);
-  }
+  return IsActorDestroyed() || !NS_WARN_IF(!SendComplete(aResult));
 }
 
 void
 NormalTransaction::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnBackgroundThread();
 
-  NoteActorDestroyed();
-
   if (!mCommittedOrAborted) {
     if (NS_SUCCEEDED(mResultCode)) {
       IDB_REPORT_INTERNAL_ERR();
       mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     mForceAborted = true;
 
     MaybeCommitOrAbort();
   }
+
+  NoteActorDestroyed();
 }
 
 bool
 NormalTransaction::RecvDeleteMe()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!IsActorDestroyed());
 
@@ -12956,18 +7930,17 @@ NormalTransaction::DeallocPBackgroundIDB
 }
 
 /*******************************************************************************
  * VersionChangeTransaction
  ******************************************************************************/
 
 VersionChangeTransaction::VersionChangeTransaction(
                                                 OpenDatabaseOp* aOpenDatabaseOp)
-  : TransactionBase(aOpenDatabaseOp->mDatabase,
-                    IDBTransaction::VERSION_CHANGE)
+  : TransactionBase(aOpenDatabaseOp->mDatabase, IDBTransaction::VERSION_CHANGE)
   , mOpenDatabaseOp(aOpenDatabaseOp)
   , mActorWasAlive(false)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aOpenDatabaseOp);
 }
 
 VersionChangeTransaction::~VersionChangeTransaction()
@@ -13115,55 +8088,55 @@ VersionChangeTransaction::UpdateMetadata
     for (uint32_t count = info->mLiveDatabases.Length(), index = 0;
          index < count;
          index++) {
       info->mLiveDatabases[index]->mMetadata = info->mMetadata;
     }
   }
 }
 
-void
+bool
 VersionChangeTransaction::SendCompleteNotification(nsresult aResult)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(mOpenDatabaseOp);
 
   nsRefPtr<OpenDatabaseOp> openDatabaseOp;
   mOpenDatabaseOp.swap(openDatabaseOp);
 
   if (NS_FAILED(aResult) && NS_SUCCEEDED(openDatabaseOp->mResultCode)) {
     openDatabaseOp->mResultCode = aResult;
   }
 
   openDatabaseOp->mState = OpenDatabaseOp::State_SendingResults;
 
-  if (!IsActorDestroyed()) {
-    unused << SendComplete(aResult);
-  }
+  bool result = IsActorDestroyed() || !NS_WARN_IF(!SendComplete(aResult));
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(openDatabaseOp->Run()));
+
+  return result;
 }
 
 void
 VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnBackgroundThread();
 
-  NoteActorDestroyed();
-
   if (!mCommittedOrAborted) {
     if (NS_SUCCEEDED(mResultCode)) {
       IDB_REPORT_INTERNAL_ERR();
       mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     mForceAborted = true;
 
     MaybeCommitOrAbort();
   }
+
+  NoteActorDestroyed();
 }
 
 bool
 VersionChangeTransaction::RecvDeleteMe()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!IsActorDestroyed());
 
@@ -13233,76 +8206,26 @@ VersionChangeTransaction::RecvCreateObje
 
   nsRefPtr<CreateObjectStoreOp> op = new CreateObjectStoreOp(this, aMetadata);
 
   if (NS_WARN_IF(!op->Init(this))) {
     op->Cleanup();
     return false;
   }
 
-  op->DispatchToConnectionPool();
+  op->DispatchToTransactionThreadPool();
 
   return true;
 }
 
 bool
 VersionChangeTransaction::RecvDeleteObjectStore(const int64_t& aObjectStoreId)
 {
   AssertIsOnBackgroundThread();
 
-  class MOZ_STACK_CLASS Helper final
-  {
-    const int64_t mObjectStoreId;
-    bool mIsLastObjectStore;
-    DebugOnly<bool> mFoundTargetId;
-
-  public:
-    static bool
-    IsLastObjectStore(const FullDatabaseMetadata* aDatabaseMetadata,
-                      const int64_t aObjectStoreId)
-    {
-      AssertIsOnBackgroundThread();
-      MOZ_ASSERT(aDatabaseMetadata);
-      MOZ_ASSERT(aObjectStoreId);
-
-      Helper helper(aObjectStoreId);
-      aDatabaseMetadata->mObjectStores.EnumerateRead(&Enumerate, &helper);
-
-      MOZ_ASSERT_IF(helper.mIsLastObjectStore, helper.mFoundTargetId);
-
-      return helper.mIsLastObjectStore;
-    }
-
-  private:
-    explicit
-    Helper(const int64_t aObjectStoreId)
-      : mObjectStoreId(aObjectStoreId)
-      , mIsLastObjectStore(true)
-      , mFoundTargetId(false)
-    { }
-
-    static PLDHashOperator
-    Enumerate(const uint64_t& aKey,
-              FullObjectStoreMetadata* aValue,
-              void* aClosure)
-    {
-      auto* helper = static_cast<Helper*>(aClosure);
-      MOZ_ASSERT(helper);
-
-      if (uint64_t(helper->mObjectStoreId) == aKey) {
-        helper->mFoundTargetId = true;
-      } else if(!aValue->mDeleted) {
-        helper->mIsLastObjectStore = false;
-        return PL_DHASH_STOP;
-      }
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
   if (NS_WARN_IF(!aObjectStoreId)) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
   const nsRefPtr<FullDatabaseMetadata> dbMetadata = GetDatabase()->Metadata();
   MOZ_ASSERT(dbMetadata);
   MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
@@ -13323,27 +8246,24 @@ VersionChangeTransaction::RecvDeleteObje
   if (NS_WARN_IF(mCommitOrAbortReceived)) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
   foundMetadata->mDeleted = true;
 
   nsRefPtr<DeleteObjectStoreOp> op =
-    new DeleteObjectStoreOp(this,
-                            foundMetadata,
-                            Helper::IsLastObjectStore(dbMetadata,
-                                                      aObjectStoreId));
+    new DeleteObjectStoreOp(this, foundMetadata);
 
   if (NS_WARN_IF(!op->Init(this))) {
     op->Cleanup();
     return false;
   }
 
-  op->DispatchToConnectionPool();
+  op->DispatchToTransactionThreadPool();
 
   return true;
 }
 
 bool
 VersionChangeTransaction::RecvCreateIndex(const int64_t& aObjectStoreId,
                                           const IndexMetadata& aMetadata)
 {
@@ -13403,75 +8323,27 @@ VersionChangeTransaction::RecvCreateInde
   nsRefPtr<CreateIndexOp> op =
     new CreateIndexOp(this, aObjectStoreId, aMetadata);
 
   if (NS_WARN_IF(!op->Init(this))) {
     op->Cleanup();
     return false;
   }
 
-  op->DispatchToConnectionPool();
+  op->DispatchToTransactionThreadPool();
 
   return true;
 }
 
 bool
 VersionChangeTransaction::RecvDeleteIndex(const int64_t& aObjectStoreId,
                                           const int64_t& aIndexId)
 {
   AssertIsOnBackgroundThread();
 
-  class MOZ_STACK_CLASS Helper final
-  {
-    const int64_t mIndexId;
-    bool mIsLastIndex;
-    DebugOnly<bool> mFoundTargetId;
-
-  public:
-    static bool
-    IsLastIndex(const FullObjectStoreMetadata* aObjectStoreMetadata,
-                const int64_t aIndexId)
-    {
-      AssertIsOnBackgroundThread();
-      MOZ_ASSERT(aObjectStoreMetadata);
-      MOZ_ASSERT(aIndexId);
-
-      Helper helper(aIndexId);
-      aObjectStoreMetadata->mIndexes.EnumerateRead(&Enumerate, &helper);
-
-      MOZ_ASSERT_IF(helper.mIsLastIndex, helper.mFoundTargetId);
-
-      return helper.mIsLastIndex;
-    }
-
-  private:
-    explicit
-    Helper(const int64_t aIndexId)
-      : mIndexId(aIndexId)
-      , mIsLastIndex(true)
-      , mFoundTargetId(false)
-    { }
-
-    static PLDHashOperator
-    Enumerate(const uint64_t& aKey, FullIndexMetadata* aValue, void* aClosure)
-    {
-      auto* helper = static_cast<Helper*>(aClosure);
-      MOZ_ASSERT(helper);
-
-      if (uint64_t(helper->mIndexId) == aKey) {
-        helper->mFoundTargetId = true;
-      } else if (!aValue->mDeleted) {
-        helper->mIsLastIndex = false;
-        return PL_DHASH_STOP;
-      }
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
   if (NS_WARN_IF(!aObjectStoreId)) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
   if (NS_WARN_IF(!aIndexId)) {
     ASSERT_UNLESS_FUZZING();
     return false;
@@ -13510,29 +8382,24 @@ VersionChangeTransaction::RecvDeleteInde
 
   if (NS_WARN_IF(mCommitOrAbortReceived)) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
   foundIndexMetadata->mDeleted = true;
 
-  nsRefPtr<DeleteIndexOp> op =
-    new DeleteIndexOp(this,
-                      aObjectStoreId,
-                      aIndexId,
-                      foundIndexMetadata->mCommonMetadata.unique(),
-                      Helper::IsLastIndex(foundObjectStoreMetadata, aIndexId));
+  nsRefPtr<DeleteIndexOp> op = new DeleteIndexOp(this, aIndexId);
 
   if (NS_WARN_IF(!op->Init(this))) {
     op->Cleanup();
     return false;
   }
 
-  op->DispatchToConnectionPool();
+  op->DispatchToTransactionThreadPool();
 
   return true;
 }
 
 PBackgroundIDBRequestParent*
 VersionChangeTransaction::AllocPBackgroundIDBRequestParent(
                                                    const RequestParams& aParams)
 {
@@ -13678,17 +8545,17 @@ Cursor::Start(const OpenCursorParams& aP
 
   nsRefPtr<OpenOp> openOp = new OpenOp(this, optionalKeyRange);
 
   if (NS_WARN_IF(!openOp->Init(mTransaction))) {
     openOp->Cleanup();
     return false;
   }
 
-  openOp->DispatchToConnectionPool();
+  openOp->DispatchToTransactionThreadPool();
   mCurrentlyRunningOp = openOp;
 
   return true;
 }
 
 void
 Cursor::SendResponseInternal(CursorResponse& aResponse,
                              const nsTArray<StructuredCloneFile>& aFiles)
@@ -13840,17 +8707,17 @@ Cursor::RecvContinue(const CursorRequest
   }
 
   nsRefPtr<ContinueOp> continueOp = new ContinueOp(this, aParams);
   if (NS_WARN_IF(!continueOp->Init(mTransaction))) {
     continueOp->Cleanup();
     return false;
   }
 
-  continueOp->DispatchToConnectionPool();
+  continueOp->DispatchToTransactionThreadPool();
   mCurrentlyRunningOp = continueOp;
 
   return true;
 }
 
 /*******************************************************************************
  * FileManager
  ******************************************************************************/
@@ -14197,23 +9064,23 @@ FileManager::InitDirectory(nsIFile* aDir
     bool hasElements;
     rv = entries->HasMoreElements(&hasElements);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (hasElements) {
       nsCOMPtr<mozIStorageConnection> connection;
-      rv = CreateStorageConnection(aDatabaseFile,
-                                   aDirectory,
-                                   NullString(),
-                                   aPersistenceType,
-                                   aGroup,
-                                   aOrigin,
-                                   getter_AddRefs(connection));
+      rv = CreateDatabaseConnection(aDatabaseFile,
+                                    aDirectory,
+                                    NullString(),
+                                    aPersistenceType,
+                                    aGroup,
+                                    aOrigin,
+                                    getter_AddRefs(connection));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       mozStorageTransaction transaction(connection, false);
 
       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "CREATE VIRTUAL TABLE fs USING filesystem;"
@@ -14292,20 +9159,17 @@ FileManager::InitDirectory(nsIFile* aDir
 
       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "DROP TABLE fs;"
       ));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
-      rv = transaction.Commit();
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
+      transaction.Commit();
     }
   }
 
   return NS_OK;
 }
 
 // static
 nsresult
@@ -14410,17 +9274,16 @@ QuotaClient::GetType()
 {
   return QuotaClient::IDB;
 }
 
 struct FileManagerInitInfo
 {
   nsCOMPtr<nsIFile> mDirectory;
   nsCOMPtr<nsIFile> mDatabaseFile;
-  nsCOMPtr<nsIFile> mDatabaseWALFile;
 };
 
 nsresult
 QuotaClient::InitOrigin(PersistenceType aPersistenceType,
                         const nsACString& aGroup,
                         const nsACString& aOrigin,
                         UsageInfo* aUsageInfo)
 {
@@ -14443,27 +9306,17 @@ QuotaClient::InitOrigin(PersistenceType 
   nsAutoTArray<FileManagerInitInfo, 20> initInfos;
 
   nsCOMPtr<nsISimpleEnumerator> entries;
   rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  const NS_ConvertASCIItoUTF16 filesSuffix(
-    kFileManagerDirectoryNameSuffix,
-    LiteralStringLength(kFileManagerDirectoryNameSuffix));
-
-  const NS_ConvertASCIItoUTF16 journalSuffix(
-    kSQLiteJournalSuffix,
-    LiteralStringLength(kSQLiteJournalSuffix));
-  const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
-                                         LiteralStringLength(kSQLiteSHMSuffix));
-  const NS_ConvertASCIItoUTF16 walSuffix(kSQLiteWALSuffix,
-                                         LiteralStringLength(kSQLiteWALSuffix));
+  const NS_ConvertASCIItoUTF16 filesSuffix(kFileManagerDirectoryNameSuffix);
 
   bool hasMore;
   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
          hasMore &&
          (!aUsageInfo || !aUsageInfo->Canceled())) {
     nsCOMPtr<nsISupports> entry;
     rv = entries->GetNext(getter_AddRefs(entry));
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -14474,87 +9327,62 @@ QuotaClient::InitOrigin(PersistenceType 
     MOZ_ASSERT(file);
 
     nsString leafName;
     rv = file->GetLeafName(leafName);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
+
     bool isDirectory;
     rv = file->IsDirectory(&isDirectory);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (isDirectory) {
       if (!StringEndsWith(leafName, filesSuffix) ||
           !validSubdirs.GetEntry(leafName)) {
         subdirsToProcess.AppendElement(leafName);
       }
       continue;
     }
 
-    // Skip Desktop Service Store (.DS_Store) files. These files are only used
-    // on Mac OS X, but the profile can be shared across different operating
-    // systems, so we check it on all platforms.
-    if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
-      continue;
-    }
-
-    // Skip SQLite temporary files. These files take up space on disk but will
-    // be deleted as soon as the database is opened, so we don't count them
-    // towards quota.
-    if (StringEndsWith(leafName, journalSuffix) ||
-        StringEndsWith(leafName, shmSuffix)) {
-      continue;
-    }
-
-    // The SQLite WAL file does count towards quota, but it is handled below
-    // once we find the actual database file.
-    if (StringEndsWith(leafName, walSuffix)) {
+    // Skip SQLite and Desktop Service Store (.DS_Store) files.
+    // Desktop Service Store file is only used on Mac OS X, but the profile
+    // can be shared across different operating systems, so we check it on
+    // all platforms.
+    if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal")) ||
+        leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
       continue;
     }
 
     nsDependentSubstring dbBaseFilename;
     if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) {
       unknownFiles.AppendElement(file);
       continue;
     }
 
+    nsString fmDirectoryBaseName = dbBaseFilename + filesSuffix;
+
     nsCOMPtr<nsIFile> fmDirectory;
     rv = directory->Clone(getter_AddRefs(fmDirectory));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    nsString fmDirectoryBaseName = dbBaseFilename + filesSuffix;
-
     rv = fmDirectory->Append(fmDirectoryBaseName);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    nsCOMPtr<nsIFile> walFile;
-    if (aUsageInfo) {
-      rv = directory->Clone(getter_AddRefs(walFile));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      rv = walFile->Append(dbBaseFilename + walSuffix);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
     FileManagerInitInfo* initInfo = initInfos.AppendElement();
     initInfo->mDirectory.swap(fmDirectory);
     initInfo->mDatabaseFile.swap(file);
-    initInfo->mDatabaseWALFile.swap(walFile);
 
     validSubdirs.PutEntry(fmDirectoryBaseName);
   }
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -14629,17 +9457,16 @@ QuotaClient::InitOrigin(PersistenceType 
       return rv;
     }
   }
 
   for (uint32_t count = initInfos.Length(), i = 0; i < count; i++) {
     FileManagerInitInfo& initInfo = initInfos[i];
     MOZ_ASSERT(initInfo.mDirectory);
     MOZ_ASSERT(initInfo.mDatabaseFile);
-    MOZ_ASSERT_IF(aUsageInfo, initInfo.mDatabaseWALFile);
 
     rv = FileManager::InitDirectory(initInfo.mDirectory,
                                     initInfo.mDatabaseFile,
                                     aPersistenceType,
                                     aGroup,
                                     aOrigin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -14651,61 +9478,41 @@ QuotaClient::InitOrigin(PersistenceType 
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       MOZ_ASSERT(fileSize >= 0);
 
       aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
 
-      rv = initInfo.mDatabaseWALFile->GetFileSize(&fileSize);
-      if (NS_SUCCEEDED(rv)) {
-        MOZ_ASSERT(fileSize >= 0);
-        aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
-      } else if (NS_WARN_IF(rv != NS_ERROR_FILE_NOT_FOUND &&
-                            rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)) {
-        return rv;
-      }
-
       uint64_t usage;
       rv = FileManager::GetUsage(initInfo.mDirectory, &usage);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       aUsageInfo->AppendToFileUsage(usage);
     }
   }
 
   // We have to do this after file manager initialization.
-  if (!unknownFiles.IsEmpty()) {
-#ifdef DEBUG
-    for (uint32_t count = unknownFiles.Length(), i = 0; i < count; i++) {
-      nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i];
-
-      nsString leafName;
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(unknownFile->GetLeafName(leafName)));
-
-      MOZ_ASSERT(!StringEndsWith(leafName, journalSuffix));
-      MOZ_ASSERT(!StringEndsWith(leafName, shmSuffix));
-      MOZ_ASSERT(!StringEndsWith(leafName, walSuffix));
-
-      nsString path;
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(unknownFile->GetPath(path)));
-      MOZ_ASSERT(!path.IsEmpty());
-
-      nsPrintfCString warning("Refusing to open databases for \"%s\" because "
-                              "an unexpected file exists in the storage "
-                              "area: \"%s\"",
-                              PromiseFlatCString(aOrigin).get(),
-                              NS_ConvertUTF16toUTF8(path).get());
-      NS_WARNING(warning.get());
-    }
-#endif
-    return NS_ERROR_UNEXPECTED;
+  for (uint32_t count = unknownFiles.Length(), i = 0; i < count; i++) {
+    nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i];
+
+    // Some temporary SQLite files could disappear during file manager
+    // initialization, so we have to check if the unknown file still exists.
+    bool exists;
+    rv = unknownFile->Exists(&exists);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (exists) {
+      return NS_ERROR_UNEXPECTED;
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
 QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType,
                                const nsACString& aGroup,
@@ -14884,42 +9691,29 @@ QuotaClient::GetUsageForDirectoryInterna
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!entries) {
     return NS_OK;
   }
 
-  const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
-                                         LiteralStringLength(kSQLiteSHMSuffix));
-
   bool hasMore;
   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
          hasMore &&
          !aUsageInfo->Canceled()) {
     nsCOMPtr<nsISupports> entry;
     rv = entries->GetNext(getter_AddRefs(entry));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
     MOZ_ASSERT(file);
 
-    nsString leafName;
-    rv = file->GetLeafName(leafName);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    if (StringEndsWith(leafName, shmSuffix)) {
-      continue;
-    }
-
     bool isDirectory;
     rv = file->IsDirectory(&isDirectory);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (isDirectory) {
       if (aDatabaseFiles) {
@@ -14968,47 +9762,51 @@ QuotaClient::GetUsageForDirectoryInterna
 void
 QuotaClient::
 WaitForTransactionsRunnable::MaybeWait()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(mState == State_Initial);
   MOZ_ASSERT(mQuotaClient);
 
-  nsRefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
-  if (connectionPool) {
+  nsRefPtr<TransactionThreadPool> threadPool = gTransactionThreadPool.get();
+  if (threadPool) {
     mState = State_WaitingForTransactions;
 
-    connectionPool->WaitForDatabasesToComplete(Move(mDatabaseIds), this);
+    threadPool->WaitForDatabasesToComplete(mDatabaseIds, this);
+
+    MOZ_ASSERT(mDatabaseIds.IsEmpty());
     return;
   }
 
   mDatabaseIds.Clear();
 
   SendToMainThread();
 }
 
 void
 QuotaClient::
 WaitForTransactionsRunnable::SendToMainThread()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(mState == State_Initial || mState == State_WaitingForTransactions);
+  MOZ_ASSERT(mDatabaseIds.IsEmpty());
 
   mState = State_CallingCallback;
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
 }
 
 void
 QuotaClient::
 WaitForTransactionsRunnable::CallCallback()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mState == State_CallingCallback);
+  MOZ_ASSERT(mDatabaseIds.IsEmpty());
 
   nsRefPtr<QuotaClient> quotaClient;
   mQuotaClient.swap(quotaClient);
 
   nsCOMPtr<nsIRunnable> callback;
   mCallback.swap(callback);
 
   callback->Run();
@@ -15064,21 +9862,21 @@ ShutdownTransactionThreadPoolRunnable::R
     return NS_OK;
   }
 
   AssertIsOnBackgroundThread();
 
   if (!mHasRequestedShutDown) {
     mHasRequestedShutDown = true;
 
-    nsRefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
-    if (connectionPool) {
-      connectionPool->Shutdown();
-
-      gConnectionPool = nullptr;
+    nsRefPtr<TransactionThreadPool> threadPool = gTransactionThreadPool.get();
+    if (threadPool) {
+      threadPool->Shutdown();
+
+      gTransactionThreadPool = nullptr;
     }
 
   }
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
 
   return NS_OK;
 }
@@ -15342,117 +10140,87 @@ DatabaseOperationBase::ReinterpretDouble
     double d;
     uint64_t u;
   } pun;
   pun.d = aDouble;
   return pun.u;
 }
 
 // static
-template <typename T>
-nsresult
-DatabaseOperationBase::GetStructuredCloneReadInfoFromSource(
-                                                 T* aSource,
-                                                 uint32_t aDataIndex,
-                                                 uint32_t aFileIdsIndex,
-                                                 FileManager* aFileManager,
-                                                 StructuredCloneReadInfo* aInfo)
+nsresult
+DatabaseOperationBase::GetStructuredCloneReadInfoFromStatement(
+                                               mozIStorageStatement* aStatement,
+                                               uint32_t aDataIndex,
+                                               uint32_t aFileIdsIndex,
+                                               FileManager* aFileManager,
+                                               StructuredCloneReadInfo* aInfo)
 {
   MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(aSource);
+  MOZ_ASSERT(aStatement);
   MOZ_ASSERT(aFileManager);
-  MOZ_ASSERT(aInfo);
+
+  PROFILER_LABEL("IndexedDB",
+                 "DatabaseOperationBase::"
+                 "GetStructuredCloneReadInfoFromStatement",
+                 js::ProfileEntry::Category::STORAGE);
 
 #ifdef DEBUG
   {
-    int32_t columnType;
-    MOZ_ASSERT(NS_SUCCEEDED(aSource->GetTypeOfIndex(aDataIndex, &columnType)));
-    MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_BLOB);
+    int32_t type;
+    MOZ_ASSERT(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)));
+    MOZ_ASSERT(type == mozIStorageStatement::VALUE_TYPE_BLOB);
   }
 #endif
 
   const uint8_t* blobData;
   uint32_t blobDataLength;
-  nsresult rv = aSource->GetSharedBlob(aDataIndex, &blobDataLength, &blobData);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  bool isNull;
-  rv = aSource->GetIsNull(aFileIdsIndex, &isNull);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsString fileIds;
-
-  if (isNull) {
-    fileIds.SetIsVoid(true);
-  } else {
-    rv = aSource->GetString(aFileIdsIndex, fileIds);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  rv = GetStructuredCloneReadInfoFromBlob(blobData,
-                                          blobDataLength,
-                                          fileIds,
-                                          aFileManager,
-                                          aInfo);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-// static
-nsresult
-DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob(
-                                                 const uint8_t* aBlobData,
-                                                 uint32_t aBlobDataLength,
-                                                 const nsAString& aFileIds,
-                                                 FileManager* aFileManager,
-                                                 StructuredCloneReadInfo* aInfo)
-{
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(aFileManager);
-  MOZ_ASSERT(aInfo);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob",
-                 js::ProfileEntry::Category::STORAGE);
-
-  const char* compressed = reinterpret_cast<const char*>(aBlobData);
-  size_t compressedLength = size_t(aBlobDataLength);
+  nsresult rv =
+    aStatement->GetSharedBlob(aDataIndex, &blobDataLength, &blobData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  const char* compressed = reinterpret_cast<const char*>(blobData);
+  size_t compressedLength = size_t(blobDataLength);
 
   size_t uncompressedLength;
   if (NS_WARN_IF(!snappy::GetUncompressedLength(compressed, compressedLength,
                                                 &uncompressedLength))) {
     return NS_ERROR_FILE_CORRUPTED;
   }
 
-  AutoFallibleTArray<uint8_t, 512> uncompressed;
+  FallibleTArray<uint8_t> uncompressed;
   if (NS_WARN_IF(!uncompressed.SetLength(uncompressedLength))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   char* uncompressedBuffer = reinterpret_cast<char*>(uncompressed.Elements());
 
   if (NS_WARN_IF(!snappy::RawUncompress(compressed, compressedLength,
                                         uncompressedBuffer))) {
     return NS_ERROR_FILE_CORRUPTED;
   }
 
   aInfo->mData.SwapElements(uncompressed);
 
-  if (!aFileIds.IsVoid()) {
+  bool isNull;
+  rv = aStatement->GetIsNull(aFileIdsIndex, &isNull);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!isNull) {
+    nsString ids;
+    rv = aStatement->GetString(aFileIdsIndex, ids);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
     nsAutoTArray<int64_t, 10> array;
-    nsresult rv = ConvertFileIdsToArray(aFileIds, array);
+    rv = ConvertFileIdsToArray(ids, array);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     for (uint32_t count = array.Length(), index = 0; index < count; index++) {
       MOZ_ASSERT(array[index] > 0);
 
       nsRefPtr<FileInfo> fileInfo = aFileManager->GetFileInfo(array[index]);
@@ -15523,565 +10291,144 @@ DatabaseOperationBase::AppendConditionCl
     aResult.Append('=');
   }
 
   aResult += NS_LITERAL_CSTRING(" :") + aArgName;
 }
 
 // static
 nsresult
-DatabaseOperationBase::GetUniqueIndexTableForObjectStore(
-                                TransactionBase* aTransaction,
-                                int64_t aObjectStoreId,
-                                Maybe<UniqueIndexTable>& aMaybeUniqueIndexTable)
-{
-  AssertIsOnBackgroundThread();
+DatabaseOperationBase::UpdateIndexes(
+                              TransactionBase* aTransaction,
+                              const UniqueIndexTable& aUniqueIndexTable,
+                              const Key& aObjectStoreKey,
+                              bool aOverwrite,
+                              int64_t aObjectDataId,
+                              const nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
+{
   MOZ_ASSERT(aTransaction);
-  MOZ_ASSERT(aObjectStoreId);
-  MOZ_ASSERT(aMaybeUniqueIndexTable.isNothing());
-
-  class MOZ_STACK_CLASS Helper final
-  {
-  public:
-    static nsresult
-    CopyUniqueValues(const IndexTable& aIndexes,
-                     Maybe<UniqueIndexTable>& aMaybeUniqueIndexTable)
-    {
-      const uint32_t indexCount = aIndexes.Count();
-      MOZ_ASSERT(indexCount);
-
-      aMaybeUniqueIndexTable.emplace();
-
-      aIndexes.EnumerateRead(Enumerate, aMaybeUniqueIndexTable.ptr());
-
-      if (NS_WARN_IF(aMaybeUniqueIndexTable.ref().Count() != indexCount)) {
-        IDB_REPORT_INTERNAL_ERR();
-        aMaybeUniqueIndexTable.reset();
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-
-#ifdef DEBUG
-      aMaybeUniqueIndexTable.ref().MarkImmutable();
-#endif
-      return NS_OK;
-    }
-
-  private:
-    static PLDHashOperator
-    Enumerate(const uint64_t& aKey, FullIndexMetadata* aValue, void* aClosure)
-    {
-      auto* uniqueIndexTable = static_cast<UniqueIndexTable*>(aClosure);
-      MOZ_ASSERT(uniqueIndexTable);
-      MOZ_ASSERT(!uniqueIndexTable->Get(aValue->mCommonMetadata.id()));
-
-      if (NS_WARN_IF(!uniqueIndexTable->Put(aValue->mCommonMetadata.id(),
-                                            aValue->mCommonMetadata.unique(),
-                                            fallible))) {
-        return PL_DHASH_STOP;
-      }
-
-      return PL_DHASH_NEXT;
-    }
-  };
-
-  const nsRefPtr<FullObjectStoreMetadata> objectStoreMetadata = 
-    aTransaction->GetMetadataForObjectStoreId(aObjectStoreId);
-  MOZ_ASSERT(objectStoreMetadata);
-
-  if (!objectStoreMetadata->mIndexes.Count()) {
-    return NS_OK;
-  }
-
-  nsresult rv = Helper::CopyUniqueValues(objectStoreMetadata->mIndexes,
-                                         aMaybeUniqueIndexTable);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-// static
-nsresult
-DatabaseOperationBase::IndexDataValuesFromUpdateInfos(
-                                  const nsTArray<IndexUpdateInfo>& aUpdateInfos,
-                                  const UniqueIndexTable& aUniqueIndexTable,
-                                  FallibleTArray<IndexDataValue>& aIndexValues)
-{
-  MOZ_ASSERT(aIndexValues.IsEmpty());
-  MOZ_ASSERT_IF(!aUpdateInfos.IsEmpty(), aUniqueIndexTable.Count());
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseOperationBase::IndexDataValuesFromUpdateInfos",
-                 js::ProfileEntry::Category::STORAGE);
-
-  const uint32_t count = aUpdateInfos.Length();
-
-  if (!count) {
-    return NS_OK;
-  }
-
-  if (NS_WARN_IF(!aIndexValues.SetCapacity(count))) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  for (uint32_t idxIndex = 0; idxIndex < count; idxIndex++) {
-    const IndexUpdateInfo& updateInfo = aUpdateInfos[idxIndex];
-    const int64_t& indexId = updateInfo.indexId();
-    const Key& key = updateInfo.value();
-
-    bool unique;
-    MOZ_ALWAYS_TRUE(aUniqueIndexTable.Get(indexId, &unique));
-
-    MOZ_ALWAYS_TRUE(
-      aIndexValues.InsertElementSorted(IndexDataValue(indexId, unique, key)));
-  }
-
-  return NS_OK;
-}
-
-// static
-nsresult
-DatabaseOperationBase::InsertIndexTableRows(
-                             DatabaseConnection* aConnection,
-                             const int64_t aObjectStoreId,
-                             const Key& aObjectStoreKey,
-                             const FallibleTArray<IndexDataValue>& aIndexValues)
-{
-  MOZ_ASSERT(aConnection);
-  aConnection->AssertIsOnConnectionThread();
+  aTransaction->AssertIsOnTransactionThread();
   MOZ_ASSERT(!aObjectStoreKey.IsUnset());
 
   PROFILER_LABEL("IndexedDB",
-                 "DatabaseOperationBase::InsertIndexTableRows",
+                 "DatabaseOperationBase::UpdateIndexes",
                  js::ProfileEntry::Category::STORAGE);
 
-  const uint32_t count = aIndexValues.Length();
-  if (!count) {
-    return NS_OK;
-  }
-
-  NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
-  NS_NAMED_LITERAL_CSTRING(objectDataKeyString, "object_data_key");
-  NS_NAMED_LITERAL_CSTRING(indexIdString, "index_id");
-  NS_NAMED_LITERAL_CSTRING(valueString, "value");
-
-  DatabaseConnection::CachedStatement insertUniqueStmt;
-  DatabaseConnection::CachedStatement insertStmt;
-
   nsresult rv;
-
-  for (uint32_t index = 0; index < count; index++) {
-    const IndexDataValue& info = aIndexValues[index];
-
-    DatabaseConnection::CachedStatement& stmt =
-      info.mUnique ? insertUniqueStmt : insertStmt;
+  NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id");
+
+  if (aOverwrite) {
+    TransactionBase::CachedStatement stmt;
+    rv = aTransaction->GetCachedStatement(
+        "DELETE FROM unique_index_data "
+        "WHERE object_data_id = :object_data_id; "
+        "DELETE FROM index_data "
+        "WHERE object_data_id = :object_data_id",
+        &stmt);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = stmt->BindInt64ByName(objectDataId, aObjectDataId);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = stmt->Execute();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  // Avoid lots of hash lookups for objectStores with lots of indexes by lazily
+  // holding the necessary statements on the stack outside the loop.
+  TransactionBase::CachedStatement insertUniqueStmt;
+  TransactionBase::CachedStatement insertStmt;
+
+  for (uint32_t idxCount = aUpdateInfoArray.Length(), idxIndex = 0;
+       idxIndex < idxCount;
+       idxIndex++) {
+    const IndexUpdateInfo& updateInfo = aUpdateInfoArray[idxIndex];
+
+    bool unique;
+    MOZ_ALWAYS_TRUE(aUniqueIndexTable.Get(updateInfo.indexId(), &unique));
+
+    TransactionBase::CachedStatement& stmt =
+      unique ? insertUniqueStmt : insertStmt;
 
     if (stmt) {
       stmt.Reset();
-    } else if (info.mUnique) {
-      rv = aConnection->GetCachedStatement(
+    } else if (unique) {
+      rv = aTransaction->GetCachedStatement(
         "INSERT INTO unique_index_data "
-          "(index_id, value, object_store_id, object_data_key) "
-          "VALUES (:index_id, :value, :object_store_id, :object_data_key);",
+          "(index_id, object_data_id, object_data_key, value) "
+        "VALUES (:index_id, :object_data_id, :object_data_key, :value)",
         &stmt);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else {
-      rv = aConnection->GetCachedStatement(
-        "INSERT OR IGNORE INTO index_data "
-          "(index_id, value, object_data_key, object_store_id) "
-          "VALUES (:index_id, :value, :object_data_key, :object_store_id);",
+      rv = aTransaction->GetCachedStatement(
+        "INSERT OR IGNORE INTO index_data ("
+          "index_id, object_data_id, object_data_key, value) "
+        "VALUES (:index_id, :object_data_id, :object_data_key, :value)",
         &stmt);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
-    rv = stmt->BindInt64ByName(indexIdString, info.mIndexId);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = info.mKey.BindToStatement(stmt, valueString);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = stmt->BindInt64ByName(objectStoreIdString, aObjectStoreId);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = aObjectStoreKey.BindToStatement(stmt, objectDataKeyString);
+    rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
+                               updateInfo.indexId());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = stmt->BindInt64ByName(objectDataId, aObjectDataId);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = aObjectStoreKey.BindToStatement(stmt,
+                                         NS_LITERAL_CSTRING("object_data_key"));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = updateInfo.value().BindToStatement(stmt, NS_LITERAL_CSTRING("value"));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     rv = stmt->Execute();
-    if (rv == NS_ERROR_STORAGE_CONSTRAINT && info.mUnique) {
+    if (rv == NS_ERROR_STORAGE_CONSTRAINT && unique) {
       // If we're inserting multiple entries for the same unique index, then
       // we might have failed to insert due to colliding with another entry for
       // the same index in which case we should ignore it.
-      for (int32_t index2 = int32_t(index) - 1;
-           index2 >= 0 && aIndexValues[index2].mIndexId == info.mIndexId;
-           --index2) {
-        if (info.mKey == aIndexValues[index2].mKey) {
+      for (int32_t index = int32_t(idxIndex) - 1;
+           index >= 0 &&
+           aUpdateInfoArray[index].indexId() == updateInfo.indexId();
+           --index) {
+        if (updateInfo.value() == aUpdateInfoArray[index].value()) {
           // We found a key with the same value for the same index. So we
           // must have had a collision with a value we just inserted.
           rv = NS_OK;
           break;
         }
       }
     }
 
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
-// static
-nsresult
-DatabaseOperationBase::DeleteIndexDataTableRows(
-                             DatabaseConnection* aConnection,
-                             const Key& aObjectStoreKey,
-                             const FallibleTArray<IndexDataValue>& aIndexValues)
-{
-  MOZ_ASSERT(aConnection);
-  aConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(!aObjectStoreKey.IsUnset());
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseOperationBase::DeleteIndexDataTableRows",
-                 js::ProfileEntry::Category::STORAGE);
-
-  const uint32_t count = aIndexValues.Length();
-  if (!count) {
-    return NS_OK;
-  }
-
-  NS_NAMED_LITERAL_CSTRING(indexIdString, "index_id");
-  NS_NAMED_LITERAL_CSTRING(valueString, "value");
-  NS_NAMED_LITERAL_CSTRING(objectDataKeyString, "object_data_key");
-
-  DatabaseConnection::CachedStatement deleteUniqueStmt;
-  DatabaseConnection::CachedStatement deleteStmt;
-
-  nsresult rv;
-
-  for (uint32_t index = 0; index < count; index++) {
-    const IndexDataValue& indexValue = aIndexValues[index];
-
-    DatabaseConnection::CachedStatement& stmt =
-      indexValue.mUnique ? deleteUniqueStmt : deleteStmt;
-
-    if (stmt) {
-      stmt.Reset();
-    } else if (indexValue.mUnique) {
-      rv = aConnection->GetCachedStatement(
-        "DELETE FROM unique_index_data "
-          "WHERE index_id = :index_id "
-          "AND value = :value;",
-        &stmt);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    } else {
-      rv = aConnection->GetCachedStatement(
-        "DELETE FROM index_data "
-          "WHERE index_id = :index_id "
-          "AND value = :value "
-          "AND object_data_key = :object_data_key;",
-        &stmt);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
-    rv = stmt->BindInt64ByName(indexIdString, indexValue.mIndexId);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = indexValue.mKey.BindToStatement(stmt, valueString);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    if (!indexValue.mUnique) {
-      rv = aObjectStoreKey.BindToStatement(stmt, objectDataKeyString);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
-    rv = stmt->Execute();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  return NS_OK;
-}
-
-// static
-nsresult
-DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes(
-                                              DatabaseConnection* aConnection,
-                                              const int64_t aObjectStoreId,
-                                              const OptionalKeyRange& aKeyRange)
-{
-  MOZ_ASSERT(aConnection);
-  aConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(aObjectStoreId);
-  MOZ_ASSERT(ObjectStoreHasIndexes(aConnection, aObjectStoreId),
-             "Don't use this slow method if there are no indexes!");
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaseOperationBase::"
-                 "DeleteObjectStoreDataTableRowsWithIndexes",
-                 js::ProfileEntry::Category::STORAGE);
-
-  const bool singleRowOnly =
-    aKeyRange.type() == OptionalKeyRange::TSerializedKeyRange &&
-    aKeyRange.get_SerializedKeyRange().isOnly();
-
-  NS_NAMED_LITERAL_CSTRING(objectStoreIdString, "object_store_id");
-  NS_NAMED_LITERAL_CSTRING(keyString, "key");
-
-  nsresult rv;
-  Key objectStoreKey;
-  DatabaseConnection::CachedStatement selectStmt;
-
-  if (singleRowOnly) {
-    rv = aConnection->GetCachedStatement(
-      "SELECT index_data_values "
-        "FROM object_data "
-        "WHERE object_store_id = :object_store_id "
-        "AND key = :key;",
-      &selectStmt);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    objectStoreKey = aKeyRange.get_SerializedKeyRange().lower();
-
-    rv = objectStoreKey.BindToStatement(selectStmt, keyString);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  } else {
-    nsAutoCString keyRangeClause;
-    if (aKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) {
-      GetBindingClauseForKeyRange(aKeyRange.get_SerializedKeyRange(),
-                                  keyString,
-                                  keyRangeClause);
-    }
-
-    rv = aConnection->GetCachedStatement(
-      NS_LITERAL_CSTRING("SELECT index_data_values, key "
-                           "FROM object_data "
-                           "WHERE object_store_id = :") + objectStoreIdString +
-      keyRangeClause +
-      NS_LITERAL_CSTRING(";"),
-      &selectStmt);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    if (aKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) {
-      rv = BindKeyRangeToStatement(aKeyRange, selectStmt);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-  }
-
-  rv = selectStmt->BindInt64ByName(objectStoreIdString, aObjectStoreId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  DatabaseConnection::CachedStatement deleteStmt;
-  AutoFallibleTArray<IndexDataValue, 32> indexValues;
-
-  DebugOnly<uint32_t> resultCountDEBUG = 0;
-
-  bool hasResult;
-  while (NS_SUCCEEDED(rv = selectStmt->ExecuteStep(&hasResult)) && hasResult) {
-    if (!singleRowOnly) {
-      rv = objectStoreKey.SetFromStatement(selectStmt, 1);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      indexValues.ClearAndRetainStorage();
-    }
-
-    rv = ReadCompressedIndexDataValues(selectStmt, 0, indexValues);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = DeleteIndexDataTableRows(aConnection, objectStoreKey, indexValues);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    if (deleteStmt) {
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(deleteStmt->Reset()));
-    } else {
-      rv = aConnection->GetCachedStatement(
-        "DELETE FROM object_data "
-          "WHERE object_store_id = :object_store_id "
-          "AND key = :key;",
-        &deleteStmt);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
-    rv = deleteStmt->BindInt64ByName(objectStoreIdString, aObjectStoreId);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = objectStoreKey.BindToStatement(deleteStmt, keyString);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = deleteStmt->Execute();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    resultCountDEBUG++;
-  }
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  MOZ_ASSERT_IF(singleRowOnly, resultCountDEBUG <= 1);
-
-  return NS_OK;
-}
-
-// static
-nsresult
-DatabaseOperationBase::UpdateIndexValues(
-                             DatabaseConnection* aConnection,
-                             const int64_t aObjectStoreId,
-                             const Key& aObjectStoreKey,
-                             const FallibleTArray<IndexDataValue>& aIndexValues)
-{
-  MOZ_ASSERT(aConnection);
-  aConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(!aObjectStoreKey.IsUnset());
-
-  PROFILER_LABEL("IndexedDB",
-                 "DatabaunseOperationBase::UpdateIndexValues",
-                 js::ProfileEntry::Category::STORAGE);
-
-  UniqueMozFreePtr<uint8_t> indexDataValues;
-  uint32_t indexDataValuesLength;
-  nsresult rv = MakeCompressedIndexDataValues(aIndexValues,
-                                              indexDataValues,
-                                              &indexDataValuesLength);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  MOZ_ASSERT(!indexDataValuesLength == !(indexDataValues.get()));
-
-  DatabaseConnection::CachedStatement updateStmt;
-  rv = aConnection->GetCachedStatement(
-    "UPDATE object_data "
-      "SET index_data_values = :index_data_values "
-      "WHERE object_store_id = :object_store_id "
-      "AND key = :key;",
-    &updateStmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  NS_NAMED_LITERAL_CSTRING(indexDataValuesString, "index_data_values");
-
-  if (indexDataValues) {
-    rv = updateStmt->BindAdoptedBlobByName(indexDataValuesString,
-                                           indexDataValues.get(),
-                                           indexDataValuesLength);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    indexDataValues.release();
-  } else {
-    rv = updateStmt->BindNullByName(indexDataValuesString);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
-                                   aObjectStoreId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aObjectStoreKey.BindToStatement(updateStmt, NS_LITERAL_CSTRING("key"));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = updateStmt->Execute();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-#ifdef DEBUG
-
-// static
-bool
-DatabaseOperationBase::ObjectStoreHasIndexes(DatabaseConnection* aConnection,
-                                             const int64_t aObjectStoreId)
-{
-  MOZ_ASSERT(aConnection);
-  aConnection->AssertIsOnConnectionThread();
-  MOZ_ASSERT(aObjectStoreId);
-
-  DatabaseConnection::CachedStatement stmt;
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-    aConnection->GetCachedStatement(
-      "SELECT id "
-        "FROM object_store_index "
-        "WHERE object_store_id = :object_store_id;",
-      &stmt)));
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-    stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_store_id"),
-                          aObjectStoreId)));
-
-  bool hasResult;
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)));
-
-  return hasResult;
-}
-
-#endif // DEBUG
-
 NS_IMPL_ISUPPORTS_INHERITED(DatabaseOperationBase,
                             nsRunnable,
                             mozIStorageProgressHandler)
 
 NS_IMETHODIMP
 DatabaseOperationBase::OnProgress(mozIStorageConnection* aConnection,
                                   bool* _retval)
 {
@@ -16090,51 +10437,50 @@ DatabaseOperationBase::OnProgress(mozISt
   MOZ_ASSERT(_retval);
 
   // This is intentionally racy.
   *_retval = !OperationMayProceed();
   return NS_OK;
 }
 
 DatabaseOperationBase::
-AutoSetProgressHandler::AutoSetProgressHandler()
-  : mConnection(nullptr)
-  , mDEBUGDatabaseOp(nullptr)
-{
-  MOZ_ASSERT(!IsOnBackgroundThread());
-}
-
-DatabaseOperationBase::
 AutoSetProgressHandler::~AutoSetProgressHandler()
 {
   MOZ_ASSERT(!IsOnBackgroundThread());
+  MOZ_ASSERT_IF(mConnection, mDEBUGDatabaseOp);
 
   if (mConnection) {
     nsCOMPtr<mozIStorageProgressHandler> oldHandler;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler))));
-    MOZ_ASSERT(oldHandler == mDEBUGDatabaseOp);
+    nsresult rv =
+      mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler));
+    if (NS_SUCCEEDED(rv)) {
+      MOZ_ASSERT(SameCOMIdentity(oldHandler,
+                                 static_cast<nsIRunnable*>(mDEBUGDatabaseOp)));
+    } else {
+      NS_WARNING("Failed to remove progress handler!");
+    }
   }
 }
 
 nsresult
 DatabaseOperationBase::
-AutoSetProgressHandler::Register(mozIStorageConnection* aConnection,
-                                 DatabaseOperationBase* aDatabaseOp)
+AutoSetProgressHandler::Register(
+                             DatabaseOperationBase* aDatabaseOp,
+                             const nsCOMPtr<mozIStorageConnection>& aConnection)
 {
   MOZ_ASSERT(!IsOnBackgroundThread());
+  MOZ_ASSERT(aDatabaseOp);
   MOZ_ASSERT(aConnection);
-  MOZ_ASSERT(aDatabaseOp);
   MOZ_ASSERT(!mConnection);
+  MOZ_ASSERT(!mDEBUGDatabaseOp);
 
   nsCOMPtr<mozIStorageProgressHandler> oldProgressHandler;
 
   nsresult rv =
-    aConnection->SetProgressHandler(kStorageProgressGranularity,
-                                    aDatabaseOp,
+    aConnection->SetProgressHandler(kStorageProgressGranularity, aDatabaseOp,
                                     getter_AddRefs(oldProgressHandler));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(!oldProgressHandler);
 
   mConnection = aConnection;
@@ -16331,25 +10677,25 @@ FactoryOp::WaitForTransactions()
   MOZ_ASSERT(mState == State_BeginVersionChange ||
              mState == State_WaitingForOtherDatabasesToClose);
   MOZ_ASSERT(!mDatabaseId.IsEmpty());
   MOZ_ASSERT(!IsActorDestroyed());
 
   nsTArray<nsCString> databaseIds;
   databaseIds.AppendElement(mDatabaseId);
 
-  nsRefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
-  MOZ_ASSERT(connectionPool);
+  nsRefPtr<TransactionThreadPool> threadPool = gTransactionThreadPool.get();
+  MOZ_ASSERT(threadPool);
 
   // WaitForDatabasesToComplete() will run this op immediately if there are no
   // transactions blocking it, so be sure to set the next state here before
   // calling it.
   mState = State_WaitingForTransactionsToComplete;
 
-  connectionPool->WaitForDatabasesToComplete(Move(databaseIds), this);
+  threadPool->WaitForDatabasesToComplete(databaseIds, this);
   return;
 }
 
 void
 FactoryOp::FinishSendResults()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State_SendingResults);
@@ -16494,17 +10840,17 @@ FactoryOp::CheckPermission(ContentParent
   nsCString group;
   nsCString origin;
   bool isApp;
   rv = QuotaManager::GetInfoFromPrincipal(principal, &group, &origin, &isApp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-#ifdef IDB_MOBILE
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
   if (persistenceType == PERSISTENCE_TYPE_PERSISTENT &&
       !QuotaManager::IsOriginWhitelistedForPersistentStorage(origin) &&
       !isApp) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 #endif
 
   PermissionRequestBase::PermissionValue permission;
@@ -16991,29 +11337,29 @@ OpenDatabaseOp::DoDatabaseWork()
   const NS_ConvertASCIItoUTF16 filesSuffix(kFileManagerDirectoryNameSuffix);
 
   rv = fmDirectory->Append(filename + filesSuffix);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<mozIStorageConnection> connection;
-  rv = CreateStorageConnection(dbFile,
-                               fmDirectory,
-                               databaseName,
-                               persistenceType,
-                               mGroup,
-                               mOrigin,
-                               getter_AddRefs(connection));
+  rv = CreateDatabaseConnection(dbFile,
+                                fmDirectory,
+                                databaseName,
+                                persistenceType,
+                                mGroup,
+                                mOrigin,
+                                getter_AddRefs(connection));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   AutoSetProgressHandler asph;
-  rv = asph.Register(connection, this);
+  rv = asph.Register(this, connection);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = LoadDatabaseInformation(connection);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -17499,25 +11845,25 @@ OpenDatabaseOp::DispatchToWorkThread()
 
   const int64_t loggingSerialNumber =
     mVersionChangeTransaction->LoggingSerialNumber();
   const nsID& backgroundChildLoggingId =
     mVersionChangeTransaction->GetLoggingInfo()->Id();
 
   nsRefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this);
 
-  uint64_t transactionId =
-    gConnectionPool->Start(backgroundChildLoggingId,
-                           mVersionChangeTransaction->DatabaseId(),
-                           loggingSerialNumber,
-                           objectStoreNames,
-                           /* aIsWriteTransaction */ true,
-                           versionChangeOp);
-
-  mVersionChangeTransaction->SetActive(transactionId);
+  gTransactionThreadPool->Start(mVersionChangeTransaction->TransactionId(),
+                                mVersionChangeTransaction->DatabaseId(),
+                                objectStoreNames,
+                                mVersionChangeTransaction->GetMode(),
+                                backgroundChildLoggingId,
+                                loggingSerialNumber,
+                                versionChangeOp);
+
+  mVersionChangeTransaction->SetActive();
 
   mVersionChangeTransaction->NoteActiveRequest();
 
   if (NS_WARN_IF(!mDatabase->RegisterTransaction(mVersionChangeTransaction))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
@@ -17627,48 +11973,27 @@ OpenDatabaseOp::SendResults()
     }
 
     unused <<
       PBackgroundIDBFactoryRequestParent::Send__delete__(this, response);
   }
 
   if (NS_FAILED(mResultCode) && mOfflineStorage) {
     mOfflineStorage->CloseOnOwningThread();
-
-    if (gConnectionPool) {
-      nsTArray<nsCString> ids(1);
-      ids.AppendElement(mDatabaseId);
-
-      nsCOMPtr<nsIRunnable> callback =
-        NS_NewRunnableMethod(this, &OpenDatabaseOp::ConnectionClosedCallback);
-
-      gConnectionPool->WaitForDatabasesToComplete(Move(ids), callback);
-    } else {
-      ConnectionClosedCallback();
-    }
+    DatabaseOfflineStorage::UnregisterOnOwningThread(mOfflineStorage.forget());
   }
 
   // Make sure to release the database on this thread.
   nsRefPtr<Database> database;
   mDatabase.swap(database);
 
   FinishSendResults();
 }
 
 void
-OpenDatabaseOp::ConnectionClosedCallback()
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(NS_FAILED(mResultCode));
-  MOZ_ASSERT(mOfflineStorage);
-
-  DatabaseOfflineStorage::UnregisterOnOwningThread(mOfflineStorage.forget());
-}
-
-void
 OpenDatabaseOp::EnsureDatabaseActor()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State_BeginVersionChange ||
              mState == State_DatabaseWorkVersionChange ||
              mState == State_SendingResults);
   MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
   MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
@@ -17957,60 +12282,62 @@ OpenDatabaseOp::AssertMetadataConsistenc
 
   Helper::AssertConsistent(thisDB->mObjectStores, otherDB->mObjectStores);
 }
 
 #endif // DEBUG
 
 nsresult
 OpenDatabaseOp::
-VersionChangeOp::DoDatabaseWork(DatabaseConnection* aConnection)
-{
-  MOZ_ASSERT(aConnection);
-  aConnection->AssertIsOnConnectionThread();
+VersionChangeOp::DoDatabaseWork(TransactionBase* aTransaction)
+{
+  MOZ_ASSERT(aTransaction);
+  aTransaction->AssertIsOnTransactionThread();
   MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange);
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
       !OperationMayProceed()) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   PROFILER_LABEL("IndexedDB",
                  "OpenDatabaseOp::VersionChangeOp::DoDatabaseWork",
                  js::ProfileEntry::Category::STORAGE);
 
-  IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
-                 "Beginning database work",
-               "IndexedDB %s: P T[%lld]: DB Start",
-               IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
-               mLoggingSerialNumber);
-
-  nsresult rv = aConnection->BeginWriteTransaction();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  NS_NAMED_LITERAL_CSTRING(version, "version");
-
-  DatabaseConnection::CachedStatement updateStmt;
-  rv = aConnection->GetCachedStatement(
+  mozIStorageConnection* connection = aTransaction->Connection();
+  MOZ_ASSERT(connection);
+
+  TransactionBase::AutoSavepoint autoSave;
+  nsresult rv = autoSave.Start(aTransaction);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<mozIStorageStatement> stmt;
+  rv = connection->CreateStatement(
     NS_LITERAL_CSTRING("UPDATE database "
-                       "SET version = :") + version,
-    &updateStmt);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = updateStmt->BindInt64ByName(version, int64_t(mRequestedVersion));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = updateStmt->Execute();
+                       "SET version = :version"),
+    getter_AddRefs(stmt));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("version"),
+                             int64_t(mRequestedVersion));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stmt->Execute();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = autoSave.Commit();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -18079,37 +12406,46 @@ DeleteDatabaseOp::LoadPreviousVersion(ns
 
   nsCOMPtr<mozIStorageService> ss =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   nsCOMPtr<mozIStorageConnection> connection;
-  rv = OpenDatabaseAndHandleBusy(ss, aDatabaseFile, getter_AddRefs(connection));
+  rv = ss->OpenDatabase(aDatabaseFile, getter_AddRefs(connection));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
 #ifdef DEBUG
   {
     nsCOMPtr<mozIStorageStatement> stmt;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      connection->CreateStatement(NS_LITERAL_CSTRING(
-        "SELECT name "
-          "FROM database"
-        ), getter_AddRefs(stmt))));
-
-    bool hasResult;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)));
-
-    nsString databaseName;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(stmt->GetString(0, databaseName)));
-
-    MOZ_ASSERT(mCommonParams.metadata().name() == databaseName);
+    rv = connection->CreateStatement(NS_LITERAL_CSTRING(
+      "SELECT name "
+      "FROM database"
+    ), getter_AddRefs(stmt));
+    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "CreateStatement failed!");
+
+    if (NS_SUCCEEDED(rv)) {
+      bool hasResult;
+      rv = stmt->ExecuteStep(&hasResult);
+      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "ExecuteStep failed!");
+
+      if (NS_SUCCEEDED(rv)) {
+        nsString databaseName;
+        rv = stmt->GetString(0, databaseName);
+        NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "GetString failed!");
+
+        if (NS_SUCCEEDED(rv)) {
+          NS_WARN_IF_FALSE(mCommonParams.metadata().name() == databaseName,
+                           "Database names don't match!");
+        }
+      }
+    }
   }
 #endif
 
   nsCOMPtr<mozIStorageStatement> stmt;
   rv = connection->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT version "
     "FROM database"
   ), getter_AddRefs(stmt));
@@ -18389,84 +12725,16 @@ VersionChangeOp::RunOnMainThread()
   if (NS_WARN_IF(NS_FAILED(rv))) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   return NS_OK;
 }
 
-
-nsresult
-DeleteDatabaseOp::
-VersionChangeOp::DeleteFile(nsIFile* aDirectory,
-                            const nsAString& aFilename,
-                            QuotaManager* aQuotaManager)
-{
-  AssertIsOnIOThread();
-  MOZ_ASSERT(aDirectory);
-  MOZ_ASSERT(!aFilename.IsEmpty());
-  MOZ_ASSERT_IF(aQuotaManager, mDeleteDatabaseOp->mEnforcingQuota);
-
-  MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DeleteDatabaseOp::VersionChangeOp::DeleteFile",
-                 js::ProfileEntry::Category::STORAGE);
-
-  nsCOMPtr<nsIFile> file;
-  nsresult rv = aDirectory->Clone(getter_AddRefs(file));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = file->Append(aFilename);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  int64_t fileSize;
-
-  if (aQuotaManager) {
-    rv = file->GetFileSize(&fileSize);
-    if (rv == NS_ERROR_FILE_NOT_FOUND ||
-        rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
-      return NS_OK;
-    }
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    MOZ_ASSERT(fileSize >= 0);
-  }
-
-  rv = file->Remove(false);
-  if (rv == NS_ERROR_FILE_NOT_FOUND ||
-      rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
-    return NS_OK;
-  }
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (aQuotaManager && fileSize > 0) {
-    const PersistenceType& persistenceType =
-      mDeleteDatabaseOp->mCommonParams.metadata().persistenceType();
-
-    aQuotaManager->DecreaseUsageForOrigin(persistenceType,
-                                          mDeleteDatabaseOp->mGroup,
-                                          mDeleteDatabaseOp->mOrigin,
-                                          fileSize);
-  }
-
-  return NS_OK;
-}
-
 nsresult
 DeleteDatabaseOp::
 VersionChangeOp::RunOnIOThread()
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange);
 
   PROFILER_LABEL("IndexedDB",
@@ -18474,94 +12742,110 @@ VersionChangeOp::RunOnIOThread()
                  js::ProfileEntry::Category::STORAGE);
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
       !OperationMayProceed()) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
-  const PersistenceType& persistenceType =
-    mDeleteDatabaseOp->mCommonParams.metadata().persistenceType();
-
-  QuotaManager* quotaManager =
-    mDeleteDatabaseOp->mEnforcingQuota ?
-    QuotaManager::Get() :
-    nullptr;
-
-  MOZ_ASSERT_IF(mDeleteDatabaseOp->mEnforcingQuota, quotaManager);
-
   nsCOMPtr<nsIFile> directory =
     GetFileForPath(mDeleteDatabaseOp->mDatabaseDirectoryPath);
   if (NS_WARN_IF(!directory)) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
-  // The database file counts towards quota.
-  nsAutoString filename =
-    mDeleteDatabaseOp->mDatabaseFilenameBase + NS_LITERAL_STRING(".sqlite");
-
-  nsresult rv = DeleteFile(directory, filename, quotaManager);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // .sqlite-journal files don't count towards quota.
-  const NS_ConvertASCIItoUTF16 journalSuffix(
-    kSQLiteJournalSuffix,
-    LiteralStringLength(kSQLiteJournalSuffix));
-
-  filename = mDeleteDatabaseOp->mDatabaseFilenameBase + journalSuffix;
-
-  rv = DeleteFile(directory, filename, /* doesn't count */ nullptr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // .sqlite-shm files don't count towards quota.
-  const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
-                                         LiteralStringLength(kSQLiteSHMSuffix));
-
-  filename = mDeleteDatabaseOp->mDatabaseFilenameBase + shmSuffix;
-
-  rv = DeleteFile(directory, filename, /* doesn't count */ nullptr);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // .sqlite-wal files do count towards quota.
-  const NS_ConvertASCIItoUTF16 walSuffix(kSQLiteWALSuffix,
-                                         LiteralStringLength(kSQLiteWALSuffix));
-
-  filename = mDeleteDatabaseOp->mDatabaseFilenameBase + walSuffix;
-
-  rv = DeleteFile(directory, filename, quotaManager);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsCOMPtr<nsIFile> fmDirectory;
-  rv = directory->Clone(getter_AddRefs(fmDirectory));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // The files directory counts towards quota.
-  const NS_ConvertASCIItoUTF16 filesSuffix(
-    kFileManagerDirectoryNameSuffix,
-    LiteralStringLength(kFileManagerDirectoryNameSuffix));
-
-  rv = fmDirectory->Append(mDeleteDatabaseOp->mDatabaseFilenameBase +
-                           filesSuffix);
+  nsCOMPtr<nsIFile> dbFile;
+  nsresult rv = directory->Clone(getter_AddRefs(dbFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = dbFile->Append(mDeleteDatabaseOp->mDatabaseFilenameBase +
+                      NS_LITERAL_STRING(".sqlite"));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   bool exists;
+  rv = dbFile->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  const nsString& databaseName =
+    mDeleteDatabaseOp->mCommonParams.metadata().name();
+  PersistenceType persistenceType =
+    mDeleteDatabaseOp->mCommonParams.metadata().persistenceType();
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  if (exists) {
+    int64_t fileSize;
+
+    if (mDeleteDatabaseOp->mEnforcingQuota) {
+      rv = dbFile->GetFileSize(&fileSize);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+
+    rv = dbFile->Remove(false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (mDeleteDatabaseOp->mEnforcingQuota) {
+      quotaManager->DecreaseUsageForOrigin(persistenceType,
+                                           mDeleteDatabaseOp->mGroup,
+                                           mDeleteDatabaseOp->mOrigin,
+                                           fileSize);
+    }
+  }
+
+  nsCOMPtr<nsIFile> dbJournalFile;
+  rv = directory->Clone(getter_AddRefs(dbJournalFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = dbJournalFile->Append(mDeleteDatabaseOp->mDatabaseFilenameBase +
+                             NS_LITERAL_STRING(".sqlite-journal"));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = dbJournalFile->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (exists) {
+    rv = dbJournalFile->Remove(false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  nsCOMPtr<nsIFile> fmDirectory;
+  rv = directory->Clone(getter_AddRefs(fmDirectory));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  const NS_ConvertASCIItoUTF16 filesSuffix(kFileManagerDirectoryNameSuffix);
+
+  rv = fmDirectory->Append(mDeleteDatabaseOp->mDatabaseFilenameBase +
+                           filesSuffix);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   rv = fmDirectory->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (exists) {
     bool isDirectory;
     rv = fmDirectory->IsDirectory(&isDirectory);
@@ -18580,45 +12864,30 @@ VersionChangeOp::RunOnIOThread()
       rv = FileManager::GetUsage(fmDirectory, &usage);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
     rv = fmDirectory->Remove(true);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      // We may have deleted some files, check if we can and update quota
-      // information before returning the error.
-      if (mDeleteDatabaseOp->mEnforcingQuota) {
-        uint64_t newUsage;
-        if (NS_SUCCEEDED(FileManager::GetUsage(fmDirectory, &newUsage))) {
-          MOZ_ASSERT(newUsage <= usage);
-          usage = usage - newUsage;
-        }
-      }
-    }
-
-    if (mDeleteDatabaseOp->mEnforcingQuota && usage) {
+      return rv;
+    }
+
+    if (mDeleteDatabaseOp->mEnforcingQuota) {
       quotaManager->DecreaseUsageForOrigin(persistenceType,
                                            mDeleteDatabaseOp->mGroup,
                                            mDeleteDatabaseOp->mOrigin,
                                            usage);
     }
-
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
   }
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   MOZ_ASSERT(mgr);
 
-  const nsString& databaseName =
-    mDeleteDatabaseOp->mCommonParams.metadata().name();
-
   mgr->InvalidateFileManager(persistenceType,
                              mDeleteDatabaseOp->mOrigin,
                              databaseName);
 
   rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -18734,111 +13003,103 @@ TransactionDatabaseOperationBase::Transa
                                                   uint64_t aLoggingSerialNumber)
   : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
                           aLoggingSerialNumber)
   , mTransaction(aTransaction)
   , mTransactionLoggingSerialNumber(aTransaction->LoggingSerialNumber())
   , mTransactionIsAborted(aTransaction->IsAborted())
 {
   MOZ_ASSERT(aTransaction);
+  MOZ_ASSERT(LoggingSerialNumber());
 }
 
 TransactionDatabaseOperationBase::~TransactionDatabaseOperationBase()
 {
   MOZ_ASSERT(!mTransaction,
              "TransactionDatabaseOperationBase::Cleanup() was not called by a "